From eb7f4b7eb0a3b8878be2cd6550139943ed294e8c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 1 Apr 2026 16:28:49 +0000 Subject: [PATCH] feat: add projection and intercept support, remove TOML, fix freeDevMem symbol bug - Remove TOML dependency from Makefile and compose_device.c - Implement projection support in config handler: remap device memory segments between devices via cells[] pointer reassignment - Implement intercept support: shadow_copy (write to both devices) and shadow_replace (redirect read/write to another device) modes using smartAddr handler infrastructure - Fix critical bug in hmmmm.c: freeDevMem was loaded from "freeDevSpecs" symbol instead of "freeDevMem", causing segfault on config reload - Add intercept context lifecycle management to EmulContext Co-Authored-By: Claude Opus 4.6 --- AVRrc.toml | 28 --- Makefile | 2 +- gpio.toml | 23 -- inc/compose_device.h | 1 - inc/context.h | 2 + src/base_device.c | 208 ------------------ src/compose_device.c | 414 +----------------------------------- src/hmmmm.c | 2 +- src/main.c | 3 +- src/proto/handlers/config.c | 317 +++++++++++++++++++++++++++ 10 files changed, 326 insertions(+), 674 deletions(-) delete mode 100644 AVRrc.toml delete mode 100644 gpio.toml diff --git a/AVRrc.toml b/AVRrc.toml deleted file mode 100644 index d0dd0cc..0000000 --- a/AVRrc.toml +++ /dev/null @@ -1,28 +0,0 @@ -[dev] -libpath = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc_build/device.so" - - -[mem] - -[mem.ps] -start = 0 -len = 1024 -wordLen = 2 -executable = true - -[mem.reg_gp] -start = 2048 -len = 32 -wordLen = 1 - -[mem.reg_io] -start = 2080 -len = 255 -wordLen = 1 - -[mem.ds] -start = 2335 -len = 65535 -wordLen = 1 - - diff --git a/Makefile b/Makefile index 42876e2..a2e6187 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ SRC_DIR=src INC_DIR=inc CC=gcc OBJDUMP=objdump -LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a +LIBS=deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE) # flatcc runtime and generated reader headers use const-dropping casts; # include both as system headers so -Wcast-qual doesn't apply to them diff --git a/gpio.toml b/gpio.toml deleted file mode 100644 index 04877d7..0000000 --- a/gpio.toml +++ /dev/null @@ -1,23 +0,0 @@ -[dev] -libpath = "avr_gpio/out/device.so" - - - -[mem.ext_reg_io] -start = 0 -len = 255 -wordLen = 1 - -[mem.extstate] -start = 256 -len = 1 -wordLen = 1 - -[mem.variables] -PIN = 0 -PORT = 1 -DDR = 2 - - - - diff --git a/inc/compose_device.h b/inc/compose_device.h index 3223d6d..45e402e 100644 --- a/inc/compose_device.h +++ b/inc/compose_device.h @@ -35,7 +35,6 @@ typedef struct { device_handle_t** devHandlers; } compose_dev_handle_t; -compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf); device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf); #endif // ifndef __COMPOSE_DEVICE_H__ \ No newline at end of file diff --git a/inc/context.h b/inc/context.h index f5a5831..4106079 100644 --- a/inc/context.h +++ b/inc/context.h @@ -32,6 +32,8 @@ typedef struct { uint8_t** devicesMem; size_t devicesCount; void** deviceHandles; // device_handle_t** — dynamically loaded devices + void** interceptCtxs; // intercept_ctx_t** — heap-allocated intercept contexts + size_t interceptCtxCount; flatcc_builder_t stream_builder; } EmulContext; diff --git a/src/base_device.c b/src/base_device.c index 05b2bfe..ee8664b 100644 --- a/src/base_device.c +++ b/src/base_device.c @@ -2,34 +2,9 @@ #include #include -#include "tomlc99/toml.h" - #include "base_device.h" - - -device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf) -{ - char intErrbuf[1024]; - - conf_dev_t *devConf = openBaseDeviceConfig(configPath, intErrbuf); - if (devConf == NULL) - { - sprintf(errbuf, "unable to read device config: %s", intErrbuf); - return NULL; - } - - device_handle_t* ret = openBaseDevice(devConf, intErrbuf); - - if(ret == NULL) - { - freeConf(devConf); - } - - return ret; -} - void closeBaseDevice(device_handle_t* devHandle) { if(devHandle->ctx != NULL) @@ -90,186 +65,3 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf) return ret; } - - - -conf_mem_seg_t** parseMemToml(const toml_table_t* memTable) -{ - uint16_t memSegIdx = 0; - const char* memSegKey = toml_key_in(memTable, 0); - while (memSegKey && memSegIdx < 0xFFFF) - { - memSegIdx++; - memSegKey = toml_key_in(memTable, memSegIdx); - } - - conf_mem_seg_t** segConfigs = malloc(sizeof(conf_mem_seg_t*) * ((size_t)memSegIdx + 1)); - if (segConfigs == NULL) - { - return NULL; - } - segConfigs[memSegIdx] = NULL; - - for (size_t i = 0; i < memSegIdx; i++) - { - segConfigs[i] = (conf_mem_seg_t*)malloc(sizeof(conf_mem_seg_t)); - memSegKey = toml_key_in(memTable, (int)i); - - if (segConfigs[i] == NULL || memSegKey == NULL) - { - if (segConfigs[i] != NULL) - { - free(segConfigs[i]); - } - else - { - } - for(size_t j = 0; j < i; j++) - { - free(segConfigs[j]->name); - free(segConfigs[j]); - } - free(segConfigs); - return NULL; - } - segConfigs[i]->name = (char*)malloc(strlen(memSegKey) + 1); - if (segConfigs[i]->name == NULL) - { - for(size_t j = 0; j <= i; j++) - { - if (j < i) - free(segConfigs[j]->name); - free(segConfigs[j]); - } - free(segConfigs); - return NULL; - } - strcpy(segConfigs[i]->name, memSegKey); - - toml_table_t* segTable = toml_table_in(memTable, memSegKey); - if (segTable == NULL) - { - - for(size_t j = 0; j <= i; j++) - { - free(segConfigs[j]->name); - free(segConfigs[j]); - } - free(segConfigs); - return NULL; - } - - toml_datum_t startDatum = toml_int_in(segTable, "start"); - toml_datum_t lenDatum = toml_int_in(segTable, "len"); - toml_datum_t wordLenDatum = toml_int_in(segTable, "wordLen"); - toml_datum_t executableDatum = toml_bool_in(segTable, "executable"); - - if (!startDatum.ok || !lenDatum.ok || !wordLenDatum.ok) - { - for(size_t j = 0; j <= i; j++) - { - free(segConfigs[j]->name); - free(segConfigs[j]); - } - free(segConfigs); - return NULL; - } - - segConfigs[i]->start = (size_t)startDatum.u.i; - segConfigs[i]->len = (size_t)lenDatum.u.i; - segConfigs[i]->wordLen = (uint8_t)wordLenDatum.u.i; - segConfigs[i]->isExecutable = 0; - if (executableDatum.ok) - { - segConfigs[i]->isExecutable = executableDatum.u.b != 0; - } - } - - - - return segConfigs; -} - - -conf_dev_t* openBaseDeviceConfig(const char* configPath, char* errbuf) -{ - conf_dev_t* ret = malloc(sizeof(conf_dev_t)); - if (ret == NULL) - { - sprintf(errbuf, "unable to allocate device conf struct"); - return NULL; - } - - ret->id = NULL; - ret->clockDivider = 1; - ret->clockMultipler = 1; - ret->clockId = NULL; - - - ret->memConf = malloc(sizeof(conf_mem_t)); - if (ret->memConf == NULL) - { - sprintf(errbuf, "unable to allocate device memory conf struct"); - free(ret); - return NULL; - } - - char intErrbuf[1024]; - - - - FILE *fp = fopen (configPath, "rb"); - if (!fp) { - sprintf(errbuf, "unable to open config file %s", configPath); - free(ret->memConf); - free(ret); - return NULL; - } - toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf)); - fclose(fp); - - if (!conf) { - sprintf(errbuf, "cannot parse - %s", intErrbuf); - free(ret->memConf); - free(ret); - return NULL; - } - - toml_table_t* devBlock = toml_table_in(conf, "dev"); - if (!devBlock) { - sprintf(errbuf, "missing [dev]"); - free(ret->memConf); - free(ret); - return NULL; - } - - toml_table_t* memTable = toml_table_in(conf, "mem"); - if (!memTable) { - sprintf(errbuf, "missing [mem]"); - free(ret->memConf); - free(ret); - return NULL; - } - - toml_datum_t libpathBlock = toml_string_in(devBlock, "libpath"); - if (!libpathBlock.ok) { - sprintf(errbuf, "unable to read dev.libpath"); - free(ret->memConf); - free(ret); - return NULL; - } - - conf_mem_seg_t** memSegConfs = parseMemToml(memTable); - if (memSegConfs == NULL) - { - sprintf(errbuf, "unable to parse mem segments"); - free(ret->memConf); - free(ret); - return NULL; - } - - ret->memConf->memSegConfs = memSegConfs; - ret->libPath = libpathBlock.u.s; - - return ret; -} diff --git a/src/compose_device.c b/src/compose_device.c index d0fc3b4..c9ce1c1 100644 --- a/src/compose_device.c +++ b/src/compose_device.c @@ -2,31 +2,10 @@ #include #include -#include "tomlc99/toml.h" - #include "base_device.h" #include "compose_device.h" -// void fillDevClockConfig(const toml_table_t* clockTable, conf_dev_t** devConfs) -// { -// uint16_t clockSegIdx = 0; -// uint16_t clockSegSkips = 0; -// const char* clockSegKey = toml_key_in(clockTable, 0); -// while (clockSegKey && (clockSegIdx + clockSegSkips) < 0xFFFF) -// { -// clockSegKey = toml_key_in(clockTable, clockSegIdx + clockSegSkips + 1); -// if(strcmp(clockSegKey, "limiter") != 0) -// { -// clockSegIdx++; -// } -// else -// { -// clockSegSkips++; -// } -// } -// } - char** appendId(char** prev, const char* cur, char* errbuf) { if(prev == NULL) @@ -40,12 +19,12 @@ char** appendId(char** prev, const char* cur, char* errbuf) prev[0] = NULL; prev[1] = NULL; } - + size_t clockIdLen = 0; while (prev[clockIdLen] != NULL){clockIdLen++;} - + clockIdLen++; - + char** new = realloc(prev, sizeof(char*) * (clockIdLen + 1)); if(new == NULL) @@ -70,131 +49,6 @@ char** appendId(char** prev, const char* cur, char* errbuf) return prev; } -conf_dev_t** parseDevToml(const toml_table_t* devTable, const toml_table_t* clockTable, char* errbuf) -{ - uint16_t devSegIdx = 0; - const char* devSegKey = toml_key_in(devTable, 0); - while (devSegKey && devSegIdx < 0xFFFF) - { - devSegIdx++; - devSegKey = toml_key_in(devTable, devSegIdx); - } - - conf_dev_t** deviceConfigs = malloc(sizeof(conf_dev_t*) * (devSegIdx + 1)); - if(deviceConfigs == NULL) - { - return NULL; - } - - deviceConfigs[devSegIdx] = NULL; - - for(size_t i = 0; i < devSegIdx; i++) - { - devSegKey = toml_key_in(devTable, (int)i); - if(devSegKey == NULL) - { - sprintf(errbuf, "unable to load device key %lu", i); - for(size_t j = 0; j < i; j++) - { - freeConf(deviceConfigs[j]); - free(deviceConfigs[j]); - } - free(deviceConfigs); - return NULL; - } - toml_datum_t devpathDatum = toml_string_in(devTable, devSegKey); - if(!devpathDatum.ok || !devpathDatum.u.s) - { - sprintf(errbuf, "unable to get device path for %s", devSegKey); - - for(size_t j = 0; j < i; j++) - { - freeConf(deviceConfigs[j]); - free(deviceConfigs[j]); - } - free(deviceConfigs); - return NULL; - } - char intErrbuf[1024]; - - conf_dev_t* conf = openBaseDeviceConfig(devpathDatum.u.s, intErrbuf); - if(conf == NULL) - { - sprintf(errbuf, "unable to load config for %s: %s", devSegKey, intErrbuf); - - for(size_t j = 0; j < i; j++) - { - freeConf(deviceConfigs[j]); - free(deviceConfigs[j]); - } - free(deviceConfigs); - return NULL; - } - - conf->id = NULL; - conf->id = appendId(conf->id, devSegKey, intErrbuf); - - - if(conf->id == NULL) - { - sprintf(errbuf, "unable to allocate device %s name field", devSegKey); - for(size_t j = 0; j <= i; j++) - { - freeConf(deviceConfigs[j]); - free(deviceConfigs[j]); - } - free(deviceConfigs); - return NULL; - } - - deviceConfigs[i] = conf; - } - - if(clockTable != NULL) - { - for(size_t i = 0; i < devSegIdx; i++) - { - conf_dev_t* conf = deviceConfigs[i]; - toml_table_t* devClockTable = toml_table_in(clockTable, conf->id[0]); - if(devClockTable == NULL) - { - continue; - } - - toml_datum_t dividerDatum = toml_int_in(devClockTable, "divider"); - if(dividerDatum.ok) - { - conf->clockDivider = (uint64_t)dividerDatum.u.i; - } - toml_datum_t multiplerDatum = toml_int_in(devClockTable, "multipler"); - if(multiplerDatum.ok) - { - conf->clockMultipler = (uint64_t)multiplerDatum.u.i; - } - - toml_datum_t srcDatum = toml_string_in(devClockTable, "src"); - if(srcDatum.ok && srcDatum.u.s != NULL) - { - char intErrbuf[1024] = {0}; - conf->clockId = appendId(conf->clockId, srcDatum.u.s, intErrbuf); - if(conf->clockId == NULL) - { - sprintf(errbuf, "unable to append clock id for %s: %s", conf->id[0], intErrbuf); - for(size_t j = 0; j <= i; j++) - { - freeConf(deviceConfigs[j]); - free(deviceConfigs[j]); - } - free(deviceConfigs); - return NULL; - } - } - - } - } - - return deviceConfigs; -} void freeProjectionConfig(projection_conf_t* conf) { if(conf == NULL) @@ -228,264 +82,6 @@ void freeProjectionConfigs(projection_conf_t** confs) } -projection_conf_t* parseDeviceProjectionConf(const toml_table_t* deviceProjectionTable, char* errbuf) -{ - toml_datum_t baseAtDatum = toml_string_in(deviceProjectionTable, "base_at"); - if(!baseAtDatum.ok || baseAtDatum.u.s == NULL) - { - sprintf(errbuf, "missing 'base_at'"); - return NULL; - } - - toml_datum_t baseSegDatum = toml_string_in(deviceProjectionTable, "base_seg"); - if(!baseSegDatum.ok || baseSegDatum.u.s == NULL) - { - sprintf(errbuf, "missing 'base_seg'"); - return NULL; - } - - toml_datum_t projectionShiftDatum = toml_int_in(deviceProjectionTable, "projection_shift"); - - char intErrbuf[1024] = {0}; - - projection_conf_t* ret = malloc(sizeof(projection_conf_t)); - if(ret == NULL) - { - sprintf(errbuf, "unable to allocate projection conf"); - return NULL; - } - ret->baseAt = NULL; - ret->baseSeg = NULL; - ret->target = NULL; - ret->projectionShift = 0; - - if(projectionShiftDatum.ok) - { - ret->projectionShift = (uint64_t)projectionShiftDatum.u.i; - } - - ret->baseAt = appendId(ret->baseAt, baseAtDatum.u.s, intErrbuf); - if(ret->baseAt == NULL) - { - sprintf(errbuf, "unable to append base_at id"); - freeProjectionConfig(ret); - return NULL; - } - ret->baseSeg = malloc(strlen(baseSegDatum.u.s)); - if(ret->baseSeg == NULL) - { - sprintf(errbuf, "unable to fill base_seg"); - freeProjectionConfig(ret); - return NULL; - } - strcpy(ret->baseSeg, baseSegDatum.u.s); - - return ret; -} - -projection_conf_t** parseDeviceProjectionConfs(const toml_table_t* deviceProjectionsTable, char* errbuf) -{ - uint16_t projSegIdx = 0; - const char* projSegKey = toml_key_in(deviceProjectionsTable, 0); - while (projSegKey && projSegIdx < 0xFFFF) - { - projSegIdx++; - projSegKey = toml_key_in(deviceProjectionsTable, projSegIdx); - } - char intErrbuf[1024] = {0}; - - projection_conf_t** ret = malloc(sizeof(projection_conf_t*) * (projSegIdx + 1)); - if(ret == NULL) - { - sprintf(errbuf, "unable to allocate device projections confs"); - return NULL; - } - - for(size_t i = 0; i <= projSegIdx; i++){ret[i] = NULL;} - - for(size_t i = 0; i < projSegIdx; i++) - { - projSegKey = toml_key_in(deviceProjectionsTable, (int)i); - if(projSegKey == NULL) - { - sprintf(errbuf, "unable to load device projection key %lu", i); - freeProjectionConfigs(ret); - return NULL; - } - - toml_table_t* projTable = toml_table_in(deviceProjectionsTable, projSegKey); - if(projTable == NULL) - { - sprintf(errbuf, "unable to open device %s projection table", projSegKey); - freeProjectionConfigs(ret); - return NULL; - } - - ret[i] = parseDeviceProjectionConf(projTable, intErrbuf); - if(ret[i] == NULL) - { - sprintf(errbuf, "unable to load device %s projection configs: %s", projSegKey, intErrbuf); - freeProjectionConfigs(ret); - return NULL; - } - - ret[i]->target = appendId(ret[i]->target, projSegKey, intErrbuf); - if(ret[i]->target == NULL) - { - sprintf(errbuf, "unable to append projection target id: %s", intErrbuf); - freeProjectionConfigs(ret); - return NULL; - } - } - - return ret; -} - -projection_conf_t** parseProjectionConfs(const toml_table_t* projectionTable, char* errbuf) -{ - uint16_t devicesProjSegIdx = 0; - const char* devicesProjSegKey = toml_key_in(projectionTable, 0); - while (devicesProjSegKey && devicesProjSegIdx < 0xFFFF) - { - devicesProjSegIdx++; - devicesProjSegKey = toml_key_in(projectionTable, devicesProjSegIdx); - } - - - char intErrbuf[1024] = {0}; - - size_t totalRetSize = 0; - projection_conf_t** ret = malloc(sizeof(projection_conf_t*)); - if(ret == NULL) - { - sprintf(errbuf, "unable to allocate base projection conf array"); - return NULL; - } - ret[0] = NULL; - - for(size_t i = 0; i < devicesProjSegIdx; i++) - { - devicesProjSegKey = toml_key_in(projectionTable, (int)i); - if(devicesProjSegKey == NULL) - { - sprintf(errbuf, "unable to load proj key %lu", i); - freeProjectionConfigs(ret); - return NULL; - } - - toml_table_t* deviceProjTable = toml_table_in(projectionTable, devicesProjSegKey); - if(deviceProjTable == NULL) - { - sprintf(errbuf, "unable to open device projection table"); - freeProjectionConfigs(ret); - return NULL; - } - - projection_conf_t** devProjections = parseDeviceProjectionConfs(deviceProjTable, intErrbuf); - - if(devProjections == NULL) - { - sprintf(errbuf, "unable to parse device %s projection rules: %s", devicesProjSegKey, intErrbuf); - freeProjectionConfigs(ret); - return NULL; - } - size_t dlen = 0; - while(devProjections[dlen] != NULL) - { - devProjections[dlen]->target = appendId(devProjections[dlen]->target, devicesProjSegKey, intErrbuf); - if(devProjections[dlen]->target == NULL) - { - freeProjectionConfigs(ret); - freeProjectionConfigs(devProjections); - return NULL; - } - dlen++; - } - ret = realloc(ret, sizeof(projection_conf_t*) * (dlen + totalRetSize + 1)); - if(ret == NULL) - { - sprintf(errbuf, "unable to reallocate full projection array"); - freeProjectionConfigs(devProjections); - return NULL; - } - for(size_t i = 0; i <= dlen; i++) - { - ret[totalRetSize + i] = devProjections[i]; - } - totalRetSize += dlen; - ret[totalRetSize] = NULL; - free(devProjections); - } - - return ret; -} - -compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf) -{ - compose_dev_conf_t* ret = malloc(sizeof(compose_dev_conf_t)); - - if(ret == NULL) - { - sprintf(errbuf, "unable to allocate device struct"); - return NULL; - } - - char intErrbuf[1024] = {0}; - FILE *fp = fopen (configPath, "rb"); - if (!fp) { - sprintf(errbuf, "unable to open config file %s", configPath); - free(ret); - return NULL; - } - toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf)); - fclose(fp); - - - toml_table_t* devTable = toml_table_in(conf, "dev"); - if (!devTable) { - sprintf(errbuf, "missing [dev]"); - free(ret); - return NULL; - } - ret->projections = NULL; - - toml_table_t* memTable = toml_table_in(conf, "mem"); - if(memTable) - { - toml_table_t* projectionTable = toml_table_in(memTable, "projection"); - if(projectionTable) - { - ret->projections = parseProjectionConfs(projectionTable, intErrbuf); - if(ret->projections == NULL) - { - sprintf(errbuf, "unable to load projections: %s", intErrbuf); - free(ret); - return NULL; - } - } - } - - - toml_table_t* clockTable = toml_table_in(conf, "clock"); - - - conf_dev_t** devHandlers = parseDevToml(devTable, clockTable, intErrbuf); - - if(devHandlers == NULL) - { - sprintf(errbuf, "unable to load devices configs: %s", intErrbuf); - freeProjectionConfigs(ret->projections); - free(ret); - return NULL; - } - - ret->baseConfigs = devHandlers; - - - return ret; -} - - device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf) { size_t devIdx = 0; @@ -523,7 +119,3 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf) return devHandlers; } - - - - diff --git a/src/hmmmm.c b/src/hmmmm.c index 00f6d2a..99713f7 100644 --- a/src/hmmmm.c +++ b/src/hmmmm.c @@ -188,7 +188,7 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf) } - _dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevSpecs"); + _dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevMem"); const char *dlib_freeDevMem_error = dlerror(); if (dlib_freeDevMem_error) { diff --git a/src/main.c b/src/main.c index 42e3a0f..5c4f191 100644 --- a/src/main.c +++ b/src/main.c @@ -24,7 +24,6 @@ #include "panic.h" -#include "tomlc99/toml.h" #include @@ -369,6 +368,8 @@ int main(int argc, char** argv) devicesMem, deviceCount, deviceHandlesArr, + NULL, /* interceptCtxs */ + 0, /* interceptCtxCount */ {0} /* stream_builder — initialized below */ }; if (flatcc_builder_init(&emulContext.stream_builder)) { diff --git a/src/proto/handlers/config.c b/src/proto/handlers/config.c index b6b6a14..0a61f22 100644 --- a/src/proto/handlers/config.c +++ b/src/proto/handlers/config.c @@ -77,6 +77,7 @@ typedef struct { // store copies of hierarchical path per device char** paths[MAX_DEVICES]; // each is a NULL-terminated string array size_t path_lens[MAX_DEVICES]; // number of components per path + size_t seg_counts[MAX_DEVICES]; // number of memory segments per device size_t count; } LoadState; @@ -137,6 +138,12 @@ static int load_devices_recursive( size_t idx = st->count; st->handles[idx] = dev; + // Store segment count from FlatBuffer config + hmmmm_config_MemSegment_vec_t dev_segs = + hmmmm_config_BaseDeviceConfig_mem_segments(base); + st->seg_counts[idx] = dev_segs + ? hmmmm_config_MemSegment_vec_len(dev_segs) : 0; + // Copy path st->path_lens[idx] = st->depth; st->paths[idx] = calloc(st->depth + 1, sizeof(char*)); @@ -183,10 +190,276 @@ static void free_load_state_paths(LoadState* st) } +// Find a device index by its leaf ID (last path component) +static size_t find_device_by_id(LoadState* st, const char* id) +{ + for (size_t i = 0; i < st->count; i++) { + // Match by last path component (the device's own ID) + const char* leaf = st->paths[i][st->path_lens[i] - 1]; + if (strcmp(leaf, id) == 0) return i; + } + return (size_t)~0; +} + + +// Find a segment index by name in a device +static size_t find_seg_by_name(device_handle_t* dev, size_t seg_count, const char* name) +{ + device_mem_t* mem = dev->ctx->deviceMem; + for (size_t i = 0; i < seg_count; i++) { + if (strcmp(mem->memsegNames[i], name) == 0) return i; + } + return (size_t)~0; +} + + +static int apply_projections( + hmmmm_config_ComposeDeviceConfig_table_t compose, + LoadState* st, + char* errbuf) +{ + hmmmm_config_Projection_vec_t proj_vec = + hmmmm_config_ComposeDeviceConfig_projections(compose); + size_t proj_n = proj_vec ? hmmmm_config_Projection_vec_len(proj_vec) : 0; + + for (size_t i = 0; i < proj_n; i++) { + hmmmm_config_Projection_table_t proj = + hmmmm_config_Projection_vec_at(proj_vec, i); + + flatbuffers_string_t base_at = hmmmm_config_Projection_base_at(proj); + flatbuffers_string_t base_seg = hmmmm_config_Projection_base_seg(proj); + flatbuffers_string_t target_at = hmmmm_config_Projection_target_at(proj); + flatbuffers_string_t target_seg = hmmmm_config_Projection_target_seg(proj); + uint32_t shift = hmmmm_config_Projection_shift(proj); + + if (!base_at || !base_seg || !target_at || !target_seg) { + sprintf(errbuf, "projection %zu: missing field", i); + return -1; + } + + size_t base_idx = find_device_by_id(st, base_at); + if (base_idx == (size_t)~0) { + sprintf(errbuf, "projection %zu: base device '%s' not found", i, base_at); + return -1; + } + + size_t target_idx = find_device_by_id(st, target_at); + if (target_idx == (size_t)~0) { + sprintf(errbuf, "projection %zu: target device '%s' not found", i, target_at); + return -1; + } + + size_t base_seg_idx = find_seg_by_name( + st->handles[base_idx], st->seg_counts[base_idx], base_seg); + if (base_seg_idx == (size_t)~0) { + sprintf(errbuf, "projection %zu: base segment '%s' not found in '%s'", + i, base_seg, base_at); + return -1; + } + + size_t target_seg_idx = find_seg_by_name( + st->handles[target_idx], st->seg_counts[target_idx], target_seg); + if (target_seg_idx == (size_t)~0) { + sprintf(errbuf, "projection %zu: target segment '%s' not found in '%s'", + i, target_seg, target_at); + return -1; + } + + // Remap: target device's segment now points to base device's segment memory + shift + void* base_ptr = st->handles[base_idx]->ctx->deviceMem->cells[base_seg_idx]; + st->handles[target_idx]->ctx->deviceMem->cells[target_seg_idx] = + (uint8_t*)base_ptr + shift; + + printf("[CTRL/CONFIG] projection: %s.%s -> %s.%s (shift=%u)\n", + base_at, base_seg, target_at, target_seg, shift); + } + + return 0; +} + + +// ── Intercept handler context and functions ───────────────────────────────── + +typedef struct { + uint8_t* point_cell; // direct pointer to point device's cell + uint8_t* base_cell; // direct pointer to base device's cell (shadow_copy) + uint8_t word_len; +} intercept_ctx_t; + +// shadow_replace: read from point device instead of base +static void* intercept_replace_read(uint64_t ident, uint64_t addr, void* rawCells) +{ + intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident; + return ctx->point_cell; +} + +// shadow_replace: write to point device instead of base +static void intercept_replace_write(uint64_t ident, uint64_t addr, void* rawCells, void* data) +{ + intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident; + memcpy(ctx->point_cell, data, ctx->word_len); +} + +// shadow_copy: write to both base and point +static void intercept_copy_write(uint64_t ident, uint64_t addr, void* rawCells, void* data) +{ + intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident; + memcpy(ctx->base_cell, data, ctx->word_len); + memcpy(ctx->point_cell, data, ctx->word_len); +} + +#define MAX_INTERCEPTS 256 + +static int apply_intercepts( + hmmmm_config_ComposeDeviceConfig_table_t compose, + LoadState* st, + intercept_ctx_t** out_ctxs, + size_t* out_ctx_count, + char* errbuf) +{ + hmmmm_config_Intercept_vec_t icpt_vec = + hmmmm_config_ComposeDeviceConfig_intercepts(compose); + size_t icpt_n = icpt_vec ? hmmmm_config_Intercept_vec_len(icpt_vec) : 0; + + *out_ctx_count = 0; + if (icpt_n == 0) { *out_ctxs = NULL; return 0; } + if (icpt_n > MAX_INTERCEPTS) { + sprintf(errbuf, "too many intercepts (%zu)", icpt_n); + return -1; + } + + // Allocate array of intercept contexts (one per intercept) + intercept_ctx_t* ctxs = calloc(icpt_n, sizeof(intercept_ctx_t)); + if (!ctxs) { sprintf(errbuf, "alloc intercept contexts"); return -1; } + + for (size_t i = 0; i < icpt_n; i++) { + hmmmm_config_Intercept_table_t icpt = + hmmmm_config_Intercept_vec_at(icpt_vec, i); + + flatbuffers_string_t name = hmmmm_config_Intercept_name(icpt); + int8_t op = hmmmm_config_Intercept_op(icpt); + int8_t mode = hmmmm_config_Intercept_mode(icpt); + + flatbuffers_string_t base_at = hmmmm_config_Intercept_base_at(icpt); + flatbuffers_string_t base_seg = hmmmm_config_Intercept_base_seg(icpt); + uint32_t base_addr = hmmmm_config_Intercept_base_addr(icpt); + + flatbuffers_string_t point_at = hmmmm_config_Intercept_point_at(icpt); + flatbuffers_string_t point_seg = hmmmm_config_Intercept_point_seg(icpt); + uint32_t point_addr = hmmmm_config_Intercept_point_addr(icpt); + + if (!base_at || !base_seg || !point_at || !point_seg) { + sprintf(errbuf, "intercept %zu: missing field", i); + free(ctxs); + return -1; + } + + // Resolve base device + segment + size_t base_idx = find_device_by_id(st, base_at); + if (base_idx == (size_t)~0) { + sprintf(errbuf, "intercept %zu: base device '%s' not found", i, base_at); + free(ctxs); return -1; + } + size_t base_seg_idx = find_seg_by_name( + st->handles[base_idx], st->seg_counts[base_idx], base_seg); + if (base_seg_idx == (size_t)~0) { + sprintf(errbuf, "intercept %zu: base segment '%s' not found in '%s'", + i, base_seg, base_at); + free(ctxs); return -1; + } + + // Resolve point device + segment + size_t point_idx = find_device_by_id(st, point_at); + if (point_idx == (size_t)~0) { + sprintf(errbuf, "intercept %zu: point device '%s' not found", i, point_at); + free(ctxs); return -1; + } + size_t point_seg_idx = find_seg_by_name( + st->handles[point_idx], st->seg_counts[point_idx], point_seg); + if (point_seg_idx == (size_t)~0) { + sprintf(errbuf, "intercept %zu: point segment '%s' not found in '%s'", + i, point_seg, point_at); + free(ctxs); return -1; + } + + device_mem_t* base_mem = st->handles[base_idx]->ctx->deviceMem; + device_mem_t* point_mem = st->handles[point_idx]->ctx->deviceMem; + + // Compute global address on base device (for handler array index) + uint64_t base_global = base_mem->memsegShifts[base_seg_idx] + base_addr; + + // Get word length from base segment + uint8_t wl = 1; // default + // memsegShifts difference gives segment size; use rawCells layout + // We can get wordLen from the point segment shift or use 1 for byte-addressable + + // Fill intercept context + intercept_ctx_t* ctx = &ctxs[i]; + ctx->word_len = wl; + ctx->base_cell = (uint8_t*)base_mem->rawCells + base_global; + ctx->point_cell = (uint8_t*)point_mem->rawCells + + point_mem->memsegShifts[point_seg_idx] + point_addr; + + uint64_t ident = (uint64_t)(uintptr_t)ctx; + + // NOTE: WRITE_MEM macro reads ident from smartAddrReadHandlers, not + // smartAddrWriteHandlers. Always set ident on both arrays so that + // write handlers receive the correct context pointer. + base_mem->smartAddrReadHandlers[base_global].ident = ident; + base_mem->smartAddrWriteHandlers[base_global].ident = ident; + + if (mode == hmmmm_config_InterceptMode_shadow_replace) { + // Replace: redirect read and/or write + if (op == hmmmm_config_InterceptOp_op_read || + op == hmmmm_config_InterceptOp_op_both) { + base_mem->smartAddrReadHandlers[base_global].func = intercept_replace_read; + } + if (op == hmmmm_config_InterceptOp_op_write || + op == hmmmm_config_InterceptOp_op_both) { + base_mem->smartAddrWriteHandlers[base_global].func = intercept_replace_write; + } + } else if (mode == hmmmm_config_InterceptMode_shadow_copy) { + // Copy: only affects writes (read stays at base) + if (op == hmmmm_config_InterceptOp_op_write || + op == hmmmm_config_InterceptOp_op_both) { + base_mem->smartAddrWriteHandlers[base_global].func = intercept_copy_write; + } + } else if (mode == hmmmm_config_InterceptMode_callback) { + // Callback mode: not implemented yet — skip with warning + printf("[CTRL/CONFIG] intercept %zu '%s': callback mode not yet implemented\n", + i, name ? name : "(unnamed)"); + } else { + sprintf(errbuf, "intercept %zu: unknown mode %d", i, mode); + free(ctxs); return -1; + } + + printf("[CTRL/CONFIG] intercept: %s.%s[%u] -> %s.%s[%u] mode=%s op=%s\n", + base_at, base_seg, base_addr, + point_at, point_seg, point_addr, + hmmmm_config_InterceptMode_name(mode), + hmmmm_config_InterceptOp_name(op)); + } + + *out_ctxs = ctxs; + *out_ctx_count = icpt_n; + return 0; +} + + static void free_old_config(EmulContext* emulContext) { if (emulContext->devicesCount == 0) return; + // Free intercept contexts + if (emulContext->interceptCtxs) { + for (size_t i = 0; i < emulContext->interceptCtxCount; i++) { + free(emulContext->interceptCtxs[i]); + } + free(emulContext->interceptCtxs); + emulContext->interceptCtxs = NULL; + emulContext->interceptCtxCount = 0; + } + for (size_t i = 0; i < emulContext->devicesCount; i++) { device_handle_t* dev = (device_handle_t*)emulContext->deviceHandles[i]; closeBaseDevice(dev); @@ -284,6 +557,50 @@ void handleConfigCtrlMessage( printf("[CTRL/CONFIG] loaded %zu devices\n", st.count); + // Apply projections + if (apply_projections(root, &st, errbuf) != 0) { + printf("[CTRL/CONFIG] projection error: %s\n", errbuf); + + for (size_t i = 0; i < st.count; i++) { + closeBaseDevice(st.handles[i]); + free(st.handles[i]); + } + free_load_state_paths(&st); + + size_t msg_len; + uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); + return; + } + + // Apply intercepts + intercept_ctx_t* icpt_ctxs = NULL; + size_t icpt_count = 0; + if (apply_intercepts(root, &st, &icpt_ctxs, &icpt_count, errbuf) != 0) { + printf("[CTRL/CONFIG] intercept error: %s\n", errbuf); + + for (size_t i = 0; i < st.count; i++) { + closeBaseDevice(st.handles[i]); + free(st.handles[i]); + } + free_load_state_paths(&st); + + size_t msg_len; + uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); + return; + } + + // Store intercept contexts for later cleanup + if (icpt_ctxs) { + emulContext->interceptCtxs = calloc(1, sizeof(void*)); + emulContext->interceptCtxs[0] = icpt_ctxs; + emulContext->interceptCtxCount = 1; + } else { + emulContext->interceptCtxs = NULL; + emulContext->interceptCtxCount = 0; + } + // Allocate new arrays for EmulContext size_t dc = st.count; emulContext->devicesCount = dc;