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:
2
Makefile
2
Makefile
@@ -73,7 +73,7 @@ target: date proto $(BUILD_DIR)/$(TARGET).elf
|
||||
|
||||
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
|
||||
@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:
|
||||
@make -C deps all
|
||||
|
||||
764
devices/avr_generic/src/device.c
Normal file
764
devices/avr_generic/src/device.c
Normal 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;
|
||||
}
|
||||
|
||||
@@ -4,17 +4,22 @@ include "clock.fbs";
|
||||
|
||||
namespace hmmmm.config;
|
||||
|
||||
// Top-level emulator / system configuration.
|
||||
// Equivalent to glob.toml — describes a full composite device tree.
|
||||
//
|
||||
// File use: flatcc -a config/config.fbs → binary system config files
|
||||
// WS use: embed in ConfigCtrlMessage when config-change protocol is ready
|
||||
// Top-level emulation configuration sent by the client via ConfigCtrlMessage.
|
||||
// Contains the root composite device and simulation parameters.
|
||||
table EmulationConfig {
|
||||
sim_rate_limit: uint64; // max virtual-seconds per real-second (0 = unlimited)
|
||||
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 {
|
||||
devices: [DeviceEntry]; // ordered list; id must be unique
|
||||
devices: [DeviceEntry];
|
||||
clock: ClockConfig;
|
||||
projections: [Projection];
|
||||
intercepts: [Intercept];
|
||||
mem_setup: [MemSetup];
|
||||
}
|
||||
|
||||
root_type SystemConfig;
|
||||
root_type EmulationConfig;
|
||||
|
||||
@@ -10,26 +10,36 @@ table BaseDeviceConfig {
|
||||
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.
|
||||
// Only fields that need changing from the base config must be set.
|
||||
table MemSegOverride {
|
||||
segment: string;
|
||||
start: uint32;
|
||||
addr: uint32;
|
||||
len: uint32;
|
||||
word_len: uint8;
|
||||
executable: bool;
|
||||
}
|
||||
|
||||
// One device entry in a composite configuration.
|
||||
// One device entry in a device tree.
|
||||
table DeviceEntry {
|
||||
// Local identifier, unique within this composite (e.g. "core", "gpio_a").
|
||||
id: string;
|
||||
|
||||
// Exactly one of config or config_path must be set.
|
||||
// config: inline base device configuration.
|
||||
// config_path: path to a serialised BaseDeviceConfig FlatBuffer file.
|
||||
config: BaseDeviceConfig;
|
||||
config_path: string;
|
||||
// Inline device configuration — either base or compose.
|
||||
config: DeviceConfig;
|
||||
|
||||
clock: DeviceClockConfig;
|
||||
overrides: [MemSegOverride];
|
||||
|
||||
@@ -10,7 +10,7 @@ table NamedOffset {
|
||||
// A contiguous memory segment within a base device.
|
||||
table MemSegment {
|
||||
name: string;
|
||||
start: uint32; // base address in device flat address space
|
||||
addr: uint32; // base address in device flat address space
|
||||
len: uint32;
|
||||
word_len: uint8 = 1; // word size in bytes
|
||||
executable: bool = false;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Config change control — WIP.
|
||||
// Schema stub; content will be defined when the config-change sub-protocol
|
||||
// is specified (requires device-loading control in the protocol first).
|
||||
include "config/config.fbs";
|
||||
|
||||
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 {
|
||||
// TODO
|
||||
config: hmmmm.config.EmulationConfig;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ table SegIdEntry {
|
||||
// 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.
|
||||
table DeviceIdEntry {
|
||||
path: [string]; // hierarchical device id, e.g. ["core"]
|
||||
path: [string]; // hierarchical device id, e.g. ["sys", "core"]
|
||||
device_id: uint32;
|
||||
seg_ids: [SegIdEntry];
|
||||
}
|
||||
@@ -26,9 +26,15 @@ table DeviceIdMappingNotif {
|
||||
entries: [DeviceIdEntry];
|
||||
}
|
||||
|
||||
// Sent when a config load request fails.
|
||||
table ConfigLoadError {
|
||||
message: string;
|
||||
}
|
||||
|
||||
union ConfigNotifPayload {
|
||||
DeviceConfigUpdateNotif,
|
||||
DeviceIdMappingNotif,
|
||||
ConfigLoadError,
|
||||
}
|
||||
|
||||
table ConfigNotifMessage {
|
||||
|
||||
@@ -31,6 +31,7 @@ typedef struct {
|
||||
DeviceSegStreamReg** deviceStreamRegs;
|
||||
uint8_t** devicesMem;
|
||||
size_t devicesCount;
|
||||
void** deviceHandles; // device_handle_t** — dynamically loaded devices
|
||||
flatcc_builder_t stream_builder;
|
||||
} EmulContext;
|
||||
|
||||
|
||||
14
inc/proto/handlers/config.h
Normal file
14
inc/proto/handlers/config.h
Normal 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__
|
||||
@@ -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,
|
||||
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__
|
||||
|
||||
@@ -25,7 +25,6 @@ device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf)
|
||||
if(ret == NULL)
|
||||
{
|
||||
freeConf(devConf);
|
||||
free(devConf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
82
src/main.c
82
src/main.c
@@ -30,6 +30,7 @@
|
||||
|
||||
|
||||
#include "compose_device.h"
|
||||
#include "base_device.h"
|
||||
|
||||
#include "linkedlist.h"
|
||||
|
||||
@@ -309,67 +310,22 @@ void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
char errbuf[1024];
|
||||
pthread_mutex_t mtx;
|
||||
pthread_mutex_init(&mtx, NULL);
|
||||
|
||||
ptQueue* regQ = ptQueueCreate(errbuf);
|
||||
NULL_GUARD(regQ, "Unable to create reg q: %s\n", errbuf);
|
||||
|
||||
size_t deviceCount = 2;
|
||||
|
||||
DeviceSegStreamReg* device0SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4);
|
||||
NULL_GUARD(device0SegStreamRegs);
|
||||
|
||||
device0SegStreamRegs->allocatedSize = 4;
|
||||
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;
|
||||
size_t deviceCount = 0;
|
||||
DeviceSegStreamReg** deviceStreamRegs = NULL;
|
||||
uint8_t** devicesMem = NULL;
|
||||
void** deviceHandlesArr = NULL;
|
||||
|
||||
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;
|
||||
SizedPtr* bufs = malloc(sizeof(SizedPtr) * outBufsCount);
|
||||
NULL_GUARD(bufs);
|
||||
@@ -412,6 +368,7 @@ int main(int argc, char** argv)
|
||||
deviceStreamRegs,
|
||||
devicesMem,
|
||||
deviceCount,
|
||||
deviceHandlesArr,
|
||||
{0} /* stream_builder — initialized below */
|
||||
};
|
||||
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");
|
||||
device0readAddrsLen = 0;
|
||||
device0writeAddrsLen = 0;
|
||||
|
||||
device1readAddrsLen = 0;
|
||||
device1writeAddrsLen = 0;
|
||||
|
||||
mockDevice0(device0mem, device0readAddrs, &device0readAddrsLen, device0writeAddrs, &device0writeAddrsLen);
|
||||
mockDevice1(device1mem, device1readAddrs, &device1readAddrsLen, device1writeAddrs, &device1writeAddrsLen);
|
||||
for (size_t di = 0; di < emulContext.devicesCount; di++)
|
||||
{
|
||||
device_handle_t* dev = (device_handle_t*)emulContext.deviceHandles[di];
|
||||
device_mem_t* devMem = dev->ctx->deviceMem;
|
||||
devMem->memreadLen = 0;
|
||||
devMem->memwriteLen = 0;
|
||||
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[0], device0mem, device0readAddrs, device0readAddrsLen, STREAM_MODE_READ);
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[0], device0mem, device0writeAddrs, device0writeAddrsLen, STREAM_MODE_WRITE);
|
||||
dev->lib->makeDeviceTick(dev->ctx);
|
||||
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[1], device1mem, device1readAddrs, device1readAddrsLen, STREAM_MODE_READ);
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[1], device1mem, device1writeAddrs, device1writeAddrsLen, STREAM_MODE_WRITE);
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memreadCellAddrs, (size_t)devMem->memreadLen, STREAM_MODE_READ);
|
||||
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memwriteCellAddrs, (size_t)devMem->memwriteLen, STREAM_MODE_WRITE);
|
||||
}
|
||||
|
||||
clockCounter++;
|
||||
}
|
||||
|
||||
314
src/proto/handlers/config.c
Normal file
314
src/proto/handlers/config.c
Normal 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);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "proto/handlers/control.h"
|
||||
#include "proto/handlers/config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "state.h"
|
||||
@@ -7,6 +8,7 @@
|
||||
|
||||
#include "control_reader.h"
|
||||
#include "exec_ctrl_reader.h"
|
||||
#include "config_ctrl_reader.h"
|
||||
#include "setup_buf_reader.h"
|
||||
#include "orphaned_reader.h"
|
||||
#include "lost_reader.h"
|
||||
@@ -90,6 +92,13 @@ void handleIncomingCtrlMessage(
|
||||
uint8_t* out = fb_build_orphaned_response(nonce, &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)
|
||||
{
|
||||
hmmmm_ctrl_lost_LostMessagesRequest_table_t req =
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "proto/dial.h"
|
||||
#include "mem_reader.h"
|
||||
|
||||
#define DEVICE_MEM_SIZE ((size_t)(128 * 1024))
|
||||
#define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
|
||||
|
||||
|
||||
void handleIncomingMemMessage(
|
||||
|
||||
102
src/proto/msg.c
102
src/proto/msg.c
@@ -248,3 +248,105 @@ uint8_t* fb_build_stream_reg_confirm(
|
||||
NULL_GUARD(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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user