Add the foundational utility libraries for parsing vmcore files: - vmcore_info: Parser for vmcoreinfo note section. Supports SYMBOL(), OFFSET(), SIZE(), NUMBER(), LENGTH() lookups via hash table. Provides cached get_page_size()/get_page_shift() accessors. Handles plain KEY=VALUE entries like PAGESIZE and OSRELEASE.
- memory: Virtual-to-physical address translation dispatcher and readmem() for reading from vmcore via kernel or user virtual addresses, or physical addresses.Uses paddr_to_offset() from elf_info for physical-to-file-offset mapping. - vmcore_tasks_util: Shared macros, typedefs, and debug helpers used by vmcore_info and memory (debug verbosity levels, type accessors, __weak definition). These files are placed in util_lib/ but are currently only built by the vmcore_tasks Makefile. Integration into the main util_lib build can be done later if other tools need them. Signed-off-by: Pnina Feder <[email protected]> --- util_lib/include/memory.h | 37 +++ util_lib/include/vmcore_info.h | 72 +++++ util_lib/include/vmcore_tasks_util.h | 62 +++++ util_lib/memory.c | 327 +++++++++++++++++++++++ util_lib/vmcore_info.c | 382 +++++++++++++++++++++++++++ 5 files changed, 880 insertions(+) create mode 100644 util_lib/include/memory.h create mode 100644 util_lib/include/vmcore_info.h create mode 100644 util_lib/include/vmcore_tasks_util.h create mode 100644 util_lib/memory.c create mode 100644 util_lib/vmcore_info.c diff --git a/util_lib/include/memory.h b/util_lib/include/memory.h new file mode 100644 index 00000000..492e0d79 --- /dev/null +++ b/util_lib/include/memory.h @@ -0,0 +1,37 @@ +#ifndef _MEMORY_H_ +#define _MEMORY_H_ + +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include "vmcore_info.h" + +#define KVADDR (0x1) +#define UVADDR (0x2) +#define PADDR (0x3) + +/* Page size */ +#define SYS_PAGE_SIZE (get_page_size()) +#define page_off(vaddr) ((vaddr) & (SYS_PAGE_SIZE - 1)) + +/* Set the current user context (mm_pgd) for UVADDR reads */ +void set_context(uint64_t mm_pgd); + +/* Get current context */ +uint64_t get_context(void); + +/* Arch-overridable functions (weak in memory.c, strong in arch/) */ +uint64_t get_page_offset(void); +bool is_kvaddr(uint64_t vaddr); +bool vtop_direct(uint64_t kvaddr, uint64_t *paddr); + +/* Arch vtop function pointer for page table walk (set by arch_init) */ +typedef bool (*vtop_fn_t)(uint64_t pgd, uint64_t vaddr, uint64_t *paddr); +extern vtop_fn_t mem_vtop; + +ssize_t readmem(uint64_t addr, void *buffer, size_t size, char *type, int memtype); + +int memory_config_init(void); + +#endif /* _MEMORY_H_ */ \ No newline at end of file diff --git a/util_lib/include/vmcore_info.h b/util_lib/include/vmcore_info.h new file mode 100644 index 00000000..26c92701 --- /dev/null +++ b/util_lib/include/vmcore_info.h @@ -0,0 +1,72 @@ +#ifndef VMCORE_INFO_H +#define VMCORE_INFO_H + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +/* Maximum number of entries per table */ +#define VMCI_MAX_ENTRIES 256 +#define VMCI_MAX_KEY_LEN 64 + +/* Single entry in a table */ +struct vmci_entry { + char key[VMCI_MAX_KEY_LEN]; + uint64_t value; + bool valid; +}; + +/* Table for one type of entries */ +struct vmci_table { + struct vmci_entry entries[VMCI_MAX_ENTRIES]; + int count; +}; + +/* All vmcoreinfo tables */ +struct vmcoreinfo { + struct vmci_table symbols; /* SYMBOL entries */ + struct vmci_table sizes; /* SIZE entries */ + struct vmci_table offsets; /* OFFSET entries */ + struct vmci_table numbers; /* NUMBER entries */ + struct vmci_table lengths; /* LENGTH entries */ + char osrelease[256]; + char pagesize[16]; + int vmcore_fd; + bool initialized; +}; + +/* Initialize vmcoreinfo from raw data */ +int vmcoreinfo_init(const char *data, size_t size); + +/* Cleanup */ +void vmcoreinfo_cleanup(void); + +/* Getters - crash if not found */ +uint64_t SYMBOL(const char *name); +uint64_t SIZE(const char *name); +uint64_t OFFSET(const char *name); +uint64_t NUMBER(const char *name); +uint64_t LENGTH(const char *name); + +/* Check if entry exists */ +bool SYMBOL_EXISTS(const char *name); +bool SIZE_EXISTS(const char *name); +bool OFFSET_EXISTS(const char *name); +bool NUMBER_EXISTS(const char *name); +bool LENGTH_EXISTS(const char *name); + +/* Cached system values */ +unsigned long get_page_size(void); +unsigned int get_page_shift(void); + +/* Get OS release string */ +const char *vmcoreinfo_osrelease(void); + +/* Vmcore file descriptor - global access */ +void vmcore_set_fd(int fd); +int vmcore_fd(void); + +/* Debug: dump all entries */ +void vmcoreinfo_dump(void); + +#endif /* VMCORE_INFO_H */ \ No newline at end of file diff --git a/util_lib/include/vmcore_tasks_util.h b/util_lib/include/vmcore_tasks_util.h new file mode 100644 index 00000000..95c0ad8a --- /dev/null +++ b/util_lib/include/vmcore_tasks_util.h @@ -0,0 +1,62 @@ +#ifndef VMCORE_TASKS_UTIL_H +#define VMCORE_TASKS_UTIL_H + +#include <sys/types.h> +#include <stdint.h> +#include <stdio.h> + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +/* Debug verbosity levels */ +#define DBG_NONE 0 +#define DBG_INFO 1 +#define DBG_VERBOSE 2 +#define DBG_TRACE 3 +#define DBG_MEM 4 + +extern int debug_level; + +#define pr_info(...) \ + do { \ + if (debug_level >= DBG_INFO) \ + fprintf(stderr, "[INFO] " __VA_ARGS__); \ + } while (0) + +#define pr_verbose(...) \ + do { \ + if (debug_level >= DBG_VERBOSE) \ + fprintf(stderr, "[VERBOSE] " __VA_ARGS__); \ + } while (0) + +#define pr_trace(...) \ + do { \ + if (debug_level >= DBG_TRACE) \ + fprintf(stderr, "[TRACE] " __VA_ARGS__); \ + } while (0) + +#define pr_mem(...) \ + do { \ + if (debug_level >= DBG_MEM) \ + fprintf(stderr, "[MEM] " __VA_ARGS__); \ + } while (0) + +#define is_info() (debug_level >= DBG_INFO) +#define is_verbose() (debug_level >= DBG_VERBOSE) +#define is_trace() (debug_level >= DBG_TRACE) +#define is_mem() (debug_level >= DBG_MEM) + +typedef unsigned long int ulong; +typedef unsigned short int ushort; +typedef unsigned int uint; + +#define ULONG(ADDR) *((ulong *)((char *)(ADDR))) +#define UINT32(ADDR) *((uint32_t *)((char *)(ADDR))) +#define UINT16(ADDR) *((uint16_t *)((char *)(ADDR))) +#define UINT64(ADDR) *((uint64_t *)((char *)(ADDR))) +#define UCHAR(ADDR) *((unsigned char *)((char *)(ADDR))) +#define VOID_PTR(ADDR) *((void **)((char *)(ADDR))) +#define UINT(ADDR) *((uint *)((char *)(ADDR))) + +#endif /* VMCORE_TASKS_UTIL_H */ diff --git a/util_lib/memory.c b/util_lib/memory.c new file mode 100644 index 00000000..60986cc4 --- /dev/null +++ b/util_lib/memory.c @@ -0,0 +1,327 @@ +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/param.h> + +#include "memory.h" +#include "vmcore_info.h" +#include "elf_info.h" +#include "vmcore_tasks_util.h" + +/* Arch vtop function pointer (set by arch_init) */ +vtop_fn_t mem_vtop; + +/* Memory layout (from vmcoreinfo) */ +static uint64_t page_offset; // PAGE_OFFSET +static uint64_t phys_offset; // Physical RAM base +static uint64_t va_pa_offset; // Linear mapping offset + +static uint64_t number_vmalloc_start; // VMALLOC_START +static uint64_t number_vmalloc_end; // VMALLOC_END +static uint64_t number_vmemmap_start; // VMEMMAP_START +static uint64_t number_vmemmap_end; // VMEMMAP_END +static uint64_t number_modules_vaddr; // MODULES_VADDR +static uint64_t number_modules_end; // MODULES_END + +static uint64_t number_kernel_link_addr; // KERNEL_LINK_ADDR (Linux >= 5.13) +static uint64_t number_va_kernel_pa_offset; // Kernel VA to PA offset (Linux >= 6.4) + +/* Kernel page table base (from symbol) */ +static uint64_t symbol_swapper_pg_dir; // Kernel PGD virtual address +static uint64_t kernel_pgd_paddr; // Kernel PGD physical address + +/* Kernel version for conditional logic */ +static const char *kernel_version; // e.g., LINUX(5,13,0) +static uint64_t current_context_pgd = 0; + +void set_context(uint64_t mm_pgd) +{ + current_context_pgd = mm_pgd; +} + +uint64_t get_context(void) +{ + return current_context_pgd; +} + + +/** + * Check if address is in vmalloc/vmemmap/modules region + * (requires page table walk) + */ +static bool is_vmalloc_addr(uint64_t vaddr) +{ + if (number_vmalloc_start && + vaddr >= number_vmalloc_start && vaddr <= number_vmalloc_end) + return true; + + if (number_vmemmap_start && + vaddr >= number_vmemmap_start && vaddr <= number_vmemmap_end) + return true; + + if (number_modules_vaddr && + vaddr >= number_modules_vaddr && vaddr <= number_modules_end) + return true; + + return false; +} + +__weak uint64_t get_page_offset(void) +{ + return NUMBER("PAGE_OFFSET"); +} + +/* Check if address is a kernel virtual address */ +__weak bool is_kvaddr(uint64_t vaddr) +{ + if (is_vmalloc_addr(vaddr)) + return true; + return (vaddr >= page_offset); +} + +/* + * Direct translation for linear-mapped addresses (fast path) + * No page table walk needed + */ +__weak bool vtop_direct(uint64_t kvaddr, uint64_t *paddr) +{ + /* + * Linux >= 5.13 on RISCV: kernel text uses different offset + * Only check if KERNEL_LINK_ADDR exists and address is in that range + */ + if(number_kernel_link_addr && kvaddr >= number_kernel_link_addr) { + *paddr = kvaddr - number_va_kernel_pa_offset; + return true; + } + + /* Standard linear mapping: VA = PA + PAGE_OFFSET - PHYS_OFFSET */ + *paddr = kvaddr - va_pa_offset; + + return true; +} + +/* + * High-level kvaddr to paddr translation + * Handles both direct mapping and page table walk + */ +static bool kvtop(uint64_t kvaddr, uint64_t *paddr) +{ + /* Validate it's a kernel address */ + if (!is_kvaddr(kvaddr)){ + fprintf(stderr, "kvtop: Address 0x%llx is not a kernel virtual address\n", + (unsigned long long)kvaddr); + return false; + } + + /* Fast path: direct-mapped address */ + if (!is_vmalloc_addr(kvaddr)) { + return vtop_direct(kvaddr, paddr); + } + + /* Slow path: page table walk required */ + if (!mem_vtop) { + fprintf(stderr, "kvtop: mem_vtop function pointer is NULL\n"); + return false; + } + return mem_vtop(kernel_pgd_paddr, kvaddr, paddr); +} + +static bool uvtop(uint64_t uvaddr, uint64_t mm_pgd, uint64_t *paddr) +{ + uint64_t pgd_paddr; + /* Get physical address of process's PGD */ + if (!kvtop(mm_pgd, &pgd_paddr)) { + fprintf(stderr, "uvtop: failed to translate mm_pgd 0x%llx to physical\n", + (unsigned long long)mm_pgd); + return false; + } + + if (!mem_vtop) { + fprintf(stderr, "uvtop: mem_vtop function pointer is NULL\n"); + return false; + } + + if (!mem_vtop(pgd_paddr, uvaddr, paddr)) { + /* Page not present - this is common for sleeping processes. + * The caller (backtrace code) should handle this gracefully. + * Only log in debug mode to avoid spam for expected cases. + */ + pr_trace("uvtop: page table walk failed for user addr 0x%llx (page not present?)\n", + (unsigned long long)uvaddr); + return false; + } + + return true; +} + + +/* + * Parse vmcoreinfo to get required values + * Returns 0 on success, -1 on error + */ +int memory_config_init(void) +{ + kernel_version = vmcoreinfo_osrelease(); + page_offset = get_page_offset(); + + /* Physical RAM base - different names on different archs */ + if (NUMBER_EXISTS("phys_ram_base")) + phys_offset = NUMBER("phys_ram_base"); + else if (NUMBER_EXISTS("PHYS_OFFSET")) + phys_offset = NUMBER("PHYS_OFFSET"); + else + phys_offset = 0; /* x86 and some others start at 0 */ + + if(NUMBER_EXISTS("VMALLOC_START")) { + number_vmalloc_start = NUMBER("VMALLOC_START"); + if (NUMBER_EXISTS("VMALLOC_END")) + number_vmalloc_end = NUMBER("VMALLOC_END"); + else + number_vmalloc_end = 0; + } else { + number_vmalloc_start = 0; + number_vmalloc_end = 0; + } + + /* VMEMMAP region - may not exist on all configs */ + if (NUMBER_EXISTS("VMEMMAP_START")) { + number_vmemmap_start = NUMBER("VMEMMAP_START"); + if (NUMBER_EXISTS("VMEMMAP_END")) + number_vmemmap_end = NUMBER("VMEMMAP_END"); + else + number_vmemmap_end = 0; + } else { + number_vmemmap_start = 0; + number_vmemmap_end = 0; + } + + if (NUMBER_EXISTS("KERNEL_LINK_ADDR")) { + number_kernel_link_addr = NUMBER("KERNEL_LINK_ADDR"); + + /* va_kernel_pa_offset exists only on linux 6.4+ */ + if (NUMBER_EXISTS("va_kernel_pa_offset")) + number_va_kernel_pa_offset = NUMBER("va_kernel_pa_offset"); + else + number_va_kernel_pa_offset = number_kernel_link_addr - phys_offset; + } else { + number_kernel_link_addr = 0; + } + + /* MODULES region - Linux >= 5.13 has separate, older uses VMALLOC */ + if (NUMBER_EXISTS("MODULES_VADDR")) + number_modules_vaddr = NUMBER("MODULES_VADDR"); + else + number_modules_vaddr = number_vmalloc_start; + + if (NUMBER_EXISTS("MODULES_END")) + number_modules_end = NUMBER("MODULES_END"); + else + number_modules_end = number_vmalloc_end; + + symbol_swapper_pg_dir = SYMBOL("swapper_pg_dir"); + + /* Calculate va_pa_offset if not directly available */ + va_pa_offset = page_offset - phys_offset; + + if (!vtop_direct(symbol_swapper_pg_dir, &kernel_pgd_paddr)) + return -1; + + return 0; +} + +static ssize_t read_phys(uint64_t paddr, void *buffer, size_t size) +{ + uint64_t file_offset; + ssize_t ret; + int fd = vmcore_fd(); + if (fd < 0) { + fprintf(stderr, "read_phys: invalid vmcore fd\n"); + return -1; + } + file_offset = paddr_to_offset(paddr); + + /* paddr_to_offset returns (uint64_t)-1 on error */ + if (file_offset == (uint64_t)-1) { + fprintf(stderr, "read_phys: failed to translate paddr 0x%llx to file offset\n", + (unsigned long long)paddr); + return -1; + } + + pr_mem("read_phys: paddr=0x%llx -> file_offset=0x%llx size=%zu\n", + (unsigned long long)paddr, (unsigned long long)file_offset, size); + + ret = pread(fd, buffer, size, file_offset); + + if (ret < 0 || (size_t)ret != size) { + fprintf(stderr, "read_phys: Failed to read %zu bytes from paddr 0x%llx: %s\n", + size, (unsigned long long)paddr, strerror(errno)); + return -1; + } + return ret; +} + +ssize_t readmem(uint64_t addr, void *buffer, size_t size, char *type, int memtype) +{ + uint64_t paddr; + ssize_t ret; + size_t bytes_read = 0; + size_t chunk_size; + pr_mem("readmem: Reading %zu bytes from addr 0x%llx (%s)\n", + size, (unsigned long long)addr, type ? type : "unknown"); + if (!buffer) { + fprintf(stderr, "readmem: NULL buffer\n"); + return -1; + } + if (size == 0) { + fprintf(stderr, "readmem: zero size requested\n"); + return -1; + } + + /* Direct physical address read */ + if(memtype == PADDR){ + ret = read_phys(addr, buffer, size); + return ret; + } + + while(bytes_read < size) { + chunk_size = MIN(size - bytes_read, SYS_PAGE_SIZE - page_off(addr + bytes_read)); + /* Convert virtual address to paddr */ + switch (memtype) + { + case KVADDR: + if (!kvtop(addr + bytes_read, &paddr)) { + fprintf(stderr, "readmem: kvtop failed for addr 0x%llx\n", (unsigned long long)(addr + bytes_read)); + return -1; + } + break; + case UVADDR: + if (!current_context_pgd) { + fprintf(stderr, "readmem: mm_pgd is zero for UVADDR read\n"); + return -1; + } + if (!uvtop(addr + bytes_read, current_context_pgd, &paddr)) { + /* uvtop already logged in debug mode. + * For user addresses, page-not-present is common + * for sleeping processes - caller should handle gracefully. + */ + return -1; + } + break; + default: + fprintf(stderr, "readmem: Unknown memory type %d\n", memtype); + return -1; + break; + } + ret = read_phys(paddr, (char *)buffer + bytes_read, chunk_size); + if (ret < 0 || (size_t)ret != chunk_size) { + fprintf(stderr, "readmem: Failed to read memory at addr 0x%llx (read %zd, expected %zu)\n", + (unsigned long long)(addr + bytes_read), ret, chunk_size); + return -1; + } + bytes_read += ret; + } + return (ssize_t)bytes_read; +} diff --git a/util_lib/vmcore_info.c b/util_lib/vmcore_info.c new file mode 100644 index 00000000..a1e1d51f --- /dev/null +++ b/util_lib/vmcore_info.c @@ -0,0 +1,382 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "vmcore_info.h" +#include "vmcore_tasks_util.h" + +/* Global vmcoreinfo instance */ +static struct vmcoreinfo vmci = {0}; + +/* Cached page size — read once from vmcoreinfo */ +static unsigned long cached_page_size = 0; +static unsigned int cached_page_shift = 0; + +unsigned long get_page_size(void) +{ + if (cached_page_size) + return cached_page_size; + + if (vmci.pagesize[0] != '\0') { + cached_page_size = strtoul(vmci.pagesize, NULL, 0); + } + + if (cached_page_size == 0 || (cached_page_size & (cached_page_size - 1)) != 0) { + fprintf(stderr, "Warning: invalid PAGESIZE='%s', defaulting to 4096\n", + vmci.pagesize); + cached_page_size = 4096; + } + + unsigned long tmp = cached_page_size; + while (tmp > 1) { + tmp >>= 1; + cached_page_shift++; + } + + return cached_page_size; +} + +unsigned int get_page_shift(void) +{ + if (!cached_page_size) + get_page_size(); /* ensure initialized */ + return cached_page_shift; +} + +/* Simple hash function for faster lookups */ +static unsigned int hash_key(const char *key) +{ + unsigned int hash = 5381; + int c; + while ((c = *key++)) + hash = ((hash << 5) + hash) + c; + return hash % VMCI_MAX_ENTRIES; +} + +/* Add entry to a table */ +static int table_add(struct vmci_table *table, const char *key, uint64_t value) +{ + if (table->count >= VMCI_MAX_ENTRIES) { + fprintf(stderr, "vmcoreinfo: table full, cannot add '%s'\n", key); + return -1; + } + + /* Use hash for initial position, linear probe on collision */ + unsigned int idx = hash_key(key); + int attempts = 0; + + while (table->entries[idx].valid && attempts < VMCI_MAX_ENTRIES) { + /* Check for duplicate key */ + if (strcmp(table->entries[idx].key, key) == 0) { + /* Update existing entry */ + table->entries[idx].value = value; + return 0; + } + idx = (idx + 1) % VMCI_MAX_ENTRIES; + attempts++; + } + + if (attempts >= VMCI_MAX_ENTRIES) { + fprintf(stderr, "vmcoreinfo: hash table full\n"); + return -1; + } + + if (strlen(key) >= VMCI_MAX_KEY_LEN) { + fprintf(stderr, "vmcoreinfo: key '%s' truncated to %d chars\n", + key, VMCI_MAX_KEY_LEN - 1); + } + strncpy(table->entries[idx].key, key, VMCI_MAX_KEY_LEN - 1); + table->entries[idx].key[VMCI_MAX_KEY_LEN - 1] = '\0'; + table->entries[idx].value = value; + table->entries[idx].valid = true; + table->count++; + + return 0; +} + +/* Lookup entry in a table */ +static struct vmci_entry *table_lookup(struct vmci_table *table, const char *key) +{ + unsigned int idx = hash_key(key); + int attempts = 0; + + while (attempts < VMCI_MAX_ENTRIES) { + if (!table->entries[idx].valid) + return NULL; // empty slot means key was never inserted + if (strcmp(table->entries[idx].key, key) == 0) + return &table->entries[idx]; + idx = (idx + 1) % VMCI_MAX_ENTRIES; + attempts++; + } + + return NULL; +} + +/* Parse a single line of vmcoreinfo */ +static void parse_line(char *line) +{ + char *eq = strchr(line, '='); + if (!eq) + return; + + *eq = '\0'; + char *key_part = line; + char *value_str = eq + 1; + + /* Trim whitespace from value */ + while (*value_str && isspace(*value_str)) + value_str++; + + /* Detect type and extract key */ + if (strncmp(key_part, "SYMBOL(", 7) == 0) { + char *end = strchr(key_part + 7, ')'); + if (end) { + *end = '\0'; + uint64_t val = strtoull(value_str, NULL, 16); + table_add(&vmci.symbols, key_part + 7, val); + } + } + else if (strncmp(key_part, "SIZE(", 5) == 0) { + char *end = strchr(key_part + 5, ')'); + if (end) { + *end = '\0'; + uint64_t val = strtoull(value_str, NULL, 10); + table_add(&vmci.sizes, key_part + 5, val); + } + } + else if (strncmp(key_part, "OFFSET(", 7) == 0) { + char *end = strchr(key_part + 7, ')'); + if (end) { + *end = '\0'; + uint64_t val = strtoull(value_str, NULL, 10); + table_add(&vmci.offsets, key_part + 7, val); + } + } + else if (strncmp(key_part, "NUMBER(", 7) == 0) { + char *end = strchr(key_part + 7, ')'); + if (end) { + *end = '\0'; + /* NUMBER can be hex or decimal */ + uint64_t val; + if (strncmp(value_str, "0x", 2) == 0 || strncmp(value_str, "0X", 2) == 0) + val = strtoull(value_str, NULL, 16); + else + val = strtoull(value_str, NULL, 0); /* auto-detect base */ + table_add(&vmci.numbers, key_part + 7, val); + } + } + else if (strncmp(key_part, "LENGTH(", 7) == 0) { + char *end = strchr(key_part + 7, ')'); + if (end) { + *end = '\0'; + uint64_t val = strtoull(value_str, NULL, 10); + table_add(&vmci.lengths, key_part + 7, val); + } + } + else if (strncmp(key_part, "OSRELEASE", 9) == 0) { + strncpy(vmci.osrelease, value_str, sizeof(vmci.osrelease) - 1); + vmci.osrelease[sizeof(vmci.osrelease) - 1] = '\0'; + } + else if (strncmp(key_part, "PAGESIZE", 8) == 0) { + strncpy(vmci.pagesize, value_str, sizeof(vmci.pagesize) - 1); + vmci.pagesize[sizeof(vmci.pagesize) - 1] = '\0'; + } + else if (strcmp(key_part, "CRASHTIME") == 0) { + long long sval = strtoll(value_str, NULL, 10); + table_add(&vmci.numbers, "CRASHTIME", (uint64_t)(int64_t)sval); + } +} + +int vmcoreinfo_init(const char *data, size_t size) +{ + if (!data || size == 0) + return -1; + + if (vmci.initialized) { + fprintf(stderr, "vmcoreinfo: already initialized, reinitializing\n"); + return -1; + } + + /* Clear existing data */ + memset(&vmci, 0, sizeof(vmci)); + + /* Make a mutable copy */ + char *buf = malloc(size + 1); + if (!buf) + return -1; + + memcpy(buf, data, size); + buf[size] = '\0'; + + /* Parse line by line */ + char *line = buf; + char *end = buf + size; + + while (line < end) { + /* Find end of line */ + char *eol = strchr(line, '\n'); + if (eol) + *eol = '\0'; + else + eol = end - 1; + + /* Skip empty lines */ + if (*line) + parse_line(line); + + line = eol + 1; + } + + free(buf); + vmci.initialized = true; + + return 0; +} + +void vmcoreinfo_cleanup(void) +{ + memset(&vmci, 0, sizeof(vmci)); +} + +/* Getter implementations - crash if not found */ +uint64_t SYMBOL(const char *name) +{ + struct vmci_entry *e = table_lookup(&vmci.symbols, name); + if (!e) { + fprintf(stderr, "FATAL: SYMBOL '%s' not found in vmcoreinfo\n", name); + abort(); + } + return e->value; +} + +uint64_t SIZE(const char *name) +{ + struct vmci_entry *e = table_lookup(&vmci.sizes, name); + if (!e) { + fprintf(stderr, "FATAL: SIZE '%s' not found in vmcoreinfo\n", name); + abort(); + } + return e->value; +} + +uint64_t OFFSET(const char *name) +{ + struct vmci_entry *e = table_lookup(&vmci.offsets, name); + if (!e) { + fprintf(stderr, "FATAL: OFFSET '%s' not found in vmcoreinfo\n", name); + abort(); + } + return e->value; +} + +uint64_t NUMBER(const char *name) +{ + struct vmci_entry *e = table_lookup(&vmci.numbers, name); + if (!e) { + fprintf(stderr, "FATAL: NUMBER '%s' not found in vmcoreinfo\n", name); + abort(); + } + return e->value; +} + +uint64_t LENGTH(const char *name) +{ + struct vmci_entry *e = table_lookup(&vmci.lengths, name); + if (!e) { + fprintf(stderr, "FATAL: LENGTH '%s' not found in vmcoreinfo\n", name); + abort(); + } + return e->value; +} + +/* Existence checks */ +bool SYMBOL_EXISTS(const char *name) +{ + return table_lookup(&vmci.symbols, name) != NULL; +} + +bool SIZE_EXISTS(const char *name) +{ + return table_lookup(&vmci.sizes, name) != NULL; +} + +bool OFFSET_EXISTS(const char *name) +{ + return table_lookup(&vmci.offsets, name) != NULL; +} + +bool NUMBER_EXISTS(const char *name) +{ + return table_lookup(&vmci.numbers, name) != NULL; +} + +bool LENGTH_EXISTS(const char *name) +{ + return table_lookup(&vmci.lengths, name) != NULL; +} + +const char *vmcoreinfo_osrelease(void) +{ + if (vmci.osrelease[0] == '\0') { + fprintf(stderr, "FATAL: OSRELEASE not found in vmcoreinfo\n"); + abort(); + } + return vmci.osrelease; +} + +/* Debug helper */ +void vmcoreinfo_dump(void) +{ + int i; + + pr_trace("=== VMCOREINFO DUMP ===\n"); + pr_trace("OSRELEASE: %s\n", vmci.osrelease); + pr_trace("PAGESIZE: %s\n", vmci.pagesize); + pr_trace("SYMBOLS (%d entries):\n", vmci.symbols.count); + + for (i = 0; i < VMCI_MAX_ENTRIES; i++) { + if (vmci.symbols.entries[i].valid) + pr_trace(" %s = 0x%llx\n", vmci.symbols.entries[i].key, + (unsigned long long) vmci.symbols.entries[i].value); + } + + pr_trace("SIZES (%d entries):\n", vmci.sizes.count); + for (i = 0; i < VMCI_MAX_ENTRIES; i++) { + if (vmci.sizes.entries[i].valid) + pr_trace(" %s = %llu\n", vmci.sizes.entries[i].key, + (unsigned long long) vmci.sizes.entries[i].value); + } + + pr_trace("OFFSETS (%d entries):\n", vmci.offsets.count); + for (i = 0; i < VMCI_MAX_ENTRIES; i++) { + if (vmci.offsets.entries[i].valid) + pr_trace(" %s = %llu\n", vmci.offsets.entries[i].key, + (unsigned long long) vmci.offsets.entries[i].value); + } + + pr_trace("NUMBERS (%d entries):\n", vmci.numbers.count); + for (i = 0; i < VMCI_MAX_ENTRIES; i++) { + if (vmci.numbers.entries[i].valid) + pr_trace(" %s = 0x%llx\n", vmci.numbers.entries[i].key, + (unsigned long long) vmci.numbers.entries[i].value); + } + + pr_trace("LENGTHS (%d entries):\n", vmci.lengths.count); + for (i = 0; i < VMCI_MAX_ENTRIES; i++) { + if (vmci.lengths.entries[i].valid) + pr_trace(" %s = %llu\n", vmci.lengths.entries[i].key, + (unsigned long long)vmci.lengths.entries[i].value); + } +} + +/* Vmcore file descriptor management */ +void vmcore_set_fd(int fd) +{ + vmci.vmcore_fd = fd; +} + +int vmcore_fd(void) +{ + return vmci.vmcore_fd; +} \ No newline at end of file -- 2.43.0
