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

@@ -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;

View File

@@ -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];

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 {