drgn/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch
Omar Sandoval b3a6d6a35f libdrgn: linux_kernel: cache PAGE_SHIFT derived from PAGE_SIZE
Rather than computing it every time we need it, compute it once when we
parse PAGE_SIZE from VMCOREINFO (and validate that PAGE_SIZE is a power
of two). This will be more important for AArch64 page table walking.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
2022-07-14 12:05:09 -07:00

93 lines
2.6 KiB
Plaintext

static struct drgn_error *parse_vmcoreinfo_u64(const char *value,
const char *newline, int base,
uint64_t *ret)
{
errno = 0;
char *end;
*ret = strtoull(value, &end, base);
if (errno == ERANGE) {
return drgn_error_create(DRGN_ERROR_OVERFLOW,
"number in VMCOREINFO is too large");
} else if (errno || end == value || end != newline) {
return drgn_error_create(DRGN_ERROR_OTHER,
"number in VMCOREINFO is invalid");
}
return NULL;
}
struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
const char *desc,
size_t descsz)
{
struct drgn_error *err;
for (const char *line = desc, *end = &desc[descsz], *newline;
(newline = memchr(line, '\n', end - line));
line = newline + 1) {
const char *equals = memchr(line, '=', newline - line);
if (!equals)
continue;
const char *value = equals + 1;
@memswitch (line, equals - line)@
@case "OSRELEASE"@
if ((size_t)(newline - value) >=
sizeof(prog->vmcoreinfo.osrelease)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"OSRELEASE in VMCOREINFO is too long");
}
memcpy(prog->vmcoreinfo.osrelease, value,
newline - value);
prog->vmcoreinfo.osrelease[newline - value] = '\0';
break;
@case "PAGESIZE"@
err = parse_vmcoreinfo_u64(value, newline, 0,
&prog->vmcoreinfo.page_size);
if (err)
return err;
break;
@case "KERNELOFFSET"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.kaslr_offset);
if (err)
return err;
break;
@case "SYMBOL(swapper_pg_dir)"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.swapper_pg_dir);
if (err)
return err;
break;
@case "NUMBER(pgtable_l5_enabled)"@
{
uint64_t tmp;
err = parse_vmcoreinfo_u64(value, newline, 0, &tmp);
if (err)
return err;
prog->vmcoreinfo.pgtable_l5_enabled = tmp;
break;
}
@case "NUMBER(KERNELPACMASK)"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->aarch64_insn_pac_mask);
if (err)
return err;
break;
@endswitch@
}
if (!prog->vmcoreinfo.osrelease[0]) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid OSRELEASE");
}
if (!is_power_of_two(prog->vmcoreinfo.page_size)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid PAGESIZE");
}
prog->vmcoreinfo.page_shift = ctz(prog->vmcoreinfo.page_size);
if (!prog->vmcoreinfo.swapper_pg_dir) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid swapper_pg_dir");
}
/* KERNELOFFSET, pgtable_l5_enabled, and KERNELPACMASK are optional. */
return NULL;
}