feat: migrate config management from TOML to FlatBuffers

Config is now loaded at runtime via WebSocket (ConfigCtrlMessage)
instead of from hardcoded TOML files. The emulator starts with no
devices and waits for clients to send configuration.

- Define FlatBuffers schemas: EmulationConfig, ComposeDeviceConfig,
  BaseDeviceConfig with recursive DeviceConfig union
- Rename MemSegment.start → addr (flatcc builder/reader name collision)
- Add ConfigCtrlMessage handler: validates STILL state, walks the
  device tree depth-first, assigns numeric IDs, responds with
  DeviceIdMappingNotif or ConfigLoadError
- Add fb_build_config_device_id_mapping() and fb_build_config_error()
  FlatBuffer builders
- Remove hardcoded device loading from main.c; iterate dynamically
  loaded devices in the exec loop
- Fix double-free: freeConf() already frees the struct itself, remove
  redundant free() calls in config.c and base_device.c
- Fix heap-buffer-overflow in device parseSpecsFromConfig: malloc
  for segment name was missing +1 for the null terminator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-31 14:02:41 +00:00
parent d83cb7fe7b
commit 589bc8d620
16 changed files with 1277 additions and 87 deletions

View File

@@ -73,7 +73,7 @@ target: date proto $(BUILD_DIR)/$(TARGET).elf
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
@echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m' @echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m'
@$(CC) $(LFLAGS) $(OBJECTS) -o $(BUILD_DIR)/$(TARGET).elf @$(CC) $(LFLAGS) $(OBJECTS) $(LDLIBS) -o $(BUILD_DIR)/$(TARGET).elf
deps: deps:
@make -C deps all @make -C deps all

View File

@@ -0,0 +1,764 @@
#include <stdint.h>
#include <string.h>
#include "device.h"
#include "mem.h"
#include "runner.h"
#include "instr.h"
#include "addrs.h"
uint64_t memSegToGlobal(device_specs_t* spec, uint8_t seg, uint64_t localaddr)
{
uint64_t offset = 0;
for (uint8_t i = 0; i < seg; i++)
{
offset += spec->memSpecs[i]->len * spec->memSpecs[i]->wordLen;
}
return offset + localaddr;
}
void freeDevMem(device_mem_t* devMem)
{
free(devMem->memsegShifts);
free(devMem->rawCells);
free(devMem->memreadCellAddrs);
free(devMem->memwriteCellAddrs);
free(devMem->cells);
free(devMem->smartAddrReadHandlers);
free(devMem->smartAddrWriteHandlers);
free(devMem);
}
void freeDevSpec(void* _specs)
{
device_specs_t* specs = _specs;
for (uint8_t i = 0; i < specs->memSpecsCount; i++)
{
free(specs->memSpecs[i]);
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
}
device_mem_t* genDevMem(device_specs_t* devSpec, char* errbuf)
{
if (devSpec->memSpecsCount < MEMDATA_OPSIZE)
{
sprintf(errbuf, "invalid amount of mem specs: %u", devSpec->memSpecsCount);
return NULL;
}
device_mem_t* devMem = (device_mem_t*)malloc(sizeof(device_mem_t));
if (devMem == NULL)
{
sprintf(errbuf, "unable to allocate dev memory struct");
return NULL;
}
uint64_t memTotalSize = 0;
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
{
size_t tmp = devSpec->memSpecs[i]->start + devSpec->memSpecs[i]->len * devSpec->memSpecs[i]->wordLen;
if (memTotalSize < tmp)
{
memTotalSize = tmp;
}
}
void* rawCells = (void*)malloc(memTotalSize);
if (rawCells == NULL)
{
sprintf(errbuf, "unable to allocate raw memory buf");
free(devMem);
return NULL;
}
for (size_t i = 0; i < memTotalSize; i++)
{
((uint8_t*)rawCells)[i] = 0;
}
devMem->memsegShifts = malloc(devSpec->memSpecsCount * sizeof(uint64_t));
if(devMem->memsegShifts == NULL)
{
sprintf(errbuf, "unable to allocate segment shift buffers");
free(rawCells);
free(devMem);
return NULL;
}
void** cells = malloc(devSpec->memSpecsCount * sizeof(void*));
if (cells == NULL)
{
sprintf(errbuf, "unable to allocate segment pointers");
free(rawCells);
free(devMem->memsegShifts);
free(devMem);
return NULL;
}
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
{
cells[i] = rawCells + devSpec->memSpecs[i]->start;
}
char** cellNames = malloc(sizeof(char*) * devSpec->memSpecsCount);
if(cellNames == NULL)
{
sprintf(errbuf, "unable to allocate segment names map");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(cells);
return NULL;
}
for(size_t i = 0; i < devSpec->memSpecsCount; i++)
{
cellNames[i] = devSpec->memSpecs[i]->name;
}
uint64_t* memreadCellAddrs = malloc(64 * sizeof(uint64_t));
if (memreadCellAddrs == NULL)
{
sprintf(errbuf, "unable to allocate read interception addrs");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(cells);
free(cellNames);
return NULL;
}
uint64_t* memwriteCellAddrs = malloc(64 * sizeof(uint64_t));
if (memwriteCellAddrs == NULL)
{
sprintf(errbuf, "unable to allocate write interception addrs");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
return NULL;
}
uint64_t smartAddrReadMask = 0;
// memSegToGlobal(devSpec, MEMDATA_IO_REGS, 0x16)
// | memSegToGlobal(devSpec, MEMDATA_IO_REGS, 0x17)
// | memSegToGlobal(devSpec, MEMDATA_IO_REGS, 0x18);
uint64_t smartAddrWriteMask = 0;
// memSegToGlobal(devSpec, MEMDATA_IO_REGS, 0x16)
// | memSegToGlobal(devSpec, MEMDATA_IO_REGS, 0x17)
// | memSegToGlobal(devSpec, MEMDATA_IO_REGS, 0x18);
mem_h_read_handler* smartAddrReadHandlers = malloc(sizeof(mem_h_read_handler) * memTotalSize);
if (smartAddrReadHandlers == NULL)
{
sprintf(errbuf, "unable to allocate read interception handlers");
free(memwriteCellAddrs);
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
return NULL;
}
mem_h_write_handler* smartAddrWriteHandlers = malloc(sizeof(mem_h_write_handler) * memTotalSize);
if (smartAddrWriteHandlers == NULL)
{
sprintf(errbuf, "unable to allocate write interception handlers");
free(smartAddrReadHandlers);
free(memwriteCellAddrs);
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
return NULL;
}
for(uint64_t i = 0; i < memTotalSize; i++)
{
smartAddrReadHandlers[i].func = NULL;
smartAddrReadHandlers[i].ident = 0;
smartAddrWriteHandlers[i].func = NULL;
smartAddrWriteHandlers[i].ident = 0;
}
for(uint64_t i = 0; i < memTotalSize; i++)
{
if((i & smartAddrReadMask) == smartAddrReadMask)
{
smartAddrReadHandlers[i].func = NULL;
}
}
if (devSpec->smartReadSpecsCount > 0)
{
for(uint64_t i = 0; i < devSpec->smartReadSpecsCount; i++)
{
smart_read_spec_t t = devSpec->smartReadSpecs[i];
uint64_t addr;
if (t.addrType == SMART_ADDR_TYPE_GLOBAL)
{
addr = t.addr;
}
else
{
addr = memSegToGlobal(devSpec, t.segno, t.localAddr);
}
smartAddrReadHandlers[addr].func = *t.handler;
smartAddrReadHandlers[addr].ident = t.ident;
smartAddrReadMask |= addr;
}
}
if (devSpec->smartWriteSpecsCount > 0)
{
for(uint64_t i = 0; i < devSpec->smartWriteSpecsCount; i++)
{
smart_write_spec_t t = devSpec->smartWriteSpecs[i];
uint64_t addr;
if (t.addrType == SMART_ADDR_TYPE_GLOBAL)
{
addr = t.addr;
}
else
{
addr = memSegToGlobal(devSpec, t.segno, t.localAddr);
}
smartAddrWriteHandlers[addr].func = *t.handler;
smartAddrWriteHandlers[addr].ident = t.ident;
smartAddrWriteMask |= t.addr;
}
}
devMem->cells = cells;
devMem->rawCells = rawCells;
devMem->memreadCellAddrs = memreadCellAddrs;
devMem->memwriteCellAddrs = memwriteCellAddrs;
devMem->memreadLen = 0;
devMem->memwriteLen = 0;
devMem->smartAddrReadMask = smartAddrReadMask;
devMem->smartAddrReadHandlers = smartAddrReadHandlers;
devMem->smartAddrWriteMask = smartAddrWriteMask;
devMem->smartAddrWriteHandlers = smartAddrWriteHandlers;
devMem->memsegNames = cellNames;
for(uint8_t i = 0; i < devSpec->memSpecsCount; i++)
{
devMem->memsegShifts[i] = memSegToGlobal(devSpec, i, 0);
}
setOpcodeSizes((uint8_t*)(devMem->cells[MEMDATA_OPSIZE]));
return devMem;
}
uint8_t makeDeviceTick(device_public_context_t* devContext)
{
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
return makeTick(devInfo->pc, devInfo->instr, devInfo->deviceMem);
}
device_info_t* initSpecs(device_specs_t* specs, char* errbuf)
{
device_info_t* devInfo = malloc(sizeof(device_info_t));
if (devInfo == NULL)
{
sprintf(errbuf, "unable to allocate dev info");
return NULL;
}
char genErrBuf[200];
device_mem_t* devMem = genDevMem(specs, genErrBuf);
if (devMem == NULL)
{
sprintf(errbuf, "unable to generate device memory: %s", genErrBuf);
free(devInfo);
return NULL;
}
devInfo->pc = (prog_counter_t*)(devMem->rawCells + specs->pcAddr);
*(devInfo->pc) = 0;
devInfo->specs = specs;
devInfo->deviceMem = devMem;
instr_h_func* instrs = genInstrArray(genErrBuf);
if (instrs == NULL)
{
sprintf(errbuf, "unable to initialize instructions: %s", genErrBuf);
freeDevMem(devInfo->deviceMem);
free(devInfo);
return NULL;
}
devInfo->instr = instrs;
return devInfo;
}
void error(const char* err, char* errbuf)
{
strcpy(errbuf, err);
}
device_specs_t* parseSpecsFromConfig(const conf_dev_t* devConf, char* errbuf)
{
conf_mem_seg_t** segments = devConf->memConf->memSegConfs;
device_specs_t* specs = malloc(sizeof(device_specs_t));
if (specs == NULL)
{
error("unable to allocate mem specs struct", errbuf);
return NULL;
}
uint8_t specCount = 0;
specs->executableSegmentsCount = 0;
while (segments[specCount] != NULL)
{
if(segments[specCount]->isExecutable)
{
specs->executableSegmentsCount++;
}
specCount++;
}
specs->memSpecsCount = specCount;
specs->memSpecs = malloc(specCount * sizeof(memseg_spec_t*));
if (specs->memSpecs == NULL)
{
free(specs);
error("unable to allocate mem segment specs", errbuf);
return NULL;
}
specs->executableSegments = malloc(sizeof(uint8_t) * specs->executableSegmentsCount);
if (specs->executableSegments == NULL)
{
error("unable to allocate mem executable specs", errbuf);
free(specs->memSpecs);
free(specs);
return NULL;
}
for (uint8_t i = 0; i < specCount; i++)
{
memseg_spec_t* spec = (memseg_spec_t*)malloc(sizeof(memseg_spec_t));
if (spec == NULL)
{
sprintf(errbuf, "unable to allocate spec %d", i);
for (uint8_t j = 0; j < i; j++)
{
free(specs->memSpecs[j]);
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
return NULL;
}
spec->name = NULL;
specs->memSpecs[i] = spec;
}
uint8_t foundSegments = 0;
uint8_t executableSegmentsFound = 0;
for (uint8_t i = 0; i < specCount; i++)
{
uint8_t specNum = 0xFF;
if (strcmp(segments[i]->name, "reg_gp") == 0)
{
specNum = MEMDATA_GP_REGS;
}
else if (strcmp(segments[i]->name, "reg_io") == 0)
{
specNum = MEMDATA_IO_REGS;
}
else if (strcmp(segments[i]->name, "ds") == 0)
{
specNum = MEMDATA_DS;
}
else if (strcmp(segments[i]->name, "ps") == 0)
{
specNum = MEMDATA_PS;
}
else
{
sprintf(errbuf, "invalid segment: %s", segments[i]->name);
for(size_t j = 0; j < specCount; j++)
{
if(specs->memSpecs[j]->name != NULL)
{
free(specs->memSpecs[j]->name);
}
free(specs->memSpecs[j]);
}
free(specs->executableSegments);
free(specs->memSpecs);
free(specs);
return NULL;
}
specs->memSpecs[specNum]->name = malloc(sizeof(char) * (strlen(segments[i]->name) + 1));
if(specs->memSpecs[specNum]->name == NULL)
{
sprintf(errbuf, "unable to allocate spec %d name", i);
for(size_t j = 0; j < specCount; j++)
{
if(specs->memSpecs[j]->name != NULL)
{
free(specs->memSpecs[j]->name);
}
free(specs->memSpecs[j]);
}
free(specs->executableSegments);
free(specs->memSpecs);
free(specs);
return NULL;
}
strcpy(specs->memSpecs[specNum]->name, segments[i]->name);
specs->memSpecs[specNum]->start = segments[i]->start;
specs->memSpecs[specNum]->len = segments[i]->len;
specs->memSpecs[specNum]->wordLen = segments[i]->wordLen;
if(segments[i]->isExecutable)
{
specs->executableSegments[executableSegmentsFound] = specNum;
executableSegmentsFound++;
}
foundSegments += 1;
}
if(executableSegmentsFound < specs->executableSegmentsCount)
{
sprintf(errbuf, "Not all executable segments found");
for(size_t j = 0; j < specCount; j++)
{
if(specs->memSpecs[j]->name != NULL)
{
free(specs->memSpecs[j]->name);
}
free(specs->memSpecs[j]);
}
free(specs->executableSegments);
free(specs->memSpecs);
free(specs);
return NULL;
}
if (foundSegments < 4)
{
sprintf(errbuf, "invalid amount of segments: must be 4");
for(size_t k = 0; k < specs->memSpecsCount; k++)
{
if(specs->memSpecs[k]->name != NULL)
{
free(specs->memSpecs[k]->name);
}
free(specs->memSpecs[k]);
}
free(specs->executableSegments);
free(specs->memSpecs);
free(specs);
return NULL;
}
return specs;
}
void freeDevSpecs(device_specs_t* specs)
{
for(size_t k = 0; k < specs->memSpecsCount; k++)
{
if(specs->memSpecs[k]->name != NULL)
{
free(specs->memSpecs[k]->name);
}
free(specs->memSpecs[k]);
}
free(specs->executableSegments);
free(specs->memSpecs);
free(specs);
}
void fillSmartReadSpecs(device_specs_t* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount)
{
specs->smartReadSpecs = smartReadSpecs;
specs->smartReadSpecsCount = smartReadSpecsCount;
}
void fillSmartWriteSpecs(device_specs_t* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount)
{
specs->smartWriteSpecs = smartWriteSpecs;
specs->smartWriteSpecsCount = smartWriteSpecsCount;
}
device_public_context_t* initDefault(smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount, char* errbuf)
{
device_specs_t* specs = malloc(sizeof(device_specs_t));
if (specs == NULL)
{
return NULL;
}
specs->memSpecsCount = 4;
specs->memSpecs = malloc(specs->memSpecsCount * sizeof(memseg_spec_t*));
if (specs->memSpecs == NULL)
{
sprintf(errbuf, "unable to allocate default mem segment specs");
free(specs);
return NULL;
}
specs->executableSegmentsCount = 1;
specs->executableSegments = malloc(sizeof(uint8_t) * 1);
if (specs->executableSegments == NULL)
{
sprintf(errbuf, "unable to allocate default executable segments");
free(specs->memSpecs);
free(specs);
return NULL;
}
specs->executableSegments[0] = 0;
for (uint8_t i = 0; i < specs->memSpecsCount; i++)
{
specs->memSpecs[i] = malloc(sizeof(memseg_spec_t));
if (specs->memSpecs[i] == NULL)
{
sprintf(errbuf, "unable to allocate default mem seg spec %u", i);
for (uint8_t j = 0; j < i; j++)
{
free(specs->memSpecs[j]);
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
return NULL;
}
}
specs->smartReadSpecs = smartReadSpecs;
specs->smartReadSpecsCount = smartReadSpecsCount;
specs->smartWriteSpecs = smartWriteSpecs;
specs->smartWriteSpecsCount = smartWriteSpecsCount;
specs->memSpecs[0]->len = 1024;
specs->memSpecs[0]->start = 0;
specs->memSpecs[0]->wordLen = OPCODE_WORDSIZE;
specs->memSpecs[1]->len = 32;
specs->memSpecs[1]->start = (1024 * OPCODE_WORDSIZE);
specs->memSpecs[1]->wordLen = GP_REG_CELL_WORDS;
specs->memSpecs[2]->len = 0xFF;
specs->memSpecs[2]->start = (1024 * OPCODE_WORDSIZE) + (32 * GP_REG_CELL_WORDS);
specs->memSpecs[2]->wordLen = IO_REG_CELL_WORDS;
specs->memSpecs[3]->len = 0xFFFF;
specs->memSpecs[3]->start = (1024 * OPCODE_WORDSIZE) + (32 * GP_REG_CELL_WORDS) + (0xFF * IO_REG_CELL_WORDS);
specs->memSpecs[3]->wordLen = RAM_CELL_WORDS;
char initErrbuf[200];
device_public_context_t* ret = init(specs, initErrbuf);
if (ret == NULL)
{
sprintf(errbuf, "unable to init default: %s", initErrbuf);
freeDevSpec(specs);
return NULL;
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
return ret;
}
device_public_context_t* init(device_specs_t* specs, char* errbuf)
{
if (specs->memSpecsCount >= 0xFF - 2)
{
sprintf(errbuf, "Too many mem specifications");
return NULL;
}
device_specs_t* realSpecs = malloc(sizeof(device_specs_t));
if (realSpecs == NULL)
{
sprintf(errbuf, "unable to allocate spec struct");
return NULL;
}
uint8_t specCount = specs->memSpecsCount + 2;
realSpecs->memSpecsCount = specCount;
realSpecs->memSpecs = (memseg_spec_t**)malloc(realSpecs->memSpecsCount * sizeof(memseg_spec_t*));
if (realSpecs->memSpecs == NULL)
{
sprintf(errbuf, "unable to allocate mem specs");
free(realSpecs);
return NULL;
}
realSpecs->executableSegments = malloc(sizeof(uint8_t) * specs->executableSegmentsCount);
if (realSpecs->executableSegments == NULL)
{
sprintf(errbuf, "unable to allocate executable segments");
free(realSpecs->memSpecs);
free(realSpecs);
return NULL;
}
for (uint8_t i = 0; i < specs->executableSegmentsCount; i++)
{
realSpecs->executableSegments[i] = specs->executableSegments[i];
}
realSpecs->executableSegmentsCount = specs->executableSegmentsCount;
realSpecs->smartReadSpecs = specs->smartReadSpecs;
realSpecs->smartReadSpecsCount = specs->smartReadSpecsCount;
realSpecs->smartWriteSpecs = specs->smartWriteSpecs;
realSpecs->smartWriteSpecsCount = specs->smartWriteSpecsCount;
uint64_t maxAddr = 0;
for (uint8_t i = 0; i < specCount; i++)
{
memseg_spec_t *segSpec = (memseg_spec_t*)malloc(sizeof(memseg_spec_t));
realSpecs->memSpecs[i] = segSpec;
if (segSpec == NULL)
{
sprintf(errbuf, "unable to allocate mem spec segment %u", i);
for (uint8_t j = 0; j < i; j++)
{
free(realSpecs->memSpecs[j]);
}
free(realSpecs->memSpecs);
free(realSpecs->executableSegments);
free(realSpecs);
return NULL;
}
if (i < specCount - 2)
{
realSpecs->memSpecs[i]->name = specs->memSpecs[i]->name;
realSpecs->memSpecs[i]->len = specs->memSpecs[i]->len;
realSpecs->memSpecs[i]->start = specs->memSpecs[i]->start;
realSpecs->memSpecs[i]->wordLen = specs->memSpecs[i]->wordLen;
uint64_t testMaxAddr = realSpecs->memSpecs[i]->start + (realSpecs->memSpecs[i]->len * realSpecs->memSpecs[i]->wordLen);
if (testMaxAddr > maxAddr)
{
maxAddr = testMaxAddr;
}
}
}
uint8_t pcSpecSegNum = specs->memSpecsCount;
realSpecs->memSpecs[MEMDATA_OPSIZE]->len = ((opcode_t)~0);
realSpecs->memSpecs[MEMDATA_OPSIZE]->start = maxAddr + PC_WORDSIZE;
realSpecs->memSpecs[MEMDATA_OPSIZE]->wordLen = 1;
realSpecs->memSpecs[MEMDATA_OPSIZE]->name = NULL;
realSpecs->memSpecs[specCount - 2]->len = 1;
realSpecs->memSpecs[specCount - 2]->start = maxAddr;
realSpecs->memSpecs[specCount - 2]->wordLen = PC_WORDSIZE;
realSpecs->memSpecs[specCount - 2]->name = NULL;
realSpecs->pcAddr = memSegToGlobal(realSpecs, pcSpecSegNum, 0);
device_public_context_t* pubDevContext = malloc(sizeof(device_public_context_t));
if (pubDevContext == NULL)
{
sprintf(errbuf, "unable to allocate public context");
freeDevSpec(realSpecs);
return NULL;
}
char initErrbuf[200];
device_info_t* devInfo = initSpecs(realSpecs, initErrbuf);
if (devInfo == NULL)
{
sprintf(errbuf, "unable to init specs: %s", initErrbuf);
freeDevSpec(realSpecs);
free(pubDevContext);
return NULL;
}
pubDevContext->deviceInfo = (void*)devInfo;
pubDevContext->deviceMem = devInfo->deviceMem;
return pubDevContext;
}
size_t pubExtractPcounter(device_public_context_t* devContext)
{
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
return (size_t)(*devInfo->pc);
}
void* pubExtractPcounterPtr(device_public_context_t* devContext)
{
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
return (void*)(devInfo->pc);
}
uint8_t pubExtractPcounterSizeWords()
{
return sizeof(prog_counter_t);
}
uint8_t pubDeviceType()
{
return EXTENDED_DEVICE_TYPE_INSTR_SIMUL;
}

View File

@@ -4,17 +4,22 @@ include "clock.fbs";
namespace hmmmm.config; namespace hmmmm.config;
// Top-level emulator / system configuration. // Top-level emulation configuration sent by the client via ConfigCtrlMessage.
// Equivalent to glob.toml — describes a full composite device tree. // Contains the root composite device and simulation parameters.
// table EmulationConfig {
// File use: flatcc -a config/config.fbs → binary system config files sim_rate_limit: uint64; // max virtual-seconds per real-second (0 = unlimited)
// WS use: embed in ConfigCtrlMessage when config-change protocol is ready root_id: string; // ID of the root compose device
root: ComposeDeviceConfig; // the root device tree
mem_setup: [MemSetup]; // firmware / memory init instructions
}
// Legacy: flat system configuration for standalone config files.
table SystemConfig { table SystemConfig {
devices: [DeviceEntry]; // ordered list; id must be unique devices: [DeviceEntry];
clock: ClockConfig; clock: ClockConfig;
projections: [Projection]; projections: [Projection];
intercepts: [Intercept]; intercepts: [Intercept];
mem_setup: [MemSetup]; mem_setup: [MemSetup];
} }
root_type SystemConfig; root_type EmulationConfig;

View File

@@ -10,26 +10,36 @@ table BaseDeviceConfig {
mem_segments: [MemSegment]; mem_segments: [MemSegment];
} }
// A composite device that contains other devices.
table ComposeDeviceConfig {
devices: [DeviceEntry]; // child devices (base or compose)
projections: [Projection]; // memory projections between children
intercepts: [Intercept]; // memory intercepts between children
}
// A device can be either a leaf (base) or a container (compose).
union DeviceConfig {
BaseDeviceConfig,
ComposeDeviceConfig,
}
// Override for one memory segment within a child device. // Override for one memory segment within a child device.
// Only fields that need changing from the base config must be set. // Only fields that need changing from the base config must be set.
table MemSegOverride { table MemSegOverride {
segment: string; segment: string;
start: uint32; addr: uint32;
len: uint32; len: uint32;
word_len: uint8; word_len: uint8;
executable: bool; executable: bool;
} }
// One device entry in a composite configuration. // One device entry in a device tree.
table DeviceEntry { table DeviceEntry {
// Local identifier, unique within this composite (e.g. "core", "gpio_a"). // Local identifier, unique within this composite (e.g. "core", "gpio_a").
id: string; id: string;
// Exactly one of config or config_path must be set. // Inline device configuration — either base or compose.
// config: inline base device configuration. config: DeviceConfig;
// config_path: path to a serialised BaseDeviceConfig FlatBuffer file.
config: BaseDeviceConfig;
config_path: string;
clock: DeviceClockConfig; clock: DeviceClockConfig;
overrides: [MemSegOverride]; overrides: [MemSegOverride];

View File

@@ -10,7 +10,7 @@ table NamedOffset {
// A contiguous memory segment within a base device. // A contiguous memory segment within a base device.
table MemSegment { table MemSegment {
name: string; name: string;
start: uint32; // base address in device flat address space addr: uint32; // base address in device flat address space
len: uint32; len: uint32;
word_len: uint8 = 1; // word size in bytes word_len: uint8 = 1; // word size in bytes
executable: bool = false; executable: bool = false;

View File

@@ -1,8 +1,10 @@
// Config change control — WIP. include "config/config.fbs";
// Schema stub; content will be defined when the config-change sub-protocol
// is specified (requires device-loading control in the protocol first).
namespace hmmmm.ctrl.config_ctrl; namespace hmmmm.ctrl.config_ctrl;
// Client → Server: load a new emulation configuration.
// Only allowed when the emulator is in STILL state.
// Completely replaces any previously loaded configuration.
table ConfigCtrlMessage { table ConfigCtrlMessage {
// TODO config: hmmmm.config.EmulationConfig;
} }

View File

@@ -15,7 +15,7 @@ table SegIdEntry {
// Maps a hierarchical device path to a compact numeric device_id + segment IDs. // Maps a hierarchical device path to a compact numeric device_id + segment IDs.
// Clients use these IDs in stream and mem packets instead of string paths. // Clients use these IDs in stream and mem packets instead of string paths.
table DeviceIdEntry { table DeviceIdEntry {
path: [string]; // hierarchical device id, e.g. ["core"] path: [string]; // hierarchical device id, e.g. ["sys", "core"]
device_id: uint32; device_id: uint32;
seg_ids: [SegIdEntry]; seg_ids: [SegIdEntry];
} }
@@ -26,9 +26,15 @@ table DeviceIdMappingNotif {
entries: [DeviceIdEntry]; entries: [DeviceIdEntry];
} }
// Sent when a config load request fails.
table ConfigLoadError {
message: string;
}
union ConfigNotifPayload { union ConfigNotifPayload {
DeviceConfigUpdateNotif, DeviceConfigUpdateNotif,
DeviceIdMappingNotif, DeviceIdMappingNotif,
ConfigLoadError,
} }
table ConfigNotifMessage { table ConfigNotifMessage {

View File

@@ -31,6 +31,7 @@ typedef struct {
DeviceSegStreamReg** deviceStreamRegs; DeviceSegStreamReg** deviceStreamRegs;
uint8_t** devicesMem; uint8_t** devicesMem;
size_t devicesCount; size_t devicesCount;
void** deviceHandles; // device_handle_t** — dynamically loaded devices
flatcc_builder_t stream_builder; flatcc_builder_t stream_builder;
} EmulContext; } EmulContext;

View File

@@ -0,0 +1,14 @@
#ifndef __PROTO_HANDLERS_CONFIG_H__
#define __PROTO_HANDLERS_CONFIG_H__
#include "context.h"
#include "proto/handlers/ws.h"
#include "config_ctrl_reader.h"
void handleConfigCtrlMessage(
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_CONFIG_H__

View File

@@ -57,4 +57,14 @@ uint8_t* fb_build_stream_reg_confirm(
uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length, uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length,
uint8_t mode, size_t* len_out); uint8_t mode, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{DeviceIdMappingNotif}}} frame.
// paths: array of NULL-terminated string arrays; path_lens: component count per path.
uint8_t* fb_build_config_device_id_mapping(
uint64_t nonce,
char** paths[], size_t path_lens[],
size_t device_count, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{ConfigLoadError}}} frame.
uint8_t* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out);
#endif // __PROTO_MSG_H__ #endif // __PROTO_MSG_H__

View File

@@ -25,7 +25,6 @@ device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf)
if(ret == NULL) if(ret == NULL)
{ {
freeConf(devConf); freeConf(devConf);
free(devConf);
} }
return ret; return ret;

View File

@@ -30,6 +30,7 @@
#include "compose_device.h" #include "compose_device.h"
#include "base_device.h"
#include "linkedlist.h" #include "linkedlist.h"
@@ -309,67 +310,22 @@ void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
(void)argc; (void)argv;
char errbuf[1024]; char errbuf[1024];
pthread_mutex_t mtx; pthread_mutex_t mtx;
pthread_mutex_init(&mtx, NULL); pthread_mutex_init(&mtx, NULL);
ptQueue* regQ = ptQueueCreate(errbuf); ptQueue* regQ = ptQueueCreate(errbuf);
NULL_GUARD(regQ, "Unable to create reg q: %s\n", errbuf); NULL_GUARD(regQ, "Unable to create reg q: %s\n", errbuf);
size_t deviceCount = 2;
DeviceSegStreamReg* device0SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4); size_t deviceCount = 0;
NULL_GUARD(device0SegStreamRegs); DeviceSegStreamReg** deviceStreamRegs = NULL;
uint8_t** devicesMem = NULL;
device0SegStreamRegs->allocatedSize = 4; void** deviceHandlesArr = NULL;
device0SegStreamRegs->regCount = 0;
device0SegStreamRegs->regs = malloc(sizeof(StreamReg) * 4);
NULL_GUARD(device0SegStreamRegs->regs);
DeviceSegStreamReg* device1SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4);
NULL_GUARD(device1SegStreamRegs);
device1SegStreamRegs->allocatedSize = 4;
device1SegStreamRegs->regCount = 0;
device1SegStreamRegs->regs = malloc(sizeof(StreamReg) * 4);
NULL_GUARD(device1SegStreamRegs->regs);
DeviceSegStreamReg** deviceStreamRegs = malloc(sizeof(DeviceSegStreamReg*) * deviceCount);
NULL_GUARD(deviceStreamRegs);
deviceStreamRegs[0] = device0SegStreamRegs;
deviceStreamRegs[1] = device1SegStreamRegs;
uint8_t emulState = EMUL_STATE_STILL; uint8_t emulState = EMUL_STATE_STILL;
uint8_t* device0mem = malloc(sizeof(uint8_t) * 1024 * 128);
NULL_GUARD(device0mem);
uint8_t* device1mem = malloc(sizeof(uint8_t) * 1024 * 128);
NULL_GUARD(device1mem);
uint8_t** devicesMem = malloc(sizeof(uint8_t*) * deviceCount);
NULL_GUARD(devicesMem);
devicesMem[0] = device0mem;
devicesMem[1] = device1mem;
uint64_t* device0readAddrs = malloc(sizeof(uint64_t) * 128);
NULL_GUARD(device0readAddrs);
size_t device0readAddrsLen = 0;
uint64_t* device0writeAddrs = malloc(sizeof(uint64_t) * 128);
NULL_GUARD(device0writeAddrs);
size_t device0writeAddrsLen = 0;
uint64_t* device1readAddrs = malloc(sizeof(uint64_t) * 128);
NULL_GUARD(device1readAddrs);
size_t device1readAddrsLen = 0;
uint64_t* device1writeAddrs = malloc(sizeof(uint64_t) * 128);
NULL_GUARD(device1writeAddrs);
size_t device1writeAddrsLen = 0;
uint8_t outBufsCount = 16; uint8_t outBufsCount = 16;
SizedPtr* bufs = malloc(sizeof(SizedPtr) * outBufsCount); SizedPtr* bufs = malloc(sizeof(SizedPtr) * outBufsCount);
NULL_GUARD(bufs); NULL_GUARD(bufs);
@@ -412,6 +368,7 @@ int main(int argc, char** argv)
deviceStreamRegs, deviceStreamRegs,
devicesMem, devicesMem,
deviceCount, deviceCount,
deviceHandlesArr,
{0} /* stream_builder — initialized below */ {0} /* stream_builder — initialized below */
}; };
if (flatcc_builder_init(&emulContext.stream_builder)) { if (flatcc_builder_init(&emulContext.stream_builder)) {
@@ -493,23 +450,20 @@ int main(int argc, char** argv)
} }
} }
if(emulState == EMUL_STATE_EXEC) if(emulState == EMUL_STATE_EXEC && emulContext.devicesCount > 0)
{ {
// printf("Running...\n"); for (size_t di = 0; di < emulContext.devicesCount; di++)
device0readAddrsLen = 0; {
device0writeAddrsLen = 0; device_handle_t* dev = (device_handle_t*)emulContext.deviceHandles[di];
device_mem_t* devMem = dev->ctx->deviceMem;
device1readAddrsLen = 0; devMem->memreadLen = 0;
device1writeAddrsLen = 0; devMem->memwriteLen = 0;
mockDevice0(device0mem, device0readAddrs, &device0readAddrsLen, device0writeAddrs, &device0writeAddrsLen);
mockDevice1(device1mem, device1readAddrs, &device1readAddrsLen, device1writeAddrs, &device1writeAddrsLen);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[0], device0mem, device0readAddrs, device0readAddrsLen, STREAM_MODE_READ); dev->lib->makeDeviceTick(dev->ctx);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[0], device0mem, device0writeAddrs, device0writeAddrsLen, STREAM_MODE_WRITE);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[1], device1mem, device1readAddrs, device1readAddrsLen, STREAM_MODE_READ); dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memreadCellAddrs, (size_t)devMem->memreadLen, STREAM_MODE_READ);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[1], device1mem, device1writeAddrs, device1writeAddrsLen, STREAM_MODE_WRITE); dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memwriteCellAddrs, (size_t)devMem->memwriteLen, STREAM_MODE_WRITE);
}
clockCounter++; clockCounter++;
} }

314
src/proto/handlers/config.c Normal file
View File

@@ -0,0 +1,314 @@
#include "proto/handlers/config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "state.h"
#include "base_device.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "config_ctrl_reader.h"
#include "config_reader.h"
#include "device_reader.h"
#include "mem_config_reader.h"
#define MAX_DEVICES 64
#define MAX_DEPTH 16
// Converts a FlatBuffer BaseDeviceConfig into a heap-allocated conf_dev_t
// suitable for openBaseDevice(). Caller must call freeConf() when done.
static conf_dev_t* conf_from_fb(
hmmmm_config_BaseDeviceConfig_table_t base,
char* errbuf)
{
conf_dev_t* dc = calloc(1, sizeof(conf_dev_t));
if (!dc) { sprintf(errbuf, "alloc conf_dev_t"); return NULL; }
dc->clockDivider = 1;
dc->clockMultipler = 1;
flatbuffers_string_t lp = hmmmm_config_BaseDeviceConfig_libpath(base);
if (!lp) { sprintf(errbuf, "missing libpath"); free(dc); return NULL; }
dc->libPath = strdup(lp);
hmmmm_config_MemSegment_vec_t segs = hmmmm_config_BaseDeviceConfig_mem_segments(base);
size_t seg_n = segs ? hmmmm_config_MemSegment_vec_len(segs) : 0;
dc->memConf = calloc(1, sizeof(conf_mem_t));
if (!dc->memConf) { free(dc->libPath); free(dc); sprintf(errbuf, "alloc memConf"); return NULL; }
dc->memConf->memSegConfs = calloc(seg_n + 1, sizeof(conf_mem_seg_t*));
if (!dc->memConf->memSegConfs) {
free(dc->memConf); free(dc->libPath); free(dc);
sprintf(errbuf, "alloc memSegConfs");
return NULL;
}
for (size_t i = 0; i < seg_n; i++) {
hmmmm_config_MemSegment_table_t seg = hmmmm_config_MemSegment_vec_at(segs, i);
conf_mem_seg_t* s = calloc(1, sizeof(conf_mem_seg_t));
if (!s) { sprintf(errbuf, "alloc seg %zu", i); goto fail; }
flatbuffers_string_t sname = hmmmm_config_MemSegment_name(seg);
s->name = strdup(sname ? sname : "");
s->start = hmmmm_config_MemSegment_addr(seg);
s->len = hmmmm_config_MemSegment_len(seg);
s->wordLen = hmmmm_config_MemSegment_word_len(seg);
s->isExecutable = hmmmm_config_MemSegment_executable(seg);
dc->memConf->memSegConfs[i] = s;
}
dc->memConf->memSegConfs[seg_n] = NULL;
return dc;
fail:
freeConf(dc); // freeConf() already calls free(dc)
return NULL;
}
typedef struct {
device_handle_t* handles[MAX_DEVICES];
const char* path_stack[MAX_DEPTH]; // current path components
size_t depth;
// For building the DeviceIdMappingNotif response:
// 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 count;
} LoadState;
static int load_devices_recursive(
hmmmm_config_ComposeDeviceConfig_table_t compose,
LoadState* st,
char* errbuf)
{
hmmmm_config_DeviceEntry_vec_t entries =
hmmmm_config_ComposeDeviceConfig_devices(compose);
size_t n = entries ? hmmmm_config_DeviceEntry_vec_len(entries) : 0;
for (size_t i = 0; i < n; i++) {
hmmmm_config_DeviceEntry_table_t entry =
hmmmm_config_DeviceEntry_vec_at(entries, i);
flatbuffers_string_t id = hmmmm_config_DeviceEntry_id(entry);
if (!id) { sprintf(errbuf, "device entry missing id"); return -1; }
if (st->depth >= MAX_DEPTH) {
sprintf(errbuf, "device tree too deep");
return -1;
}
st->path_stack[st->depth] = id;
st->depth++;
hmmmm_config_DeviceConfig_union_type_t ctype =
hmmmm_config_DeviceEntry_config_type(entry);
if (ctype == hmmmm_config_DeviceConfig_BaseDeviceConfig) {
if (st->count >= MAX_DEVICES) {
sprintf(errbuf, "too many devices");
return -1;
}
hmmmm_config_BaseDeviceConfig_table_t base =
(hmmmm_config_BaseDeviceConfig_table_t)
hmmmm_config_DeviceEntry_config(entry);
conf_dev_t* dc = conf_from_fb(base, errbuf);
if (!dc) return -1;
device_handle_t* dev = openBaseDevice(dc, errbuf);
freeConf(dc); // freeConf() already calls free(dc)
if (!dev) return -1;
dev->lib->fillSmartReadSpecs(dev->specs, NULL, 0);
dev->lib->fillSmartWriteSpecs(dev->specs, NULL, 0);
dev->ctx = dev->lib->init(dev->specs, errbuf);
if (!dev->ctx) {
closeBaseDevice(dev);
free(dev);
return -1;
}
size_t idx = st->count;
st->handles[idx] = dev;
// Copy path
st->path_lens[idx] = st->depth;
st->paths[idx] = calloc(st->depth + 1, sizeof(char*));
if (!st->paths[idx]) {
sprintf(errbuf, "alloc path array");
return -1;
}
for (size_t p = 0; p < st->depth; p++) {
st->paths[idx][p] = strdup(st->path_stack[p]);
}
st->paths[idx][st->depth] = NULL;
st->count++;
} else if (ctype == hmmmm_config_DeviceConfig_ComposeDeviceConfig) {
hmmmm_config_ComposeDeviceConfig_table_t child =
(hmmmm_config_ComposeDeviceConfig_table_t)
hmmmm_config_DeviceEntry_config(entry);
if (load_devices_recursive(child, st, errbuf) != 0) {
return -1;
}
} else {
sprintf(errbuf, "unknown device config type %u", ctype);
return -1;
}
st->depth--;
}
return 0;
}
static void free_load_state_paths(LoadState* st)
{
for (size_t i = 0; i < st->count; i++) {
if (st->paths[i]) {
for (size_t j = 0; j < st->path_lens[i]; j++) {
free(st->paths[i][j]);
}
free(st->paths[i]);
}
}
}
static void free_old_config(EmulContext* emulContext)
{
if (emulContext->devicesCount == 0) return;
for (size_t i = 0; i < emulContext->devicesCount; i++) {
device_handle_t* dev = (device_handle_t*)emulContext->deviceHandles[i];
closeBaseDevice(dev);
free(dev);
if (emulContext->deviceStreamRegs[i]) {
free(emulContext->deviceStreamRegs[i]->regs);
free(emulContext->deviceStreamRegs[i]);
}
}
free(emulContext->deviceHandles);
free(emulContext->devicesMem);
free(emulContext->deviceStreamRegs);
emulContext->deviceHandles = NULL;
emulContext->devicesMem = NULL;
emulContext->deviceStreamRegs = NULL;
emulContext->devicesCount = 0;
}
void handleConfigCtrlMessage(
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{
printf("[CTRL/CONFIG] received config load request\n");
char errbuf[1024];
// Must be in STILL state
if (*emulContext->emulState != EMUL_STATE_STILL) {
sprintf(errbuf, "config load requires STILL state");
printf("[CTRL/CONFIG] error: %s\n", errbuf);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
return;
}
hmmmm_config_EmulationConfig_table_t econf =
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_config(msg);
if (!econf) {
sprintf(errbuf, "missing EmulationConfig");
printf("[CTRL/CONFIG] error: %s\n", errbuf);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
return;
}
flatbuffers_string_t root_id = hmmmm_config_EmulationConfig_root_id(econf);
hmmmm_config_ComposeDeviceConfig_table_t root =
hmmmm_config_EmulationConfig_root(econf);
if (!root) {
sprintf(errbuf, "missing root ComposeDeviceConfig");
printf("[CTRL/CONFIG] error: %s\n", errbuf);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
return;
}
// Free old config
free_old_config(emulContext);
// Load new config
LoadState st = {0};
if (root_id) {
st.path_stack[0] = root_id;
st.depth = 1;
}
if (load_devices_recursive(root, &st, errbuf) != 0) {
printf("[CTRL/CONFIG] load error: %s\n", errbuf);
// Close any devices that were opened before the failure
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;
}
printf("[CTRL/CONFIG] loaded %zu devices\n", st.count);
// Allocate new arrays for EmulContext
size_t dc = st.count;
emulContext->devicesCount = dc;
emulContext->deviceHandles = calloc(dc, sizeof(void*));
emulContext->devicesMem = calloc(dc, sizeof(uint8_t*));
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*));
for (size_t i = 0; i < dc; i++) {
emulContext->deviceHandles[i] = st.handles[i];
emulContext->devicesMem[i] = st.handles[i]->ctx->deviceMem->rawCells;
DeviceSegStreamReg* dsr = calloc(1, sizeof(DeviceSegStreamReg));
dsr->allocatedSize = 4;
dsr->regCount = 0;
dsr->regs = calloc(4, sizeof(StreamReg));
emulContext->deviceStreamRegs[i] = dsr;
}
// Build DeviceIdMappingNotif response
size_t msg_len;
uint8_t* out = fb_build_config_device_id_mapping(
nonce, st.paths, st.path_lens, dc, &msg_len);
free_load_state_paths(&st);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
}

View File

@@ -1,4 +1,5 @@
#include "proto/handlers/control.h" #include "proto/handlers/control.h"
#include "proto/handlers/config.h"
#include <stdio.h> #include <stdio.h>
#include "state.h" #include "state.h"
@@ -7,6 +8,7 @@
#include "control_reader.h" #include "control_reader.h"
#include "exec_ctrl_reader.h" #include "exec_ctrl_reader.h"
#include "config_ctrl_reader.h"
#include "setup_buf_reader.h" #include "setup_buf_reader.h"
#include "orphaned_reader.h" #include "orphaned_reader.h"
#include "lost_reader.h" #include "lost_reader.h"
@@ -90,6 +92,13 @@ void handleIncomingCtrlMessage(
uint8_t* out = fb_build_orphaned_response(nonce, &msg_len); uint8_t* out = fb_build_orphaned_response(nonce, &msg_len);
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
} }
else if (ptype == hmmmm_ctrl_CtrlClientPayload_ConfigCtrlMessage)
{
handleConfigCtrlMessage(
(hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg),
nonce, ctx, emulContext);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest) else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest)
{ {
hmmmm_ctrl_lost_LostMessagesRequest_table_t req = hmmmm_ctrl_lost_LostMessagesRequest_table_t req =

View File

@@ -6,7 +6,7 @@
#include "proto/dial.h" #include "proto/dial.h"
#include "mem_reader.h" #include "mem_reader.h"
#define DEVICE_MEM_SIZE ((size_t)(128 * 1024)) #define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
void handleIncomingMemMessage( void handleIncomingMemMessage(

View File

@@ -248,3 +248,105 @@ uint8_t* fb_build_stream_reg_confirm(
NULL_GUARD(buf); NULL_GUARD(buf);
return buf; return buf;
} }
uint8_t* fb_build_config_device_id_mapping(
uint64_t nonce,
char** paths[], size_t path_lens[],
size_t device_count, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Pre-create DeviceIdEntry refs (bottom-up: strings first, then tables)
hmmmm_ctrl_config_notif_DeviceIdEntry_ref_t entry_refs[device_count];
for (size_t i = 0; i < device_count; i++) {
// Build string vector for path
flatbuffers_string_vec_start(&B);
for (size_t j = 0; j < path_lens[i]; j++) {
flatbuffers_string_ref_t sref = flatbuffers_string_create_str(&B, paths[i][j]);
flatbuffers_string_vec_push(&B, sref);
}
flatbuffers_string_vec_ref_t path_vec = flatbuffers_string_vec_end(&B);
// Empty seg_ids vector
hmmmm_ctrl_config_notif_SegIdEntry_vec_start(&B);
hmmmm_ctrl_config_notif_SegIdEntry_vec_ref_t seg_ids_vec =
hmmmm_ctrl_config_notif_SegIdEntry_vec_end(&B);
entry_refs[i] = hmmmm_ctrl_config_notif_DeviceIdEntry_create(
&B, path_vec, (uint32_t)i, seg_ids_vec);
}
// Build entries vector
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_start(&B);
for (size_t i = 0; i < device_count; i++) {
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_push(&B, entry_refs[i]);
}
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_ref_t entries_vec =
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_end(&B);
hmmmm_ctrl_config_notif_DeviceIdMappingNotif_ref_t mapping =
hmmmm_ctrl_config_notif_DeviceIdMappingNotif_create(&B, entries_vec);
hmmmm_ctrl_config_notif_ConfigNotifPayload_union_ref_t notif_payload =
hmmmm_ctrl_config_notif_ConfigNotifPayload_as_DeviceIdMappingNotif(mapping);
hmmmm_ctrl_config_notif_ConfigNotifMessage_ref_t notif_msg =
hmmmm_ctrl_config_notif_ConfigNotifMessage_create(&B, 0, notif_payload);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_ConfigNotifMessage(notif_msg);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
flatbuffers_string_ref_t msg_str = flatbuffers_string_create_str(&B, message);
hmmmm_ctrl_config_notif_ConfigLoadError_ref_t err =
hmmmm_ctrl_config_notif_ConfigLoadError_create(&B, msg_str);
hmmmm_ctrl_config_notif_ConfigNotifPayload_union_ref_t notif_payload =
hmmmm_ctrl_config_notif_ConfigNotifPayload_as_ConfigLoadError(err);
hmmmm_ctrl_config_notif_ConfigNotifMessage_ref_t notif_msg =
hmmmm_ctrl_config_notif_ConfigNotifMessage_create(&B, 0, notif_payload);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_ConfigNotifMessage(notif_msg);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}