Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F139863659
D6804.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
50 KB
Referenced Files
None
Subscribers
None
D6804.diff
View Options
Index: lib/libkvm/Makefile
===================================================================
--- lib/libkvm/Makefile
+++ lib/libkvm/Makefile
@@ -11,7 +11,7 @@
WARNS?= 3
SRCS= kvm.c kvm_cptime.c kvm_getloadavg.c \
- kvm_getswapinfo.c kvm_pcpu.c kvm_proc.c kvm_vnet.c \
+ kvm_getswapinfo.c kvm_pcpu.c kvm_private.c kvm_proc.c kvm_vnet.c \
kvm_minidump_aarch64.c \
kvm_amd64.c kvm_minidump_amd64.c \
kvm_arm.c kvm_minidump_arm.c \
Index: lib/libkvm/kvm.c
===================================================================
--- lib/libkvm/kvm.c
+++ lib/libkvm/kvm.c
@@ -66,114 +66,12 @@
SET_DECLARE(kvm_arch, struct kvm_arch);
-/* from src/lib/libc/gen/nlist.c */
-int __fdnlist(int, struct nlist *);
-
-static int
-kvm_fdnlist(kvm_t *kd, struct kvm_nlist *list)
-{
- kvaddr_t addr;
- int error, nfail;
-
- if (kd->resolve_symbol == NULL) {
- struct nlist *nl;
- int count, i;
-
- for (count = 0; list[count].n_name != NULL &&
- list[count].n_name[0] != '\0'; count++)
- ;
- nl = calloc(count + 1, sizeof(*nl));
- for (i = 0; i < count; i++)
- nl[i].n_name = list[i].n_name;
- nfail = __fdnlist(kd->nlfd, nl);
- for (i = 0; i < count; i++) {
- list[i].n_type = nl[i].n_type;
- list[i].n_value = nl[i].n_value;
- }
- free(nl);
- return (nfail);
- }
-
- nfail = 0;
- while (list->n_name != NULL && list->n_name[0] != '\0') {
- error = kd->resolve_symbol(list->n_name, &addr);
- if (error != 0) {
- nfail++;
- list->n_value = 0;
- list->n_type = 0;
- } else {
- list->n_value = addr;
- list->n_type = N_DATA | N_EXT;
- }
- list++;
- }
- return (nfail);
-}
-
char *
kvm_geterr(kvm_t *kd)
{
return (kd->errbuf);
}
-#include <stdarg.h>
-
-/*
- * Report an error using printf style arguments. "program" is kd->program
- * on hard errors, and 0 on soft errors, so that under sun error emulation,
- * only hard errors are printed out (otherwise, programs like gdb will
- * generate tons of error messages when trying to access bogus pointers).
- */
-void
-_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- if (program != NULL) {
- (void)fprintf(stderr, "%s: ", program);
- (void)vfprintf(stderr, fmt, ap);
- (void)fputc('\n', stderr);
- } else
- (void)vsnprintf(kd->errbuf,
- sizeof(kd->errbuf), fmt, ap);
-
- va_end(ap);
-}
-
-void
-_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
-{
- va_list ap;
- int n;
-
- va_start(ap, fmt);
- if (program != NULL) {
- (void)fprintf(stderr, "%s: ", program);
- (void)vfprintf(stderr, fmt, ap);
- (void)fprintf(stderr, ": %s\n", strerror(errno));
- } else {
- char *cp = kd->errbuf;
-
- (void)vsnprintf(cp, sizeof(kd->errbuf), fmt, ap);
- n = strlen(cp);
- (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
- strerror(errno));
- }
- va_end(ap);
-}
-
-void *
-_kvm_malloc(kvm_t *kd, size_t n)
-{
- void *p;
-
- if ((p = calloc(n, sizeof(char))) == NULL)
- _kvm_err(kd, kd->program, "can't allocate %zu bytes: %s",
- n, strerror(errno));
- return (p);
-}
-
static int
_kvm_read_kernel_ehdr(kvm_t *kd)
{
@@ -210,166 +108,6 @@
}
}
-int
-_kvm_probe_elf_kernel(kvm_t *kd, int class, int machine)
-{
-
- return (kd->nlehdr.e_ident[EI_CLASS] == class &&
- kd->nlehdr.e_type == ET_EXEC &&
- kd->nlehdr.e_machine == machine);
-}
-
-int
-_kvm_is_minidump(kvm_t *kd)
-{
- char minihdr[8];
-
- if (kd->rawdump)
- return (0);
- if (pread(kd->pmfd, &minihdr, 8, 0) == 8 &&
- memcmp(&minihdr, "minidump", 8) == 0)
- return (1);
- return (0);
-}
-
-/*
- * The powerpc backend has a hack to strip a leading kerneldump
- * header from the core before treating it as an ELF header.
- *
- * We can add that here if we can get a change to libelf to support
- * an initial offset into the file. Alternatively we could patch
- * savecore to extract cores from a regular file instead.
- */
-int
-_kvm_read_core_phdrs(kvm_t *kd, size_t *phnump, GElf_Phdr **phdrp)
-{
- GElf_Ehdr ehdr;
- GElf_Phdr *phdr;
- Elf *elf;
- size_t i, phnum;
-
- elf = elf_begin(kd->pmfd, ELF_C_READ, NULL);
- if (elf == NULL) {
- _kvm_err(kd, kd->program, "%s", elf_errmsg(0));
- return (-1);
- }
- if (elf_kind(elf) != ELF_K_ELF) {
- _kvm_err(kd, kd->program, "invalid core");
- goto bad;
- }
- if (gelf_getclass(elf) != kd->nlehdr.e_ident[EI_CLASS]) {
- _kvm_err(kd, kd->program, "invalid core");
- goto bad;
- }
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- _kvm_err(kd, kd->program, "%s", elf_errmsg(0));
- goto bad;
- }
- if (ehdr.e_type != ET_CORE) {
- _kvm_err(kd, kd->program, "invalid core");
- goto bad;
- }
- if (ehdr.e_machine != kd->nlehdr.e_machine) {
- _kvm_err(kd, kd->program, "invalid core");
- goto bad;
- }
-
- if (elf_getphdrnum(elf, &phnum) == -1) {
- _kvm_err(kd, kd->program, "%s", elf_errmsg(0));
- goto bad;
- }
-
- phdr = calloc(phnum, sizeof(*phdr));
- if (phdr == NULL) {
- _kvm_err(kd, kd->program, "failed to allocate phdrs");
- goto bad;
- }
-
- for (i = 0; i < phnum; i++) {
- if (gelf_getphdr(elf, i, &phdr[i]) == NULL) {
- _kvm_err(kd, kd->program, "%s", elf_errmsg(0));
- goto bad;
- }
- }
- elf_end(elf);
- *phnump = phnum;
- *phdrp = phdr;
- return (0);
-
-bad:
- elf_end(elf);
- return (-1);
-}
-
-static void
-_kvm_hpt_insert(struct hpt *hpt, uint64_t pa, off_t off)
-{
- struct hpte *hpte;
- uint32_t fnv = FNV1_32_INIT;
-
- fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
- fnv &= (HPT_SIZE - 1);
- hpte = malloc(sizeof(*hpte));
- hpte->pa = pa;
- hpte->off = off;
- hpte->next = hpt->hpt_head[fnv];
- hpt->hpt_head[fnv] = hpte;
-}
-
-void
-_kvm_hpt_init(kvm_t *kd, struct hpt *hpt, void *base, size_t len, off_t off,
- int page_size, int word_size)
-{
- uint64_t bits, idx, pa;
- uint64_t *base64;
- uint32_t *base32;
-
- base64 = base;
- base32 = base;
- for (idx = 0; idx < len / word_size; idx++) {
- if (word_size == sizeof(uint64_t))
- bits = _kvm64toh(kd, base64[idx]);
- else
- bits = _kvm32toh(kd, base32[idx]);
- pa = idx * word_size * NBBY * page_size;
- for (; bits != 0; bits >>= 1, pa += page_size) {
- if ((bits & 1) == 0)
- continue;
- _kvm_hpt_insert(hpt, pa, off);
- off += page_size;
- }
- }
-}
-
-off_t
-_kvm_hpt_find(struct hpt *hpt, uint64_t pa)
-{
- struct hpte *hpte;
- uint32_t fnv = FNV1_32_INIT;
-
- fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
- fnv &= (HPT_SIZE - 1);
- for (hpte = hpt->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
- if (pa == hpte->pa)
- return (hpte->off);
- }
- return (-1);
-}
-
-void
-_kvm_hpt_free(struct hpt *hpt)
-{
- struct hpte *hpte, *next;
- int i;
-
- for (i = 0; i < HPT_SIZE; i++) {
- for (hpte = hpt->hpt_head[i]; hpte != NULL; hpte = next) {
- next = hpte->next;
- free(hpte);
- }
- }
-}
-
static kvm_t *
_kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
{
@@ -545,212 +283,15 @@
free((void *) kd->argspc);
if (kd->argv != 0)
free((void *)kd->argv);
+ if (kd->pt_map != NULL)
+ _kvm_unmap(kd->pt_map, kd->pt_map_size);
+ if (kd->pt_sparse_pages != NULL)
+ _kvm_unmap(kd->pt_sparse_pages, kd->pt_sparse_size);
free((void *)kd);
return (0);
}
-/*
- * Walk the list of unresolved symbols, generate a new list and prefix the
- * symbol names, try again, and merge back what we could resolve.
- */
-static int
-kvm_fdnlist_prefix(kvm_t *kd, struct kvm_nlist *nl, int missing,
- const char *prefix, kvaddr_t (*validate_fn)(kvm_t *, kvaddr_t))
-{
- struct kvm_nlist *n, *np, *p;
- char *cp, *ce;
- const char *ccp;
- size_t len;
- int slen, unresolved;
-
- /*
- * Calculate the space we need to malloc for nlist and names.
- * We are going to store the name twice for later lookups: once
- * with the prefix and once the unmodified name delmited by \0.
- */
- len = 0;
- unresolved = 0;
- for (p = nl; p->n_name && p->n_name[0]; ++p) {
- if (p->n_type != N_UNDF)
- continue;
- len += sizeof(struct kvm_nlist) + strlen(prefix) +
- 2 * (strlen(p->n_name) + 1);
- unresolved++;
- }
- if (unresolved == 0)
- return (unresolved);
- /* Add space for the terminating nlist entry. */
- len += sizeof(struct kvm_nlist);
- unresolved++;
-
- /* Alloc one chunk for (nlist, [names]) and setup pointers. */
- n = np = malloc(len);
- bzero(n, len);
- if (n == NULL)
- return (missing);
- cp = ce = (char *)np;
- cp += unresolved * sizeof(struct kvm_nlist);
- ce += len;
-
- /* Generate shortened nlist with special prefix. */
- unresolved = 0;
- for (p = nl; p->n_name && p->n_name[0]; ++p) {
- if (p->n_type != N_UNDF)
- continue;
- *np = *p;
- /* Save the new\0orig. name so we can later match it again. */
- slen = snprintf(cp, ce - cp, "%s%s%c%s", prefix,
- (prefix[0] != '\0' && p->n_name[0] == '_') ?
- (p->n_name + 1) : p->n_name, '\0', p->n_name);
- if (slen < 0 || slen >= ce - cp)
- continue;
- np->n_name = cp;
- cp += slen + 1;
- np++;
- unresolved++;
- }
-
- /* Do lookup on the reduced list. */
- np = n;
- unresolved = kvm_fdnlist(kd, np);
-
- /* Check if we could resolve further symbols and update the list. */
- if (unresolved >= 0 && unresolved < missing) {
- /* Find the first freshly resolved entry. */
- for (; np->n_name && np->n_name[0]; np++)
- if (np->n_type != N_UNDF)
- break;
- /*
- * The lists are both in the same order,
- * so we can walk them in parallel.
- */
- for (p = nl; np->n_name && np->n_name[0] &&
- p->n_name && p->n_name[0]; ++p) {
- if (p->n_type != N_UNDF)
- continue;
- /* Skip expanded name and compare to orig. one. */
- ccp = np->n_name + strlen(np->n_name) + 1;
- if (strcmp(ccp, p->n_name) != 0)
- continue;
- /* Update nlist with new, translated results. */
- p->n_type = np->n_type;
- if (validate_fn)
- p->n_value = (*validate_fn)(kd, np->n_value);
- else
- p->n_value = np->n_value;
- missing--;
- /* Find next freshly resolved entry. */
- for (np++; np->n_name && np->n_name[0]; np++)
- if (np->n_type != N_UNDF)
- break;
- }
- }
- /* We could assert missing = unresolved here. */
-
- free(n);
- return (unresolved);
-}
-
-int
-_kvm_nlist(kvm_t *kd, struct kvm_nlist *nl, int initialize)
-{
- struct kvm_nlist *p;
- int nvalid;
- struct kld_sym_lookup lookup;
- int error;
- const char *prefix = "";
- char symname[1024]; /* XXX-BZ symbol name length limit? */
- int tried_vnet, tried_dpcpu;
-
- /*
- * If we can't use the kld symbol lookup, revert to the
- * slow library call.
- */
- if (!ISALIVE(kd)) {
- error = kvm_fdnlist(kd, nl);
- if (error <= 0) /* Hard error or success. */
- return (error);
-
- if (_kvm_vnet_initialized(kd, initialize))
- error = kvm_fdnlist_prefix(kd, nl, error,
- VNET_SYMPREFIX, _kvm_vnet_validaddr);
-
- if (error > 0 && _kvm_dpcpu_initialized(kd, initialize))
- error = kvm_fdnlist_prefix(kd, nl, error,
- DPCPU_SYMPREFIX, _kvm_dpcpu_validaddr);
-
- return (error);
- }
-
- /*
- * We can use the kld lookup syscall. Go through each nlist entry
- * and look it up with a kldsym(2) syscall.
- */
- nvalid = 0;
- tried_vnet = 0;
- tried_dpcpu = 0;
-again:
- for (p = nl; p->n_name && p->n_name[0]; ++p) {
- if (p->n_type != N_UNDF)
- continue;
-
- lookup.version = sizeof(lookup);
- lookup.symvalue = 0;
- lookup.symsize = 0;
-
- error = snprintf(symname, sizeof(symname), "%s%s", prefix,
- (prefix[0] != '\0' && p->n_name[0] == '_') ?
- (p->n_name + 1) : p->n_name);
- if (error < 0 || error >= (int)sizeof(symname))
- continue;
- lookup.symname = symname;
- if (lookup.symname[0] == '_')
- lookup.symname++;
-
- if (kldsym(0, KLDSYM_LOOKUP, &lookup) != -1) {
- p->n_type = N_TEXT;
- if (_kvm_vnet_initialized(kd, initialize) &&
- strcmp(prefix, VNET_SYMPREFIX) == 0)
- p->n_value =
- _kvm_vnet_validaddr(kd, lookup.symvalue);
- else if (_kvm_dpcpu_initialized(kd, initialize) &&
- strcmp(prefix, DPCPU_SYMPREFIX) == 0)
- p->n_value =
- _kvm_dpcpu_validaddr(kd, lookup.symvalue);
- else
- p->n_value = lookup.symvalue;
- ++nvalid;
- /* lookup.symsize */
- }
- }
-
- /*
- * Check the number of entries that weren't found. If they exist,
- * try again with a prefix for virtualized or DPCPU symbol names.
- */
- error = ((p - nl) - nvalid);
- if (error && _kvm_vnet_initialized(kd, initialize) && !tried_vnet) {
- tried_vnet = 1;
- prefix = VNET_SYMPREFIX;
- goto again;
- }
- if (error && _kvm_dpcpu_initialized(kd, initialize) && !tried_dpcpu) {
- tried_dpcpu = 1;
- prefix = DPCPU_SYMPREFIX;
- goto again;
- }
-
- /*
- * Return the number of entries that weren't found. If they exist,
- * also fill internal error buffer.
- */
- error = ((p - nl) - nvalid);
- if (error)
- _kvm_syserr(kd, kd->program, "kvm_nlist");
- return (error);
-}
-
int
kvm_nlist2(kvm_t *kd, struct kvm_nlist *nl)
{
Index: lib/libkvm/kvm_minidump_aarch64.c
===================================================================
--- lib/libkvm/kvm_minidump_aarch64.c
+++ lib/libkvm/kvm_minidump_aarch64.c
@@ -50,7 +50,6 @@
struct vmstate {
struct minidumphdr hdr;
- struct hpt hpt;
uint64_t *page_map;
};
@@ -67,8 +66,7 @@
{
struct vmstate *vm = kd->vmst;
- _kvm_hpt_free(&vm->hpt);
- free(vm->page_map);
+ _kvm_unmap(vm->page_map, vm->hdr.pmapsize);
free(vm);
kd->vmst = NULL;
}
@@ -77,8 +75,7 @@
_aarch64_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- uint64_t *bitmap;
- off_t off;
+ off_t off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -114,50 +111,22 @@
/* Skip header and msgbuf */
off = AARCH64_PAGE_SIZE + aarch64_round_page(vmst->hdr.msgbufsize);
- bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
- if (bitmap == NULL) {
- _kvm_err(kd, kd->program,
- "cannot allocate %d bytes for bitmap",
- vmst->hdr.bitmapsize);
- return (-1);
- }
- if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
- (ssize_t)vmst->hdr.bitmapsize) {
- _kvm_err(kd, kd->program,
- "cannot read %d bytes for page bitmap",
- vmst->hdr.bitmapsize);
- free(bitmap);
+ /* build physical address lookup table for sparse pages */
+ sparse_off = off + aarch64_round_page(vmst->hdr.bitmapsize) +
+ aarch64_round_page(vmst->hdr.pmapsize);
+ if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
+ AARCH64_PAGE_SIZE, sizeof(uint64_t)) == -1) {
+ _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += aarch64_round_page(vmst->hdr.bitmapsize);
- vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
- if (vmst->page_map == NULL) {
- _kvm_err(kd, kd->program,
- "cannot allocate %d bytes for page_map",
+ if (_kvm_map(kd, vmst->hdr.pmapsize, off, (void **)&vmst->page_map) == -1) {
+ _kvm_err(kd, kd->program, "cannot map %d bytes for page_map",
vmst->hdr.pmapsize);
- free(bitmap);
return (-1);
}
- /* This is the end of the dump, savecore may have truncated it. */
- /*
- * XXX: This doesn't make sense. The pmap is not at the end,
- * and if it is truncated we don't have any actual data (it's
- * all stored after the bitmap and pmap. -- jhb
- */
- if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) <
- AARCH64_PAGE_SIZE) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page_map",
- vmst->hdr.pmapsize);
- free(bitmap);
- return (-1);
- }
- off += vmst->hdr.pmapsize;
-
- /* build physical address hash table for sparse pages */
- _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
- AARCH64_PAGE_SIZE, sizeof(*bitmap));
- free(bitmap);
+ off += aarch64_round_page(vmst->hdr.pmapsize);
return (0);
}
@@ -178,7 +147,7 @@
if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
a = (va - vm->hdr.dmapbase + vm->hdr.dmapphys) &
~AARCH64_PAGE_MASK;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
"direct map address 0x%jx not in minidump",
@@ -198,7 +167,7 @@
goto invalid;
}
a = l3 & ~AARCH64_ATTR_MASK;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_aarch64_minidump_vatop: "
"physical address 0x%jx not in minidump",
Index: lib/libkvm/kvm_minidump_amd64.c
===================================================================
--- lib/libkvm/kvm_minidump_amd64.c
+++ lib/libkvm/kvm_minidump_amd64.c
@@ -49,7 +49,6 @@
struct vmstate {
struct minidumphdr hdr;
- struct hpt hpt;
amd64_pte_t *page_map;
};
@@ -66,9 +65,7 @@
{
struct vmstate *vm = kd->vmst;
- _kvm_hpt_free(&vm->hpt);
- if (vm->page_map)
- free(vm->page_map);
+ _kvm_unmap(vm->page_map, vm->hdr.pmapsize);
free(vm);
kd->vmst = NULL;
}
@@ -77,8 +74,7 @@
_amd64_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- uint64_t *bitmap;
- off_t off;
+ off_t off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -116,37 +112,21 @@
/* Skip header and msgbuf */
off = AMD64_PAGE_SIZE + amd64_round_page(vmst->hdr.msgbufsize);
- bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
- if (bitmap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
- return (-1);
- }
- if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
- (ssize_t)vmst->hdr.bitmapsize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
- free(bitmap);
+ sparse_off = off + amd64_round_page(vmst->hdr.bitmapsize) +
+ amd64_round_page(vmst->hdr.pmapsize);
+ if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
+ AMD64_PAGE_SIZE, sizeof(uint64_t)) == -1) {
+ _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += amd64_round_page(vmst->hdr.bitmapsize);
- vmst->page_map = _kvm_malloc(kd, vmst->hdr.pmapsize);
- if (vmst->page_map == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for page_map", vmst->hdr.pmapsize);
- free(bitmap);
+ if (_kvm_map(kd, vmst->hdr.pmapsize, off, (void **)&vmst->page_map) == -1) {
+ _kvm_err(kd, kd->program, "cannot map %d bytes for page_map",
+ vmst->hdr.pmapsize);
return (-1);
}
- if (pread(kd->pmfd, vmst->page_map, vmst->hdr.pmapsize, off) !=
- (ssize_t)vmst->hdr.pmapsize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page_map", vmst->hdr.pmapsize);
- free(bitmap);
- return (-1);
- }
- off += vmst->hdr.pmapsize;
-
- /* build physical address hash table for sparse pages */
- _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
- AMD64_PAGE_SIZE, sizeof(*bitmap));
- free(bitmap);
+ off += amd64_round_page(vmst->hdr.pmapsize);
return (0);
}
@@ -175,7 +155,7 @@
goto invalid;
}
a = pte & AMD64_PG_FRAME;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop_v1: physical address 0x%jx not in minidump",
@@ -186,7 +166,7 @@
return (AMD64_PAGE_SIZE - offset);
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop_v1: direct map address 0x%jx not in minidump",
@@ -235,20 +215,12 @@
}
if ((pde & AMD64_PG_PS) == 0) {
a = pde & AMD64_PG_FRAME;
- ofs = _kvm_hpt_find(&vm->hpt, a);
- if (ofs == -1) {
- _kvm_err(kd, kd->program,
- "_amd64_minidump_vatop: pt physical address 0x%jx not in minidump",
- (uintmax_t)a);
- goto invalid;
- }
/* TODO: Just read the single PTE */
- if (pread(kd->pmfd, &pt, AMD64_PAGE_SIZE, ofs) !=
- AMD64_PAGE_SIZE) {
+ if (_kvm_pt_read(kd, a, AMD64_PAGE_SIZE, pt) == -1) {
_kvm_err(kd, kd->program,
"cannot read %d bytes for page table",
AMD64_PAGE_SIZE);
- return (-1);
+ goto invalid;
}
pteindex = (va >> AMD64_PAGE_SHIFT) &
(AMD64_NPTEPG - 1);
@@ -263,7 +235,7 @@
a = pde & AMD64_PG_PS_FRAME;
a += (va & AMD64_PDRMASK) ^ offset;
}
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop: physical address 0x%jx not in minidump",
@@ -274,7 +246,7 @@
return (AMD64_PAGE_SIZE - offset);
} else if (va >= vm->hdr.dmapbase && va < vm->hdr.dmapend) {
a = (va - vm->hdr.dmapbase) & ~AMD64_PAGE_MASK;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_amd64_minidump_vatop: direct map address 0x%jx not in minidump",
Index: lib/libkvm/kvm_minidump_arm.c
===================================================================
--- lib/libkvm/kvm_minidump_arm.c
+++ lib/libkvm/kvm_minidump_arm.c
@@ -51,7 +51,6 @@
struct vmstate {
struct minidumphdr hdr;
- struct hpt hpt;
void *ptemap;
unsigned char ei_data;
};
@@ -69,9 +68,7 @@
{
struct vmstate *vm = kd->vmst;
- _kvm_hpt_free(&vm->hpt);
- if (vm->ptemap)
- free(vm->ptemap);
+ _kvm_unmap(vm->ptemap, vm->hdr.ptesize);
free(vm);
kd->vmst = NULL;
}
@@ -80,8 +77,7 @@
_arm_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- uint32_t *bitmap;
- off_t off;
+ off_t off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -122,44 +118,21 @@
/* Skip header and msgbuf */
off = ARM_PAGE_SIZE + arm_round_page(vmst->hdr.msgbufsize);
- bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
- if (bitmap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for "
- "bitmap", vmst->hdr.bitmapsize);
- return (-1);
- }
-
- if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
- (ssize_t)vmst->hdr.bitmapsize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
- vmst->hdr.bitmapsize);
- free(bitmap);
+ sparse_off = off + arm_round_page(vmst->hdr.bitmapsize) +
+ arm_round_page(vmst->hdr.ptesize);
+ if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
+ ARM_PAGE_SIZE, sizeof(uint32_t)) == -1) {
+ _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += arm_round_page(vmst->hdr.bitmapsize);
- vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
- if (vmst->ptemap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for "
- "ptemap", vmst->hdr.ptesize);
- free(bitmap);
- return (-1);
- }
-
- if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
- (ssize_t)vmst->hdr.ptesize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
+ if (_kvm_map(kd, vmst->hdr.ptesize, off, (void **)&vmst->ptemap) == -1) {
+ _kvm_err(kd, kd->program, "cannot map %d bytes for ptemap",
vmst->hdr.ptesize);
- free(bitmap);
return (-1);
}
-
- off += vmst->hdr.ptesize;
-
- /* Build physical address hash table for sparse pages */
- _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
- ARM_PAGE_SIZE, sizeof(*bitmap));
- free(bitmap);
+ off += arm_round_page(vmst->hdr.ptesize);
return (0);
}
@@ -184,6 +157,8 @@
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> ARM_PAGE_SHIFT;
+ if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap))
+ goto invalid;
pte = _kvm32toh(kd, ptemap[pteindex]);
if ((pte & ARM_L2_TYPE_MASK) == ARM_L2_TYPE_INV) {
_kvm_err(kd, kd->program,
@@ -207,7 +182,7 @@
a = pte & ARM_L2_S_FRAME;
}
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_arm_minidump_kvatop: "
"physical address 0x%jx not in minidump",
Index: lib/libkvm/kvm_minidump_i386.c
===================================================================
--- lib/libkvm/kvm_minidump_i386.c
+++ lib/libkvm/kvm_minidump_i386.c
@@ -49,7 +49,6 @@
struct vmstate {
struct minidumphdr hdr;
- struct hpt hpt;
void *ptemap;
};
@@ -66,9 +65,7 @@
{
struct vmstate *vm = kd->vmst;
- _kvm_hpt_free(&vm->hpt);
- if (vm->ptemap)
- free(vm->ptemap);
+ _kvm_unmap(vm->ptemap, vm->hdr.ptesize);
free(vm);
kd->vmst = NULL;
}
@@ -77,8 +74,7 @@
_i386_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- uint32_t *bitmap;
- off_t off;
+ off_t off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -110,37 +106,21 @@
/* Skip header and msgbuf */
off = I386_PAGE_SIZE + i386_round_page(vmst->hdr.msgbufsize);
- bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
- if (bitmap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for bitmap", vmst->hdr.bitmapsize);
- return (-1);
- }
- if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
- (ssize_t)vmst->hdr.bitmapsize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap", vmst->hdr.bitmapsize);
- free(bitmap);
+ sparse_off = off + i386_round_page(vmst->hdr.bitmapsize) +
+ i386_round_page(vmst->hdr.ptesize);
+ if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
+ I386_PAGE_SIZE, sizeof(uint32_t)) == -1) {
+ _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += i386_round_page(vmst->hdr.bitmapsize);
- vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
- if (vmst->ptemap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for ptemap", vmst->hdr.ptesize);
- free(bitmap);
- return (-1);
- }
- if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
- (ssize_t)vmst->hdr.ptesize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap", vmst->hdr.ptesize);
- free(bitmap);
+ if (_kvm_map(kd, vmst->hdr.ptesize, off, (void **)&vmst->ptemap) == -1) {
+ _kvm_err(kd, kd->program, "cannot map %d bytes for ptemap",
+ vmst->hdr.ptesize);
return (-1);
}
- off += vmst->hdr.ptesize;
-
- /* build physical address hash table for sparse pages */
- _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
- I386_PAGE_SIZE, sizeof(*bitmap));
- free(bitmap);
+ off += i386_round_page(vmst->hdr.ptesize);
return (0);
}
@@ -162,6 +142,8 @@
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
+ if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap))
+ goto invalid;
pte = le64toh(ptemap[pteindex]);
if ((pte & I386_PG_V) == 0) {
_kvm_err(kd, kd->program,
@@ -169,7 +151,7 @@
goto invalid;
}
a = pte & I386_PG_FRAME_PAE;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_i386_minidump_vatop_pae: physical address 0x%jx not in minidump",
@@ -207,6 +189,8 @@
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> I386_PAGE_SHIFT;
+ if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap))
+ goto invalid;
pte = le32toh(ptemap[pteindex]);
if ((pte & I386_PG_V) == 0) {
_kvm_err(kd, kd->program,
@@ -214,7 +198,7 @@
goto invalid;
}
a = pte & I386_PG_FRAME;
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program,
"_i386_minidump_vatop: physical address 0x%jx not in minidump",
Index: lib/libkvm/kvm_minidump_mips.c
===================================================================
--- lib/libkvm/kvm_minidump_mips.c
+++ lib/libkvm/kvm_minidump_mips.c
@@ -52,7 +52,6 @@
struct vmstate {
struct minidumphdr hdr;
- struct hpt hpt;
void *ptemap;
int pte_size;
};
@@ -74,9 +73,7 @@
{
struct vmstate *vm = kd->vmst;
- _kvm_hpt_free(&vm->hpt);
- if (vm->ptemap)
- free(vm->ptemap);
+ _kvm_unmap(vm->ptemap, vm->hdr.ptesize);
free(vm);
kd->vmst = NULL;
}
@@ -85,8 +82,7 @@
_mips_minidump_initvtop(kvm_t *kd)
{
struct vmstate *vmst;
- uint32_t *bitmap;
- off_t off;
+ off_t off, sparse_off;
vmst = _kvm_malloc(kd, sizeof(*vmst));
if (vmst == NULL) {
@@ -129,44 +125,21 @@
/* Skip header and msgbuf */
off = MIPS_PAGE_SIZE + mips_round_page(vmst->hdr.msgbufsize);
- bitmap = _kvm_malloc(kd, vmst->hdr.bitmapsize);
- if (bitmap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for "
- "bitmap", vmst->hdr.bitmapsize);
- return (-1);
- }
-
- if (pread(kd->pmfd, bitmap, vmst->hdr.bitmapsize, off) !=
- (ssize_t)vmst->hdr.bitmapsize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for page bitmap",
- vmst->hdr.bitmapsize);
- free(bitmap);
+ sparse_off = off + mips_round_page(vmst->hdr.bitmapsize) +
+ mips_round_page(vmst->hdr.ptesize);
+ if (_kvm_pt_init(kd, vmst->hdr.bitmapsize, off, sparse_off,
+ MIPS_PAGE_SIZE, sizeof(uint32_t)) == -1) {
+ _kvm_err(kd, kd->program, "cannot load core bitmap");
return (-1);
}
off += mips_round_page(vmst->hdr.bitmapsize);
- vmst->ptemap = _kvm_malloc(kd, vmst->hdr.ptesize);
- if (vmst->ptemap == NULL) {
- _kvm_err(kd, kd->program, "cannot allocate %d bytes for "
- "ptemap", vmst->hdr.ptesize);
- free(bitmap);
- return (-1);
- }
-
- if (pread(kd->pmfd, vmst->ptemap, vmst->hdr.ptesize, off) !=
- (ssize_t)vmst->hdr.ptesize) {
- _kvm_err(kd, kd->program, "cannot read %d bytes for ptemap",
+ if (_kvm_map(kd, vmst->hdr.ptesize, off, (void **)&vmst->ptemap) == -1) {
+ _kvm_err(kd, kd->program, "cannot map %d bytes for ptemap",
vmst->hdr.ptesize);
- free(bitmap);
return (-1);
}
-
- off += vmst->hdr.ptesize;
-
- /* Build physical address hash table for sparse pages */
- _kvm_hpt_init(kd, &vmst->hpt, bitmap, vmst->hdr.bitmapsize, off,
- MIPS_PAGE_SIZE, sizeof(*bitmap));
- free(bitmap);
+ off += mips_round_page(vmst->hdr.ptesize);
return (0);
}
@@ -221,9 +194,13 @@
if (va >= vm->hdr.kernbase) {
pteindex = (va - vm->hdr.kernbase) >> MIPS_PAGE_SHIFT;
if (vm->pte_size == 64) {
+ if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap64))
+ goto invalid;
pte = _kvm64toh(kd, ptemap64[pteindex]);
a = MIPS64_PTE_TO_PA(pte);
} else {
+ if (pteindex >= vm->hdr.ptesize / sizeof(*ptemap32))
+ goto invalid;
pte = _kvm32toh(kd, ptemap32[pteindex]);
a = MIPS32_PTE_TO_PA(pte);
}
@@ -239,7 +216,7 @@
}
found:
- ofs = _kvm_hpt_find(&vm->hpt, a);
+ ofs = _kvm_pt_find(kd, a);
if (ofs == -1) {
_kvm_err(kd, kd->program, "_mips_minidump_kvatop: physical "
"address 0x%jx not in minidump", (uintmax_t)a);
Index: lib/libkvm/kvm_private.h
===================================================================
--- lib/libkvm/kvm_private.h
+++ lib/libkvm/kvm_private.h
@@ -97,23 +97,22 @@
uintptr_t *dpcpu_off; /* base array, indexed by CPU ID */
u_int dpcpu_curcpu; /* CPU we're currently working with */
kvaddr_t dpcpu_curoff; /* dpcpu base of current CPU */
-};
-/*
- * Page table hash used by minidump backends to map physical addresses
- * to file offsets.
- */
-struct hpte {
- struct hpte *next;
- uint64_t pa;
- off_t off;
+ /* Page table lookup structures. */
+ uint64_t *pt_map;
+ size_t pt_map_size;
+ off_t pt_sparse_off;
+ uint64_t pt_sparse_size;
+ void *pt_sparse_pages;
+ uint32_t *pt_popcounts;
+ unsigned int pt_page_size;
+ unsigned int pt_word_size;
};
-#define HPT_SIZE 1024
-
-struct hpt {
- struct hpte *hpt_head[HPT_SIZE];
-};
+/* Page table lookup constants. */
+#define POPCOUNT_BITS 1024
+#define BITS_IN(v) (sizeof(v) * NBBY)
+#define POPCOUNTS_IN(v) (POPCOUNT_BITS / BITS_IN(v))
/*
* Functions used internally by kvm, but across kvm modules.
@@ -154,6 +153,9 @@
int _kvm_probe_elf_kernel(kvm_t *, int, int);
int _kvm_is_minidump(kvm_t *);
int _kvm_read_core_phdrs(kvm_t *, size_t *, GElf_Phdr **);
-void _kvm_hpt_init(kvm_t *, struct hpt *, void *, size_t, off_t, int, int);
-off_t _kvm_hpt_find(struct hpt *, uint64_t);
-void _kvm_hpt_free(struct hpt *);
+int _kvm_pt_init(kvm_t *, size_t, off_t, off_t, int, int);
+off_t _kvm_pt_find(kvm_t *, uint64_t);
+
+int _kvm_map(kvm_t *, size_t, off_t, void **);
+void _kvm_unmap(void *, size_t);
+int _kvm_pt_read(kvm_t *, uint64_t, size_t, void *);
Index: lib/libkvm/kvm_private.c
===================================================================
--- lib/libkvm/kvm_private.c
+++ lib/libkvm/kvm_private.c
@@ -34,12 +34,6 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#if defined(LIBC_SCCS) && !defined(lint)
-#if 0
-static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94";
-#endif
-#endif /* LIBC_SCCS and not lint */
-
#include <sys/param.h>
#include <sys/fnv_hash.h>
@@ -52,6 +46,7 @@
#include <net/vnet.h>
+#include <assert.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
@@ -61,63 +56,19 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <stdarg.h>
+
+#include <sys/mman.h>
#include "kvm_private.h"
-SET_DECLARE(kvm_arch, struct kvm_arch);
+/*
+ * Routines private to libkvm.
+ */
/* from src/lib/libc/gen/nlist.c */
int __fdnlist(int, struct nlist *);
-static int
-kvm_fdnlist(kvm_t *kd, struct kvm_nlist *list)
-{
- kvaddr_t addr;
- int error, nfail;
-
- if (kd->resolve_symbol == NULL) {
- struct nlist *nl;
- int count, i;
-
- for (count = 0; list[count].n_name != NULL &&
- list[count].n_name[0] != '\0'; count++)
- ;
- nl = calloc(count + 1, sizeof(*nl));
- for (i = 0; i < count; i++)
- nl[i].n_name = list[i].n_name;
- nfail = __fdnlist(kd->nlfd, nl);
- for (i = 0; i < count; i++) {
- list[i].n_type = nl[i].n_type;
- list[i].n_value = nl[i].n_value;
- }
- free(nl);
- return (nfail);
- }
-
- nfail = 0;
- while (list->n_name != NULL && list->n_name[0] != '\0') {
- error = kd->resolve_symbol(list->n_name, &addr);
- if (error != 0) {
- nfail++;
- list->n_value = 0;
- list->n_type = 0;
- } else {
- list->n_value = addr;
- list->n_type = N_DATA | N_EXT;
- }
- list++;
- }
- return (nfail);
-}
-
-char *
-kvm_geterr(kvm_t *kd)
-{
- return (kd->errbuf);
-}
-
-#include <stdarg.h>
-
/*
* Report an error using printf style arguments. "program" is kd->program
* on hard errors, and 0 on soft errors, so that under sun error emulation,
@@ -174,40 +125,23 @@
return (p);
}
-static int
-_kvm_read_kernel_ehdr(kvm_t *kd)
+int
+_kvm_map(kvm_t *kd, size_t len, off_t off, void **addrp)
{
- Elf *elf;
+ void *addr;
- if (elf_version(EV_CURRENT) == EV_NONE) {
- _kvm_err(kd, kd->program, "Unsupported libelf");
- return (-1);
- }
- elf = elf_begin(kd->nlfd, ELF_C_READ, NULL);
- if (elf == NULL) {
- _kvm_err(kd, kd->program, "%s", elf_errmsg(0));
- return (-1);
- }
- if (elf_kind(elf) != ELF_K_ELF) {
- _kvm_err(kd, kd->program, "kernel is not an ELF file");
- return (-1);
- }
- if (gelf_getehdr(elf, &kd->nlehdr) == NULL) {
- _kvm_err(kd, kd->program, "%s", elf_errmsg(0));
- elf_end(elf);
+ addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, kd->pmfd, off);
+ if (addr == MAP_FAILED)
return (-1);
- }
- elf_end(elf);
+ *addrp = addr;
+ return (0);
+}
- switch (kd->nlehdr.e_ident[EI_DATA]) {
- case ELFDATA2LSB:
- case ELFDATA2MSB:
- return (0);
- default:
- _kvm_err(kd, kd->program,
- "unsupported ELF data encoding for kernel");
- return (-1);
- }
+void
+_kvm_unmap(void *addr, size_t len)
+{
+ if (addr != NULL)
+ (void) munmap(addr, len);
}
int
@@ -301,253 +235,251 @@
return (-1);
}
-static void
-_kvm_hpt_insert(struct hpt *hpt, uint64_t pa, off_t off)
+/*
+ * Transform v such that only bits [bit0, bitN) may be set. Generates a
+ * bitmask covering the number of bits, then shifts so +bit0+ is the first.
+ */
+static uint64_t
+bitmask_range(uint64_t v, uint64_t bit0, uint64_t bitN)
{
- struct hpte *hpte;
- uint32_t fnv = FNV1_32_INIT;
-
- fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
- fnv &= (HPT_SIZE - 1);
- hpte = malloc(sizeof(*hpte));
- hpte->pa = pa;
- hpte->off = off;
- hpte->next = hpt->hpt_head[fnv];
- hpt->hpt_head[fnv] = hpte;
-}
+ if (bit0 == 0 && bitN == BITS_IN(v))
+ return (v);
-void
-_kvm_hpt_init(kvm_t *kd, struct hpt *hpt, void *base, size_t len, off_t off,
- int page_size, int word_size)
-{
- uint64_t bits, idx, pa;
- uint64_t *base64;
- uint32_t *base32;
-
- base64 = base;
- base32 = base;
- for (idx = 0; idx < len / word_size; idx++) {
- if (word_size == sizeof(uint64_t))
- bits = _kvm64toh(kd, base64[idx]);
- else
- bits = _kvm32toh(kd, base32[idx]);
- pa = idx * word_size * NBBY * page_size;
- for (; bits != 0; bits >>= 1, pa += page_size) {
- if ((bits & 1) == 0)
- continue;
- _kvm_hpt_insert(hpt, pa, off);
- off += page_size;
- }
- }
+ return (v & (((1ULL << (bitN - bit0)) - 1ULL) << bit0));
}
-off_t
-_kvm_hpt_find(struct hpt *hpt, uint64_t pa)
+/*
+ * Returns the number of bits set in a given u64. For explanation, see:
+ * https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ * https://en.wikipedia.org/wiki/Hamming_weight
+ *
+ * Can also use POPCNT CPU instruction, but this is portable and fast enough.
+ */
+static uint64_t
+popcount64(uint64_t v)
{
- struct hpte *hpte;
- uint32_t fnv = FNV1_32_INIT;
-
- fnv = fnv_32_buf(&pa, sizeof(pa), fnv);
- fnv &= (HPT_SIZE - 1);
- for (hpte = hpt->hpt_head[fnv]; hpte != NULL; hpte = hpte->next) {
- if (pa == hpte->pa)
- return (hpte->off);
- }
- return (-1);
+ v -= ((v >> 1) & 0x5555555555555555ULL);
+ v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
+ v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
+ return ((uint64_t)(v * 0x0101010101010101ULL) >> 56);
}
-void
-_kvm_hpt_free(struct hpt *hpt)
+/*
+ * Returns the number of bits in a given byte array range starting at a
+ * given base, from bit0 to bitN. bit0 may be non-zero in the case of
+ * counting backwards from bitN.
+ */
+static uint64_t
+popcount_bytes(uint64_t *addr, uint32_t bit0, uint32_t bitN)
{
- struct hpte *hpte, *next;
- int i;
+ uint32_t res = bitN - bit0;
+ uint64_t count = 0;
+ uint32_t bound;
- for (i = 0; i < HPT_SIZE; i++) {
- for (hpte = hpt->hpt_head[i]; hpte != NULL; hpte = next) {
- next = hpte->next;
- free(hpte);
- }
+ /* Align to 64-bit boundary on the left side if needed. */
+ if ((bit0 % BITS_IN(*addr)) != 0) {
+ bound = MIN(bitN, roundup2(bit0, BITS_IN(*addr)));
+ count += popcount64(bitmask_range(*addr, bit0, bound));
+ res -= (bound - bit0);
+ addr++;
+ }
+
+ while (res > 0) {
+ bound = MIN(res, BITS_IN(*addr));
+ count += popcount64(bitmask_range(*addr, 0, bound));
+ res -= bound;
+ addr++;
}
+
+ return (count);
}
-static kvm_t *
-_kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
+int
+_kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off,
+ int page_size, int word_size)
{
- struct kvm_arch **parch;
- struct stat st;
-
- kd->vmfd = -1;
- kd->pmfd = -1;
- kd->nlfd = -1;
- kd->vmst = NULL;
- kd->procbase = NULL;
- kd->argspc = NULL;
- kd->argv = NULL;
-
- if (uf == NULL)
- uf = getbootfile();
- else if (strlen(uf) >= MAXPATHLEN) {
- _kvm_err(kd, kd->program, "exec file name too long");
- goto failed;
- }
- if (flag & ~O_RDWR) {
- _kvm_err(kd, kd->program, "bad flags arg");
- goto failed;
- }
- if (mf == NULL)
- mf = _PATH_MEM;
+ uint64_t *addr;
+ uint32_t *popcount_bin;
+ int bin_popcounts = 0;
+ uint64_t pc_bins, res;
- if ((kd->pmfd = open(mf, flag | O_CLOEXEC, 0)) < 0) {
- _kvm_syserr(kd, kd->program, "%s", mf);
- goto failed;
- }
- if (fstat(kd->pmfd, &st) < 0) {
- _kvm_syserr(kd, kd->program, "%s", mf);
- goto failed;
- }
- if (S_ISREG(st.st_mode) && st.st_size <= 0) {
- errno = EINVAL;
- _kvm_syserr(kd, kd->program, "empty file");
- goto failed;
- }
- if (S_ISCHR(st.st_mode)) {
- /*
- * If this is a character special device, then check that
- * it's /dev/mem. If so, open kmem too. (Maybe we should
- * make it work for either /dev/mem or /dev/kmem -- in either
- * case you're working with a live kernel.)
- */
- if (strcmp(mf, _PATH_DEVNULL) == 0) {
- kd->vmfd = open(_PATH_DEVNULL, O_RDONLY | O_CLOEXEC);
- return (kd);
- } else if (strcmp(mf, _PATH_MEM) == 0) {
- if ((kd->vmfd = open(_PATH_KMEM, flag | O_CLOEXEC)) <
- 0) {
- _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
- goto failed;
- }
- return (kd);
- }
- }
/*
- * This is a crash dump.
- * Open the namelist fd and determine the architecture.
+ * Map the bitmap specified by the arguments.
*/
- if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) {
- _kvm_syserr(kd, kd->program, "%s", uf);
- goto failed;
- }
- if (_kvm_read_kernel_ehdr(kd) < 0)
- goto failed;
- if (strncmp(mf, _PATH_FWMEM, strlen(_PATH_FWMEM)) == 0)
- kd->rawdump = 1;
- SET_FOREACH(parch, kvm_arch) {
- if ((*parch)->ka_probe(kd)) {
- kd->arch = *parch;
- break;
- }
- }
- if (kd->arch == NULL) {
- _kvm_err(kd, kd->program, "unsupported architecture");
- goto failed;
- }
+ if (_kvm_map(kd, map_len, map_off, (void **)&kd->pt_map) == -1)
+ return (-1);
+ kd->pt_map_size = map_len;
/*
- * Non-native kernels require a symbol resolver.
+ * Generate a popcount cache for every POPCOUNT_BITS in the bitmap,
+ * so lookups only have to calculate the number of bits set between
+ * a cache point and their bit. This reduces lookups to O(1),
+ * without significantly increasing memory requirements.
+ *
+ * Round up the number of bins so that 'upper half' lookups work for
+ * the final bin, if needed. The first popcount is 0, since no bits
+ * precede bit 0, so add 1 for that also. Without this, extra work
+ * would be needed to handle the first PTEs in _kvm_pt_find().
*/
- if (!kd->arch->ka_native(kd) && kd->resolve_symbol == NULL) {
- _kvm_err(kd, kd->program,
- "non-native kernel requires a symbol resolver");
- goto failed;
+ addr = kd->pt_map;
+ res = map_len;
+ pc_bins = 1 + (res * NBBY + POPCOUNT_BITS / 2) / POPCOUNT_BITS;
+ kd->pt_popcounts = calloc(pc_bins, sizeof(uint32_t));
+ if (kd->pt_popcounts == NULL)
+ return (-1);
+
+ for (popcount_bin = &kd->pt_popcounts[1]; res > 0;
+ addr++, res -= sizeof(*addr)) {
+ *popcount_bin += popcount_bytes(addr, 0,
+ MIN(res * NBBY, BITS_IN(*addr)));
+ if (++bin_popcounts == POPCOUNTS_IN(*addr)) {
+ popcount_bin++;
+ *popcount_bin = *(popcount_bin - 1);
+ bin_popcounts = 0;
+ }
}
+ assert(pc_bins * sizeof(*popcount_bin) ==
+ ((uintptr_t)popcount_bin - (uintptr_t)kd->pt_popcounts));
+
/*
- * Initialize the virtual address translation machinery.
- */
- if (kd->arch->ka_initvtop(kd) < 0)
- goto failed;
- return (kd);
-failed:
- /*
- * Copy out the error if doing sane error semantics.
+ * Map the sparse page map. This is useful for reading specific
+ * pages via _kvm_pt_read.
*/
- if (errout != NULL)
- strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX);
- (void)kvm_close(kd);
+ kd->pt_sparse_off = sparse_off;
+ kd->pt_sparse_size = (uint64_t)*popcount_bin * PAGE_SIZE;
+ if (_kvm_map(kd, kd->pt_sparse_size, kd->pt_sparse_off,
+ (void **)&kd->pt_sparse_pages) == -1)
+ return (-1);
+
+ kd->pt_page_size = page_size;
+ kd->pt_word_size = word_size;
return (0);
}
-kvm_t *
-kvm_openfiles(const char *uf, const char *mf, const char *sf __unused, int flag,
- char *errout)
+/*
+ * Find the offset for the given physical page address; returns -1 otherwise.
+ *
+ * A page's offset is represented by the sparse page base offset plus the
+ * number of bits set before its bit multiplied by PAGE_SIZE. This means
+ * that if a page exists in the dump, it's necessary to know how many pages
+ * in the dump precede it. Reduce this O(n) counting to O(1) by caching the
+ * number of bits set at POPCOUNT_BITS intervals.
+ *
+ * Then to find the number of pages before the requested address, simply
+ * index into the cache and count the number of bits set between that cache
+ * bin and the page's bit. Halve the number of bytes that have to be
+ * checked by also counting down from the next higher bin if it's closer.
+ */
+off_t
+_kvm_pt_find(kvm_t *kd, uint64_t pa)
{
- kvm_t *kd;
+ uint64_t *bitmap = kd->pt_map;
+ uint64_t pte_bit_id = pa / PAGE_SIZE;
+ uint64_t pte_u64 = pte_bit_id / BITS_IN(*bitmap);
+ uint64_t popcount_id = pte_bit_id / POPCOUNT_BITS;
+ uint64_t pte_mask = 1ULL << (pte_bit_id % BITS_IN(*bitmap));
+ uint64_t bitN;
+ uint32_t count;
+
+ /* Check whether the page address requested is in the dump. */
+ if (pte_bit_id >= (kd->pt_map_size * NBBY) ||
+ (bitmap[pte_u64] & pte_mask) == 0)
+ return (-1);
- if ((kd = calloc(1, sizeof(*kd))) == NULL) {
- if (errout != NULL)
- (void)strlcpy(errout, strerror(errno),
- _POSIX2_LINE_MAX);
- return (0);
+ /*
+ * Add/sub popcounts from the bitmap until the PTE's bit is reached.
+ * For bits that are in the upper half between the calculated
+ * popcount id and the next one, use the next one and subtract to
+ * minimize the number of popcounts required.
+ */
+ if ((pte_bit_id % POPCOUNT_BITS) < (POPCOUNT_BITS / 2)) {
+ count = kd->pt_popcounts[popcount_id] + popcount_bytes(
+ bitmap + popcount_id * POPCOUNTS_IN(*bitmap),
+ 0, pte_bit_id - popcount_id * POPCOUNT_BITS);
+ } else {
+ /*
+ * Counting in reverse is trickier, since we must avoid
+ * reading from bytes that are not in range, and invert.
+ */
+ uint64_t pte_u64_bit_off = pte_u64 * BITS_IN(*bitmap);
+
+ popcount_id++;
+ bitN = MIN(popcount_id * POPCOUNT_BITS,
+ kd->pt_map_size * BITS_IN(uint8_t));
+ count = kd->pt_popcounts[popcount_id] - popcount_bytes(
+ bitmap + pte_u64,
+ pte_bit_id - pte_u64_bit_off, bitN - pte_u64_bit_off);
}
- return (_kvm_open(kd, uf, mf, flag, errout));
-}
-kvm_t *
-kvm_open(const char *uf, const char *mf, const char *sf __unused, int flag,
- const char *errstr)
-{
- kvm_t *kd;
+ /*
+ * This can only happen if the core is truncated. Treat these
+ * entries as if they don't exist, since their backing doesn't.
+ */
+ if (count >= (kd->pt_sparse_size / PAGE_SIZE))
+ return (-1);
- if ((kd = calloc(1, sizeof(*kd))) == NULL) {
- if (errstr != NULL)
- (void)fprintf(stderr, "%s: %s\n",
- errstr, strerror(errno));
- return (0);
- }
- kd->program = errstr;
- return (_kvm_open(kd, uf, mf, flag, NULL));
+ return (kd->pt_sparse_off + (uint64_t)count * PAGE_SIZE);
}
-kvm_t *
-kvm_open2(const char *uf, const char *mf, int flag, char *errout,
- int (*resolver)(const char *, kvaddr_t *))
+int
+_kvm_pt_read(kvm_t *kd, uint64_t pa, size_t length, void *buf)
{
- kvm_t *kd;
+ off_t off;
+ void *src;
- if ((kd = calloc(1, sizeof(*kd))) == NULL) {
- if (errout != NULL)
- (void)strlcpy(errout, strerror(errno),
- _POSIX2_LINE_MAX);
- return (0);
- }
- kd->resolve_symbol = resolver;
- return (_kvm_open(kd, uf, mf, flag, errout));
+ off = _kvm_pt_find(kd, pa);
+ /* Make sure the request doesn't go off the end. */
+ if (off == -1)
+ return (-1);
+ off -= kd->pt_sparse_off;
+ if ((uint64_t)off + length > kd->pt_sparse_size)
+ return (-1);
+
+ src = (void *)((uintptr_t)kd->pt_sparse_pages + off);
+ memcpy(buf, src, length);
+ return (0);
}
-int
-kvm_close(kvm_t *kd)
+static int
+kvm_fdnlist(kvm_t *kd, struct kvm_nlist *list)
{
- int error = 0;
-
- if (kd->vmst != NULL)
- kd->arch->ka_freevtop(kd);
- if (kd->pmfd >= 0)
- error |= close(kd->pmfd);
- if (kd->vmfd >= 0)
- error |= close(kd->vmfd);
- if (kd->nlfd >= 0)
- error |= close(kd->nlfd);
- if (kd->procbase != 0)
- free((void *)kd->procbase);
- if (kd->argbuf != 0)
- free((void *) kd->argbuf);
- if (kd->argspc != 0)
- free((void *) kd->argspc);
- if (kd->argv != 0)
- free((void *)kd->argv);
- free((void *)kd);
+ kvaddr_t addr;
+ int error, nfail;
- return (0);
+ if (kd->resolve_symbol == NULL) {
+ struct nlist *nl;
+ int count, i;
+
+ for (count = 0; list[count].n_name != NULL &&
+ list[count].n_name[0] != '\0'; count++)
+ ;
+ nl = calloc(count + 1, sizeof(*nl));
+ for (i = 0; i < count; i++)
+ nl[i].n_name = list[i].n_name;
+ nfail = __fdnlist(kd->nlfd, nl);
+ for (i = 0; i < count; i++) {
+ list[i].n_type = nl[i].n_type;
+ list[i].n_value = nl[i].n_value;
+ }
+ free(nl);
+ return (nfail);
+ }
+
+ nfail = 0;
+ while (list->n_name != NULL && list->n_name[0] != '\0') {
+ error = kd->resolve_symbol(list->n_name, &addr);
+ if (error != 0) {
+ nfail++;
+ list->n_value = 0;
+ list->n_type = 0;
+ } else {
+ list->n_value = addr;
+ list->n_type = N_DATA | N_EXT;
+ }
+ list++;
+ }
+ return (nfail);
}
/*
@@ -750,152 +682,3 @@
_kvm_syserr(kd, kd->program, "kvm_nlist");
return (error);
}
-
-int
-kvm_nlist2(kvm_t *kd, struct kvm_nlist *nl)
-{
-
- /*
- * If called via the public interface, permit initialization of
- * further virtualized modules on demand.
- */
- return (_kvm_nlist(kd, nl, 1));
-}
-
-int
-kvm_nlist(kvm_t *kd, struct nlist *nl)
-{
- struct kvm_nlist *kl;
- int count, i, nfail;
-
- /*
- * Avoid reporting truncated addresses by failing for non-native
- * cores.
- */
- if (!kvm_native(kd)) {
- _kvm_err(kd, kd->program, "kvm_nlist of non-native vmcore");
- return (-1);
- }
-
- for (count = 0; nl[count].n_name != NULL && nl[count].n_name[0] != '\0';
- count++)
- ;
- if (count == 0)
- return (0);
- kl = calloc(count + 1, sizeof(*kl));
- for (i = 0; i < count; i++)
- kl[i].n_name = nl[i].n_name;
- nfail = kvm_nlist2(kd, kl);
- for (i = 0; i < count; i++) {
- nl[i].n_type = kl[i].n_type;
- nl[i].n_other = 0;
- nl[i].n_desc = 0;
- nl[i].n_value = kl[i].n_value;
- }
- return (nfail);
-}
-
-ssize_t
-kvm_read(kvm_t *kd, u_long kva, void *buf, size_t len)
-{
-
- return (kvm_read2(kd, kva, buf, len));
-}
-
-ssize_t
-kvm_read2(kvm_t *kd, kvaddr_t kva, void *buf, size_t len)
-{
- int cc;
- ssize_t cr;
- off_t pa;
- char *cp;
-
- if (ISALIVE(kd)) {
- /*
- * We're using /dev/kmem. Just read straight from the
- * device and let the active kernel do the address translation.
- */
- errno = 0;
- if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
- _kvm_err(kd, 0, "invalid address (0x%jx)",
- (uintmax_t)kva);
- return (-1);
- }
- cr = read(kd->vmfd, buf, len);
- if (cr < 0) {
- _kvm_syserr(kd, 0, "kvm_read");
- return (-1);
- } else if (cr < (ssize_t)len)
- _kvm_err(kd, kd->program, "short read");
- return (cr);
- }
-
- cp = buf;
- while (len > 0) {
- cc = kd->arch->ka_kvatop(kd, kva, &pa);
- if (cc == 0)
- return (-1);
- if (cc > (ssize_t)len)
- cc = len;
- errno = 0;
- if (lseek(kd->pmfd, pa, 0) == -1 && errno != 0) {
- _kvm_syserr(kd, 0, _PATH_MEM);
- break;
- }
- cr = read(kd->pmfd, cp, cc);
- if (cr < 0) {
- _kvm_syserr(kd, kd->program, "kvm_read");
- break;
- }
- /*
- * If ka_kvatop returns a bogus value or our core file is
- * truncated, we might wind up seeking beyond the end of the
- * core file in which case the read will return 0 (EOF).
- */
- if (cr == 0)
- break;
- cp += cr;
- kva += cr;
- len -= cr;
- }
-
- return (cp - (char *)buf);
-}
-
-ssize_t
-kvm_write(kvm_t *kd, u_long kva, const void *buf, size_t len)
-{
- int cc;
-
- if (ISALIVE(kd)) {
- /*
- * Just like kvm_read, only we write.
- */
- errno = 0;
- if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) {
- _kvm_err(kd, 0, "invalid address (%lx)", kva);
- return (-1);
- }
- cc = write(kd->vmfd, buf, len);
- if (cc < 0) {
- _kvm_syserr(kd, 0, "kvm_write");
- return (-1);
- } else if ((size_t)cc < len)
- _kvm_err(kd, kd->program, "short write");
- return (cc);
- } else {
- _kvm_err(kd, kd->program,
- "kvm_write not implemented for dead kernels");
- return (-1);
- }
- /* NOTREACHED */
-}
-
-int
-kvm_native(kvm_t *kd)
-{
-
- if (ISALIVE(kd))
- return (1);
- return (kd->arch->ka_native(kd));
-}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Dec 18, 6:47 AM (19 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27014536
Default Alt Text
D6804.diff (50 KB)
Attached To
Mode
D6804: libkvm: performance improvements for minidump read routines
Attached
Detach File
Event Timeline
Log In to Comment