From: Joshua Wise Date: Wed, 2 Sep 2009 05:09:56 +0000 (-0400) Subject: Commit GDB patch from Jacob Potter. X-Git-Url: http://git.joshuawise.com/netwatch.git/commitdiff_plain/4e3ef36be657e274f91162edbbf13265abb23319?ds=sidebyside;hp=db9fad13f192963786c7ac90d305467ac00bd145 Commit GDB patch from Jacob Potter. --- diff --git a/gdb/proto.c b/gdb/proto.c new file mode 100644 index 0000000..f532a16 --- /dev/null +++ b/gdb/proto.c @@ -0,0 +1,362 @@ +/* proto.c + * GDB remote serial protocol implementation + * 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. + */ + +#define GDB_BUF_SIZE 1024 + +#include +#include +#include + +#include + +#include "stub.h" + +#define GDB_PORT 2159 + +enum fsm_result { NEEDMORE, OK, FAIL }; + +static struct tcp_pcb * last_conn = NULL; + +void enhexificate (void * ibuf, char * obuf, int len) { + unsigned char * cibuf = ibuf; + while (len > 0) { + btohex(obuf, *(cibuf++)); + obuf += 2; + len--; + } + + *obuf = '\0'; +} + +void rle (char * buf) { + char * inptr = buf; + char * outptr = buf; + int lastchar = buf[0]; + int repcount = 0; + + do { + if (*inptr == lastchar && repcount < 97) { + repcount += 1; + } else { + *(outptr++) = lastchar; + + if (repcount == 2) + *(outptr++) = lastchar; + + while (repcount == 6 || repcount == 7) { + *(outptr++) = lastchar; + repcount--; + } + + if (repcount > 2) { + *(outptr++) = '*'; + *(outptr++) = repcount + 29; + } + + repcount = 1; + lastchar = *inptr; + } + + inptr++; + } while (lastchar); + + *(outptr) = 0; +} + + +struct gdb_state { + char data[128]; + int writepos; + int readpos; +}; + + +static void close_conn(struct tcp_pcb *pcb, struct gdb_state *state) { + outputf("close_conn: bailing"); + set_run_mode(RM_UNENCUMBERED); + last_conn = NULL; + tcp_arg(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_recv(pcb, NULL); + mem_free(state); + tcp_close(pcb); + outputf("close_conn: done"); +} + +static int dehexbyte (char * p) { + int v0, v1; + v0 = dehexit (p[0]); + v1 = dehexit (p[1]); + if (v0 < 0 || v1 < 0) return -1; + return v0 << 4 | v1; +} + +static void finish_and_send(struct tcp_pcb *pcb, char * output_buf, int datalength) { + char * p; + unsigned char checksum = 0; + + output_buf[0] = '+'; + output_buf[1] = '$'; + + for (p = output_buf + 2; p < output_buf + datalength + 2; p++) + checksum += *(unsigned char *)p; + + output_buf[datalength + 2] = '#'; + + btohex(output_buf + datalength + 3, checksum); + tcp_write(pcb, output_buf, datalength + 5, TCP_WRITE_FLAG_COPY); +} + +void send_stop_packet() { + if (!last_conn) return; + tcp_write(last_conn, "$T05thread:01;#07", 17, 0); +} + +static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct gdb_state *state) { + char * endp; + char * p; + unsigned char checksum = 0; + int i; + uint64_t addr; + uint64_t length; + + char output_buf[256]; + + /* Make sure we have at least 4 bytes (the size of the smallest legal + packet), and that the packet does in fact start with $. */ + + if ((state->data[0] == '+') || (state->data[0] == '-')) { + outputf("GDB: chomp"); + state->readpos++; + return OK; + } + + if (state->writepos < 4) return NEEDMORE; + + if (state->data[0] != '$') return FAIL; + + /* Find the end; make sure there's room for the checksum after. */ + + endp = memchr(state->data, '#', state->writepos); + + if ((!endp) || (endp - state->data > state->writepos - 3)) + return NEEDMORE; + + /* Checksum. */ + + for (p = state->data + 1; p < endp; p++) + checksum += *(unsigned char *)p; + + if (checksum != dehexbyte(endp + 1)) + { + outputf("GDB: bad checksum: %d vs %d", checksum , dehexbyte(endp + 1)); + return FAIL; + } + + /* Null-terminate, for processing convenience */ + *endp = '\0'; + + outputf("GDB: Got \'%s\'", state->data + 1); + + /* OK, process the packet */ + + switch (state->data[1]) { + case '?': + tcp_write(pcb, "+$T05thread:01;#07", 18, 0); + break; + case 'g': + read_registers_32(output_buf + 2); + finish_and_send(pcb, output_buf, 128); + break; + + case 'm': + /* Parse the address */ + p = memchr(state->data, ',', endp - state->data); + if (!p) return FAIL; + *p = '\0'; + addr = 0; + length = 0; + + if (!dehexstring(&addr, state->data + 2, -1)) return FAIL; + if (!dehexstring(&length, p + 1, -1)) return FAIL; + + outputf("GDB: read %d from %d", (uint32_t)length, (uint32_t)addr); + + if (length > 120) length = 120; + + for (i = 0; i < length; i++) { + p = demap(addr++); + if (!p) break; + btohex(output_buf + 2 + (2*i), *(char *)p); + } + + finish_and_send(pcb, output_buf, 2*i); + break; + + case 's': + /* Step. */ + set_run_mode(RM_STEPPING); + tcp_write(pcb, "+", 1, 0); + break; + + default: + tcp_write(pcb, "+$#00", 5, 0); + break; + } + + state->readpos += (endp - state->data) + 3; + + return OK; +} + +err_t +gdb_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { + struct gdb_state *state = arg; + uint16_t copylen; + + if (err != ERR_OK) { + outputf("GDB: recv err %d", err); + /* FIXME do something better here? */ + return ERR_OK; + } + + if (p == NULL) { + outputf("GDB: Connection closed"); + close_conn(pcb, state); + return ERR_OK; + } + + if (p->tot_len > (GDB_BUF_SIZE - state->writepos)) { + /* Overflow! */ + outputf("GDB: Overflow!"); + close_conn(pcb, state); + return ERR_OK; + } + + copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0); + + outputf("GDB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen); + + state->writepos += p->tot_len; + + tcp_recved(pcb, p->tot_len); + pbuf_free(p); + + while (1) { + switch (recv_fsm(pcb, state)) { + case NEEDMORE: + outputf("GDB FSM: blocking"); + goto doneprocessing; + + case OK: + if (state->readpos == state->writepos) { + state->readpos = 0; + state->writepos = 0; + goto doneprocessing; + } else { + memmove(state->data, + state->data + state->readpos, + state->writepos - state->readpos); + state->writepos -= state->readpos; + state->readpos = 0; + } + break; + case FAIL: + /* Shit */ + outputf("GDB: Protocol error"); + close_conn(pcb, state); + return ERR_OK; + } + } + +doneprocessing: + return ERR_OK; +} + +static err_t gdb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { +/* + struct gdb_state *state = arg; + send_fsm(pcb, state); +*/ + return ERR_OK; +} + +static err_t gdb_poll(void *arg, struct tcp_pcb *pcb) { + /* Nothing? */ + return ERR_OK; +} + + +static err_t gdb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { + struct gdb_state *state; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(err); + + outputf("GDB: accept"); + + last_conn = pcb; + + set_run_mode(RM_STOPPED); + + state = (struct gdb_state *)mem_malloc(sizeof(struct gdb_state)); + + if (!state) + { + outputf("gdb_accept: out of memory\n"); + return ERR_MEM; + } + + memset(state, 0, sizeof(struct gdb_state)); + + tcp_arg(pcb, state); + tcp_recv(pcb, gdb_recv); + tcp_sent(pcb, gdb_sent); + tcp_poll(pcb, gdb_poll, 1); +/* + tcp_err(pcb, gdb_err); +*/ + return ERR_OK; +} + + +void gdb_init() { + struct tcp_pcb *pcb; + pcb = tcp_new(); + tcp_bind(pcb, IP_ADDR_ANY, GDB_PORT); + pcb = tcp_listen(pcb); + tcp_accept(pcb, gdb_accept); +} + +PROTOCOL(gdb_init); diff --git a/gdb/proto.h b/gdb/proto.h new file mode 100644 index 0000000..7fea289 --- /dev/null +++ b/gdb/proto.h @@ -0,0 +1,41 @@ +/* gdb.h + * GDB server + * 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. + */ + +#ifndef _GDB_PROTO_H +#define _GDB_PROTO_H + +void enhexificate (void * ibuf, char * obuf, int len); +void gdb_init(void); + +#endif /* _GDB_PROTO_H */ diff --git a/gdb/stub.c b/gdb/stub.c new file mode 100644 index 0000000..0c49756 --- /dev/null +++ b/gdb/stub.c @@ -0,0 +1,285 @@ +/* stub.c + * GDB stub system manipultion code. + * 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 +#include +#include +#include +#include +#include +#include +#include "proto.h" +#include "stub.h" + +void send_stop_packet(); + +uint16_t saved_db_entry; +uint64_t saved_db_entry_location; +enum operating_mode step_start_mode; +uint64_t saved_cs_base; +uint64_t saved_eflags; +uint64_t saved_cs_limit; +uint64_t saved_cs_attrib; +uint64_t saved_ss_base; +uint64_t saved_ss_limit; +uint64_t saved_ss_attrib; + +#define P64(x) (uint32_t)((x)>>32), (uint32_t)(x) + +static const enum state_reg_t regs32 [] = { + STATE_REG_EAX, STATE_REG_ECX, STATE_REG_EDX, STATE_REG_EBX, + STATE_REG_ESP, STATE_REG_EBP, STATE_REG_ESI, STATE_REG_EDI, + STATE_REG_EIP, STATE_REG_EFLAGS, STATE_REG_CS, STATE_REG_SS, + STATE_REG_DS, STATE_REG_ES, STATE_REG_FS, STATE_REG_GS +}; + +static enum run_mode run_mode; + +void set_run_mode(enum run_mode mode) { + run_mode = mode; +} + +int gdb_post_smi_hook() { + uint64_t r, entry; + uint32_t * ptr; + uint16_t * entry_v; + enum operating_mode m = get_operating_mode(); + + + switch (run_mode) { + case RM_STOPPED: + /* Until we have a better way of handling this - spin in + * a loop. */ + return 1; + + case RM_STEPPING: + /* First, set RFLAGS.TF */ + r = state_get_reg(STATE_REG_EFLAGS); + saved_eflags = r; + r |= EFLAGS_TF; + /* We also don't want to deal with interrupts. */ + r &= ~EFLAGS_IF; + state_set_reg(STATE_REG_EFLAGS, r); + + /* Track down the handler for a debug exception */ + r = state_get_reg(STATE_REG_IDT_LIMIT); + r = state_get_reg(STATE_REG_IDT_BASE); + if (m == LONG_64BIT || m == LONG_COMPAT) { + /* 64-bit. Index up 16 bytes to get to the + * debug trap descriptor. */ + ptr = demap(r + 16); + entry = (*ptr) & 0xFFFF; + ptr = demap(r + 20); + entry |= (*ptr) & 0xFFFF0000; + ptr = demap(r + 24); + entry |= (uint64_t)(*ptr) << 32; + } else { + /* Assume 32-bit for now. */ + ptr = demap(r + 8); + entry = (*ptr) & 0xFFFF; + ptr = demap(r + 12); + entry |= (*ptr) & 0xFFFF0000; + } + + outputf("entry is at %08x %08x", (uint32_t)(entry>>32), (uint32_t)entry); + /* MAGIC_BREAK */ + saved_db_entry_location = demap_phys(entry); + entry_v = p2v(saved_db_entry_location); + outputf("entry_v mapped to %08x", entry_v); + if (!entry_v) { + run_mode = RM_UNENCUMBERED; break; + } + saved_db_entry = *entry_v; + *entry_v = 0xB2E6; /* "out %al, $0xb2" */ + + step_start_mode = m; + + /* Turn off the safety */ + WRMSR(0xc0010054, 0x8002); + outputf("Have fun!"); + + saved_cs_base = state_get_reg(STATE_REG_CS_BASE); + saved_cs_limit = state_get_reg(STATE_REG_CS_LIMIT); + saved_cs_attrib = state_get_reg(STATE_REG_CS_ATTRIB); + saved_ss_base = state_get_reg(STATE_REG_SS_BASE); + saved_ss_limit = state_get_reg(STATE_REG_SS_LIMIT); + saved_ss_attrib = state_get_reg(STATE_REG_SS_ATTRIB); + + break; + + case RM_CONTINUING: + /* There may be breakpoints. Fall through to + * "unencumbered" for now. */ + + case RM_UNENCUMBERED: + /* Nada. */ + return 0; + + default: + break; + } + + return 0; +} + +#define READ_QWORD(pa) ({ uint64_t * p = demap(pa); if (!p) goto fail; outputf("pq %08x %08x", (uint32_t)((*p)>>32), (uint32_t)(*p)); *p; }) +#define READ_WORD(pa) ({ uint16_t * p = demap(pa); if (!p) goto fail; outputf("p %04x", (*p)); *p; }) + +static void gdb_unmangle_stepped_system() { + uint64_t r; + uint16_t * entry_v; + + /* The unexpected breakpoint has caused an interrupt stack to be + * built, which we must get rid of. */ + + if (step_start_mode == LONG_64BIT) { + r = state_get_reg(STATE_REG_RSP); + state_set_reg(STATE_REG_RIP, READ_QWORD(r)); + state_set_reg(STATE_REG_CS, READ_WORD(r+8)); + state_set_reg(STATE_REG_RFLAGS, READ_QWORD(r+16)); + state_set_reg(STATE_REG_RSP, READ_QWORD(r+24)); + state_set_reg(STATE_REG_SS, READ_WORD(r+32)); + } else if (step_start_mode == LONG_COMPAT) { + r = state_get_reg(STATE_REG_RSP); + state_set_reg(STATE_REG_RIP, READ_QWORD(r) & 0xFFFFFFFF); + state_set_reg(STATE_REG_CS, READ_WORD(r+8)); + state_set_reg(STATE_REG_RFLAGS, READ_QWORD(r+16) & 0xFFFFFFFF); + state_set_reg(STATE_REG_RSP, READ_QWORD(r+24) & 0xFFFFFFFF); + state_set_reg(STATE_REG_SS, READ_WORD(r+32)); + } + entry_v = p2v(saved_db_entry_location); + *entry_v = saved_db_entry; + + state_set_reg(STATE_REG_CS_BASE, saved_cs_base); + state_set_reg(STATE_REG_CS_LIMIT, saved_cs_limit); + state_set_reg(STATE_REG_CS_ATTRIB, saved_cs_attrib); + state_set_reg(STATE_REG_SS_BASE, saved_ss_base); + state_set_reg(STATE_REG_SS_LIMIT, saved_ss_limit); + state_set_reg(STATE_REG_SS_ATTRIB, saved_ss_attrib); + state_set_reg(STATE_REG_EFLAGS, saved_eflags); + + /* Put us back in "stopped" mode, until the GDB server gets around + * to handling the step. + */ + + reset_operating_mode_memo(); + run_mode = RM_STOPPED; + + send_stop_packet(); + return; + +fail: + outputf("ERROR: Failed to restore state!"); + while(1); +} + +void gdb_pre_smi_hook() { + enum operating_mode m = get_operating_mode(); + uint64_t ip, break_phys; + + if (run_mode == RM_STEPPING) { + /* Oh hey, we are probably stopped on a breakpoint. + * Let's check. */ + ip = state_get_reg( + (m == LONG_64BIT) + ? STATE_REG_RIP : STATE_REG_EIP + ); + break_phys = demap_phys(ip); + + if ((break_phys != saved_db_entry_location) + && (break_phys != saved_db_entry_location + 2)) { + /* Some other event caused us to enter SMM. We'll deal + * with the single step when we *actually* get to + * the breakpoint. */ + return; + } + + /* Great. Now we have caused a debug exception. + * + * "Your problems just got worse. Think: what have you done?" + * + * We don't want the running system to know that anything + * ever happened, so we manually unwind the stack and undo + * everything that happened. + */ + + gdb_unmangle_stepped_system(); + } +} + + +void read_registers_32 (char * buf) { + int i, r = 0, offset = 0, size = 0; + + /* Dump registers to buffer. */ + + for (i = 0; i < (sizeof(regs32) / sizeof(enum state_reg_t)); i++) { + enum state_reg_t reg = regs32[i]; + r = state_get_reg(reg); + //size = state_reg_size(reg); + size = 4; + enhexificate(&r, buf + offset, size); + offset += (size * 2); + } + + /* XXX: The rest of the buffer "should be" filled with floating point + stuff. We'll worry about that later. */ +} + +void write_registers_32 (char * buf) { + //int size = 0, i; + + + uint32_t *ubuf = (uint32_t *)buf; + + state_set_reg(STATE_REG_EAX, ubuf[0]); + state_set_reg(STATE_REG_ECX, ubuf[1]); + state_set_reg(STATE_REG_EDX, ubuf[2]); + state_set_reg(STATE_REG_EBX, ubuf[3]); + state_set_reg(STATE_REG_ESP, ubuf[4]); + state_set_reg(STATE_REG_EBP, ubuf[5]); + state_set_reg(STATE_REG_ESI, ubuf[6]); + state_set_reg(STATE_REG_EDI, ubuf[7]); + + state_set_reg(STATE_REG_EIP, ubuf[8]); + state_set_reg(STATE_REG_EFLAGS, ubuf[9]); + state_set_reg(STATE_REG_CS, ubuf[10]); + state_set_reg(STATE_REG_SS, ubuf[11]); + state_set_reg(STATE_REG_DS, ubuf[12]); + state_set_reg(STATE_REG_ES, ubuf[13]); + state_set_reg(STATE_REG_FS, ubuf[14]); + state_set_reg(STATE_REG_GS, ubuf[15]); + + /* XXX: Again, need to deal with floating point. */ +} diff --git a/gdb/stub.h b/gdb/stub.h new file mode 100644 index 0000000..eb697d9 --- /dev/null +++ b/gdb/stub.h @@ -0,0 +1,50 @@ +/* stub.h + * Headers for GDB stub system manipulation. + * 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. + */ + +#ifndef __STUB_H +#define __STUB_H + +void read_registers_32 (char * buf); +void write_registers_32 (char * buf); + +enum run_mode { + RM_UNENCUMBERED, + RM_STOPPED, + RM_STEPPING, + RM_CONTINUING +}; + +void set_run_mode(enum run_mode mode); + +#endif diff --git a/include/demap.h b/include/demap.h new file mode 100644 index 0000000..e727bd1 --- /dev/null +++ b/include/demap.h @@ -0,0 +1,56 @@ +/* demap.h + * Definitions for system page table de-mapping + * 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. + */ + +#ifndef __DEMAP_H +#define __DEMAP_H + +#include + +enum operating_mode { + UNKNOWN = 0, + LONG_64BIT, + LONG_COMPAT, + PROTECTED_PAGING, + PROTECTED_NOPAGING, + REAL, + V8086 +}; + +enum operating_mode get_operating_mode(void); +void reset_operating_mode_memo(void); + +uint64_t demap_phys (uint64_t vaddr); +void * demap (uint64_t vaddr); + +#endif diff --git a/include/paging.h b/include/paging.h index 4a27bb9..86f5676 100644 --- a/include/paging.h +++ b/include/paging.h @@ -11,10 +11,12 @@ #ifndef __PAGING_H #define __PAGING_H +#include + extern unsigned long v2p(void *virt); extern void *p2v(unsigned long phys); +extern void *p2v64(uint64_t phys); extern int addmap(unsigned long vaddr, unsigned long paddr); extern int addmap_4m(unsigned long vaddr, unsigned long paddr); -extern void *demap(unsigned long _pd, unsigned long vaddr); #endif diff --git a/include/reg-k8-msr.h b/include/reg-k8-msr.h new file mode 100644 index 0000000..25cad75 --- /dev/null +++ b/include/reg-k8-msr.h @@ -0,0 +1,50 @@ +/* reg-k8-msr.h + * K8 / AMD64 architectural MSR macros. + * 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. + */ + +#ifndef REG_K8_MSR_H +#define REG_K8_MSR_H + +#define SMM_ADDR_MSR 0xC0010112 +#define SMM_MASK_MSR 0xC0010113 + +#define HWCR_MSR 0xC0010015 + +#define HWCR_MSR_SMMLOCK (1 << 0) + +#define EFER_MSR 0xC0000080 + +#define EFER_MSR_LMA (1 << 10) + + +#endif diff --git a/include/reg-x86.h b/include/reg-x86.h new file mode 100644 index 0000000..94dd53b --- /dev/null +++ b/include/reg-x86.h @@ -0,0 +1,84 @@ +/* reg-x86.h + * X86 standard registers + * 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. + */ + +#ifndef __REG_X86_H +#define __REG_X86_H + +#define CR0_PE (1<<0) +#define CR0_MP (1<<1) +#define CR0_EM (1<<2) +#define CR0_TS (1<<3) +#define CR0_ET (1<<4) +#define CR0_NE (1<<5) +#define CR0_WP (1<<16) +#define CR0_AM (1<<18) +#define CR0_NW (1<<29) +#define CR0_CD (1<<30) +#define CR0_PG (1<<31) + +#define CR3_PWT (1<<3) +#define CR3_PCD (1<<4) + +#define CR4_VME (1<<0) +#define CR4_PVI (1<<1) +#define CR4_TSD (1<<2) +#define CR4_DE (1<<3) +#define CR4_PSE (1<<4) +#define CR4_PAE (1<<5) +#define CR4_MCE (1<<6) +#define CR4_PGE (1<<7) +#define CR4_PCE (1<<8) +#define CR4_OSFXSR (1<<9) +#define CR4_OSXMMEXCPT (1<<10) + +#define EFLAGS_CF (1<<0) +#define EFLAGS_PF (1<<2) +#define EFLAGS_AF (1<<4) +#define EFLAGS_ZF (1<<6) +#define EFLAGS_SF (1<<7) +#define EFLAGS_TF (1<<8) +#define EFLAGS_IF (1<<9) +#define EFLAGS_DF (1<<10) +#define EFLAGS_OF (1<<11) +#define EFLAGS_IOPL (3<<12) +#define EFLAGS_NT (1<<14) +#define EFLAGS_RF (1<<16) +#define EFLAGS_VM (1<<17) +#define EFLAGS_AC (1<<18) +#define EFLAGS_VIF (1<<19) +#define EFLAGS_VIP (1<<20) +#define EFLAGS_ID (1<<21) + +#endif /* __REG_X86_H */ + diff --git a/include/state.h b/include/state.h index fd19612..9bc4b90 100644 --- a/include/state.h +++ b/include/state.h @@ -59,11 +59,31 @@ enum state_reg_t { STATE_REG_CR3, STATE_REG_CS, + STATE_REG_CS_ATTRIB, + STATE_REG_CS_BASE, + STATE_REG_CS_LIMIT, STATE_REG_SS, + STATE_REG_SS_ATTRIB, + STATE_REG_SS_BASE, + STATE_REG_SS_LIMIT, STATE_REG_DS, + STATE_REG_DS_ATTRIB, + STATE_REG_DS_BASE, + STATE_REG_DS_LIMIT, STATE_REG_ES, + STATE_REG_ES_ATTRIB, + STATE_REG_ES_BASE, + STATE_REG_ES_LIMIT, STATE_REG_FS, + STATE_REG_FS_ATTRIB, + STATE_REG_FS_BASE, + STATE_REG_FS_LIMIT, STATE_REG_GS, + STATE_REG_GS_ATTRIB, + STATE_REG_GS_BASE, + STATE_REG_GS_LIMIT, + STATE_REG_IDT_BASE, + STATE_REG_IDT_LIMIT, /* 64-bit registers */ STATE_REG_RAX, @@ -82,18 +102,28 @@ enum state_reg_t { STATE_REG_R13, STATE_REG_R14, STATE_REG_R15, - STATE_REG_RIP + STATE_REG_RIP, + STATE_REG_RFLAGS, + + STATE_REG_EFER, + + NUM_REGISTERS }; + enum smm_type { SMM_TYPE_UNKNOWN, SMM_TYPE_32, SMM_TYPE_64 }; +enum smm_type state_get_type(void); + uint64_t state_get_reg (enum state_reg_t reg); int state_reg_size (enum state_reg_t reg); int state_set_reg (enum state_reg_t reg, uint64_t value); +int state_dump_reg(char * dest, int max, enum state_reg_t reg); + #endif /* __STATE_H */ diff --git a/include/tables.h b/include/tables.h new file mode 100644 index 0000000..13ee0df --- /dev/null +++ b/include/tables.h @@ -0,0 +1,71 @@ +/* tables.h + * Linker table helper macros. + * 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. + */ + +#ifndef __TABLES_H +#define __TABLES_H + +/* Linker tables are a beautiful, evil piece of magic. + * + * The idea is this; + * - For each table, define start and end pointers as zero-length arrays. + * - Define however many data values as constants of the appropriate type + * - Put each in its own section. For example, 'start' is in section + * .table.foo; the data elemnts are in section .table.foo.1 (or other values + * for explicit ordering); and 'end' is in .table.foo.END. + * - Merge the sections together with the following linker script entry: + * *(SORT(.table.*)) + * + * This has the effect that the start symbol points to the first value in the + * table, and the end symbol points to the last. The number of values is + * simply (table_foo_end - table_foo_start). + * + * And everything Just Works. Etherboot and the Linux kernel both use this + * for identifying linked-in modules; they have a somewhat more elaborate + * macro infastructure, but at the moment there are only two tables in + * NetWatch (network protocols and built-in drivers), so less is needed. + */ + +#define PROTOCOL(x) void (* const x##_ptr)(void) \ + __attribute__((section(".table.protocols.1"))) = x + + + +#define TABLE(typ, name) \ + typ name##_table[0] __attribute__((section(".table." #name ))); \ + typ name##_table_end[0] __attribute__((section(".table." #name ".END"))); + +#define TABLE_LENGTH(name) (name##_table_end - name##_table) + +#endif /* __TABLES_H */ + diff --git a/lib/demap.c b/lib/demap.c new file mode 100644 index 0000000..5eb303a --- /dev/null +++ b/lib/demap.c @@ -0,0 +1,202 @@ +/* demap.h + * Paging lookup 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 +#include +#include +#include +#include +#include +#include + +#define REG_CS_ATTRIB_L (1<<9) +#define PTE_PRESENT (1<<0) +#define PTE_LARGE (1<<7) + +#define PTE_FOR(x) (((unsigned int)(x) >> 12) & 0x3FF) +#define PDE_FOR(x) ((unsigned int)(x) >> 22) +#define ADDR_12_MASK(x) ((unsigned int)(x) & ~((1 << 12) - 1)) + +/* Keep a memoized copy of the operating mode the CPU was in when we + * entered SMM, so we don't have to look it up every time. + */ + +static enum operating_mode mode; + +void reset_operating_mode_memo() { + mode = UNKNOWN; +} + +/* Check the saved state map to determine what mode we were in. */ + +void probe_operating_mode() { + unsigned long cr0; + + if (state_get_type() == SMM_TYPE_64) { + /* This is a 64-bit processor, so we may be in 64-bit + * full or compatibility mode. Check if we are. */ + + uint64_t efer = state_get_reg(STATE_REG_EFER); + + if (efer & EFER_MSR_LMA) { + /* We are in long mode. Is this full 64-bit, or + * comatibility mode? Check the "L" bit of the + * saved CS descriptor to be sure. */ + + if (state_get_reg(STATE_REG_CS_ATTRIB) + & REG_CS_ATTRIB_L) { + mode = LONG_64BIT; + } else { + mode = LONG_COMPAT; + } + + return; + } + /* Otherwise, we are in legacy mode, so do the normal + * 32-bit probes. */ + } + + /* Either we are on a 32-bit processor, or we are on a 64-bit + * processor in legacy mode. */ + + if (state_get_reg(STATE_REG_EFLAGS) & EFLAGS_VM) { + mode = V8086; + return; + } + + cr0 = state_get_reg(STATE_REG_CR0); + + if (cr0 & CR0_PE) + if (cr0 & CR0_PG) + mode = PROTECTED_PAGING; + else + mode = PROTECTED_NOPAGING; + else + mode = REAL; +} + +enum operating_mode get_operating_mode() { + if (mode == UNKNOWN) + probe_operating_mode(); + + return mode; +} + +#define LONG_ADDR_MASK 0x000FFFFFFFFFF000 +#define PAGE_1G_MASK 0x000FFFFFC0000000 +#define PAGE_2M_MASK 0x000FFFFFFFE00000 + +#define LONG_ADDR_SECTION(addr, offset) ((((addr) >> (offset)) & 0x1FF) * 8) + +#define READ_PHYS_QWORD(pa) ({ uint64_t * p = p2v(pa); if (!p) return 0; *p; }) + +/* Given a virtual address from the current CPU context, determine what the + * actual corresponding physical address would be, then convert that back + * to a virtual address suitable for use within NetWatch. + * + * If the given address is not mapped in to RAM or is mapped to RAM which + * cannot be accessed, returns null. + * + * XXX: This currently handles both long and 32-bit modes, but only knows + * how to parse standard 4-kbyte pages. It assumes all segments span the full + * 32-bit address space. Thus, it will return correct results for most + * userspace environments in most sane OS kernels, but not necessarily kernel + * space (likely to be mapped with large pages) or anything that plays tricks + * with segmentation (like NaCl). + */ + +uint64_t demap_phys (uint64_t vaddr) { + + uint64_t pa = state_get_reg(STATE_REG_CR3) & LONG_ADDR_MASK; + uint64_t entry; + + if (mode == UNKNOWN) + probe_operating_mode(); + +outputf("demapping %08x %08x m %d", (uint32_t)(vaddr>>32), (uint32_t)vaddr, mode); + switch (mode) { + case LONG_64BIT: + case LONG_COMPAT: { + /* Get PML4 entry */ + entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 39)); + if (!(entry & PTE_PRESENT)) return 0; + pa = entry & LONG_ADDR_MASK; + + /* Get PDP entry */ + entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 30)); + if (!(entry & PTE_PRESENT)) return 0; + pa = entry & LONG_ADDR_MASK; + + if (entry & PTE_LARGE) + return (entry & PAGE_1G_MASK) + (vaddr & 0x3FFFFFFF); + + /* Get PDE */ + entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 21)); + if (!(entry & PTE_PRESENT)) return 0; + pa = entry & LONG_ADDR_MASK; + + if (entry & PTE_LARGE) + return (entry & PAGE_2M_MASK) + (vaddr & 0x1FFFFF); + + /* Get PTE */ + entry = READ_PHYS_QWORD(pa + LONG_ADDR_SECTION(vaddr, 12)); + if (!(entry & PTE_PRESENT)) return 0; + pa = entry & LONG_ADDR_MASK; + + return pa + (vaddr & 0xFFF); + } + case PROTECTED_NOPAGING: + case REAL: + return vaddr; + + default: { + unsigned long pde = ((unsigned long *)p2v(pa))[PDE_FOR(vaddr)]; + unsigned long pte; + + if (!(pde & PTE_PRESENT)) return 0; + pte = ((unsigned long *)p2v(ADDR_12_MASK(pde)))[PTE_FOR(vaddr)]; + if (!(pte & PTE_PRESENT)) return 0; + + return (pte & ~0xFFF) + (vaddr & 0xFFF); + } + } +} + +void *demap(uint64_t vaddr) { + uint64_t paddr = demap_phys(vaddr); + outputf("demap: paddr 0x%08x %08x", (uint32_t)(paddr>>32), (uint32_t)paddr); + if (!paddr) return 0; + return p2v(paddr); +} + diff --git a/lib/state.c b/lib/state.c index 368029b..46a56e8 100644 --- a/lib/state.c +++ b/lib/state.c @@ -34,6 +34,8 @@ #include "state.h" #include +#include +#include /* Size flags. */ #define SZ_BYTE 0x10000000 @@ -66,7 +68,8 @@ static const uint32_t offset_table_legacy[] = { [STATE_REG_DS] = 0xffb4 | SZ_DWORD, [STATE_REG_ES] = 0xffa8 | SZ_DWORD, [STATE_REG_FS] = 0xffb8 | SZ_DWORD, - [STATE_REG_GS] = 0xffbc | SZ_DWORD + [STATE_REG_GS] = 0xffbc | SZ_DWORD, + [STATE_REG_IDT_BASE] = 0xff94 | SZ_DWORD }; #define MAX_REG_LEGACY (sizeof(offset_table_legacy)/sizeof(uint32_t)) @@ -87,15 +90,35 @@ static const uint32_t offset_table_amd64[] = { [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_CR0] = 0xff58 | SZ_QWORD, + [STATE_REG_CR3] = 0xff50 | SZ_QWORD, + + [STATE_REG_CS] = 0xfe10 | SZ_WORD, + [STATE_REG_CS_ATTRIB] = 0xfe12 | SZ_WORD, + [STATE_REG_CS_BASE] = 0xfe18 | SZ_QWORD, + [STATE_REG_CS_LIMIT] = 0xfe14 | SZ_DWORD, + [STATE_REG_SS] = 0xfe20 | SZ_WORD, + [STATE_REG_SS_ATTRIB] = 0xfe22 | SZ_WORD, + [STATE_REG_SS_BASE] = 0xfe28 | SZ_QWORD, + [STATE_REG_SS_LIMIT] = 0xfe24 | SZ_DWORD, + [STATE_REG_DS] = 0xfe30 | SZ_WORD, + [STATE_REG_DS_ATTRIB] = 0xfe32 | SZ_WORD, + [STATE_REG_DS_BASE] = 0xfe38 | SZ_QWORD, + [STATE_REG_DS_LIMIT] = 0xfe34 | SZ_DWORD, + [STATE_REG_ES] = 0xfe00 | SZ_WORD, + [STATE_REG_ES_ATTRIB] = 0xfe02 | SZ_WORD, + [STATE_REG_ES_BASE] = 0xfe08 | SZ_QWORD, + [STATE_REG_ES_LIMIT] = 0xfe04 | SZ_DWORD, + [STATE_REG_FS] = 0xfe40 | SZ_WORD, + [STATE_REG_FS_ATTRIB] = 0xfe42 | SZ_WORD, + [STATE_REG_FS_BASE] = 0xfe48 | SZ_QWORD, + [STATE_REG_FS_LIMIT] = 0xfe44 | SZ_DWORD, + [STATE_REG_GS] = 0xfe50 | SZ_WORD, + [STATE_REG_GS_ATTRIB] = 0xfe52 | SZ_WORD, + [STATE_REG_GS_BASE] = 0xfe58 | SZ_QWORD, + [STATE_REG_GS_LIMIT] = 0xfe54 | SZ_DWORD, + [STATE_REG_IDT_BASE] = 0xfe88 | SZ_QWORD, + [STATE_REG_IDT_LIMIT] = 0xfe84 | SZ_DWORD, [STATE_REG_RAX] = 0xfff8 | SZ_QWORD, [STATE_REG_RBX] = 0xffe0 | SZ_QWORD, @@ -113,7 +136,75 @@ static const uint32_t offset_table_amd64[] = { [STATE_REG_R13] = 0xff90 | SZ_QWORD, [STATE_REG_R14] = 0xff88 | SZ_QWORD, [STATE_REG_R15] = 0xff80 | SZ_QWORD, - [STATE_REG_RIP] = 0xff78 | SZ_QWORD + [STATE_REG_RIP] = 0xff78 | SZ_QWORD, + [STATE_REG_RFLAGS] = 0xff70 | SZ_QWORD, + + [STATE_REG_EFER] = 0xfed0 | SZ_QWORD +}; + +static const char register_names[][4] = { + [STATE_REV] = "sREV", + [STATE_REG_SMBASE] = "sBSE", + [STATE_REG_IORESTART] = "IOrs", + [STATE_REG_HALTRESTART] = "HLrs", + + [STATE_REG_EAX] = "%eax", + [STATE_REG_EBX] = "%ebx", + [STATE_REG_ECX] = "%ecx", + [STATE_REG_EDX] = "%edx", + [STATE_REG_ESI] = "%esi", + [STATE_REG_EDI] = "%edi", + [STATE_REG_ESP] = "%esp", + [STATE_REG_EBP] = "%ebp", + [STATE_REG_EIP] = "%eip", + [STATE_REG_EFLAGS] = "%eFL", + [STATE_REG_CR0] = "%cr0", + [STATE_REG_CR3] = "%cr3", + + [STATE_REG_CS] = "%cs ", + [STATE_REG_CS_ATTRIB] = "csAT", + [STATE_REG_CS_BASE] = "csBA", + [STATE_REG_CS_LIMIT] = "csLI", + [STATE_REG_SS] = "%ss ", + [STATE_REG_SS_ATTRIB] = "ssAT", + [STATE_REG_SS_BASE] = "ssBA", + [STATE_REG_SS_LIMIT] = "ssLI", + [STATE_REG_DS] = "%ds ", + [STATE_REG_DS_ATTRIB] = "dsAT", + [STATE_REG_DS_BASE] = "dsBA", + [STATE_REG_DS_LIMIT] = "dsLI", + [STATE_REG_ES] = "%es ", + [STATE_REG_ES_ATTRIB] = "esAT", + [STATE_REG_ES_BASE] = "esBA", + [STATE_REG_ES_LIMIT] = "esLI", + [STATE_REG_FS] = "%fs ", + [STATE_REG_FS_ATTRIB] = "fsAT", + [STATE_REG_FS_BASE] = "fsBA", + [STATE_REG_FS_LIMIT] = "fsLI", + [STATE_REG_GS] = "%gs ", + [STATE_REG_GS_ATTRIB] = "gsAT", + [STATE_REG_GS_BASE] = "gsBA", + [STATE_REG_GS_LIMIT] = "gsLI", + + [STATE_REG_RAX] = "%rax", + [STATE_REG_RBX] = "%rbx", + [STATE_REG_RCX] = "%rcx", + [STATE_REG_RDX] = "%rdx", + [STATE_REG_RSI] = "%rsi", + [STATE_REG_RDI] = "%rdi", + [STATE_REG_RSP] = "%rsp", + [STATE_REG_RBP] = "%rbp", + [STATE_REG_R8] = "%r8 ", + [STATE_REG_R9] = "%r9 ", + [STATE_REG_R10] = "%r10", + [STATE_REG_R11] = "%r11", + [STATE_REG_R12] = "%r12", + [STATE_REG_R13] = "%r13", + [STATE_REG_R14] = "%r14", + [STATE_REG_R15] = "%r15", + [STATE_REG_RIP] = "%rip", + + [STATE_REG_EFER] = "EFER" }; #define MAX_REG_AMD64 (sizeof(offset_table_amd64)/sizeof(uint32_t)) @@ -237,3 +328,30 @@ int state_set_reg (enum state_reg_t reg, uint64_t value) { return 0; } + +/* Dump the name and contents of a register to a string. + * + * Returns: The number of bytes written. + */ + +int state_dump_reg(char * dest, int max, enum state_reg_t reg) { + const char const * name = register_names[reg]; + switch (state_reg_size(reg)) { + case 1: + return snprintf(dest, max, "%.4s: 0x%02x\n", + name, (unsigned int)state_get_reg(reg)); + case 2: + return snprintf(dest, max, "%.4s: 0x%04x\n", + name, (unsigned int)state_get_reg(reg)); + case 4: + return snprintf(dest, max, "%.4s: 0x%08x\n", + name, (unsigned int)state_get_reg(reg)); + case 8: { + uint64_t v = state_get_reg(reg); + return snprintf(dest, max, "%.4s: 0x%08x%08x\n", + name, (unsigned int)(v>>32), (unsigned int)v); + } + default: + return 0; + } +} diff --git a/lwip/src/core/memp.c b/lwip/src/core/memp.c index 0ef2839..cce0892 100644 --- a/lwip/src/core/memp.c +++ b/lwip/src/core/memp.c @@ -325,7 +325,7 @@ memp_malloc_fn(memp_t type, const char* file, const int line) ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0); memp = (struct memp*)((u8_t*)memp + MEMP_SIZE); } else { - LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %s\n", memp_desc[type])); + LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: OOM in pool %s\n", memp_desc[type])); #if MEMP_STATS ++lwip_stats.memp[type].err; #endif /* MEMP_STATS */ diff --git a/net/http/fs.c b/net/http/fs.c index 4fe2c41..832d3dd 100644 --- a/net/http/fs.c +++ b/net/http/fs.c @@ -36,35 +36,26 @@ #include #include #include +#include #include +#include -static char http_output_buffer[1024]; +static char http_output_buffer[1280]; /*-----------------------------------------------------------------------------------*/ void handle_regs(struct fs_file *file) { - file->len = snprintf(http_output_buffer, sizeof(http_output_buffer), - "Registers" - "

At the time you requested this page, the system's registers were:

" - "
"
-    "%%eax: 0x%08x    %%ebx: 0x%08x    %%ecx: 0x%08x    %%edx: 0x%08x\n"
-    "%%ebp: 0x%08x    %%esi: 0x%08x    %%edi: 0x%08x    %%esp: 0x%08x\n"
-    "%%cr0: 0x%08x    %%cr3: 0x%08x    %%eip: 0x%08x    %%eflags: 0x%08x\n"
-    "
", - *(unsigned long*)0xAFFD0, - *(unsigned long*)0xAFFDC, - *(unsigned long*)0xAFFD4, - *(unsigned long*)0xAFFD8, - *(unsigned long*)0xAFFE4, - *(unsigned long*)0xAFFE8, - *(unsigned long*)0xAFFEC, - *(unsigned long*)0xAFFE0, - *(unsigned long*)0xAFFFC, - *(unsigned long*)0xAFFF8, - *(unsigned long*)0xAFFF0, - *(unsigned long*)0xAFFF4); - + int i; + int len; + + len = snprintf(http_output_buffer, sizeof(http_output_buffer), "
");
+
+  for (i = 0; i < NUM_REGISTERS; i++) {
+    len += state_dump_reg(http_output_buffer + len, sizeof(http_output_buffer) - len, i);
+  }
+
+  file->len = len;
   file->data = http_output_buffer;
 }
 
@@ -74,36 +65,56 @@ void handle_backtrace(struct fs_file *file)
 {
   int i = 10;
   int len;
-  unsigned long *pebp, *peip;
-  unsigned long ebp;
-  unsigned long cr3;
+  void *pebp, *peip;
+  uint64_t bp, next;
+
+  int longmode = (get_operating_mode() == LONG_64BIT);
 
   char * buf = http_output_buffer;
   
   strcpy(buf, "Backtrace
");
   len = strlen(buf);
-  ebp = *(unsigned long *)0xAFFE4;
-  cr3 = *(unsigned long *)0xAFFF8;
+
+  bp = state_get_reg(STATE_REG_RIP);
+
+  if (longmode)
+    len += snprintf(buf + len, LEFT, "0x%08x%08x, from\n", (uint32_t)(bp >> 32), (uint32_t)bp);
+  else
+    len += snprintf(buf + len, LEFT, "0x%08x, from\n", (uint32_t)bp);
   
-  len += snprintf(buf + len, LEFT, "0x%08x, from\n", *(unsigned long*)0xAFFF0);
+  bp = state_get_reg(STATE_REG_RBP);
   
   /* I never thought I'd do this again. */
-  while ((peip = demap(cr3, ebp+4)) != 0x0 && i--)
+  while ((peip = demap(bp+(longmode?8:4))) != 0x0 && i--)
   {
-    len += snprintf(buf + len, LEFT, "0x%08x, from\n", *peip);
+    if (longmode) {
+      next = *(uint64_t *)peip;
+      len += snprintf(buf + len, LEFT, "0x%08x%08x, from\n", (uint32_t)(next >> 32), (uint32_t)next);
+    } else {
+      next = *(uint32_t *)peip;
+      len += snprintf(buf + len, LEFT, "0x%08x, from\n", (uint32_t)next);
+    }
+
+    pebp = demap(bp);
 
-    pebp = demap(cr3, ebp);
     if (!pebp)
     {
-      len += snprintf(buf + len, LEFT, "<unreadable %ebp>\n");
+      len += snprintf(buf + len, LEFT, "<unreadable frame>\n");
       break;
     }
-    if (ebp >= *pebp && *pebp)
+
+    if (longmode)
+      next = *(uint64_t *)pebp;
+    else
+      next = *(uint32_t *)pebp;
+
+    if (bp >= next && next)
     {
-      len += snprintf(buf + len, LEFT, "<recursive %ebp>\n");
+      len += snprintf(buf + len, LEFT, "<recursive frame>\n");
       break;
     }
-    ebp = *pebp;
+
+    bp = next;
   }
 
   if (i == -1)
diff --git a/net/http/fsdata.c b/net/http/fsdata.c
index 066f058..e42d5ad 100644
--- a/net/http/fsdata.c
+++ b/net/http/fsdata.c
@@ -5,9 +5,9 @@ static const char data_404_html[] =
 
 static const char data_index_html[] =
   "NetWatch"
-  "

NetWatch

" - "
" - "" + "

NetWatch

" + "" + "
" "
" ""; diff --git a/net/http/httpd.c b/net/http/httpd.c index ed08b0c..81d4a3f 100644 --- a/net/http/httpd.c +++ b/net/http/httpd.c @@ -32,6 +32,7 @@ #include #include +#include #include "lwip/debug.h" #include "lwip/stats.h" @@ -258,3 +259,4 @@ httpd_init(void) } /*-----------------------------------------------------------------------------------*/ +PROTOCOL(httpd_init); diff --git a/net/net.c b/net/net.c index 1462f09..d762b47 100644 --- a/net/net.c +++ b/net/net.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "net.h" @@ -28,8 +29,6 @@ #include "netif/etharp.h" #include "netif/ppp_oe.h" -#include "rfb.h" - static struct nic *_nic = 0x0; static struct netif _netif; @@ -139,15 +138,19 @@ int eth_register(struct nic *nic) return 0; } +typedef void(*thunk_t)(); + +TABLE(thunk_t, protocols); + void eth_init() { - extern void httpd_init(); + int i; /* Required for DMA to work. :( */ smram_tseg_set_state(SMRAM_TSEG_OPEN); - lwip_init(); - httpd_init(); - rfb_init(); + lwip_init(); + for (i = 0; i < TABLE_LENGTH(protocols); i++) + protocols_table[i](); } diff --git a/net/rfb.c b/net/rfb.c index 514dfc7..de81688 100644 --- a/net/rfb.c +++ b/net/rfb.c @@ -13,11 +13,12 @@ #include #include #include +#include #include "lwip/tcp.h" #include "lwip/stats.h" -#include "rfb.h" +#define RFB_PORT 5900 #define SET_PIXEL_FORMAT 0 #define SET_ENCODINGS 2 @@ -654,7 +655,7 @@ static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { return ERR_OK; } -void rfb_init() { +static void rfb_init() { struct tcp_pcb *pcb; init_server_info(); @@ -664,3 +665,5 @@ void rfb_init() { pcb = tcp_listen(pcb); tcp_accept(pcb, rfb_accept); } + +PROTOCOL(rfb_init); diff --git a/net/rfb.h b/net/rfb.h deleted file mode 100644 index 25fa3fb..0000000 --- a/net/rfb.h +++ /dev/null @@ -1,18 +0,0 @@ -/* rfb.h - * Remote framebuffer server - * NetWatch system management mode administration console - * - * Copyright (c) 2008 Jacob Potter and Joshua Wise. All rights reserved. - * This program is free software; you can redistribute and/or modify it under - * the terms found in the file LICENSE in the root of this source tree. - * - */ - -#ifndef _RFB_H -#define _RFB_H - -void rfb_init(void); - -#define RFB_PORT 5900 - -#endif /* _RFB_H */ diff --git a/netwatch/aseg.lds b/netwatch/netwatch-large.lds similarity index 83% rename from netwatch/aseg.lds rename to netwatch/netwatch-large.lds index cf14790..9bf3b8a 100644 --- a/netwatch/aseg.lds +++ b/netwatch/netwatch-large.lds @@ -19,8 +19,11 @@ SECTIONS . = 0x200000; - .text : { *(.text); } - .data : { *(.data); } + .text : { + *(.text); + *(SORT(.table.*)); + } + .data : { *(.data); *(.data2); } .rodata : { *(.rodata); } .text : { *(.text); } @@ -32,7 +35,7 @@ SECTIONS .stack : { . = . + 0x10000; . = ALIGN(0x10); - _stacktop = .; + _primary_stack_top = .; } . = 0x10000; diff --git a/netwatch/pagingstub-asm.S b/netwatch/pagingstub-asm.S index 964d06e..7440a7a 100644 --- a/netwatch/pagingstub-asm.S +++ b/netwatch/pagingstub-asm.S @@ -23,5 +23,11 @@ set_cr0_cont: # Hey, here we are! ps_switch_stack: mov 4(%esp), %eax mov 8(%esp), %esp + # Put a sentinel value (FEEDFACE) at the top of the stack + mov $0xCEFAEDFE, %edx + push %edx + push %edx + push %edx + push %edx call *%eax rsm