summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2007-11-16 22:17:37 +0000
committerjhb <jhb@FreeBSD.org>2007-11-16 22:17:37 +0000
commit5b26270d093ab5e2d9bd18eb90756e8c45a29106 (patch)
tree0ae21b085bc5abb202d3c9c7ec8c111f2598256c
parent34d08946eefbe42dc7f210995f595dc5c6dd95ec (diff)
downloadFreeBSD-src-5b26270d093ab5e2d9bd18eb90756e8c45a29106.zip
FreeBSD-src-5b26270d093ab5e2d9bd18eb90756e8c45a29106.tar.gz
Teach kgdb how to handle double fault frames on i386:
- Save td_oncpu in 'struct kthr' so the i386 target code can see which CPU a thread is running on. - Add a new frame unwinder for double fault frames. This unwinder is used when "dblfault_handler" is encountered in the stack. It uses the CPU of the current thread to lookup the base address of the TSS used for the double fault from the GDT. It then fetches the various registers out of the TSS similar to how the current trapframe unwinder fetches registers out of the trapframe. MFC after: 3 days
-rw-r--r--gnu/usr.bin/gdb/kgdb/kgdb.h1
-rw-r--r--gnu/usr.bin/gdb/kgdb/kthr.c1
-rw-r--r--gnu/usr.bin/gdb/kgdb/trgt_i386.c153
3 files changed, 154 insertions, 1 deletions
diff --git a/gnu/usr.bin/gdb/kgdb/kgdb.h b/gnu/usr.bin/gdb/kgdb/kgdb.h
index bfebe17..d723a5f 100644
--- a/gnu/usr.bin/gdb/kgdb/kgdb.h
+++ b/gnu/usr.bin/gdb/kgdb/kgdb.h
@@ -41,6 +41,7 @@ struct kthr {
uintptr_t pcb;
int tid;
int pid;
+ u_char cpu;
};
extern struct kthr *curkthr;
diff --git a/gnu/usr.bin/gdb/kgdb/kthr.c b/gnu/usr.bin/gdb/kgdb/kthr.c
index 582cfa7..4c7f556 100644
--- a/gnu/usr.bin/gdb/kgdb/kthr.c
+++ b/gnu/usr.bin/gdb/kgdb/kthr.c
@@ -129,6 +129,7 @@ kgdb_thr_init(void)
kt->tid = td.td_tid;
kt->pid = p.p_pid;
kt->paddr = paddr;
+ kt->cpu = td.td_oncpu;
first = kt;
addr = (uintptr_t)TAILQ_NEXT(&td, td_plist);
}
diff --git a/gnu/usr.bin/gdb/kgdb/trgt_i386.c b/gnu/usr.bin/gdb/kgdb/trgt_i386.c
index 0d40236..c3581b5 100644
--- a/gnu/usr.bin/gdb/kgdb/trgt_i386.c
+++ b/gnu/usr.bin/gdb/kgdb/trgt_i386.c
@@ -27,9 +27,12 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/proc.h>
#include <machine/pcb.h>
#include <machine/frame.h>
+#include <machine/segments.h>
+#include <machine/tss.h>
#include <err.h>
#include <kvm.h>
#include <string.h>
@@ -71,6 +74,152 @@ kgdb_trgt_store_registers(int regno __unused)
fprintf_unfiltered(gdb_stderr, "XXX: %s\n", __func__);
}
+struct kgdb_tss_cache {
+ CORE_ADDR pc;
+ CORE_ADDR sp;
+ CORE_ADDR tss;
+};
+
+static int kgdb_trgt_tss_offset[15] = {
+ offsetof(struct i386tss, tss_eax),
+ offsetof(struct i386tss, tss_ecx),
+ offsetof(struct i386tss, tss_edx),
+ offsetof(struct i386tss, tss_ebx),
+ offsetof(struct i386tss, tss_esp),
+ offsetof(struct i386tss, tss_ebp),
+ offsetof(struct i386tss, tss_esi),
+ offsetof(struct i386tss, tss_edi),
+ offsetof(struct i386tss, tss_eip),
+ offsetof(struct i386tss, tss_eflags),
+ offsetof(struct i386tss, tss_cs),
+ offsetof(struct i386tss, tss_ss),
+ offsetof(struct i386tss, tss_ds),
+ offsetof(struct i386tss, tss_es),
+ offsetof(struct i386tss, tss_fs)
+};
+
+/*
+ * If the current thread is executing on a CPU, fetch the common_tss
+ * for that CPU.
+ *
+ * This is painful because 'struct pcpu' is variant sized, so we can't
+ * use it. Instead, we lookup the GDT selector for this CPU and
+ * extract the base of the TSS from there.
+ */
+static CORE_ADDR
+kgdb_trgt_fetch_tss(void)
+{
+ struct kthr *kt;
+ struct segment_descriptor sd;
+ uintptr_t addr, cpu0prvpage, tss;
+
+ kt = kgdb_thr_lookup_tid(ptid_get_tid(inferior_ptid));
+ if (kt == NULL || kt->cpu == NOCPU)
+ return (0);
+
+ addr = kgdb_lookup("_gdt");
+ if (addr == 0)
+ return (0);
+ addr += (kt->cpu * NGDT + GPROC0_SEL) * sizeof(sd);
+ if (kvm_read(kvm, addr, &sd, sizeof(sd)) != sizeof(sd)) {
+ warnx("kvm_read: %s", kvm_geterr(kvm));
+ return (0);
+ }
+ if (sd.sd_type != SDT_SYS386BSY) {
+ warnx("descriptor is not a busy TSS");
+ return (0);
+ }
+ tss = sd.sd_hibase << 24 | sd.sd_lobase;
+
+ /*
+ * In SMP kernels, the TSS is stored as part of the per-CPU
+ * data. On older kernels, the CPU0's private page
+ * is stored at an address that isn't mapped in minidumps.
+ * However, the data is mapped at the alternate cpu0prvpage
+ * address. Thus, if the TSS is at the invalid address,
+ * change it to be relative to cpu0prvpage instead.
+ */
+ if (trunc_page(tss) == 0xffc00000) {
+ addr = kgdb_lookup("_cpu0prvpage");
+ if (addr == 0)
+ return (0);
+ if (kvm_read(kvm, addr, &cpu0prvpage, sizeof(cpu0prvpage)) !=
+ sizeof(cpu0prvpage)) {
+ warnx("kvm_read: %s", kvm_geterr(kvm));
+ return (0);
+ }
+ tss = cpu0prvpage + (tss & PAGE_MASK);
+ }
+ return ((CORE_ADDR)tss);
+}
+
+static struct kgdb_tss_cache *
+kgdb_trgt_tss_cache(struct frame_info *next_frame, void **this_cache)
+{
+ char buf[MAX_REGISTER_SIZE];
+ struct kgdb_tss_cache *cache;
+
+ cache = *this_cache;
+ if (cache == NULL) {
+ cache = FRAME_OBSTACK_ZALLOC(struct kgdb_tss_cache);
+ *this_cache = cache;
+ cache->pc = frame_func_unwind(next_frame);
+ frame_unwind_register(next_frame, SP_REGNUM, buf);
+ cache->sp = extract_unsigned_integer(buf,
+ register_size(current_gdbarch, SP_REGNUM));
+ cache->tss = kgdb_trgt_fetch_tss();
+ }
+ return (cache);
+}
+
+static void
+kgdb_trgt_dblfault_this_id(struct frame_info *next_frame, void **this_cache,
+ struct frame_id *this_id)
+{
+ struct kgdb_tss_cache *cache;
+
+ cache = kgdb_trgt_tss_cache(next_frame, this_cache);
+ *this_id = frame_id_build(cache->sp, cache->pc);
+}
+
+static void
+kgdb_trgt_dblfault_prev_register(struct frame_info *next_frame,
+ void **this_cache, int regnum, int *optimizedp, enum lval_type *lvalp,
+ CORE_ADDR *addrp, int *realnump, void *valuep)
+{
+ char dummy_valuep[MAX_REGISTER_SIZE];
+ struct kgdb_tss_cache *cache;
+ int ofs, regsz;
+
+ regsz = register_size(current_gdbarch, regnum);
+
+ if (valuep == NULL)
+ valuep = dummy_valuep;
+ memset(valuep, 0, regsz);
+ *optimizedp = 0;
+ *addrp = 0;
+ *lvalp = not_lval;
+ *realnump = -1;
+
+ ofs = (regnum >= I386_EAX_REGNUM && regnum <= I386_FS_REGNUM)
+ ? kgdb_trgt_tss_offset[regnum] : -1;
+ if (ofs == -1)
+ return;
+
+ cache = kgdb_trgt_tss_cache(next_frame, this_cache);
+ if (cache->tss == 0)
+ return;
+ *addrp = cache->tss + ofs;
+ *lvalp = lval_memory;
+ target_read_memory(*addrp, valuep, regsz);
+}
+
+static const struct frame_unwind kgdb_trgt_dblfault_unwind = {
+ UNKNOWN_FRAME,
+ &kgdb_trgt_dblfault_this_id,
+ &kgdb_trgt_dblfault_prev_register
+};
+
struct kgdb_frame_cache {
int intrframe;
CORE_ADDR pc;
@@ -194,6 +343,8 @@ kgdb_trgt_trapframe_sniffer(struct frame_info *next_frame)
find_pc_partial_function(pc, &pname, NULL, NULL);
if (pname == NULL)
return (NULL);
+ if (strcmp(pname, "dblfault_handler") == 0)
+ return (&kgdb_trgt_dblfault_unwind);
if (strcmp(pname, "calltrap") == 0 ||
(pname[0] == 'X' && pname[1] != '_'))
return (&kgdb_trgt_trapframe_unwind);
OpenPOWER on IntegriCloud