Changeset View
Changeset View
Standalone View
Standalone View
head/lib/libkvm/kvm_private.c
Show First 20 Lines • Show All 284 Lines • ▼ Show 20 Lines | _kvm_map_get(kvm_t *kd, u_long pa, unsigned int page_size) | ||||
addr = (uintptr_t)kd->page_map + off; | addr = (uintptr_t)kd->page_map + off; | ||||
if (off >= kd->pt_sparse_off) | if (off >= kd->pt_sparse_off) | ||||
addr = (uintptr_t)kd->sparse_map + (off - kd->pt_sparse_off); | addr = (uintptr_t)kd->sparse_map + (off - kd->pt_sparse_off); | ||||
return (void *)addr; | return (void *)addr; | ||||
} | } | ||||
int | int | ||||
_kvm_pt_init(kvm_t *kd, size_t map_len, off_t map_off, off_t sparse_off, | _kvm_pt_init(kvm_t *kd, size_t dump_avail_size, off_t dump_avail_off, | ||||
int page_size, int word_size) | size_t map_len, off_t map_off, off_t sparse_off, int page_size, | ||||
int word_size) | |||||
{ | { | ||||
uint64_t *addr; | uint64_t *addr; | ||||
uint32_t *popcount_bin; | uint32_t *popcount_bin; | ||||
int bin_popcounts = 0; | int bin_popcounts = 0; | ||||
uint64_t pc_bins, res; | uint64_t pc_bins, res; | ||||
ssize_t rd; | ssize_t rd; | ||||
kd->dump_avail_size = dump_avail_size; | |||||
if (dump_avail_size > 0) { | |||||
kd->dump_avail = mmap(NULL, kd->dump_avail_size, PROT_READ, | |||||
MAP_PRIVATE, kd->pmfd, dump_avail_off); | |||||
} else { | |||||
/* | /* | ||||
* Older version minidumps don't provide dump_avail[], | |||||
* so the bitmap is fully populated from 0 to | |||||
* last_pa. Create an implied dump_avail that | |||||
* expresses this. | |||||
*/ | |||||
kd->dump_avail = calloc(4, word_size); | |||||
if (word_size == sizeof(uint32_t)) { | |||||
((uint32_t *)kd->dump_avail)[1] = _kvm32toh(kd, | |||||
map_len * 8 * page_size); | |||||
} else { | |||||
kd->dump_avail[1] = _kvm64toh(kd, | |||||
map_len * 8 * page_size); | |||||
} | |||||
} | |||||
/* | |||||
* Map the bitmap specified by the arguments. | * Map the bitmap specified by the arguments. | ||||
*/ | */ | ||||
kd->pt_map = _kvm_malloc(kd, map_len); | kd->pt_map = _kvm_malloc(kd, map_len); | ||||
if (kd->pt_map == NULL) { | if (kd->pt_map == NULL) { | ||||
_kvm_err(kd, kd->program, "cannot allocate %zu bytes for bitmap", | _kvm_err(kd, kd->program, "cannot allocate %zu bytes for bitmap", | ||||
map_len); | map_len); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | _kvm_pmap_init(kvm_t *kd, uint32_t pmap_size, off_t pmap_off) | ||||
if (pread(kd->pmfd, kd->page_map, pmap_size, pmap_off) != exp_len) { | if (pread(kd->pmfd, kd->page_map, pmap_size, pmap_off) != exp_len) { | ||||
_kvm_err(kd, kd->program, "cannot read %d bytes from " | _kvm_err(kd, kd->program, "cannot read %d bytes from " | ||||
"offset %jd for page map", pmap_size, (intmax_t)pmap_off); | "offset %jd for page map", pmap_size, (intmax_t)pmap_off); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static inline uint64_t | |||||
dump_avail_n(kvm_t *kd, long i) | |||||
{ | |||||
uint32_t *d32; | |||||
if (kd->pt_word_size == sizeof(uint32_t)) { | |||||
d32 = (uint32_t *)kd->dump_avail; | |||||
return (_kvm32toh(kd, d32[i])); | |||||
} else | |||||
return (_kvm64toh(kd, kd->dump_avail[i])); | |||||
} | |||||
uint64_t | |||||
_kvm_pa_bit_id(kvm_t *kd, uint64_t pa, unsigned int page_size) | |||||
{ | |||||
uint64_t adj; | |||||
long i; | |||||
adj = 0; | |||||
for (i = 0; dump_avail_n(kd, i + 1) != 0; i += 2) { | |||||
if (pa >= dump_avail_n(kd, i + 1)) { | |||||
adj += howmany(dump_avail_n(kd, i + 1), page_size) - | |||||
dump_avail_n(kd, i) / page_size; | |||||
} else { | |||||
return (pa / page_size - | |||||
dump_avail_n(kd, i) / page_size + adj); | |||||
} | |||||
} | |||||
return (_KVM_BIT_ID_INVALID); | |||||
} | |||||
uint64_t | |||||
_kvm_bit_id_pa(kvm_t *kd, uint64_t bit_id, unsigned int page_size) | |||||
{ | |||||
uint64_t sz; | |||||
long i; | |||||
for (i = 0; dump_avail_n(kd, i + 1) != 0; i += 2) { | |||||
sz = howmany(dump_avail_n(kd, i + 1), page_size) - | |||||
dump_avail_n(kd, i) / page_size; | |||||
if (bit_id < sz) { | |||||
return (rounddown2(dump_avail_n(kd, i), page_size) + | |||||
bit_id * page_size); | |||||
} | |||||
bit_id -= sz; | |||||
} | |||||
return (_KVM_PA_INVALID); | |||||
} | |||||
/* | /* | ||||
* Find the offset for the given physical page address; returns -1 otherwise. | * 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 | * 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 | * 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 | * 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 | * in the dump precede it. Reduce this O(n) counting to O(1) by caching the | ||||
* number of bits set at POPCOUNT_BITS intervals. | * number of bits set at POPCOUNT_BITS intervals. | ||||
* | * | ||||
* Then to find the number of pages before the requested address, simply | * 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 | * 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 | * 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. | * checked by also counting down from the next higher bin if it's closer. | ||||
*/ | */ | ||||
off_t | off_t | ||||
_kvm_pt_find(kvm_t *kd, uint64_t pa, unsigned int page_size) | _kvm_pt_find(kvm_t *kd, uint64_t pa, unsigned int page_size) | ||||
{ | { | ||||
uint64_t *bitmap = kd->pt_map; | uint64_t *bitmap = kd->pt_map; | ||||
uint64_t pte_bit_id = pa / page_size; | uint64_t pte_bit_id = _kvm_pa_bit_id(kd, pa, page_size); | ||||
uint64_t pte_u64 = pte_bit_id / BITS_IN(*bitmap); | uint64_t pte_u64 = pte_bit_id / BITS_IN(*bitmap); | ||||
uint64_t popcount_id = pte_bit_id / POPCOUNT_BITS; | uint64_t popcount_id = pte_bit_id / POPCOUNT_BITS; | ||||
uint64_t pte_mask = 1ULL << (pte_bit_id % BITS_IN(*bitmap)); | uint64_t pte_mask = 1ULL << (pte_bit_id % BITS_IN(*bitmap)); | ||||
uint64_t bitN; | uint64_t bitN; | ||||
uint32_t count; | uint32_t count; | ||||
/* Check whether the page address requested is in the dump. */ | /* Check whether the page address requested is in the dump. */ | ||||
if (pte_bit_id >= (kd->pt_map_size * NBBY) || | if (pte_bit_id == _KVM_BIT_ID_INVALID || | ||||
pte_bit_id >= (kd->pt_map_size * NBBY) || | |||||
(bitmap[pte_u64] & pte_mask) == 0) | (bitmap[pte_u64] & pte_mask) == 0) | ||||
return (-1); | return (-1); | ||||
/* | /* | ||||
* Add/sub popcounts from the bitmap until the PTE's bit is reached. | * Add/sub popcounts from the bitmap until the PTE's bit is reached. | ||||
* For bits that are in the upper half between the calculated | * For bits that are in the upper half between the calculated | ||||
* popcount id and the next one, use the next one and subtract to | * popcount id and the next one, use the next one and subtract to | ||||
* minimize the number of popcounts required. | * minimize the number of popcounts required. | ||||
▲ Show 20 Lines • Show All 277 Lines • ▼ Show 20 Lines | _kvm_bitmap_init(struct kvm_bitmap *bm, u_long bitmapsize, u_long *idx) | ||||
bm->map = calloc(bitmapsize, sizeof *bm->map); | bm->map = calloc(bitmapsize, sizeof *bm->map); | ||||
if (bm->map == NULL) | if (bm->map == NULL) | ||||
return (0); | return (0); | ||||
bm->size = bitmapsize; | bm->size = bitmapsize; | ||||
return (1); | return (1); | ||||
} | } | ||||
void | void | ||||
_kvm_bitmap_set(struct kvm_bitmap *bm, u_long pa, unsigned int page_size) | _kvm_bitmap_set(struct kvm_bitmap *bm, u_long bm_index) | ||||
{ | { | ||||
u_long bm_index = pa / page_size; | |||||
uint8_t *byte = &bm->map[bm_index / 8]; | uint8_t *byte = &bm->map[bm_index / 8]; | ||||
if (bm_index / 8 < bm->size) | |||||
*byte |= (1UL << (bm_index % 8)); | *byte |= (1UL << (bm_index % 8)); | ||||
} | } | ||||
int | int | ||||
_kvm_bitmap_next(struct kvm_bitmap *bm, u_long *idx) | _kvm_bitmap_next(struct kvm_bitmap *bm, u_long *idx) | ||||
{ | { | ||||
u_long first_invalid = bm->size * CHAR_BIT; | u_long first_invalid = bm->size * CHAR_BIT; | ||||
if (*idx == ULONG_MAX) | if (*idx == ULONG_MAX) | ||||
Show All 39 Lines |