diff --git a/AVRrc.toml b/AVRrc.toml new file mode 100644 index 0000000..d0dd0cc --- /dev/null +++ b/AVRrc.toml @@ -0,0 +1,28 @@ +[dev] +libpath = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc_build/device.so" + + +[mem] + +[mem.ps] +start = 0 +len = 1024 +wordLen = 2 +executable = true + +[mem.reg_gp] +start = 2048 +len = 32 +wordLen = 1 + +[mem.reg_io] +start = 2080 +len = 255 +wordLen = 1 + +[mem.ds] +start = 2335 +len = 65535 +wordLen = 1 + + diff --git a/Makefile b/Makefile index e1291c5..7985ff4 100644 --- a/Makefile +++ b/Makefile @@ -4,13 +4,27 @@ SRC_DIR=src INC_DIR=inc CC=gcc OBJDUMP=objdump -LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a +LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE) +# flatcc runtime and generated reader headers use const-dropping casts; +# include both as system headers so -Wcast-qual doesn't apply to them +SYSTEM_INCLUDES=-isystem deps/flatcc/include/ -isystem $(PROTO_INC_DIR)/ STATIC_LIBS=crypto STANDART=c23 OPTIMIZE=-O3 TARGET=main +FLATCC = deps/flatcc/bin/flatcc +FLATC = deps/flatbuffers/build/flatc +FBS_DIR = flatbuffers +PROTO_INC_DIR = inc/flatbuf_autogen +PROTO_SRC_DIR = src/flatbuf_autogen +PROTO_STAMP = $(BUILD_DIR)/.proto_stamp +PY_PROTO_DIR = hmmmm_scripts/flatbuf_autogen +PY_PROTO_STAMP = $(BUILD_DIR)/.py_proto_stamp + +FBS_SOURCES := $(shell find $(FBS_DIR) -name '*.fbs') + DEFINES=#-DOPCODE_WORDSIZE=2 -DMEM_CELL_WORDS=1 -DPC_WORDSIZE=2 -DGP_REG_CELL_WORDS=1 -DIO_REG_CELL_WORDS=1 @@ -24,9 +38,10 @@ DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno PEDANTIC_FLAGS=-pedantic -pedantic-errors $(DISABLE_FLAGS) -Wall -Wcast-align -Wcast-qual -Wconversion -Wduplicated-branches -Wduplicated-cond -Werror -Wextra -Wfloat-equal -Wlogical-op -Wpedantic -Wredundant-decls -Wsign-conversion ANALYZER_FLAGS=-fanalyzer -fdiagnostics-show-option -fdiagnostics-color=always LSECTIONS=-ffunction-sections -fdata-sections -Wl,--gc-sections -CFLAGS=$(C_DEFS) -g $(C_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP +CFLAGS=$(C_DEFS) -g $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP # -fno-omit-frame-pointer -fno-inline -fno-lto STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS)) -LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm +# LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline -fno-lto +LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline @@ -41,7 +56,7 @@ vpath %.c $(sort $(dir $(C_SOURCES))) all: build -build: date deps Dir target compile_commands +build: date deps Dir proto target compile_commands rebuild: clean | build @@ -54,7 +69,7 @@ $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) @$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ @$(OBJDUMP) -d -S $@ > $(BUILD_DIR)/$(notdir $(<:.c=.casm)) -target: date $(BUILD_DIR)/$(TARGET).elf +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' @@ -63,6 +78,16 @@ $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) deps: @make -C deps all +proto: $(PROTO_STAMP) + +$(PROTO_STAMP): $(FBS_SOURCES) | ProtoDir BuildDir + @echo -e '\033[1;32mFLATCC\tGenerating FlatBuffers headers\033[0m' + @for fbs in $(FBS_SOURCES); do \ + echo -e '\033[1;32mFLATCC\t'$$fbs'\033[0m'; \ + $(FLATCC) -a -I $(FBS_DIR) -o $(PROTO_INC_DIR) $$fbs || exit 1; \ + done + @touch $@ + BuildDir: @@ -75,16 +100,34 @@ SrcDir: IncDir: @mkdir -p $(INC_DIR) -Dir: BuildDir SrcDir IncDir +ProtoDir: + @mkdir -p $(PROTO_INC_DIR) + @mkdir -p $(PROTO_SRC_DIR) + +Dir: BuildDir SrcDir IncDir ProtoDir -.PHONY: clean deps +python-proto: $(PY_PROTO_STAMP) + +$(PY_PROTO_STAMP): $(FBS_SOURCES) $(FLATC) | $(PY_PROTO_DIR) BuildDir + @echo -e '\033[1;32mFLATC\tGenerating Python FlatBuffers bindings\033[0m' + @$(FLATC) --python --gen-all -I $(FBS_DIR) -o $(PY_PROTO_DIR) $(FBS_DIR)/proto.fbs + @touch $@ + +$(PY_PROTO_DIR): + @mkdir -p $@ + +$(FLATC): + @$(MAKE) -C deps flatbuffers/build/flatc + + +.PHONY: clean deps proto python-proto clean: - @rm -rf $(BUILD_DIR)/* + @rm -rf $(BUILD_DIR)/* $(BUILD_DIR)/.* $(PROTO_INC_DIR)/* $(PROTO_SRC_DIR)/* # @rm -f compile_commands.json @echo -e '\033[0;31mCleaned\033[0m' -.NOTPARALLEL: date target rebuild deps +.NOTPARALLEL: date target rebuild deps proto date: @echo -e '\033[1;32m'"Starting build at " | tr -d '\n' @date diff --git a/deps/.gitignore b/deps/.gitignore index 5abe836..ae4c72d 100644 --- a/deps/.gitignore +++ b/deps/.gitignore @@ -3,3 +3,4 @@ ptQueue tomlc99 wsServer flatcc +flatbuffers diff --git a/deps/Makefile b/deps/Makefile index 8fa5293..96fc1b0 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -1,4 +1,4 @@ -all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc +all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc/bin/flatcc flatbuffers/build/flatc tomlc99: @git clone https://github.com/cktan/tomlc99 @@ -7,13 +7,13 @@ tomlc99/libtoml.a: tomlc99 @make -C tomlc99 libtoml.a -flatcc_src: +flatcc: @git clone https://github.com/dvidelabs/flatcc -flatcc: flatcc/bin/flatcc +flatcc_src: flatcc/bin/flatcc flatcc/lib/libflatccrt.a: flatcc/bin/flatcc -flatcc/bin/flatcc: flatcc_src +flatcc/bin/flatcc: flatcc @flatcc/scripts/initbuild.sh make @flatcc/scripts/build.sh @@ -30,8 +30,23 @@ wsServer: wsServer/libws.a: wsServer @make -C wsServer +flatbuffers: + @git clone --depth=1 https://github.com/google/flatbuffers + +flatbuffers/build/flatc: flatbuffers + @cmake -B flatbuffers/build -S flatbuffers \ + -DCMAKE_BUILD_TYPE=Release \ + -DFLATBUFFERS_BUILD_TESTS=OFF \ + -DFLATBUFFERS_BUILD_FLATLIB=OFF \ + -DFLATBUFFERS_BUILD_FLATHASH=OFF \ + -DFLATBUFFERS_BUILD_GRPCTEST=OFF \ + -DFLATBUFFERS_BUILD_SHAREDLIB=OFF \ + -DFLATBUFFERS_INSTALL=OFF + @cmake --build flatbuffers/build --target flatc -j$(shell nproc) + + clean: - @rm -rf wsServer ptQueue tomlc99 flatcc + @rm -rf wsServer ptQueue tomlc99 flatcc flatbuffers .PHONY: all clean diff --git a/deps/flatbuffers b/deps/flatbuffers new file mode 160000 index 0000000..4e582b0 --- /dev/null +++ b/deps/flatbuffers @@ -0,0 +1 @@ +Subproject commit 4e582b0c1d60c55f9a0a90f9740d4a4c48f3b53b diff --git a/flamegraph.svg b/flamegraph.svg new file mode 100644 index 0000000..e69de29 diff --git a/flatbuffers/auth/auth.fbs b/flatbuffers/auth/auth.fbs new file mode 100644 index 0000000..e104bf1 --- /dev/null +++ b/flatbuffers/auth/auth.fbs @@ -0,0 +1,11 @@ +namespace hmmmm.auth; + +// Client → Server: SHA-512(access_token + str(unix_timestamp / 30)) +table AuthRequest { + hash: [ubyte]; // 64 bytes +} + +// Server → Client: seat assigned, current emulation state echoed +table AuthResponse { + seat_id: uint64; +} diff --git a/flatbuffers/config/clock.fbs b/flatbuffers/config/clock.fbs new file mode 100644 index 0000000..45c01c4 --- /dev/null +++ b/flatbuffers/config/clock.fbs @@ -0,0 +1,16 @@ +namespace hmmmm.config; + +// Global emulator clock constraints. +table ClockConfig { + // Maximum emulated ticks per real second. + // 0 = unlimited (run as fast as possible). + limiter: uint64; +} + +// Clock source and frequency relationship for one device. +// Devices form a clock tree: each node derives its frequency from its src. +table DeviceClockConfig { + src: string; // id of the parent device; empty = root clock + divider: uint32 = 1; + multiplier: uint32 = 1; +} diff --git a/flatbuffers/config/config.fbs b/flatbuffers/config/config.fbs new file mode 100644 index 0000000..3a299ba --- /dev/null +++ b/flatbuffers/config/config.fbs @@ -0,0 +1,20 @@ +include "device.fbs"; +include "mem_config.fbs"; +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 +table SystemConfig { + devices: [DeviceEntry]; // ordered list; id must be unique + clock: ClockConfig; + projections: [Projection]; + intercepts: [Intercept]; + mem_setup: [MemSetup]; +} + +root_type SystemConfig; diff --git a/flatbuffers/config/device.fbs b/flatbuffers/config/device.fbs new file mode 100644 index 0000000..8a97afa --- /dev/null +++ b/flatbuffers/config/device.fbs @@ -0,0 +1,39 @@ +include "mem_config.fbs"; +include "clock.fbs"; + +namespace hmmmm.config; + +// Configuration for a single base device (leaf node in the device tree). +// Equivalent to a standalone device TOML (e.g. AVRrc.toml, gpio.toml). +table BaseDeviceConfig { + libpath: string; + mem_segments: [MemSegment]; +} + +// 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; + len: uint32; + word_len: uint8; + executable: bool; +} + +// One device entry in a composite configuration. +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; + + clock: DeviceClockConfig; + overrides: [MemSegOverride]; +} + +// Root type when serialising a standalone device config to a binary file. +root_type BaseDeviceConfig; diff --git a/flatbuffers/config/mem_config.fbs b/flatbuffers/config/mem_config.fbs new file mode 100644 index 0000000..6bbe901 --- /dev/null +++ b/flatbuffers/config/mem_config.fbs @@ -0,0 +1,73 @@ +namespace hmmmm.config; + +// Named offset within a segment — symbolic variable for protocol/tool use +// (e.g. PIN=0, PORT=1, DDR=2 in a GPIO device). +table NamedOffset { + name: string; + offset: uint32; +} + +// A contiguous memory segment within a base device. +table MemSegment { + name: string; + start: uint32; // base address in device flat address space + len: uint32; + word_len: uint8 = 1; // word size in bytes + executable: bool = false; + variables: [NamedOffset]; // optional symbolic offsets within segment +} + +// ── Projections ────────────────────────────────────────────────────────────── + +// Maps a segment from one device into the flat address space of another. +// After projection, accesses to [base_seg + shift .. shift+len) in base_at +// are transparently forwarded to target_at:target_seg. +table Projection { + base_at: string; // device where the projection appears + base_seg: string; // segment in that device that is the projection window + target_at: string; // device owning the real data + target_seg: string; + shift: uint32 = 0; // offset from base_seg start +} + +// ── Intercepts ─────────────────────────────────────────────────────────────── + +enum InterceptOp: byte { + op_read = 0, + op_write = 1, + op_both = 2, +} + +enum InterceptMode: byte { + shadow_copy = 1, // duplicate write to point_at:point_addr as well + shadow_replace = 2, // redirect access to point_at:point_addr instead + callback = 3, // invoke a handler registered by point_at device +} + +table Intercept { + name: string; + op: InterceptOp = op_both; + mode: InterceptMode = callback; + base_at: string; // device where the triggering access occurs + base_seg: string; + base_addr: uint32; + point_at: string; // device that handles/receives the intercept + point_seg: string; + point_addr: uint32; +} + +// ── Memory initialisation ───────────────────────────────────────────────────── + +enum OverflowBehaviour: byte { + overflow_error = 0, // abort if file is larger than segment + overflow_ignore = 1, // stop reading at segment end, no error + overflow_wrap = 2, // wrap around and continue writing from segment start +} + +// Load initial segment contents from a file (e.g. firmware binary, /dev/urandom). +table MemSetup { + device: string; + segment: string; + filepath: string; + overflow: OverflowBehaviour = overflow_error; +} diff --git a/flatbuffers/control/config_ctrl.fbs b/flatbuffers/control/config_ctrl.fbs new file mode 100644 index 0000000..8ad337a --- /dev/null +++ b/flatbuffers/control/config_ctrl.fbs @@ -0,0 +1,8 @@ +// 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). +namespace hmmmm.ctrl.config_ctrl; + +table ConfigCtrlMessage { + // TODO +} diff --git a/flatbuffers/control/config_notif.fbs b/flatbuffers/control/config_notif.fbs new file mode 100644 index 0000000..67b095d --- /dev/null +++ b/flatbuffers/control/config_notif.fbs @@ -0,0 +1,37 @@ +namespace hmmmm.ctrl.config_notif; + +// Notification subtype 0001 — a device's configuration was updated. +// Carries the path of the affected device; client should re-request full config. +table DeviceConfigUpdateNotif { + device_path: [string]; // hierarchical path, e.g. ["core"] or ["gpio_a"] +} + +// Numeric ID assigned to one named segment within a device. +table SegIdEntry { + name: string; + id: uint32; +} + +// 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"] + device_id: uint32; + seg_ids: [SegIdEntry]; +} + +// Notification subtype 0010 — full device-id / segment-id mapping table. +// Sent after auth and after any config change that affects the device tree. +table DeviceIdMappingNotif { + entries: [DeviceIdEntry]; +} + +union ConfigNotifPayload { + DeviceConfigUpdateNotif, + DeviceIdMappingNotif, +} + +table ConfigNotifMessage { + tclk: uint64; + payload: ConfigNotifPayload; +} diff --git a/flatbuffers/control/control.fbs b/flatbuffers/control/control.fbs new file mode 100644 index 0000000..a1c7a12 --- /dev/null +++ b/flatbuffers/control/control.fbs @@ -0,0 +1,39 @@ +include "exec_ctrl.fbs"; +include "exec_notif.fbs"; +include "config_ctrl.fbs"; +include "config_notif.fbs"; +include "lost.fbs"; +include "orphaned.fbs"; +include "setup_buf.fbs"; +include "qos.fbs"; + +namespace hmmmm.ctrl; + +// ── Client → Server ────────────────────────────────────────────────────────── + +union CtrlClientPayload { + hmmmm.ctrl.exec.ExecCtrlMessage, + hmmmm.ctrl.config_ctrl.ConfigCtrlMessage, + hmmmm.ctrl.lost.LostMessagesRequest, + hmmmm.ctrl.orphaned.OrphanedRequest, + hmmmm.ctrl.setup_buf.SetupBuf, +} + +table CtrlClientMessage { + payload: CtrlClientPayload; +} + +// ── Server → Client ────────────────────────────────────────────────────────── + +union CtrlServerPayload { + hmmmm.ctrl.exec_notif.ExecNotifyMessage, + hmmmm.ctrl.config_notif.ConfigNotifMessage, + hmmmm.ctrl.lost.LostMessagesResponse, + hmmmm.ctrl.orphaned.OrphanedResponse, + hmmmm.ctrl.setup_buf.SetupBuf, + hmmmm.ctrl.qos.QosReport, +} + +table CtrlServerMessage { + payload: CtrlServerPayload; +} diff --git a/flatbuffers/control/exec_ctrl.fbs b/flatbuffers/control/exec_ctrl.fbs new file mode 100644 index 0000000..ab389ed --- /dev/null +++ b/flatbuffers/control/exec_ctrl.fbs @@ -0,0 +1,14 @@ +namespace hmmmm.ctrl.exec; + +enum ExecPrompt: byte { + _invalid = 0, + start = 1, + pause = 2, + resume = 3, + stop = 4, + reset = 5, +} + +table ExecCtrlMessage { + prompt: ExecPrompt; +} diff --git a/flatbuffers/control/exec_notif.fbs b/flatbuffers/control/exec_notif.fbs new file mode 100644 index 0000000..07c5dcd --- /dev/null +++ b/flatbuffers/control/exec_notif.fbs @@ -0,0 +1,13 @@ +namespace hmmmm.ctrl.exec_notif; + +enum ExecState: byte { + still = 0, + executing = 1, + paused = 2, + stopped = 3, +} + +table ExecNotifyMessage { + tclk: uint64; + state: ExecState; +} diff --git a/flatbuffers/control/lost.fbs b/flatbuffers/control/lost.fbs new file mode 100644 index 0000000..86b32bb --- /dev/null +++ b/flatbuffers/control/lost.fbs @@ -0,0 +1,19 @@ +namespace hmmmm.ctrl.lost; + +// Client → Server: request buffered (undelivered) messages for a seat. +table LostMessagesRequest { + seat_id: uint64; + clear: bool = false; // if true, drop the buffer after reading +} + +// One buffered message that was not delivered before the seat disconnected. +table LostMessage { + original_nonce: uint64; + data: [ubyte]; // raw FlatBuffer bytes of the original ServerMessage +} + +// Server → Client: buffered messages for the requested seat. +table LostMessagesResponse { + seat_id: uint64; + messages: [LostMessage]; +} diff --git a/flatbuffers/control/orphaned.fbs b/flatbuffers/control/orphaned.fbs new file mode 100644 index 0000000..f8c07da --- /dev/null +++ b/flatbuffers/control/orphaned.fbs @@ -0,0 +1,16 @@ +namespace hmmmm.ctrl.orphaned; + +// Client → Server: enumerate disconnected seats that still have buffered messages. +// No body — request is the signal itself. +table OrphanedRequest {} + +table OrphanedEntry { + seat_id: uint64; + disconnect_tclk: uint64; // virtual clock tick at time of disconnect + lost_count: uint32; // number of buffered messages waiting +} + +// Server → Client: list of orphaned seats with pending buffers. +table OrphanedResponse { + entries: [OrphanedEntry]; +} diff --git a/flatbuffers/control/qos.fbs b/flatbuffers/control/qos.fbs new file mode 100644 index 0000000..c3cd3eb --- /dev/null +++ b/flatbuffers/control/qos.fbs @@ -0,0 +1,10 @@ +namespace hmmmm.ctrl.qos; + +// Server → Client: connection quality snapshot. +// Sent periodically or on request (TBD). +table QosReport { + rtt_us: uint64; // measured round-trip time, microseconds + packets_sent: uint64; + packets_lost: uint32; + buf_pressure: float; // outgoing buffer fill level, 0.0 .. 1.0 +} diff --git a/flatbuffers/control/setup_buf.fbs b/flatbuffers/control/setup_buf.fbs new file mode 100644 index 0000000..8c1d777 --- /dev/null +++ b/flatbuffers/control/setup_buf.fbs @@ -0,0 +1,8 @@ +namespace hmmmm.ctrl.setup_buf; + +// Client → Server: configure the lost-message buffer for this seat. +// Server → Client: echoes back the confirmed (possibly clamped) settings. +table SetupBuf { + lost_buf_size: uint32; // max buffered messages per seat + client_lifetime_ticks: uint64; // virtual ticks to keep buffer after disconnect +} diff --git a/flatbuffers/mem/mem.fbs b/flatbuffers/mem/mem.fbs new file mode 100644 index 0000000..584787a --- /dev/null +++ b/flatbuffers/mem/mem.fbs @@ -0,0 +1,46 @@ +namespace hmmmm.mem; + +// Client → Server: read a contiguous memory region. Server responds with +// MemReadResponse carrying the same nonce as the enclosing ClientMessage. +table MemReadRequest { + device_id: uint32; + seg_id: uint32; + offset: uint32; + length: uint32; +} + +// Server → Client: memory region contents at the moment of the read. +table MemReadResponse { + tclk: uint64; // virtual clock tick at time of read + device_id: uint32; + seg_id: uint32; + offset: uint32; + data: [ubyte]; +} + +// Client → Server: write a contiguous memory region. No response. +table MemWriteRequest { + device_id: uint32; + seg_id: uint32; + offset: uint32; + data: [ubyte]; +} + +// ── Unions ─────────────────────────────────────────────────────────────────── + +union MemClientPayload { + MemReadRequest, + MemWriteRequest, +} + +table MemClientMessage { + payload: MemClientPayload; +} + +union MemServerPayload { + MemReadResponse, +} + +table MemServerMessage { + payload: MemServerPayload; +} diff --git a/flatbuffers/proto.fbs b/flatbuffers/proto.fbs new file mode 100644 index 0000000..a248c77 --- /dev/null +++ b/flatbuffers/proto.fbs @@ -0,0 +1,42 @@ +include "auth/auth.fbs"; +include "control/control.fbs"; +include "stream/stream.fbs"; +include "mem/mem.fbs"; + +namespace hmmmm; + +// ── Client → Server ────────────────────────────────────────────────────────── + +union ClientPayload { + hmmmm.auth.AuthRequest, + hmmmm.ctrl.CtrlClientMessage, + hmmmm.stream.StreamClientMessage, + hmmmm.mem.MemClientMessage, +} + +// Every frame sent by the client is a ClientMessage. +// nonce: client-chosen identifier echoed in the server response. +// Use 0xFFFFFFFFFFFFFFFF for fire-and-forget messages (no reply expected). +table ClientMessage { + nonce: uint64; + payload: ClientPayload; +} + +// ── Server → Client ────────────────────────────────────────────────────────── + +union ServerPayload { + hmmmm.auth.AuthResponse, + hmmmm.ctrl.CtrlServerMessage, + hmmmm.stream.StreamServerMessage, + hmmmm.mem.MemServerMessage, +} + +// Every frame sent by the server is a ServerMessage. +// nonce: echoes the triggering ClientMessage nonce. +// 0xFFFFFFFFFFFFFFFF for unsolicited notifications (state changes, stream pushes). +table ServerMessage { + nonce: uint64; + payload: ServerPayload; +} + +root_type ClientMessage; diff --git a/flatbuffers/stream/stream.fbs b/flatbuffers/stream/stream.fbs new file mode 100644 index 0000000..1c8ea07 --- /dev/null +++ b/flatbuffers/stream/stream.fbs @@ -0,0 +1,27 @@ +include "stream_reg.fbs"; +include "stream_data.fbs"; + +namespace hmmmm.stream; + +// ── Client → Server ────────────────────────────────────────────────────────── + +union StreamClientPayload { + StreamRegRequest, + StreamDeregRequest, + StreamWritePush, +} + +table StreamClientMessage { + payload: StreamClientPayload; +} + +// ── Server → Client ────────────────────────────────────────────────────────── + +union StreamServerPayload { + StreamRegConfirm, + StreamDataPush, +} + +table StreamServerMessage { + payload: StreamServerPayload; +} diff --git a/flatbuffers/stream/stream_data.fbs b/flatbuffers/stream/stream_data.fbs new file mode 100644 index 0000000..8785e08 --- /dev/null +++ b/flatbuffers/stream/stream_data.fbs @@ -0,0 +1,18 @@ +namespace hmmmm.stream; + +// Server → Client: memory region snapshot pushed by the server. +// tclk is the virtual clock tick at which this data became valid — +// clients may buffer and apply it at the appropriate simulation point. +table StreamDataPush { + stream_id: uint32; + tclk: uint64; + data: [ubyte]; +} + +// Client → Server: write into a registered write-mode stream. +// offset is relative to the registered region start. +table StreamWritePush { + stream_id: uint32; + offset: uint32; + data: [ubyte]; +} diff --git a/flatbuffers/stream/stream_reg.fbs b/flatbuffers/stream/stream_reg.fbs new file mode 100644 index 0000000..e2dbd1e --- /dev/null +++ b/flatbuffers/stream/stream_reg.fbs @@ -0,0 +1,32 @@ +namespace hmmmm.stream; + +enum StreamMode: byte { + mode_read = 0, + mode_write = 1, +} + +// Client → Server: subscribe to a memory region. +table StreamRegRequest { + device_id: uint32; // from DeviceIdMappingNotif + seg_id: uint32; // from DeviceIdMappingNotif + offset: uint32; // start offset within segment, in bytes + length: uint32; // region length, in bytes + mode: StreamMode; +} + +// Client → Server: cancel a stream subscription. +// No confirmation is sent by the server. +table StreamDeregRequest { + stream_id: uint32; +} + +// Server → Client: subscription confirmed, stream_id assigned. +// Echoes the original request fields for client-side reconciliation. +table StreamRegConfirm { + stream_id: uint32; + device_id: uint32; + seg_id: uint32; + offset: uint32; + length: uint32; + mode: StreamMode; +} diff --git a/flatbuffers_example_main.c b/flatbuffers_example_main.c new file mode 100644 index 0000000..e4adf53 --- /dev/null +++ b/flatbuffers_example_main.c @@ -0,0 +1,120 @@ +#include +#include + +#include "monster_reader.h" +#include "monster_verifier.h" +#include "monster_builder.h" + +#undef ns +#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) + +#define c_vec_len(V) (sizeof(V)/sizeof((V)[0])) + +int main() +{ + flatcc_builder_t builder, *B = &builder; + flatcc_builder_init(B); + + printf("[+] Builder initialized\n"); + + flatbuffers_string_ref_t weapon_one_name = + flatbuffers_string_create_str(B, "Sword"); + flatbuffers_string_ref_t weapon_two_name = + flatbuffers_string_create_str(B, "Axe"); + + ns(Weapon_ref_t) sword = + ns(Weapon_create(B, weapon_one_name, 3)); + ns(Weapon_ref_t) axe = + ns(Weapon_create(B, weapon_two_name, 5)); + + ns(Weapon_vec_start(B)); + ns(Weapon_vec_push(B, sword)); + ns(Weapon_vec_push(B, axe)); + ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B)); + + uint8_t treasure[] = {0,1,2,3,4,5,6,7,8,9}; + flatbuffers_uint8_vec_ref_t inventory = + flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure)); + + flatbuffers_string_ref_t name = + flatbuffers_string_create_str(B, "Orc"); + + uint16_t hp = 300; + uint16_t mana = 150; + + ns(Vec3_t) pos = {1.0f, 2.0f, 3.0f}; + + ns(Vec3_vec_start(B)); + ns(Vec3_vec_push(B, &pos)); + ns(Vec3_vec_ref_t) path = ns(Vec3_vec_end(B)); + + ns(Equipment_union_ref_t) equipped = + ns(Equipment_as_Weapon(axe)); + + ns(Monster_create_as_root( + B, &pos, mana, hp, name, + inventory, ns(Color_Red), + weapons, equipped, path)); + + uint8_t *buf; + size_t size; + + buf = flatcc_builder_finalize_buffer(B, &size); + + printf("[+] Buffer built, size = %zu bytes\n", size); + + + // 🔍 Верификация + if (ns(Monster_verify_as_root(buf, size))) { + printf("[!] Buffer verification FAILED\n"); + return -1; + } + printf("[+] Buffer verification OK\n"); + + // 📖 Чтение + ns(Monster_table_t) m = ns(Monster_as_root(buf)); + + printf("\n===== MONSTER DUMP =====\n"); + + printf("Name: %s\n", ns(Monster_name(m))); + printf("HP: %u\n", ns(Monster_hp(m))); + printf("Mana: %u\n", ns(Monster_mana(m))); + + ns(Vec3_struct_t) p = ns(Monster_pos(m)); + printf("Position: x=%.2f y=%.2f z=%.2f\n", + ns(Vec3_x(p)), ns(Vec3_y(p)), ns(Vec3_z(p))); + + flatbuffers_uint8_vec_t inv = ns(Monster_inventory(m)); + size_t inv_len = flatbuffers_uint8_vec_len(inv); + + printf("Inventory (%zu): ", inv_len); + for (size_t i = 0; i < inv_len; i++) { + printf("%u ", flatbuffers_uint8_vec_at(inv, i)); + } + printf("\n"); + + ns(Weapon_vec_t) wv = ns(Monster_weapons(m)); + size_t wlen = ns(Weapon_vec_len(wv)); + + printf("Weapons (%zu):\n", wlen); + for (size_t i = 0; i < wlen; i++) { + ns(Weapon_table_t) w = ns(Weapon_vec_at(wv, i)); + printf(" - %s (dmg=%u)\n", + ns(Weapon_name(w)), + ns(Weapon_damage(w))); + } + + if (ns(Monster_equipped_type(m)) == ns(Equipment_Weapon)) { + ns(Weapon_table_t) w = ns(Monster_equipped(m)); + printf("Equipped: %s (dmg=%u)\n", + ns(Weapon_name(w)), + ns(Weapon_damage(w))); + } + + printf("========================\n\n"); + + free(buf); + flatcc_builder_clear(B); + + return 0; +} diff --git a/gpio.toml b/gpio.toml new file mode 100644 index 0000000..04877d7 --- /dev/null +++ b/gpio.toml @@ -0,0 +1,23 @@ +[dev] +libpath = "avr_gpio/out/device.so" + + + +[mem.ext_reg_io] +start = 0 +len = 255 +wordLen = 1 + +[mem.extstate] +start = 256 +len = 1 +wordLen = 1 + +[mem.variables] +PIN = 0 +PORT = 1 +DDR = 2 + + + + diff --git a/inc/context.h b/inc/context.h index 4c4190f..f5230bf 100644 --- a/inc/context.h +++ b/inc/context.h @@ -10,6 +10,7 @@ #include "sized_ptr.h" #include "streamed.h" +#include "flatcc/flatcc_builder.h" typedef struct { SizedPtr* bufs; @@ -30,6 +31,7 @@ typedef struct { DeviceSegStreamReg** deviceStreamRegs; uint8_t** devicesMem; size_t devicesCount; + flatcc_builder_t stream_builder; } EmulContext; diff --git a/inc/proto/handlers/control.h b/inc/proto/handlers/control.h index 2e2726b..518c605 100644 --- a/inc/proto/handlers/control.h +++ b/inc/proto/handlers/control.h @@ -1,10 +1,16 @@ #ifndef __PROTO_HANDLERS_CONTROL_H__ #define __PROTO_HANDLERS_CONTROL_H__ -#include "proto/msg.h" +#include +#include "client.h" +#include "context.h" #include "proto/dial.h" +#include "control_reader.h" -void handleIncomingControlMessage(BaseMessage* msg, EmulContext* emulContext); +void handleIncomingCtrlMessage( + hmmmm_ctrl_CtrlClientMessage_table_t msg, + uint64_t nonce, + ClientContext* ctx, + EmulContext* emulContext); - -#endif //ifndef __PROTO_HANDLERS_CONTROL_H__ \ No newline at end of file +#endif // __PROTO_HANDLERS_CONTROL_H__ diff --git a/inc/proto/handlers/mem.h b/inc/proto/handlers/mem.h index b913a89..9090c91 100644 --- a/inc/proto/handlers/mem.h +++ b/inc/proto/handlers/mem.h @@ -1,10 +1,16 @@ #ifndef __PROTO_HANDLERS_MEM_H__ #define __PROTO_HANDLERS_MEM_H__ -#include "proto/msg.h" +#include +#include "context.h" +#include "client.h" #include "proto/dial.h" +#include "mem_reader.h" -void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext); - -#endif //ifndef __PROTO_HANDLERS_MEM_H__ +void handleIncomingMemMessage( + hmmmm_mem_MemClientMessage_table_t msg, + uint64_t nonce, + ClientContext* ctx, + EmulContext* emulContext); +#endif // __PROTO_HANDLERS_MEM_H__ diff --git a/inc/proto/handlers/stream.h b/inc/proto/handlers/stream.h index 7073dfd..e2c3be5 100644 --- a/inc/proto/handlers/stream.h +++ b/inc/proto/handlers/stream.h @@ -1,12 +1,19 @@ #ifndef __PROTO_HANDLERS_STREAM_H__ #define __PROTO_HANDLERS_STREAM_H__ -#include "proto/msg.h" +#include +#include "context.h" +#include "client.h" #include "proto/dial.h" - +#include "stream_reader.h" void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId); void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx); -void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext); -#endif //ifndef __PROTO_HANDLERS_STREAM_H__ \ No newline at end of file +void handleIncomingStreamMessage( + hmmmm_stream_StreamClientMessage_table_t msg, + uint64_t nonce, + ClientContext* ctx, + EmulContext* emulContext); + +#endif // __PROTO_HANDLERS_STREAM_H__ diff --git a/inc/proto/msg.h b/inc/proto/msg.h index f1855be..c7e9174 100644 --- a/inc/proto/msg.h +++ b/inc/proto/msg.h @@ -2,25 +2,59 @@ #define __PROTO_MSG_H__ #include "wsServer/include/ws.h" +#include #include +#include "flatcc/flatcc_builder.h" + +// Raw FlatBuffers frame copied from the WS thread, queued to the main loop. +typedef struct { + uint8_t* data; + size_t size; +} FbMessage; typedef struct { ws_cli_conn_t clientIdx; - uint8_t* msg; - size_t msgLen; + uint8_t* msg; + size_t msgLen; } OutgoingMessage; -typedef struct { - uint64_t nonce; - uint8_t packetType; - uint8_t payloadHeader; - const void* payload; - size_t payloadLen; -} BaseMessage; +// Build a ServerMessage{AuthResponse} frame. Returns a malloc'd buffer; +// ownership is transferred to the caller (dispatchOutgoingMessage will free it). +uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out); -BaseMessage* parseMessage(const uint8_t* bytes, size_t size); -uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut); -uint8_t* createDoneRegMessage(uint64_t nonce, uint8_t X, uint64_t devId, uint64_t segId, uint64_t startAddr, uint64_t segLength, uint32_t regId, size_t* lenOut); -uint8_t* createStreamSegmentPush(uint8_t mode, uint32_t regId, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut); +// Build a ServerMessage{CtrlServerMessage{ExecNotifyMessage}} frame. +uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out); -#endif //ifndef __PROTO_MSG_H__ \ No newline at end of file +// Build a ServerMessage{StreamServerMessage{StreamDataPush}} frame. +// B must be a pre-initialized flatcc_builder_t; it is reset before use +// so the caller does not need to reset it manually between calls. +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); + +// Build a ServerMessage{CtrlServerMessage{SetupBuf}} frame (echo back to client). +uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size, + uint64_t client_lifetime_ticks, size_t* len_out); + +// Build a ServerMessage{CtrlServerMessage{OrphanedResponse{entries=[]}}} frame. +uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out); + +// Build a ServerMessage{CtrlServerMessage{LostMessagesResponse{seat_id, messages=[]}}} frame. +uint8_t* fb_build_lost_messages_response(uint64_t nonce, uint64_t seat_id, size_t* len_out); + +// Build a ServerMessage{MemServerMessage{MemReadResponse}} frame. +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); + +// Build a ServerMessage{StreamServerMessage{StreamRegConfirm}} frame. +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); + +#endif // __PROTO_MSG_H__ diff --git a/src/main.c b/src/main.c index 953a1e9..4218203 100644 --- a/src/main.c +++ b/src/main.c @@ -185,9 +185,7 @@ void* outgoingMain(void* args) { panic("Got double read on buf %d while writing on buf %d\n", currBufIdx, currWritingIdxPtr); } - // printf("\t%lu -> [%lu]\n", outMsg->msgLen, outMsg->clientIdx); ws_sendframe_bin(outMsg->clientIdx, (const char*)outMsg->msg, outMsg->msgLen); - // printf("running free on buf %d message %lu\n", currBufIdx, i); free(outMsg->msg); outMsg->msg = NULL; // printf("done free on buf %d message %lu\n", currBufIdx, i); @@ -251,7 +249,10 @@ void mockDevice1(uint8_t* mem, uint64_t* readAddrs, size_t* readAddrsLen, uint64 void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, uint8_t* mem) { size_t mlen = 0; - uint8_t* msg = createStreamSegmentPush(reg->mode, reg->regId, *emulContext->clockCounter, mem + reg->startAddr, reg->segLen, &mlen); + uint8_t* msg = fb_build_stream_data_push( + &emulContext->stream_builder, + UINT64_MAX, reg->regId, *emulContext->clockCounter, + mem + reg->startAddr, reg->segLen, &mlen); dispatchOutgoingMessage(emulContext->outBufs, reg->clientContext->clientId, msg, mlen); } @@ -320,17 +321,18 @@ int main(int argc, char** argv) DeviceSegStreamReg* device0SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4); NULL_GUARD(device0SegStreamRegs); - device0SegStreamRegs->allocatedSize = 0; + device0SegStreamRegs->allocatedSize = 4; device0SegStreamRegs->regCount = 0; - device0SegStreamRegs->regs = NULL; + device0SegStreamRegs->regs = malloc(sizeof(StreamReg) * 4); + NULL_GUARD(device0SegStreamRegs->regs); DeviceSegStreamReg* device1SegStreamRegs = malloc(sizeof(DeviceSegStreamReg) * 4); NULL_GUARD(device1SegStreamRegs); - - device1SegStreamRegs->allocatedSize = 0; + device1SegStreamRegs->allocatedSize = 4; device1SegStreamRegs->regCount = 0; - device1SegStreamRegs->regs = NULL; + device1SegStreamRegs->regs = malloc(sizeof(StreamReg) * 4); + NULL_GUARD(device1SegStreamRegs->regs); DeviceSegStreamReg** deviceStreamRegs = malloc(sizeof(DeviceSegStreamReg*) * deviceCount); NULL_GUARD(deviceStreamRegs); @@ -409,8 +411,12 @@ int main(int argc, char** argv) &outBufs, deviceStreamRegs, devicesMem, - deviceCount + deviceCount, + {0} /* stream_builder — initialized below */ }; + if (flatcc_builder_init(&emulContext.stream_builder)) { + panic("flatcc_builder_init failed\n"); + } uint8_t access_token[] = "jtd3dhBWG2p73mkRCyaZ63KLmOjl5OgItIDAPa8HcyGQI2SOFA6hsJVr1NiG2Y96AvwyYSShr4GAL3sjJEo1wsmCo2vuhiqRhj8HVRrOVocD2e1jtuMwMsCmizYakT63"; @@ -443,6 +449,8 @@ int main(int argc, char** argv) ptQueueElem* regQueueTail = regQ->tail; + uint8_t clients_try_timer = 100; + while(1) { ClientRegistrationEvent* payload = regQueueTail->payload; @@ -455,12 +463,19 @@ int main(int argc, char** argv) } - handleAllClients(&emulContext); + if (clients_try_timer == 0) + { + handleAllClients(&emulContext); + clients_try_timer = 100; + } + else + { + clients_try_timer--; + } uint8_t readReqIdx = atomic_load(&outBufs.readRequestIdx); if(readReqIdx == outBufs.currWritingIdx || outBufs.bufs[outBufs.currWritingIdx].size >= outBufs.bufs[outBufs.currWritingIdx].allocatedSize / 2) { - uint8_t oldWriteIdx = outBufs.currWritingIdx; uint8_t newWriteIdx = outBufs.currWritingIdx + 1; if(outBufs.bufs[outBufs.currWritingIdx].size != 0 || readReqIdx != newWriteIdx) { @@ -468,13 +483,6 @@ int main(int argc, char** argv) { newWriteIdx = 0; } - // printf("Switching write idx %d->%d\n", oldWriteIdx, newWriteIdx); - - - // if(readReqIdx == newWriteIdx) - // { - // printf("Flood control breach, waiting until outgoing buffers empty!\n"); - // } while(readReqIdx == newWriteIdx) { my_sleep(1000); @@ -487,7 +495,7 @@ int main(int argc, char** argv) if(emulState == EMUL_STATE_EXEC) { - printf("Running...\n"); + // printf("Running...\n"); device0readAddrsLen = 0; device0writeAddrsLen = 0; @@ -507,7 +515,7 @@ int main(int argc, char** argv) } else if(!utilizedFlag) { - my_sleep(100); + my_sleep(1000); } } diff --git a/src/proto/dial.c b/src/proto/dial.c index 73e9871..3a6228b 100644 --- a/src/proto/dial.c +++ b/src/proto/dial.c @@ -32,7 +32,7 @@ void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ws_cli_conn_t clientIdx, SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx]; if(p->size + 1 >= p->allocatedSize) { - printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx); + // printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx); OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 2); NULL_GUARD(newPtr); p->ptr = newPtr; diff --git a/src/proto/handlers.c b/src/proto/handlers.c index 22244db..c0342ca 100644 --- a/src/proto/handlers.c +++ b/src/proto/handlers.c @@ -1,19 +1,25 @@ #include "proto/handlers.h" #include -#include "proto/enums.h" #include "panic.h" +#include "proto/enums.h" #include "proto/handlers/auth.h" #include "proto/handlers/stream.h" #include "proto/handlers/control.h" #include "proto/handlers/mem.h" +#include "proto/msg.h" + +#include "proto_reader.h" +#include "proto_verifier.h" +#include "control_reader.h" +#include "stream_reader.h" +#include "mem_reader.h" void handleCloseClient(EmulContext* emulContext, ClientContext* ctx) { - if(ctx->streamRegIterator > 0) - { + if (ctx->streamRegIterator > 0) { unregisterClientStreams(emulContext, ctx); } } @@ -21,29 +27,25 @@ void handleCloseClient(EmulContext* emulContext, ClientContext* ctx) void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev) { - if(ev->regType == REG_EVTYPE_CONNECT) + if (ev->regType == REG_EVTYPE_CONNECT) { printf("open client %lu\n", ev->ctx->clientId); - LinkedListEntry* newClientsLinkedListHead = malloc(sizeof(LinkedListEntry)); - NULL_GUARD(newClientsLinkedListHead); - - newClientsLinkedListHead->payload = ev->ctx; - if(*emulContext->clientsHead != NULL) - { - (*emulContext->clientsHead)->prevEntry = newClientsLinkedListHead; + LinkedListEntry* entry = malloc(sizeof(LinkedListEntry)); + NULL_GUARD(entry); + + entry->payload = ev->ctx; + if (*emulContext->clientsHead != NULL) { + (*emulContext->clientsHead)->prevEntry = entry; } - newClientsLinkedListHead->prevEntry = NULL; - newClientsLinkedListHead->nextEntry = *emulContext->clientsHead; - *emulContext->clientsHead = newClientsLinkedListHead; + entry->prevEntry = NULL; + entry->nextEntry = *emulContext->clientsHead; + *emulContext->clientsHead = entry; } else if (ev->regType == REG_EVTYPE_CLOSE) { LinkedListEntry* clientEntry = *emulContext->clientsHead; - - while(clientEntry != NULL) - { - if(clientEntry->payload == ev->ctx) - { + while (clientEntry != NULL) { + if (clientEntry->payload == ev->ctx) { printf("close client %lu\n", ev->ctx->clientId); handleCloseClient(emulContext, ev->ctx); removeLinkedListEntry(emulContext->clientsHead, clientEntry); @@ -61,33 +63,47 @@ void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev) } - - -void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) +static void handleIncomingMessage( + hmmmm_ClientMessage_table_t cm, ClientContext* ctx, EmulContext* emulContext) { - switch (msg->packetType) + uint64_t nonce = hmmmm_ClientMessage_nonce(cm); + hmmmm_ClientPayload_union_type_t ptype = hmmmm_ClientMessage_payload_type(cm); + + printf("client %lu: nonce=%lu payload=%s\n", + ctx->clientId, nonce, hmmmm_ClientPayload_type_name(ptype)); + + switch (ptype) { - case PACKET_TYPE_CTRL: + case hmmmm_ClientPayload_CtrlClientMessage: { - printf("CTRL packet\n"); - handleIncomingControlMessage(msg, emulContext); + hmmmm_ctrl_CtrlClientMessage_table_t ctrl = + (hmmmm_ctrl_CtrlClientMessage_table_t)hmmmm_ClientMessage_payload(cm); + handleIncomingCtrlMessage(ctrl, nonce, ctx, emulContext); break; } - case PACKET_TYPE_STREAM: + case hmmmm_ClientPayload_StreamClientMessage: { - printf("STREAM packet\n"); - handleIncomingStreamMessage(msg, ctx, emulContext); + hmmmm_stream_StreamClientMessage_table_t stream = + (hmmmm_stream_StreamClientMessage_table_t)hmmmm_ClientMessage_payload(cm); + handleIncomingStreamMessage(stream, nonce, ctx, emulContext); break; } - case PACKET_TYPE_MEM: + case hmmmm_ClientPayload_MemClientMessage: { - printf("MEM packet\n"); - handleIncomingMemMessage(msg, ctx, emulContext); + hmmmm_mem_MemClientMessage_table_t mem = + (hmmmm_mem_MemClientMessage_table_t)hmmmm_ClientMessage_payload(cm); + handleIncomingMemMessage(mem, nonce, ctx, emulContext); + break; + } + case hmmmm_ClientPayload_AuthRequest: + { + // AuthRequest must only arrive before auth; drop if seen here + printf("client %lu: unexpected AuthRequest after auth\n", ctx->clientId); break; } default: { - printf("Unsupported packet type: %u\n", msg->packetType); + printf("client %lu: unknown payload type %u\n", ctx->clientId, ptype); break; } } @@ -97,46 +113,34 @@ void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* em void handleAllClients(EmulContext* emulContext) { LinkedListEntry* clientEntry = *emulContext->clientsHead; - size_t handleLimit = 128; - while(clientEntry != NULL && handleLimit > 0) - { + while (clientEntry != NULL && handleLimit > 0) { handleLimit--; ClientContext* ctx = clientEntry->payload; - if(!ctx->isAuthed) - { + + if (!ctx->isAuthed) { clientEntry = disconnectDueTimeout(emulContext, clientEntry); - if(clientEntry == NULL) - { - break; - } - if(*emulContext->utilizedFlag) - { - continue; - } - } - else - { - void* payload = ctx->incomeQ->head->payload; - if(payload != NULL) - { + if (clientEntry == NULL) break; + if (*emulContext->utilizedFlag) continue; + } else { + FbMessage* fbmsg = ctx->incomeQ->head->payload; + if (fbmsg != NULL) { *emulContext->utilizedFlag = 1; - BaseMessage* msg = payload; - printf("client %lu sent data: \nnonce %lu, ptype %4u, ph: %u\n", ctx->clientId, msg->nonce, msg->packetType, msg->payloadHeader); - - handleIncomingMessage(msg, ctx, emulContext); + if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) { + hmmmm_ClientMessage_table_t cm = + hmmmm_ClientMessage_as_root(fbmsg->data); + handleIncomingMessage(cm, ctx, emulContext); + } else { + printf("client %lu: dropped malformed FlatBuffer\n", ctx->clientId); + } - free(payload); + free(fbmsg->data); + free(fbmsg); ctx->incomeQ->head = ctx->incomeQ->head->nextEl; } clientEntry = clientEntry->nextEntry; } } } - - - - - diff --git a/src/proto/handlers/auth.c b/src/proto/handlers/auth.c index 5e6af54..a379206 100644 --- a/src/proto/handlers/auth.c +++ b/src/proto/handlers/auth.c @@ -1,97 +1,105 @@ #include "proto/handlers/auth.h" + #include #include #include #include #include "my_mutex.h" - -#include "proto/enums.h" #include "events.h" #include "panic.h" - - -#include "proto/pack.h" +#include "proto/enums.h" +#include "proto/msg.h" #include "proto/dial.h" -#include "proto/handlers/control.h" -uint8_t validateAccessTokenDeterministic(const uint8_t* data, const uint8_t* token, uint64_t timestamp) +#include "proto_reader.h" +#include "proto_verifier.h" +#include "auth_reader.h" + + +static uint8_t validateAccessTokenDeterministic( + const uint8_t* data, const uint8_t* token, uint64_t timestamp) { char buf[1024]; - sprintf(buf, "%s%lu", token, timestamp); + (void)snprintf(buf, sizeof(buf), "%s%lu", token, timestamp); uint8_t hash[SHA512_DIGEST_LENGTH]; - SHA512((uint8_t*)buf, strlen(buf), hash); + SHA512((const uint8_t*)buf, strlen(buf), hash); uint8_t valid = 1; - - for(size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) - { - if (data[i] != hash[i]) - { + for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) { + if (data[i] != hash[i]) { valid = 0; } } - return valid; } -uint8_t validateAccessToken(const uint8_t* data, const uint8_t* access_token) +static uint8_t validateAccessToken(const uint8_t* data, const uint8_t* access_token) { uint64_t t = (uint64_t)time(NULL) / 30; - - uint8_t valid1 = validateAccessTokenDeterministic(data, access_token, t); - uint8_t valid2 = validateAccessTokenDeterministic(data, access_token, t - 1); - - return valid1 || valid2; + return validateAccessTokenDeterministic(data, access_token, t) + || validateAccessTokenDeterministic(data, access_token, t - 1); } -uint8_t handle_auth(ClientContext* cctx, ws_cli_conn_t client, const uint8_t* msg, uint64_t msgSize, int msgType) +uint8_t handle_auth( + ClientContext* cctx, ws_cli_conn_t client, + const uint8_t* msg, uint64_t msgSize, int msgType) { + (void)msgType; + ServerContext* ctx = ws_get_server_context(client); - if(msgSize != SHA512_DIGEST_LENGTH) - { - int ret = ws_close_client(client); - if(ret == -1) - { - printf("Unable to close client %lu\n", client); - } + + // Verify it's a valid ClientMessage FlatBuffer + if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)msgSize)) { + printf("Auth: invalid FlatBuffer from client %lu\n", client); + ws_close_client(client); return 0; } - uint8_t isValid = validateAccessToken(msg, (const uint8_t*)ctx->accessToken); - if(!isValid) - { - printf("Auth token invalid\n"); - int ret = ws_close_client(client); - if(ret == -1) - { - printf("Unable to close client %lu\n", client); - } + + hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_as_root(msg); + + if (hmmmm_ClientMessage_payload_type(cm) != hmmmm_ClientPayload_AuthRequest) { + printf("Auth: expected AuthRequest, got type %u from client %lu\n", + hmmmm_ClientMessage_payload_type(cm), client); + ws_close_client(client); return 0; } + hmmmm_auth_AuthRequest_table_t ar = + (hmmmm_auth_AuthRequest_table_t)hmmmm_ClientMessage_payload(cm); + + flatbuffers_uint8_vec_t hash_vec = hmmmm_auth_AuthRequest_hash(ar); + size_t hash_len = flatbuffers_uint8_vec_len(hash_vec); + + if (hash_len != SHA512_DIGEST_LENGTH) { + printf("Auth: wrong hash length %zu from client %lu\n", hash_len, client); + ws_close_client(client); + return 0; + } + + if (!validateAccessToken(hash_vec, (const uint8_t*)ctx->accessToken)) { + printf("Auth: token invalid for client %lu\n", client); + ws_close_client(client); + return 0; + } + + printf("Auth: token valid for client %lu\n", client); + char errbuf[1024]; - - printf("Auth token is valid\n"); - - ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); - if(ev == NULL) - { + ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent)); + if (ev == NULL) { panic("Unable to allocate register event"); } ev->regType = REG_EVTYPE_AUTH; ev->ctx = cctx; - ws_set_connection_context(client, cctx); - - with_lock(&ctx->registerMutex) { printf("Writing auth event\n"); int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); - if(exitCode) - { + if (exitCode) { panic("Unable to push to reg queue: %s\n", errbuf); } } @@ -99,42 +107,38 @@ uint8_t handle_auth(ClientContext* cctx, ws_cli_conn_t client, const uint8_t* ms } -LinkedListEntry* disconnectDueTimeout(EmulContext* emulContext, LinkedListEntry* clientEntry) +LinkedListEntry* disconnectDueTimeout( + EmulContext* emulContext, LinkedListEntry* clientEntry) { - ClientContext* ctx = clientEntry->payload; + ClientContext* cctx = clientEntry->payload; uint64_t now = (uint64_t)time(NULL); - if(now - ctx->connectedAt <= 30) - { + if (now - cctx->connectedAt <= 30) { return clientEntry->nextEntry; } - printf("Timeout on connection %lu\n", ctx->clientId); - int ret = ws_close_client(ctx->clientId); - if(ret == -1) - { + printf("Timeout on connection %lu\n", cctx->clientId); + int ret = ws_close_client(cctx->clientId); + if (ret == -1) { printf("Unable to close client\n"); } LinkedListEntry* nextEntry = clientEntry->nextEntry; - removeLinkedListEntry(emulContext->clientsHead, clientEntry); - clientEntry = nextEntry; *emulContext->utilizedFlag = 1; - return clientEntry; + return nextEntry; } -void handleOnClientAuthDone(ClientContext* ctx, EmulContext* emulContext) + +void handleOnClientAuthDone(ClientContext* cctx, EmulContext* emulContext) { - uint8_t* framedata = malloc(sizeof(uint8_t) * 8); - NULL_GUARD(framedata); - - encodeUintToBytes(ctx->clientId, framedata); - - dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, framedata, 8); - - + // Send AuthResponse with assigned seat_id size_t len = 0; - uint8_t* msg = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, *emulContext->emulState, &len); - dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, msg, len); + uint8_t* authResp = fb_build_auth_response(UINT64_MAX, cctx->clientId, &len); + dispatchOutgoingMessage(emulContext->outBufs, cctx->clientId, authResp, len); -} \ No newline at end of file + // Send current execution state + len = 0; + uint8_t* stateMsg = fb_build_exec_notify( + UINT64_MAX, *emulContext->clockCounter, *emulContext->emulState, &len); + dispatchOutgoingMessage(emulContext->outBufs, cctx->clientId, stateMsg, len); +} diff --git a/src/proto/handlers/control.c b/src/proto/handlers/control.c index 63e960b..732e303 100644 --- a/src/proto/handlers/control.c +++ b/src/proto/handlers/control.c @@ -1,33 +1,107 @@ #include "proto/handlers/control.h" -#include "proto/pack.h" -#include "proto/enums.h" -#include "panic.h" +#include #include "state.h" +#include "proto/msg.h" +#include "proto/dial.h" + +#include "control_reader.h" +#include "exec_ctrl_reader.h" +#include "setup_buf_reader.h" +#include "orphaned_reader.h" +#include "lost_reader.h" - - -void handleIncomingControlMessage(BaseMessage* msg, EmulContext* emulContext) +// Maps FlatBuffers ExecPrompt values to EMUL_STATE_OP_* constants. +// ExecPrompt: start=1, pause=2, resume=3, stop=4, reset=5 +// EMUL_STATE_OP: START=1, PAUSE=2, RESUME=3, RESET=4, STOP=5 +static uint8_t prompt_to_state_op(hmmmm_ctrl_exec_ExecPrompt_enum_t prompt) { - if(msg->payloadHeader == CTRL_TYPE_EXEC) - { - printf("ctrl exec\n"); - uint8_t stateOp = ((const uint8_t*)(msg->payload))[0]; - printf("state operation: %u\n", stateOp); - uint8_t newEmulState = switchNewEmulState(*emulContext->emulState, stateOp); - if(newEmulState != *emulContext->emulState) - { - printf("Switch state %u -> %u\n", *emulContext->emulState, newEmulState); - *emulContext->emulState = newEmulState; + switch (prompt) { + case hmmmm_ctrl_exec_ExecPrompt_start: return EMUL_STATE_OP_START; + case hmmmm_ctrl_exec_ExecPrompt_pause: return EMUL_STATE_OP_PAUSE; + case hmmmm_ctrl_exec_ExecPrompt_resume: return EMUL_STATE_OP_RESUME; + case hmmmm_ctrl_exec_ExecPrompt_stop: return EMUL_STATE_OP_STOP; + case hmmmm_ctrl_exec_ExecPrompt_reset: return EMUL_STATE_OP_RESET; + default: return 0; + } +} - size_t len = 0; - uint8_t* notify = createControlNotifyMessage(msg->nonce, *emulContext->clockCounter, newEmulState, &len); - broadcastClients(emulContext, notify, len); - } - } - else + +void handleIncomingCtrlMessage( + hmmmm_ctrl_CtrlClientMessage_table_t msg, + uint64_t nonce, + ClientContext* ctx, + EmulContext* emulContext) +{ + hmmmm_ctrl_CtrlClientPayload_union_type_t ptype = + hmmmm_ctrl_CtrlClientMessage_payload_type(msg); + + printf("[CTRL] client=%lu nonce=%lu type=%s\n", + ctx->clientId, nonce, + hmmmm_ctrl_CtrlClientPayload_type_name(ptype)); + + if (ptype == hmmmm_ctrl_CtrlClientPayload_ExecCtrlMessage) { - printf("invalid payload header: %u\n", msg->payloadHeader); + hmmmm_ctrl_exec_ExecCtrlMessage_table_t exec = + (hmmmm_ctrl_exec_ExecCtrlMessage_table_t) + hmmmm_ctrl_CtrlClientMessage_payload(msg); + + hmmmm_ctrl_exec_ExecPrompt_enum_t prompt = + hmmmm_ctrl_exec_ExecCtrlMessage_prompt(exec); + + printf("[CTRL/EXEC] prompt=%s\n", hmmmm_ctrl_exec_ExecPrompt_name(prompt)); + + uint8_t state_op = prompt_to_state_op(prompt); + if (state_op == 0) { + printf("[CTRL/EXEC] unknown prompt, ignoring\n"); + return; + } + + uint8_t new_state = switchNewEmulState(*emulContext->emulState, state_op); + *emulContext->emulState = new_state; + printf("[CTRL/EXEC] state -> %u\n", new_state); + + size_t msg_len; + uint8_t* out = fb_build_exec_notify(0, *emulContext->clockCounter, new_state, &msg_len); + broadcastClients(emulContext, out, msg_len); } -} \ No newline at end of file + else if (ptype == hmmmm_ctrl_CtrlClientPayload_SetupBuf) + { + hmmmm_ctrl_setup_buf_SetupBuf_table_t sb = + (hmmmm_ctrl_setup_buf_SetupBuf_table_t) + hmmmm_ctrl_CtrlClientMessage_payload(msg); + + uint32_t lost_buf_size = hmmmm_ctrl_setup_buf_SetupBuf_lost_buf_size(sb); + uint64_t lifetime_ticks = hmmmm_ctrl_setup_buf_SetupBuf_client_lifetime_ticks(sb); + + printf("[CTRL/SETUP] lost_buf_size=%u lifetime_ticks=%lu\n", + lost_buf_size, lifetime_ticks); + + size_t msg_len; + uint8_t* out = fb_build_setup_buf(nonce, lost_buf_size, lifetime_ticks, &msg_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); + } + else if (ptype == hmmmm_ctrl_CtrlClientPayload_OrphanedRequest) + { + printf("[CTRL/ORPHANED] returning empty list\n"); + + size_t msg_len; + uint8_t* out = fb_build_orphaned_response(nonce, &msg_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); + } + else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest) + { + hmmmm_ctrl_lost_LostMessagesRequest_table_t req = + (hmmmm_ctrl_lost_LostMessagesRequest_table_t) + hmmmm_ctrl_CtrlClientMessage_payload(msg); + + uint64_t seat_id = hmmmm_ctrl_lost_LostMessagesRequest_seat_id(req); + + printf("[CTRL/LOST] seat_id=%lu returning empty list\n", seat_id); + + size_t msg_len; + uint8_t* out = fb_build_lost_messages_response(nonce, seat_id, &msg_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); + } +} diff --git a/src/proto/handlers/mem.c b/src/proto/handlers/mem.c index 09bae46..a93308a 100644 --- a/src/proto/handlers/mem.c +++ b/src/proto/handlers/mem.c @@ -1,80 +1,92 @@ #include "proto/handlers/mem.h" -#include "proto/enums.h" -#include "proto/pack.h" -#include "panic.h" + +#include #include +#include "proto/msg.h" +#include "proto/dial.h" +#include "mem_reader.h" + +#define DEVICE_MEM_SIZE ((size_t)(128 * 1024)) -uint8_t* createMemReadResponseMessage(uint64_t nonce, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut) +void handleIncomingMemMessage( + hmmmm_mem_MemClientMessage_table_t msg, + uint64_t nonce, + ClientContext* ctx, + EmulContext* emulContext) { - *lenOut = 9 + 8 + payloadLen; - uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); - NULL_GUARD(outmsg); - - encodeUintToBytes(nonce, outmsg); + hmmmm_mem_MemClientPayload_union_type_t ptype = + hmmmm_mem_MemClientMessage_payload_type(msg); - outmsg[8] = PACKET_TYPE_MEM << 4; - outmsg[8] |= MEM_TYPE_READ_RESP; + printf("[MEM] client=%lu nonce=%lu type=%s\n", + ctx->clientId, nonce, + hmmmm_mem_MemClientPayload_type_name(ptype)); - encodeUintToBytes(clockCounter, outmsg + 9); - - memcpy(outmsg + 9 + 8, payload, payloadLen); + if (ptype == hmmmm_mem_MemClientPayload_MemReadRequest) + { + hmmmm_mem_MemReadRequest_table_t req = + (hmmmm_mem_MemReadRequest_table_t) + hmmmm_mem_MemClientMessage_payload(msg); - return outmsg; + uint32_t dev_id = hmmmm_mem_MemReadRequest_device_id(req); + uint32_t seg_id = hmmmm_mem_MemReadRequest_seg_id(req); + uint32_t offset = hmmmm_mem_MemReadRequest_offset(req); + uint32_t length = hmmmm_mem_MemReadRequest_length(req); + + printf("[MEM/READ] device=%u seg=%u offset=%u len=%u\n", + dev_id, seg_id, offset, length); + + if (dev_id >= (uint32_t)emulContext->devicesCount) { + printf("[MEM/READ] invalid device %u\n", dev_id); + return; + } + if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) { + printf("[MEM/READ] out of bounds\n"); + return; + } + + const uint8_t* base = emulContext->devicesMem[dev_id]; + size_t out_len; + uint8_t* out = fb_build_mem_read_response( + nonce, *emulContext->clockCounter, + dev_id, seg_id, offset, + base + offset, (size_t)length, + &out_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, out_len); + } + else if (ptype == hmmmm_mem_MemClientPayload_MemWriteRequest) + { + hmmmm_mem_MemWriteRequest_table_t req = + (hmmmm_mem_MemWriteRequest_table_t) + hmmmm_mem_MemClientMessage_payload(msg); + + uint32_t dev_id = hmmmm_mem_MemWriteRequest_device_id(req); + uint32_t seg_id = hmmmm_mem_MemWriteRequest_seg_id(req); + uint32_t offset = hmmmm_mem_MemWriteRequest_offset(req); + flatbuffers_uint8_vec_t data = hmmmm_mem_MemWriteRequest_data(req); + size_t data_len = flatbuffers_uint8_vec_len(data); + + printf("[MEM/WRITE] device=%u seg=%u offset=%u len=%zu\n", + dev_id, seg_id, offset, data_len); + + if (dev_id >= (uint32_t)emulContext->devicesCount) { + printf("[MEM/WRITE] invalid device %u\n", dev_id); + return; + } + if ((size_t)offset + data_len > DEVICE_MEM_SIZE) { + printf("[MEM/WRITE] out of bounds\n"); + return; + } + + uint8_t* base = emulContext->devicesMem[dev_id]; + memcpy(base + offset, data, data_len); + + size_t out_len; + uint8_t* out = fb_build_mem_read_response( + nonce, *emulContext->clockCounter, + dev_id, seg_id, offset, + base + offset, data_len, + &out_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, out_len); + } } - -void handleIncomingMemReadReq(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) -{ - uint64_t devId = decodeBytesToU64(msg->payload); - uint64_t segId = decodeBytesToU64(msg->payload + 8); - uint64_t startAddr = decodeBytesToU64(msg->payload + 16); - uint64_t readLen = decodeBytesToU64(msg->payload + 24); - - if(devId >= emulContext->devicesCount) - { - return; - } - - - //TODO: lookup config for global addr conversion - uint64_t globalAddr = startAddr + segId * 128; - - uint8_t* readPtr = emulContext->devicesMem[devId] + globalAddr; - - size_t outLen = 0; - uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, readPtr, readLen, &outLen); - dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, outMsg, outLen); -} - - -void handleIncomingMemWrite(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) -{ - uint64_t devId = decodeBytesToU64(msg->payload); - uint64_t segId = decodeBytesToU64(msg->payload + 8); - uint64_t startAddr = decodeBytesToU64(msg->payload + 16); - - if(devId >= emulContext->devicesCount) - { - return; - } - - //TODO: lookup config for global addr conversion - uint64_t globalAddr = startAddr + segId * 128; - - uint8_t* writePtr = emulContext->devicesMem[devId] + globalAddr; - - memcpy(writePtr, msg->payload + 24, msg->payloadLen - 24); -} - - -void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) -{ - if(msg->payloadHeader == MEM_TYPE_READ_REQ) - { - handleIncomingMemReadReq(msg, ctx, emulContext); - } - else if(msg->payloadHeader == MEM_TYPE_WRITE_PUSH) - { - handleIncomingMemWrite(msg, ctx, emulContext); - } -} \ No newline at end of file diff --git a/src/proto/handlers/stream.c b/src/proto/handlers/stream.c index dbfaf87..139f1ac 100644 --- a/src/proto/handlers/stream.c +++ b/src/proto/handlers/stream.c @@ -1,26 +1,31 @@ #include "proto/handlers/stream.h" +#include +#include #include "streamed.h" -#include "proto/pack.h" -#include "proto/enums.h" #include "panic.h" +#include "proto/msg.h" +#include "proto/dial.h" + +#include "stream_reader.h" +#include "stream_reg_reader.h" +// ── Stream registration bookkeeping (unchanged logic) ──────────────────────── -void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId) +void unregisterClientStream( + EmulContext* emulContext, ClientContext* ctx, uint32_t regId) { - for(size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) - { + for (size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) { DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId]; - for(size_t i = 0; i < deviceRegs->regCount; i++) - { + for (size_t i = 0; i < deviceRegs->regCount; i++) { StreamReg* iReg = &deviceRegs->regs[i]; - if(iReg->clientContext->clientId == ctx->clientId && iReg->regId == regId) + if (iReg->clientContext->clientId == ctx->clientId + && iReg->regId == regId) { - printf("Discard stream %u register for client %lu\n", regId, ctx->clientId); - deviceRegs->regCount -= 1; - for(size_t j = i; j < deviceRegs->regCount; j++) - { + printf("Discard stream %u for client %lu\n", regId, ctx->clientId); + deviceRegs->regCount--; + for (size_t j = i; j < deviceRegs->regCount; j++) { deviceRegs->regs[j] = deviceRegs->regs[j + 1]; } break; @@ -31,94 +36,112 @@ void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32 void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx) { - for(size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) - { + for (size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) { DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId]; - StreamReg* newStreamRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount); - NULL_GUARD(newStreamRegs); - size_t newStreamRegsId = 0; + StreamReg* newRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount); + NULL_GUARD(newRegs); + size_t newCount = 0; - for(size_t i = 0; i < deviceRegs->regCount; i++) - { + for (size_t i = 0; i < deviceRegs->regCount; i++) { StreamReg* reg = &deviceRegs->regs[i]; - if(reg->clientContext->clientId != ctx->clientId) - { - newStreamRegs[newStreamRegsId] = *reg; - newStreamRegsId++; - } - else - { - printf("Removing reg %d mode stream for %lu/%lu+%lu: [%u]\n", reg->mode, deviceId, reg->startAddr, reg->segLen, reg->regId); + if (reg->clientContext->clientId != ctx->clientId) { + newRegs[newCount++] = *reg; + } else { + printf("Removing stream reg [%u] for client %lu\n", + reg->regId, ctx->clientId); } } free(deviceRegs->regs); - deviceRegs->regCount = newStreamRegsId; - deviceRegs->regs = newStreamRegs; - } -} - - -void handleStreamRegMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext, uint8_t X) -{ - uint64_t devId = decodeBytesToU64(msg->payload); - uint64_t segId = decodeBytesToU64(msg->payload + 8); - uint64_t startAddr = decodeBytesToU64(msg->payload + 16); - uint64_t segLen = decodeBytesToU64(msg->payload + 24); - - if(devId >= emulContext->devicesCount) - { - return; - } - - DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[devId]; - if(deviceRegs->regCount + 1 >= deviceRegs->allocatedSize) - { - size_t newAllocatedSize = deviceRegs->allocatedSize; - if(newAllocatedSize <= deviceRegs->regCount + 1) - { - newAllocatedSize = deviceRegs->regCount + 1; - } - StreamReg* newRegs = realloc(deviceRegs->regs, sizeof(StreamReg) * newAllocatedSize); - NULL_GUARD(newRegs); - deviceRegs->allocatedSize = newAllocatedSize; + deviceRegs->regCount = newCount; deviceRegs->regs = newRegs; } - - //TODO: lookup config for global segment addr conversion - uint64_t globalStartAddr = startAddr + segId * 128; - - StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount]; - - reg->clientContext = ctx; - reg->segLen = segLen; - reg->startAddr = globalStartAddr; - reg->regId = ctx->streamRegIterator; - reg->mode = X; - - ctx->streamRegIterator++; - deviceRegs->regCount++; - - printf("Done registering %d mode stream for %lu/%lu+%lu: [%u]\n", X, devId, globalStartAddr, segLen, reg->regId); - - size_t len = 0; - uint8_t* notifMsg = createDoneRegMessage(msg->nonce, X, devId, segId, startAddr, segLen, reg->regId, &len); - dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, notifMsg, len); - } -void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) -{ - uint8_t X = msg->payloadHeader >> 3; - uint8_t streamType = msg->payloadHeader & 0b111; - if(streamType == STREAM_TYPE_REG_REQUEST) +// ── Incoming stream messages ────────────────────────────────────────────────── + +void handleIncomingStreamMessage( + hmmmm_stream_StreamClientMessage_table_t msg, + uint64_t nonce, + ClientContext* ctx, + EmulContext* emulContext) +{ + hmmmm_stream_StreamClientPayload_union_type_t ptype = + hmmmm_stream_StreamClientMessage_payload_type(msg); + + printf("[STREAM] client=%lu nonce=%lu type=%s\n", + ctx->clientId, nonce, + hmmmm_stream_StreamClientPayload_type_name(ptype)); + + if (ptype == hmmmm_stream_StreamClientPayload_StreamRegRequest) { - handleStreamRegMessage(msg, ctx, emulContext, X); + hmmmm_stream_StreamRegRequest_table_t req = + (hmmmm_stream_StreamRegRequest_table_t) + hmmmm_stream_StreamClientMessage_payload(msg); + + uint32_t dev_id = hmmmm_stream_StreamRegRequest_device_id(req); + uint32_t seg_id = hmmmm_stream_StreamRegRequest_seg_id(req); + uint32_t offset = hmmmm_stream_StreamRegRequest_offset(req); + uint32_t length = hmmmm_stream_StreamRegRequest_length(req); + hmmmm_stream_StreamMode_enum_t mode = hmmmm_stream_StreamRegRequest_mode(req); + + printf("[STREAM/REG] device=%u seg=%u offset=%u len=%u mode=%d\n", + dev_id, seg_id, offset, length, (int)mode); + + if (dev_id >= (uint32_t)emulContext->devicesCount) { + printf("[STREAM/REG] invalid device %u\n", dev_id); + return; + } + + DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[dev_id]; + + if (deviceRegs->regCount >= deviceRegs->allocatedSize) { + size_t new_size = deviceRegs->allocatedSize * 2; + StreamReg* new_regs = realloc(deviceRegs->regs, sizeof(StreamReg) * new_size); + NULL_GUARD(new_regs); + deviceRegs->regs = new_regs; + deviceRegs->allocatedSize = new_size; + } + + uint32_t reg_id = ctx->streamRegIterator++; + StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++]; + reg->clientContext = ctx; + reg->regId = reg_id; + reg->startAddr = (uint64_t)offset; + reg->segLen = (uint64_t)length; + reg->mode = (uint8_t)mode; + + printf("[STREAM/REG] assigned reg_id=%u to client=%lu\n", reg_id, ctx->clientId); + + size_t msg_len; + uint8_t* out = fb_build_stream_reg_confirm( + nonce, reg_id, dev_id, seg_id, offset, length, (uint8_t)mode, &msg_len); + dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len); } - else if(streamType == STREAM_TYPE_REG_DISCARD) + else if (ptype == hmmmm_stream_StreamClientPayload_StreamDeregRequest) { - uint32_t regId = decodeBytesToU32(msg->payload); - unregisterClientStream(emulContext, ctx, regId); + hmmmm_stream_StreamDeregRequest_table_t req = + (hmmmm_stream_StreamDeregRequest_table_t) + hmmmm_stream_StreamClientMessage_payload(msg); + + uint32_t stream_id = hmmmm_stream_StreamDeregRequest_stream_id(req); + printf("[STREAM/DEREG] stream_id=%u\n", stream_id); + + unregisterClientStream(emulContext, ctx, stream_id); } -} \ No newline at end of file + else if (ptype == hmmmm_stream_StreamClientPayload_StreamWritePush) + { + hmmmm_stream_StreamWritePush_table_t push = + (hmmmm_stream_StreamWritePush_table_t) + hmmmm_stream_StreamClientMessage_payload(msg); + + printf("[STREAM/WRITE] stream_id=%u offset=%u len=%zu\n", + hmmmm_stream_StreamWritePush_stream_id(push), + hmmmm_stream_StreamWritePush_offset(push), + flatbuffers_uint8_vec_len( + hmmmm_stream_StreamWritePush_data(push))); + + // TODO: apply write to device memory + } +} diff --git a/src/proto/handlers/ws.c b/src/proto/handlers/ws.c index 23e2d90..1f9cfff 100644 --- a/src/proto/handlers/ws.c +++ b/src/proto/handlers/ws.c @@ -1,12 +1,17 @@ #include "proto/handlers/ws.h" #include "proto/handlers.h" +#include #include "panic.h" -#include "proto/enums.h" #include "my_mutex.h" +#include "proto/enums.h" #include "proto/handlers/auth.h" #include "proto/msg.h" +#include "proto_verifier.h" +#include "proto_reader.h" + + void onWsOpen(ws_cli_conn_t client) { char errbuf[1024]; @@ -15,34 +20,30 @@ void onWsOpen(ws_cli_conn_t client) NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf); ptQueue* outcomeQueue = ptQueueCreate(errbuf); - NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf); - + ClientContext* cctx = malloc(sizeof(ClientContext)); - if(cctx == NULL) - { + if (cctx == NULL) { ptQueueFree(incomeQueue); ptQueueFree(outcomeQueue); panic("Unable to allocate client context\n"); } - cctx->clientId = client; - cctx->isAuthed = 0; + cctx->clientId = client; + cctx->isAuthed = 0; cctx->streamRegIterator = 0; - cctx->incomeQ = incomeQueue; - cctx->outcomeQ = outcomeQueue; - cctx->connectedAt = (uint64_t)time(NULL); + cctx->incomeQ = incomeQueue; + cctx->outcomeQ = outcomeQueue; + cctx->connectedAt = (uint64_t)time(NULL); - ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); - if(ev == NULL) - { + ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent)); + if (ev == NULL) { ptQueueFree(incomeQueue); ptQueueFree(outcomeQueue); free(cctx); - panic("Unable to allocate register event"); } ev->regType = REG_EVTYPE_CONNECT; - ev->ctx = cctx; + ev->ctx = cctx; ws_set_connection_context(client, cctx); @@ -51,90 +52,87 @@ void onWsOpen(ws_cli_conn_t client) with_lock(&ctx->registerMutex) { int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); - if(exitCode) - { + if (exitCode) { ptQueueFree(incomeQueue); ptQueueFree(outcomeQueue); free(cctx); - panic("Unable to push to reg queue: %s\n", errbuf); } } - char *cli; - cli = ws_getaddress(client); + char* cli = ws_getaddress(client); printf("Connection %lu opened, addr: %s\n", client, cli); } void onWsClose(ws_cli_conn_t client) { - pthread_t tid = pthread_self(); - char *cli; - cli = ws_getaddress(client); - printf("Connection %lu closed, addr: %s, thread id: %lu\n", client, cli, tid); - + char* cli = ws_getaddress(client); + printf("Connection %lu closed, addr: %s\n", client, cli); char errbuf[1024]; - + ClientContext* cctx = ws_get_connection_context(client); - if(cctx == NULL) - { - cctx = NULL; - printf("Unable to get client context\n"); + if (cctx == NULL) { + printf("Unable to get client context for %lu\n", client); } - ServerContext* ctx = ws_get_server_context(client); - if(ctx == NULL) - { - printf("Unable to get server context\n"); + if (ctx == NULL) { + printf("Unable to get server context for %lu\n", client); return; } - ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); + ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent)); NULL_GUARD(ev); ev->regType = REG_EVTYPE_CLOSE; - ev->ctx = cctx; - + ev->ctx = cctx; with_lock(&ctx->registerMutex) { int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); - if(exitCode) - { + if (exitCode) { panic("Unable to push to reg queue: %s\n", errbuf); } } } - - -void onWsMessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type) +void onWsMessage( + ws_cli_conn_t client, const unsigned char* msg, uint64_t size, int type) { ClientContext* cctx = ws_get_connection_context(client); - if(cctx == NULL) - { + if (cctx == NULL) { panic("Unable to get client context\n"); } - if (cctx->isAuthed == 0) - { + if (!cctx->isAuthed) { handle_auth(cctx, client, msg, size, type); + return; } - else - { - char errbuf[1024]; - BaseMessage* baseMsg = parseMessage(msg, size); - printf("msg\n"); - int isErr = ptQueuePush(cctx->incomeQ, baseMsg, errbuf); - if(isErr) - { - panic("Unable to dispatch client message: %s\n", errbuf); - } + + // Verify the FlatBuffer before queuing + if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)size)) { + printf("Client %lu sent invalid FlatBuffer, dropping\n", client); + return; + } + + // Copy bytes — the WS buffer is only valid for this callback's duration + FbMessage* fbmsg = malloc(sizeof(FbMessage)); + NULL_GUARD(fbmsg); + fbmsg->data = malloc((size_t)size); + if (fbmsg->data == NULL) { + free(fbmsg); + panic("Unable to allocate FbMessage buffer\n"); + } + fbmsg->size = (size_t)size; + memcpy(fbmsg->data, msg, (size_t)size); + + char errbuf[1024]; + int isErr = ptQueuePush(cctx->incomeQ, fbmsg, errbuf); + if (isErr) { + free(fbmsg->data); + free(fbmsg); + panic("Unable to queue client message: %s\n", errbuf); } } - - - diff --git a/src/proto/msg.c b/src/proto/msg.c index 934c26f..6f2133f 100644 --- a/src/proto/msg.c +++ b/src/proto/msg.c @@ -1,85 +1,250 @@ #include "proto/msg.h" #include "panic.h" -#include "proto/enums.h" -#include "proto/pack.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" -#include -BaseMessage* parseMessage(const uint8_t* bytes, size_t size) +uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out) { - const uint8_t headerSize = 9; - BaseMessage* msg = malloc(sizeof(BaseMessage)); - NULL_GUARD(msg); - uint64_t nonce = decodeBytesToU64(bytes); + 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); - msg->nonce = nonce; + hmmmm_ServerPayload_union_ref_t payload = + hmmmm_ServerPayload_as_AuthResponse(ar); - msg->packetType = bytes[8] >> 4; - msg->payloadHeader = bytes[8] & 0b1111; + hmmmm_ServerMessage_create_as_root(&B, nonce, payload); - msg->payloadLen = size - headerSize; - - uint8_t* payload = malloc(sizeof(uint8_t) * (msg->payloadLen)); - NULL_GUARD(payload); - - memcpy(payload, bytes + headerSize, msg->payloadLen); - msg->payload = payload; - - return msg; + uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out); + flatcc_builder_clear(&B); + NULL_GUARD(buf); + return buf; } -uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut) +uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out) { - *lenOut = 9 + 10; - uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); - NULL_GUARD(outmsg, "Unable to allocate message"); - - encodeUintToBytes(nonce, outmsg); + flatcc_builder_t B; + if (flatcc_builder_init(&B)) { + panic("flatcc_builder_init failed\n"); + } - outmsg[8] = PACKET_TYPE_CTRL << 4; - outmsg[8] |= CTRL_TYPE_NOTIF_STATE; + 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); - outmsg[9] = NOTIF_TYPE_EXEC; - encodeUintToBytes(clockCounter, outmsg + 10); - outmsg[18] = newEmulState; - return outmsg; + 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* createDoneRegMessage(uint64_t nonce, uint8_t X, uint64_t devId, uint64_t segId, uint64_t startAddr, uint64_t segLength, uint32_t regId, size_t* lenOut) +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) { - *lenOut = 36 + 9; - uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); - NULL_GUARD(outmsg); + flatcc_builder_reset(B); - encodeUintToBytes(nonce, outmsg); - outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (X << 3) | STREAM_TYPE_REG_CONFIRM); - encodeUintToBytes(devId, outmsg + 9); - encodeUintToBytes(segId, outmsg + 9 + 8); - encodeUintToBytes(startAddr, outmsg + 9 + 8 + 8); - encodeUintToBytes(segLength, outmsg + 9 + 8 + 8 + 8); - encodeUintToBytes(regId, outmsg + 9 + 8 + 8 + 8 + 8); - return outmsg; + 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* createStreamSegmentPush(uint8_t mode, uint32_t regId, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut) +uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size, + uint64_t client_lifetime_ticks, size_t* len_out) { - *lenOut = 9 + 4 + 8 + payloadLen; + flatcc_builder_t B; + if (flatcc_builder_init(&B)) { + panic("flatcc_builder_init failed\n"); + } - uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); - NULL_GUARD(outmsg); + hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb = + hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks); - uint64_t nonce = (uint64_t)~0; - encodeUintToBytes(nonce, outmsg); - outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (mode << 3) | STREAM_TYPE_SEND); + hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload = + hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb); - encodeUintToBytes(regId, outmsg + 9); - encodeUintToBytes(clockCounter, outmsg + 9 + 4); - memcpy(outmsg + 9 + 4 + 8, payload, payloadLen); + hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg = + hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload); - return outmsg; + 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; }