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


Reply via email to