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>
353 lines
11 KiB
C
353 lines
11 KiB
C
#include "proto/msg.h"
|
|
#include "panic.h"
|
|
|
|
#include "flatcc/flatcc_builder.h"
|
|
#include "proto_builder.h"
|
|
#include "control_builder.h"
|
|
#include "exec_notif_builder.h"
|
|
#include "auth_builder.h"
|
|
#include "stream_builder.h"
|
|
#include "stream_data_builder.h"
|
|
|
|
|
|
uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
hmmmm_auth_AuthResponse_ref_t ar =
|
|
hmmmm_auth_AuthResponse_create(&B, seat_id);
|
|
|
|
hmmmm_ServerPayload_union_ref_t payload =
|
|
hmmmm_ServerPayload_as_AuthResponse(ar);
|
|
|
|
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_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
hmmmm_ctrl_exec_notif_ExecNotifyMessage_ref_t notif =
|
|
hmmmm_ctrl_exec_notif_ExecNotifyMessage_create(
|
|
&B, tclk, (hmmmm_ctrl_exec_notif_ExecState_enum_t)(int8_t)state);
|
|
|
|
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
|
hmmmm_ctrl_CtrlServerPayload_as_ExecNotifyMessage(notif);
|
|
|
|
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_stream_data_push(
|
|
flatcc_builder_t *B,
|
|
uint64_t nonce, uint32_t stream_id, uint64_t tclk,
|
|
const uint8_t* data, size_t data_len,
|
|
size_t* len_out)
|
|
{
|
|
flatcc_builder_reset(B);
|
|
|
|
flatbuffers_uint8_vec_ref_t data_vec =
|
|
flatbuffers_uint8_vec_create(B, data, data_len);
|
|
|
|
hmmmm_stream_StreamDataPush_ref_t push =
|
|
hmmmm_stream_StreamDataPush_create(B, stream_id, tclk, data_vec);
|
|
|
|
hmmmm_stream_StreamServerPayload_union_ref_t stream_payload =
|
|
hmmmm_stream_StreamServerPayload_as_StreamDataPush(push);
|
|
|
|
hmmmm_stream_StreamServerMessage_ref_t stream_msg =
|
|
hmmmm_stream_StreamServerMessage_create(B, stream_payload);
|
|
|
|
hmmmm_ServerPayload_union_ref_t payload =
|
|
hmmmm_ServerPayload_as_StreamServerMessage(stream_msg);
|
|
|
|
hmmmm_ServerMessage_create_as_root(B, nonce, payload);
|
|
|
|
uint8_t* buf = flatcc_builder_finalize_buffer(B, len_out);
|
|
NULL_GUARD(buf);
|
|
return buf;
|
|
}
|
|
|
|
|
|
uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size,
|
|
uint64_t client_lifetime_ticks, size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb =
|
|
hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks);
|
|
|
|
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
|
hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb);
|
|
|
|
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
|
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
|
|
|
hmmmm_ServerPayload_union_ref_t setup_outer =
|
|
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
|
|
|
|
hmmmm_ServerMessage_create_as_root(&B, nonce, setup_outer);
|
|
|
|
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
|
flatcc_builder_clear(&B);
|
|
NULL_GUARD(buf);
|
|
return buf;
|
|
}
|
|
|
|
|
|
uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
// Use start/end directly: passing 0 to _create causes entries_add to return -1
|
|
hmmmm_ctrl_orphaned_OrphanedResponse_start(&B);
|
|
hmmmm_ctrl_orphaned_OrphanedResponse_ref_t resp =
|
|
hmmmm_ctrl_orphaned_OrphanedResponse_end(&B);
|
|
|
|
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
|
hmmmm_ctrl_CtrlServerPayload_as_OrphanedResponse(resp);
|
|
|
|
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
|
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
|
|
|
hmmmm_ServerPayload_union_ref_t orphan_outer =
|
|
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
|
|
|
|
hmmmm_ServerMessage_create_as_root(&B, nonce, orphan_outer);
|
|
|
|
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
|
flatcc_builder_clear(&B);
|
|
NULL_GUARD(buf);
|
|
return buf;
|
|
}
|
|
|
|
|
|
uint8_t* fb_build_lost_messages_response(uint64_t nonce, uint64_t seat_id, size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
// Use start/end directly: passing 0 for messages vec causes messages_add to return -1
|
|
hmmmm_ctrl_lost_LostMessagesResponse_start(&B);
|
|
hmmmm_ctrl_lost_LostMessagesResponse_seat_id_add(&B, seat_id);
|
|
hmmmm_ctrl_lost_LostMessagesResponse_ref_t resp =
|
|
hmmmm_ctrl_lost_LostMessagesResponse_end(&B);
|
|
|
|
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
|
|
hmmmm_ctrl_CtrlServerPayload_as_LostMessagesResponse(resp);
|
|
|
|
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
|
|
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
|
|
|
|
hmmmm_ServerPayload_union_ref_t lost_outer =
|
|
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
|
|
|
|
hmmmm_ServerMessage_create_as_root(&B, nonce, lost_outer);
|
|
|
|
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
|
flatcc_builder_clear(&B);
|
|
NULL_GUARD(buf);
|
|
return buf;
|
|
}
|
|
|
|
|
|
uint8_t* fb_build_mem_read_response(
|
|
uint64_t nonce, uint64_t tclk,
|
|
uint32_t device_id, uint32_t seg_id, uint32_t offset,
|
|
const uint8_t* data, size_t data_len,
|
|
size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
flatbuffers_uint8_vec_ref_t data_vec =
|
|
flatbuffers_uint8_vec_create(&B, data, data_len);
|
|
|
|
hmmmm_mem_MemReadResponse_ref_t resp =
|
|
hmmmm_mem_MemReadResponse_create(&B, tclk, device_id, seg_id, offset, data_vec);
|
|
|
|
hmmmm_mem_MemServerPayload_union_ref_t mem_payload =
|
|
hmmmm_mem_MemServerPayload_as_MemReadResponse(resp);
|
|
|
|
hmmmm_mem_MemServerMessage_ref_t mem_msg =
|
|
hmmmm_mem_MemServerMessage_create(&B, mem_payload);
|
|
|
|
hmmmm_ServerPayload_union_ref_t mem_outer =
|
|
hmmmm_ServerPayload_as_MemServerMessage(mem_msg);
|
|
|
|
hmmmm_ServerMessage_create_as_root(&B, nonce, mem_outer);
|
|
|
|
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
|
flatcc_builder_clear(&B);
|
|
NULL_GUARD(buf);
|
|
return buf;
|
|
}
|
|
|
|
|
|
uint8_t* fb_build_stream_reg_confirm(
|
|
uint64_t nonce, uint32_t stream_id,
|
|
uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length,
|
|
uint8_t mode, size_t* len_out)
|
|
{
|
|
flatcc_builder_t B;
|
|
if (flatcc_builder_init(&B)) {
|
|
panic("flatcc_builder_init failed\n");
|
|
}
|
|
|
|
hmmmm_stream_StreamRegConfirm_ref_t confirm =
|
|
hmmmm_stream_StreamRegConfirm_create(
|
|
&B, stream_id, device_id, seg_id, offset, length,
|
|
(hmmmm_stream_StreamMode_enum_t)(int8_t)mode);
|
|
|
|
hmmmm_stream_StreamServerPayload_union_ref_t sreg_payload =
|
|
hmmmm_stream_StreamServerPayload_as_StreamRegConfirm(confirm);
|
|
|
|
hmmmm_stream_StreamServerMessage_ref_t sreg_msg =
|
|
hmmmm_stream_StreamServerMessage_create(&B, sreg_payload);
|
|
|
|
hmmmm_ServerPayload_union_ref_t sreg_outer =
|
|
hmmmm_ServerPayload_as_StreamServerMessage(sreg_msg);
|
|
|
|
hmmmm_ServerMessage_create_as_root(&B, nonce, sreg_outer);
|
|
|
|
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
|
|
flatcc_builder_clear(&B);
|
|
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;
|
|
}
|