summaryrefslogtreecommitdiffstats
path: root/lib/libkvm
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2011-08-06 15:59:54 +0000
committermarcel <marcel@FreeBSD.org>2011-08-06 15:59:54 +0000
commit6c9e5b8c0d919fb59426f7b426b2c982d64900db (patch)
tree41baa5191ba3d55a536241c873fd22b0ebc0a4ed /lib/libkvm
parent9d5fe42c4ea8bf40bcf707a9bbd43f0436f582ff (diff)
downloadFreeBSD-src-6c9e5b8c0d919fb59426f7b426b2c982d64900db.zip
FreeBSD-src-6c9e5b8c0d919fb59426f7b426b2c982d64900db.tar.gz
Add support for PBVM addresses. In a nutshell this means:
o get the physical address and size of the PBVM page table. This can be found in the bootinfo structure, of which the physical address is recorded as the ELF entry point. o translate region 4 virtual addresses to physical addresses using the PBVM page table. In _kvm_kvatop() make the distinction between physical address and core file offset a little clearer to avoid confusion. To further enhance readability, always store the translated address into pa so that it's obvious how the translation from va to pa happened. Approved by: re (blanket)
Diffstat (limited to 'lib/libkvm')
-rw-r--r--lib/libkvm/kvm_ia64.c88
1 files changed, 81 insertions, 7 deletions
diff --git a/lib/libkvm/kvm_ia64.c b/lib/libkvm/kvm_ia64.c
index 04b3c21..74e2b80 100644
--- a/lib/libkvm/kvm_ia64.c
+++ b/lib/libkvm/kvm_ia64.c
@@ -33,6 +33,7 @@
#include <sys/mman.h>
#include <machine/atomic.h>
+#include <machine/bootinfo.h>
#include <machine/pte.h>
#include <kvm.h>
@@ -51,11 +52,16 @@
#define KPTE_DIR0_INDEX(va,ps) ((((va)/(ps)) / NKPTEPG(ps)) / NKPTEDIR(ps))
#define KPTE_DIR1_INDEX(va,ps) ((((va)/(ps)) / NKPTEPG(ps)) % NKPTEDIR(ps))
+#define PBVM_BASE 0x9ffc000000000000UL
+#define PBVM_PGSZ (64 * 1024)
+
struct vmstate {
void *mmapbase;
size_t mmapsize;
size_t pagesize;
u_long kptdir;
+ u_long *pbvm_pgtbl;
+ u_int pbvm_pgtblsz;
};
/*
@@ -110,11 +116,28 @@ _kvm_pa2off(kvm_t *kd, uint64_t pa, off_t *ofs, size_t pgsz)
return (pgsz - ((size_t)pa & (pgsz - 1)));
}
+static ssize_t
+_kvm_read_phys(kvm_t *kd, uint64_t pa, void *buf, size_t bufsz)
+{
+ off_t ofs;
+ size_t sz;
+
+ sz = _kvm_pa2off(kd, pa, &ofs, 0);
+ if (sz < bufsz)
+ return ((ssize_t)sz);
+
+ if (lseek(kd->pmfd, ofs, 0) == -1)
+ return (-1);
+ return (read(kd->pmfd, buf, bufsz));
+}
+
void
_kvm_freevtop(kvm_t *kd)
{
struct vmstate *vm = kd->vmst;
+ if (vm->pbvm_pgtbl != NULL)
+ free(vm->pbvm_pgtbl);
if (vm->mmapbase != NULL)
munmap(vm->mmapbase, vm->mmapsize);
free(vm);
@@ -124,10 +147,12 @@ _kvm_freevtop(kvm_t *kd)
int
_kvm_initvtop(kvm_t *kd)
{
+ struct bootinfo bi;
struct nlist nl[2];
uint64_t va;
Elf64_Ehdr *ehdr;
size_t hdrsz;
+ ssize_t sz;
kd->vmst = (struct vmstate *)_kvm_malloc(kd, sizeof(*kd->vmst));
if (kd->vmst == NULL) {
@@ -146,6 +171,42 @@ _kvm_initvtop(kvm_t *kd)
return (-1);
/*
+ * Load the PBVM page table. We need this to resolve PBVM addresses.
+ * The PBVM page table is obtained from the bootinfo structure, of
+ * which the physical address is given to us in e_entry. If e_entry
+ * is 0, then this is assumed to be a pre-PBVM kernel.
+ */
+ if (ehdr->e_entry != 0) {
+ sz = _kvm_read_phys(kd, ehdr->e_entry, &bi, sizeof(bi));
+ if (sz != sizeof(bi)) {
+ _kvm_err(kd, kd->program,
+ "cannot read bootinfo from PA %#lx", ehdr->e_entry);
+ return (-1);
+ }
+ if (bi.bi_magic != BOOTINFO_MAGIC) {
+ _kvm_err(kd, kd->program, "invalid bootinfo");
+ return (-1);
+ }
+ kd->vmst->pbvm_pgtbl = _kvm_malloc(kd, bi.bi_pbvm_pgtblsz);
+ if (kd->vmst->pbvm_pgtbl == NULL) {
+ _kvm_err(kd, kd->program, "cannot allocate page table");
+ return (-1);
+ }
+ kd->vmst->pbvm_pgtblsz = bi.bi_pbvm_pgtblsz;
+ sz = _kvm_read_phys(kd, bi.bi_pbvm_pgtbl, kd->vmst->pbvm_pgtbl,
+ bi.bi_pbvm_pgtblsz);
+ if (sz != bi.bi_pbvm_pgtblsz) {
+ _kvm_err(kd, kd->program,
+ "cannot read page table from PA %#lx",
+ bi.bi_pbvm_pgtbl);
+ return (-1);
+ }
+ } else {
+ kd->vmst->pbvm_pgtbl = NULL;
+ kd->vmst->pbvm_pgtblsz = 0;
+ }
+
+ /*
* At this point we've got enough information to use kvm_read() for
* direct mapped (ie region 6 and region 7) address, such as symbol
* addresses/values.
@@ -174,17 +235,18 @@ _kvm_initvtop(kvm_t *kd)
}
int
-_kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
+_kvm_kvatop(kvm_t *kd, u_long va, off_t *ofs)
{
struct ia64_lpte pte;
- uint64_t pgaddr, pt0addr, pt1addr;
+ uint64_t pa, pgaddr, pt0addr, pt1addr;
size_t pgno, pgsz, pt0no, pt1no;
if (va >= REGION_BASE(6)) {
/* Regions 6 and 7: direct mapped. */
- return (_kvm_pa2off(kd, REGION_ADDR(va), pa, 0));
+ pa = REGION_ADDR(va);
+ return (_kvm_pa2off(kd, pa, ofs, 0));
} else if (va >= REGION_BASE(5)) {
- /* Region 5: virtual. */
+ /* Region 5: Kernel Virtual Memory. */
va = REGION_ADDR(va);
pgsz = kd->vmst->pagesize;
pt0no = KPTE_DIR0_INDEX(va, pgsz);
@@ -207,12 +269,24 @@ _kvm_kvatop(kvm_t *kd, u_long va, off_t *pa)
goto fail;
if (!(pte.pte & PTE_PRESENT))
goto fail;
- va = (pte.pte & PTE_PPN_MASK) + (va & (pgsz - 1));
- return (_kvm_pa2off(kd, va, pa, pgsz));
+ pa = (pte.pte & PTE_PPN_MASK) + (va & (pgsz - 1));
+ return (_kvm_pa2off(kd, pa, ofs, pgsz));
+ } else if (va >= PBVM_BASE) {
+ /* Region 4: Pre-Boot Virtual Memory (PBVM). */
+ va -= PBVM_BASE;
+ pgsz = PBVM_PGSZ;
+ pt0no = va / pgsz;
+ if (pt0no >= (kd->vmst->pbvm_pgtblsz >> 3))
+ goto fail;
+ pt0addr = kd->vmst->pbvm_pgtbl[pt0no];
+ if (!(pt0addr & PTE_PRESENT))
+ goto fail;
+ pa = (pt0addr & PTE_PPN_MASK) + va % pgsz;
+ return (_kvm_pa2off(kd, pa, ofs, pgsz));
}
fail:
_kvm_err(kd, kd->program, "invalid kernel virtual address");
- *pa = ~0UL;
+ *ofs = ~0UL;
return (0);
}
OpenPOWER on IntegriCloud