[PATCH 02/10] util_lib: Add vmcoreinfo parser and memory reader
Pnina Feder
pnina.feder at mobileye.com
Mon Jun 22 13:54:40 PDT 2026
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 <pnina.feder at mobileye.com>
---
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
More information about the kexec
mailing list