X-Git-Url: http://git.joshuawise.com/netwatch.git/blobdiff_plain/18a0d8bbdf5091d39ce852c03a2a35000e1e0760..db9fad13f192963786c7ac90d305467ac00bd145:/lib/state.c diff --git a/lib/state.c b/lib/state.c new file mode 100644 index 0000000..368029b --- /dev/null +++ b/lib/state.c @@ -0,0 +1,239 @@ +/* state.c + * SMM saved state manipulation functions. + * NetWatch system management mode administration console + * + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "state.h" +#include + +/* Size flags. */ +#define SZ_BYTE 0x10000000 +#define SZ_WORD 0x20000000 +#define SZ_DWORD 0x40000000 +#define SZ_QWORD 0x80000000 +#define OFFSET_MASK 0x0FFFFFFF + +static const uint32_t offset_table_legacy[] = { + [STATE_REV] = 0xfefc | SZ_DWORD, + [STATE_REG_SMBASE] = 0xfef8 | SZ_DWORD, + [STATE_REG_IORESTART] = 0xff00 | SZ_WORD, + [STATE_REG_HALTRESTART] = 0xff02 | SZ_WORD, + + [STATE_REG_EAX] = 0xffd0 | SZ_DWORD, + [STATE_REG_EBX] = 0xffdc | SZ_DWORD, + [STATE_REG_ECX] = 0xffd4 | SZ_DWORD, + [STATE_REG_EDX] = 0xffd8 | SZ_DWORD, + [STATE_REG_ESI] = 0xffe8 | SZ_DWORD, + [STATE_REG_EDI] = 0xffec | SZ_DWORD, + [STATE_REG_ESP] = 0xffe0 | SZ_DWORD, + [STATE_REG_EBP] = 0xffe4 | SZ_DWORD, + [STATE_REG_EIP] = 0xfff0 | SZ_DWORD, + [STATE_REG_EFLAGS] = 0xfff4 | SZ_DWORD, + [STATE_REG_CR0] = 0xfffc | SZ_DWORD, + [STATE_REG_CR3] = 0xfff8 | SZ_DWORD, + + [STATE_REG_CS] = 0xffac | SZ_DWORD, + [STATE_REG_SS] = 0xffb0 | SZ_DWORD, + [STATE_REG_DS] = 0xffb4 | SZ_DWORD, + [STATE_REG_ES] = 0xffa8 | SZ_DWORD, + [STATE_REG_FS] = 0xffb8 | SZ_DWORD, + [STATE_REG_GS] = 0xffbc | SZ_DWORD +}; + +#define MAX_REG_LEGACY (sizeof(offset_table_legacy)/sizeof(uint32_t)) + +static const uint32_t offset_table_amd64[] = { + [STATE_REV] = 0xfefc | SZ_DWORD, + [STATE_REG_SMBASE] = 0xff00 | SZ_DWORD, + [STATE_REG_IORESTART] = 0xfec8 | SZ_BYTE, + [STATE_REG_HALTRESTART] = 0xfec9 | SZ_BYTE, + + [STATE_REG_EAX] = 0xfff8 | SZ_DWORD, + [STATE_REG_EBX] = 0xffe0 | SZ_DWORD, + [STATE_REG_ECX] = 0xfff0 | SZ_DWORD, + [STATE_REG_EDX] = 0xffe8 | SZ_DWORD, + [STATE_REG_ESI] = 0xffc8 | SZ_DWORD, + [STATE_REG_EDI] = 0xffc0 | SZ_DWORD, + [STATE_REG_ESP] = 0xffd8 | SZ_DWORD, + [STATE_REG_EBP] = 0xffd0 | SZ_DWORD, + [STATE_REG_EIP] = 0xff78 | SZ_DWORD, + [STATE_REG_EFLAGS] = 0xff70 | SZ_DWORD, + [STATE_REG_CR0] = 0xff58 | SZ_DWORD, + [STATE_REG_CR3] = 0xff50 | SZ_DWORD, + + [STATE_REG_CS] = 0xfe10 | SZ_DWORD, + [STATE_REG_SS] = 0xfe20 | SZ_DWORD, + [STATE_REG_DS] = 0xfe30 | SZ_DWORD, + [STATE_REG_ES] = 0xfe00 | SZ_DWORD, + [STATE_REG_FS] = 0xfe40 | SZ_DWORD, + [STATE_REG_GS] = 0xfe50 | SZ_DWORD, + + [STATE_REG_RAX] = 0xfff8 | SZ_QWORD, + [STATE_REG_RBX] = 0xffe0 | SZ_QWORD, + [STATE_REG_RCX] = 0xfff0 | SZ_QWORD, + [STATE_REG_RDX] = 0xffe8 | SZ_QWORD, + [STATE_REG_RSI] = 0xffc8 | SZ_QWORD, + [STATE_REG_RDI] = 0xffc0 | SZ_QWORD, + [STATE_REG_RSP] = 0xffd8 | SZ_QWORD, + [STATE_REG_RBP] = 0xffd0 | SZ_QWORD, + [STATE_REG_R8] = 0xffb8 | SZ_QWORD, + [STATE_REG_R9] = 0xffb0 | SZ_QWORD, + [STATE_REG_R10] = 0xffa8 | SZ_QWORD, + [STATE_REG_R11] = 0xffa0 | SZ_QWORD, + [STATE_REG_R12] = 0xff98 | SZ_QWORD, + [STATE_REG_R13] = 0xff90 | SZ_QWORD, + [STATE_REG_R14] = 0xff88 | SZ_QWORD, + [STATE_REG_R15] = 0xff80 | SZ_QWORD, + [STATE_REG_RIP] = 0xff78 | SZ_QWORD +}; + +#define MAX_REG_AMD64 (sizeof(offset_table_amd64)/sizeof(uint32_t)) + +static enum smm_type smm_type = SMM_TYPE_64; + +/* Probe CPUID to figure out what kind of processor this actually is. + * We memoize this in 'smm_type', so cpuid only needs to happen once. + */ + +static void check_smm_type (void) { + + struct cpuid_result r; + + if (smm_type != SMM_TYPE_UNKNOWN) + return; + + cpuid(0x80000000, &r); + + if (r.eax < 0x80000001) { + smm_type = SMM_TYPE_32; + return; + } + + cpuid(0x80000001, &r); + + if (r.edx & 0x20000000) { + smm_type = SMM_TYPE_64; + } else { + smm_type = SMM_TYPE_32; + } +} + +/* Get the offset of a register, by looking up in the appropriate table. + */ + +static unsigned long get_offset(enum state_reg_t reg) { + + check_smm_type(); + + if (smm_type == SMM_TYPE_32 && reg < MAX_REG_LEGACY) + return offset_table_legacy[reg]; + else if (smm_type == SMM_TYPE_64 && reg < MAX_REG_AMD64) + return offset_table_amd64[reg]; + else + return 0; +} + +/* Which variety of processor are we running on? + */ + +enum smm_type state_get_type (void) { + check_smm_type(); + return smm_type; +} + +/* Get a register. + * + * We assume that Aseg is always direct-mapped at 0xA0000. This may + * not be the case in the future, with multiple cores, but it is a + * safe assumption now. + */ + +uint64_t state_get_reg (enum state_reg_t reg) { + unsigned long offset = get_offset(reg); + void * addr; + uint64_t value; + + if (!offset) + return 0; + + addr = (void *)((offset & OFFSET_MASK) + 0xA0000); + + if (offset & SZ_BYTE) + value = *(uint8_t *)addr; + else if (offset & SZ_WORD) + value = *(uint16_t *)addr; + else if (offset & SZ_DWORD) + value = *(uint32_t *)addr; + else + value = *(uint64_t *)addr; + + return value; +} + +/* Get the size of a register, extracted from the saved state offset table. + */ + +int state_reg_size (enum state_reg_t reg) { + unsigned long offset = get_offset(reg); + + if (offset & SZ_BYTE) return 1; + else if (offset & SZ_WORD) return 2; + else if (offset & SZ_DWORD) return 4; + else if (offset & SZ_QWORD) return 8; + else return 0; +} + +/* Modify a saved register. + * + * The same caveat about aseg's location applies here as well. + */ + +int state_set_reg (enum state_reg_t reg, uint64_t value) { + unsigned long offset = get_offset(reg); + void * addr; + + if (!offset) + return -1; + + addr = (void *)((offset & OFFSET_MASK) + 0xA0000); + + if (offset & SZ_BYTE) + *(uint8_t *)addr = (uint8_t) value; + else if (offset & SZ_WORD) + *(uint16_t *)addr = (uint16_t) value; + else if (offset & SZ_DWORD) + *(uint32_t *)addr = (uint32_t) value; + else + *(uint64_t *)addr = value; + + return 0; +}