13 Commits

Author SHA1 Message Date
26e594cee6 change queues, run exec speed optimizations 2026-06-23 21:30:01 +03:00
7a272788f8 Request reset at config upload, increase client req handling freq on idle 2026-04-24 14:48:17 +03:00
14b005dbb3 Exec cycle limit 2026-04-24 14:42:32 +03:00
c907d45763 Add write-after-cycles, reset handling 2026-04-16 15:37:32 +03:00
b5a4fdf755 update proto, add config update publish broadcast, add messages compacting 2026-04-10 22:25:32 +03:00
e053d445da add dlopen error info 2026-04-06 23:40:36 +03:00
d8804c9ae2 Generate README.md, add clock dividers and limiters, load configs from WS client, fix stream per-segment subscriptions according to memory projections mechanism 2026-04-05 17:15:04 +03:00
71ec472510 fix: code quality improvements across C codebase
- Replace sprintf with snprintf (1024-byte errbuf) in hmmmm.c, base_device.c,
  compose_device.c, config.c
- Fix WRITE_MEM macro: use smartAddrWriteHandlers for write ident (was
  incorrectly reading from smartAddrReadHandlers)
- Replace alloca with malloc+free in dispatchMemAccessNotifications
- Guard closeBaseDevice against NULL lib/partial initialization
- Simplify intercept context storage to single contiguous allocation
- Add NULL checks after calloc in config handler with proper cleanup
- Guard find_device_by_id against zero-length path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 22:18:23 +00:00
eb7f4b7eb0 feat: add projection and intercept support, remove TOML, fix freeDevMem symbol bug
- Remove TOML dependency from Makefile and compose_device.c
- Implement projection support in config handler: remap device memory
  segments between devices via cells[] pointer reassignment
- Implement intercept support: shadow_copy (write to both devices) and
  shadow_replace (redirect read/write to another device) modes using
  smartAddr handler infrastructure
- Fix critical bug in hmmmm.c: freeDevMem was loaded from "freeDevSpecs"
  symbol instead of "freeDevMem", causing segfault on config reload
- Add intercept context lifecycle management to EmulContext

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 16:28:49 +00:00
589bc8d620 feat: migrate config management from TOML to FlatBuffers
Config is now loaded at runtime via WebSocket (ConfigCtrlMessage)
instead of from hardcoded TOML files. The emulator starts with no
devices and waits for clients to send configuration.

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

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 14:02:41 +00:00
d83cb7fe7b flatbuffer base 2026-03-30 16:20:14 +03:00
9a4b78f272 add flatcc dependency 2026-03-24 16:37:41 +03:00
aaa858b903 ws server +proto 2026-02-11 12:18:59 +03:00
68 changed files with 6078 additions and 1128 deletions

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ build
devices devices
.cache .cache
*.bin *.bin
inc/flatbuf_autogen

View File

@@ -4,13 +4,28 @@ SRC_DIR=src
INC_DIR=inc INC_DIR=inc
CC=gcc CC=gcc
OBJDUMP=objdump OBJDUMP=objdump
LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a LIBS=deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a
LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE) 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 STATIC_LIBS=crypto
STANDART=c23 STANDART=c23
OPTIMIZE=-Og # OPTIMIZE=-Og #3 -march=native
OPTIMIZE=-O3 -march=native
TARGET=main 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 DEFINES=#-DOPCODE_WORDSIZE=2 -DMEM_CELL_WORDS=1 -DPC_WORDSIZE=2 -DGP_REG_CELL_WORDS=1 -DIO_REG_CELL_WORDS=1
@@ -20,32 +35,43 @@ C_INCLUDES=-I$(INC_DIR)/ $(addprefix -I,$(LIBS_HEADERS))
C_DEFS=-D_POSIX_C_SOURCE=199309L C_DEFS=-D_POSIX_C_SOURCE=199309L
DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith -Wno-analyzer-infinite-loop DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith -Wno-analyzer-infinite-loop -Wno-unused-function -Wno-unused-but-set-variable
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 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 ANALYZER_FLAGS=-fanalyzer -fdiagnostics-show-option -fdiagnostics-color=always
LSECTIONS=-ffunction-sections -fdata-sections -Wl,--gc-sections 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 -pthread $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline # -fno-omit-frame-pointer -fno-inline -fno-lto
CFLAGS=$(C_DEFS) -g -pthread $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline # -fno-omit-frame-pointer -fno-inline -fno-lto
STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS)) STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS))
LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm # LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline -fno-lto
# LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -fuse-linker-plugin $(LSECTIONS) -lm -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fno-omit-frame-pointer -fno-inline -flto -lgcov # -fno-omit-frame-pointer -fno-inline # #-fprofile-generate # -flto
LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -fuse-linker-plugin $(LSECTIONS) -lm # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fno-omit-frame-pointer -fno-inline -flto -lgcov # -fno-omit-frame-pointer -fno-inline # #-fprofile-generate # -flto
OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES)) OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
DEP = $(filter %.d, $(OBJECTS:.o=.d)) DEP = $(filter %.d, $(OBJECTS:.o=.d))
$(info $(DEP)) #$(info $(DEP))
OBJECTS += $(LIBS) OBJECTS += $(LIBS)
vpath %.c $(sort $(dir $(C_SOURCES))) vpath %.c $(sort $(dir $(C_SOURCES)))
# asan: CFLAGS := $(filter-out -fanalyzer,$(CFLAGS)) -fsanitize=address,undefined
# asan: LFLAGS := $(filter-out -flto -fuse-linker-plugin,$(LFLAGS)) -fsanitize=address,undefined
# asan: build
# tsan: CFLAGS := $(filter-out -fanalyzer,$(CFLAGS)) -fsanitize=thread
# tsan: LFLAGS := $(filter-out -flto -fuse-linker-plugin,$(LFLAGS)) -fsanitize=thread
# tsan: build
all: build all: build
build: date deps Dir target compile_commands build: date deps Dir proto python-proto target
rebuild: clean | build rebuild: clean | build
$(info $(DEP)) #$(info $(DEP))
-include $(DEP) -include $(DEP)
@@ -54,20 +80,30 @@ $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@ @$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
@$(OBJDUMP) -d -S $@ > $(BUILD_DIR)/$(notdir $(<:.c=.casm)) @$(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) $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
@echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m' @echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m'
@$(CC) $(LFLAGS) $(OBJECTS) -o $(BUILD_DIR)/$(TARGET).elf @$(CC) $(LFLAGS) $(OBJECTS) $(LDLIBS) -o $(BUILD_DIR)/$(TARGET).elf
deps: deps:
@make -C deps all @make -C deps all
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: BuildDir:
@mkdir -p $(BUILD_DIR) @mkdir -p $(BUILD_DIR)
$(shell mkdir -p $(dir $(OBJECTS))) @$(shell mkdir -p $(dir $(OBJECTS)))
SrcDir: SrcDir:
@mkdir -p $(SRC_DIR) @mkdir -p $(SRC_DIR)
@@ -75,21 +111,36 @@ SrcDir:
IncDir: IncDir:
@mkdir -p $(INC_DIR) @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 asan tsan clean
clean: 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' @echo -e '\033[0;31mCleaned\033[0m'
.NOTPARALLEL: date target rebuild deps .NOTPARALLEL: date target rebuild deps proto
date: date:
@echo -e '\033[1;32m'"Starting build at " | tr -d '\n' @echo -e '\033[1;32m'"Starting build at " | tr -d '\n'
@date @date
@echo -e '\033[0m' @echo -e '\033[0m'
compile_commands:
# @bear -- ./.gen_compile_commands.sh $(TARGET) $(CC) "$(CFLAGS)" "$(LFLAGS)" "$(OBJECTS)"

250
README.md Normal file
View File

@@ -0,0 +1,250 @@
# hmmmm
**Модульный эмулятор микроконтроллеров с сетевым интерфейсом**
hmmmm — это эмулятор микроконтроллеров, который позволяет эмулировать AVR и другие архитектуры с возможностью управления через WebSocket. Эмулятор поддерживает динамическую загрузку устройств, композицию сложных устройств и потоковую передачу изменений памяти в реальном времени.
## Возможности
- **Эмуляция AVR-микроконтроллеров** — поддержка ~99 инструкций AVR
- **Модульная архитектура** — устройства загружаются как динамические библиотеки (.so)
- **Композиция устройств** — создание сложных устройств через TOML-конфигурацию
- **WebSocket интерфейс** — управление эмуляцией и получение данных в реальном времени
- **Потоковая передача** — подписка на изменения памяти (чтение/запись)
- **Аутентификация** — SHA-512 токен с временным окном (TOTP-подобный)
- **Сегментированная память** — PS (программная), GP_REGS, IO_REGS, DS (данные)
## Структура проекта
```
hmmmm/
├── src/ # Исходный код эмулятора
│ ├── main.c # WebSocket сервер, главный цикл
│ ├── hmmmm.c # Загрузка динамических библиотек
│ ├── config.c # Парсинг TOML-конфигураций
│ └── proto/ # Обработчики WebSocket-протокола
├── inc/ # Заголовочные файлы
├── devices/ # Реализации эмулируемых устройств
│ ├── avr_generic/ # Универсальный эмулятор AVR
│ └── avr_gpio/ # GPIO устройство
├── flatbuffers/ # FlatBuffers схемы протокола
├── deps/ # Внешние зависимости
├── hmmmm_scripts/ # Python скрипты и утилиты
└── Makefile # Основной файл сборки
```
## Требования
- **GCC** с поддержкой C23
- **OpenSSL** (для SHA-512)
- **Git** (для клонирования зависимостей)
- **Python 3.12+** (опционально, для скриптов)
## Сборка
### 1. Инициализация зависимостей
```bash
# Клонирование и сборка внешних зависимостей
make deps
```
Это клонирует следующие зависимости в `deps/`:
- `tomlc99` — TOML парсер
- `ptQueue` — lock-free очередь
- `wsServer` — WebSocket сервер
- `flatcc` — FlatBuffers runtime для C
- `flatbuffers` — Google FlatBuffers (flatc компилятор)
### 2. Генерация FlatBuffers кода
```bash
# Генерация C заголовков из .fbs схем
make proto
# Генерация Python bindings (опционально)
make python-proto
```
### 3. Сборка эмулятора
```bash
# Основная сборка
make build
# Полная пересборка
make rebuild
# Очистка
make clean
```
После успешной сборки исполняемый файл будет находиться в `out/main.elf`.
## Быстрый старт
### 1. Подготовка конфигурации
Создайте TOML-файл с конфигурацией эмулятора:
```toml
# example.toml
[dev]
avr = "devices/avr_generic/AVRrc.toml"
[clock]
limiter = 16000000 # 16 MHz
[mem]
# Настройка памяти
```
### 2. Запуск эмулятора
```bash
./out/main.elf example.toml
```
Эмулятор запустит WebSocket сервер на порту **8181**.
### 3. Подключение клиента
Используйте Python-клиент для подключения:
```bash
cd ../hmmmm_client_py
poetry install
poetry run python -m model.client
```
## Конфигурация
### TOML-конфигурация устройства
Пример конфигурации AVR устройства:
```toml
[device]
type = "avr_generic"
libpath = "devices/avr_generic/AVRrc_build/device.so"
firmware = "test.bin"
[memory]
ps_size = 65536 # Program space
ds_size = 4096 # Data space
io_regs = 256 # I/O registers
gp_regs = 32 # General purpose registers
```
### Композиция устройств
Сложные устройства создаются через композицию:
```toml
[dev]
core = "avr_generic.toml"
gpio_a = "avr_gpio.toml"
[clock]
limiter = 16000000
[mem.intercept.PORTA]
base_at = "core"
point_at = "gpio_a"
addr = 0
seg = "reg_io"
```
## Протокол
Эмулятор использует **FlatBuffers over WebSocket** для обмена сообщениями.
### Типы сообщений
#### Клиент → Сервер:
- **AuthRequest** — аутентификация (SHA-512 токен + timestamp)
- **ExecCtrlMessage** — управление эмуляцией (старт/стоп/сброс)
- **MemReadRequest/MemWriteRequest** — операции с памятью
- **StreamRegRequest** — подписка на изменения памяти
#### Сервер → Клиент:
- **AuthResponse** — подтверждение аутентификации (seat_id)
- **ExecNotifyMessage** — уведомления о состоянии эмуляции
- **MemReadResponse** — данные из памяти
- **StreamDataPush** — потоковые изменения памяти
### Аутентификация
```python
import hashlib
import time
def generate_auth(access_token: str) -> bytes:
now = int(time.time())
prepared = f'{access_token}{now // 30}'
return hashlib.sha512(prepared.encode()).digest()
```
Токен действителен в течение 30 секунд.
## API устройств
Каждое устройство должно экспортировать интерфейс `device_lib_t`:
```c
typedef struct {
uint8_t devType;
device_public_context_t* (*init)(void*, char* errbuf);
uint8_t (*makeDeviceTick)(device_public_context_t* devInfo);
void* extendedHandlers;
void* (*parseSpecsFromConfig)(const conf_dev_t* devConf, char* errbuf);
void (*freeSpecs)(void* specs);
void (*freeDevMem)(device_mem_t* mem);
void (*fillSmartReadSpecs)(void* specs, smart_read_spec_t*, uint64_t);
void (*fillSmartWriteSpecs)(void* specs, smart_write_spec_t*, uint64_t);
} device_lib_t;
```
## Отладка
### Генерация compile_commands.json
Для интеграции с clangd:
```bash
./.gen_compile_commands.sh
```
### Логирование
Эмулятор выводит отладочную информацию в stdout. Для подробного логирования можно модифицировать `src/main.c`.
### GDB
```bash
gdb ./out/main.elf
(gdb) run example.toml
```
## Производительность
- **Тактовая частота** — до 16 MHz (эмулируемая)
- **WebSocket** — асинхронная отправка сообщений
- **Lock-free очереди** — эффективный межпоточный обмен
- **FlatBuffers** — сериализация с нулевым копированием
## Известные ограничения
- Абсолютные пути в конфигурационных файлах (требует замены на относительные)
- Хардкод токена аутентификации (требует выноса в ENV)
- Отсутствие graceful shutdown (обработка SIGINT/SIGTERM)
- Нет unit-тестов для основного кода
## Лицензия
Проект разработан @nikto_b. Все права защищены.
## Контакты
- Документация: https://about.hmmmm.nikto-b.ru/
- Автор: @nikto_b

2
deps/.gitignore vendored
View File

@@ -2,3 +2,5 @@ tomlc99
ptQueue ptQueue
tomlc99 tomlc99
wsServer wsServer
flatcc
flatbuffers

40
deps/Makefile vendored
View File

@@ -1,26 +1,52 @@
all: tomlc99.a ptQueue.a libws.a all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc/bin/flatcc flatbuffers/build/flatc
tomlc99: tomlc99:
@git clone https://github.com/cktan/tomlc99 @git clone https://github.com/cktan/tomlc99
tomlc99.a: tomlc99 tomlc99/libtoml.a: tomlc99
@make -C tomlc99 libtoml.a @make -C tomlc99 libtoml.a
ptQueue: flatcc:
@git clone git@192.168.8.167:nikto_b/ptQueue.git @git clone https://github.com/dvidelabs/flatcc
ptQueue.a: ptQueue flatcc_src: flatcc/bin/flatcc
flatcc/lib/libflatccrt.a: flatcc/bin/flatcc
flatcc/bin/flatcc: flatcc
@flatcc/scripts/initbuild.sh make
@flatcc/scripts/build.sh
ptQueue:
@git clone https://git.nikto-b.ru/nikto_b/ptQueue
ptQueue/out/ptQueue.a: ptQueue
@make -C ptQueue @make -C ptQueue
wsServer: wsServer:
@git clone https://github.com/Theldus/wsServer @git clone https://github.com/Theldus/wsServer
libws.a: wsServer wsServer/libws.a: wsServer
@make -C 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: clean:
@rm -rf wsServer ptQueue tomlc99 @rm -rf wsServer ptQueue tomlc99 flatcc flatbuffers
.PHONY: all clean .PHONY: all clean

1
deps/flatbuffers vendored Submodule

Submodule deps/flatbuffers added at e223d69b36

View File

@@ -0,0 +1,72 @@
#ifndef __INSTR_H__
#define __INSTR_H__
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "mem.h"
#ifndef OPCODE_WORDSIZE
#error OPCODE_WORDSIZE must be provided
// #define OPCODE_WORDSIZE 1
#endif
#if OPCODE_WORDSIZE == 1
typedef uint8_t opcode_t;
#elif OPCODE_WORDSIZE == 2
typedef uint16_t opcode_t;
#elif OPCODE_WORDSIZE == 3
typedef uint32_t opcode_t;
#elif OPCODE_WORDSIZE == 4
typedef uint32_t opcode_t;
#else
#error OPCODE_WORDSIZE must be one of 1,2,3,4
#endif
#ifndef PC_WORDSIZE
#error PC_WORDSIZE must be provided
// #define PC_WORDSIZE 1
#endif
#if PC_WORDSIZE == 1
typedef uint8_t prog_counter_t;
#elif PC_WORDSIZE == 2
typedef uint16_t prog_counter_t;
#elif PC_WORDSIZE == 3
typedef uint32_t prog_counter_t;
#elif PC_WORDSIZE == 4
typedef uint32_t prog_counter_t;
#else
#error PC_WORDSIZE must be one of 1,2,3,4
#endif
typedef uint8_t (*instr_h_func)(prog_counter_t*, device_mem_t*);
typedef struct
{
instr_h_func h;
opcode_t base;
opcode_t argsMask;
uint8_t opcodeLen;
} instruction_metadata_t;
instr_h_func* genInstrArray(char* errbuf);
void setOpcodeSizes(uint8_t* opcodeSizes);
opcode_t extractOpcode(device_mem_t* devMem, prog_counter_t programCounter);
#define AVRe 1
#define AVRxm 2
#define AVRxt 3
#define AVRrc 4
#ifndef ARCH
#define ARCH AVRe
#endif
#endif // ifndef __INSTR_H__

View File

@@ -0,0 +1,833 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "device.h"
#include "libhmmmm/mem.h"
#include "mem.h"
#include "mem_seg.h"
#include "runner.h"
#include "instr.h"
#include "addrs.h"
uint64_t memSegToGlobal(device_specs_t* spec, uint8_t seg, uint64_t localaddr)
{
// uint64_t offset = 0;
// for (uint8_t i = 0; i < seg; i++)
// {
// offset += spec->memSpecs[i]->len * spec->memSpecs[i]->wordLen;
// }
return spec->memSpecs[seg]->start + localaddr;
}
void freeDevMem(device_mem_t* devMem)
{
free(devMem->memsegShifts);
free(devMem->memsegSizes);
free(devMem->memsegCellSizes);
free(devMem->rawCells);
free(devMem->memreadCellAddrs);
free(devMem->memwriteCellAddrs);
free(devMem->memwriteCellSegments);
free(devMem->memwriteValues);
free(devMem->cells);
free(devMem->smartAddrReadHandlers);
free(devMem->smartAddrWriteHandlers);
free(devMem);
}
void freeDevSpec(void* _specs)
{
device_specs_t* specs = _specs;
for (uint8_t i = 0; i < specs->memSpecsCount; i++)
{
free(specs->memSpecs[i]);
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
}
device_mem_t* genDevMem(device_specs_t* devSpec, char* errbuf)
{
device_mem_t* devMem = (device_mem_t*)calloc(1, sizeof(device_mem_t));
if (devMem == NULL)
{
sprintf(errbuf, "unable to allocate dev memory struct");
return NULL;
}
uint64_t memTotalSize = 0;
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
{
size_t tmp = devSpec->memSpecs[i]->start + (devSpec->memSpecs[i]->len * devSpec->memSpecs[i]->wordLen);
if (memTotalSize < tmp)
{
memTotalSize = tmp;
}
}
void* rawCells = (void*)calloc(memTotalSize, sizeof(void*));
if (rawCells == NULL)
{
sprintf(errbuf, "unable to allocate raw memory buf %lu bytes", memTotalSize);
free(devMem);
return NULL;
}
for (size_t i = 0; i < memTotalSize; i++)
{
((uint8_t*)rawCells)[i] = 0;
}
devMem->memsegShifts = calloc(devSpec->memSpecsCount, sizeof(uint64_t));
if(devMem->memsegShifts == NULL)
{
sprintf(errbuf, "unable to allocate segment shift buffers");
free(rawCells);
free(devMem);
return NULL;
}
void** cells = calloc(devSpec->memSpecsCount, sizeof(void*));
if (cells == NULL)
{
sprintf(errbuf, "unable to allocate segment pointers");
free(rawCells);
free(devMem->memsegShifts);
free(devMem);
return NULL;
}
char** cellNames = calloc(devSpec->memSpecsCount, sizeof(char*));
if(cellNames == NULL)
{
sprintf(errbuf, "unable to allocate segment names map");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(cells);
return NULL;
}
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
{
printf("init seg \"%s\": %lu:%lu\n", devSpec->memSpecs[i]->name, devSpec->memSpecs[i]->start, devSpec->memSpecs[i]->len);
cells[i] = &(((uint8_t*)rawCells)[devSpec->memSpecs[i]->start]);
cellNames[i] = devSpec->memSpecs[i]->name;
// cells[i] = (void*)((size_t)rawCells + (size_t)devSpec->memSpecs[i]->start);
}
uint64_t* memreadCellAddrs = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint64_t));
if (memreadCellAddrs == NULL)
{
sprintf(errbuf, "unable to allocate read interception addrs");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(cells);
free(cellNames);
return NULL;
}
uint64_t* memwriteCellAddrs = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint64_t));
if (memwriteCellAddrs == NULL)
{
sprintf(errbuf, "unable to allocate write interception addrs");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
return NULL;
}
uint8_t* memwriteCellSegments = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint8_t));
if(memwriteCellSegments == NULL)
{
sprintf(errbuf, "unable to allocate write interception addrs");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
free(memwriteCellAddrs);
return NULL;
}
uint64_t* memwriteValues = calloc(MEM_ACCESS_INTERCEPT_BUF_SIZE, sizeof(uint64_t));
if(memwriteValues == NULL)
{
sprintf(errbuf, "unable to allocate write interception addrs");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
free(memwriteCellAddrs);
free(memwriteCellSegments);
return NULL;
}
uint64_t smartAddrReadMask = 0;
uint64_t smartAddrWriteMask = 0;
mem_h_read_handler* smartAddrReadHandlers = calloc(memTotalSize, sizeof(mem_h_read_handler));
if(memwriteCellSegments == NULL)
{
sprintf(errbuf, "unable to allocate read interception handlers");
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(cells);
free(cellNames);
free(memwriteCellAddrs);
free(memwriteCellSegments);
free(memwriteValues);
return NULL;
}
mem_h_write_handler* smartAddrWriteHandlers = calloc(memTotalSize, sizeof(mem_h_write_handler));
if (smartAddrWriteHandlers == NULL)
{
sprintf(errbuf, "unable to allocate write interception handlers");
free(smartAddrReadHandlers);
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(memwriteCellAddrs);
free(memwriteCellSegments);
free(memwriteValues);
free(cells);
free(cellNames);
return NULL;
}
uint64_t* memsegSizes = calloc(devSpec->memSpecsCount, sizeof(uint64_t));
if(memsegSizes == NULL)
{
sprintf(errbuf, "unable to allocate memseg sizes");
free(smartAddrWriteHandlers);
free(smartAddrReadHandlers);
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(memwriteCellAddrs);
free(memwriteCellSegments);
free(memwriteValues);
free(cells);
free(cellNames);
return NULL;
}
uint8_t* memsegCellSizes = calloc(devSpec->memSpecsCount, sizeof(uint8_t));
if(memsegCellSizes == NULL)
{
sprintf(errbuf, "unable to allocate memseg cell sizes");
free(memsegSizes);
free(smartAddrWriteHandlers);
free(smartAddrReadHandlers);
free(devMem->memsegShifts);
free(devMem);
free(rawCells);
free(memreadCellAddrs);
free(memwriteCellAddrs);
free(memwriteCellSegments);
free(memwriteValues);
free(cells);
free(cellNames);
return NULL;
}
// for(uint64_t i = 0; i < memTotalSize; i++)
// {
// smartAddrReadHandlers[i].func = NULL;
// smartAddrReadHandlers[i].ident = 0;
// smartAddrWriteHandlers[i].func = NULL;
// smartAddrWriteHandlers[i].ident = 0;
// }
// for(uint64_t i = 0; i < memTotalSize; i++)
// {
// if((i & smartAddrReadMask) == smartAddrReadMask)
// {
// smartAddrReadHandlers[i].func = NULL;
// }
// }
if (devSpec->smartReadSpecsCount > 0)
{
for(uint64_t i = 0; i < devSpec->smartReadSpecsCount; i++)
{
smart_read_spec_t t = devSpec->smartReadSpecs[i];
uint64_t addr;
if (t.addrType == SMART_ADDR_TYPE_GLOBAL)
{
addr = t.addr;
}
else
{
addr = memSegToGlobal(devSpec, t.segno, t.localAddr);
}
smartAddrReadHandlers[addr].func = *t.handler;
smartAddrReadHandlers[addr].ident = t.ident;
smartAddrReadMask |= addr;
}
}
if (devSpec->smartWriteSpecsCount > 0)
{
for(uint64_t i = 0; i < devSpec->smartWriteSpecsCount; i++)
{
smart_write_spec_t t = devSpec->smartWriteSpecs[i];
uint64_t addr;
if (t.addrType == SMART_ADDR_TYPE_GLOBAL)
{
addr = t.addr;
}
else
{
addr = memSegToGlobal(devSpec, t.segno, t.localAddr);
}
smartAddrWriteHandlers[addr].func = *t.handler;
smartAddrWriteHandlers[addr].ident = t.ident;
smartAddrWriteMask |= t.addr;
}
}
devMem->cells = cells;
devMem->rawCells = rawCells;
devMem->memreadCellAddrs = memreadCellAddrs;
devMem->memwriteCellAddrs = memwriteCellAddrs;
devMem->memwriteCellSegments = memwriteCellSegments;
devMem->memwriteValues = memwriteValues;
devMem->memreadLen = 0;
devMem->memwriteLen = 0;
devMem->smartAddrReadMask = smartAddrReadMask;
devMem->smartAddrReadHandlers = smartAddrReadHandlers;
devMem->smartAddrWriteMask = smartAddrWriteMask;
devMem->smartAddrWriteHandlers = smartAddrWriteHandlers;
devMem->memsegNames = cellNames;
devMem->memsegSizes = memsegSizes;
devMem->memsegCellSizes = memsegCellSizes;
memseg_metadata_t requiredSegments[] = MEMSEG_DEFINES;
for (uint8_t i = 0; i < devSpec->memSpecsCount; i++)
{
for(uint8_t j = 0; j < sizeof(requiredSegments) / sizeof(memseg_metadata_t); j++)
{
const memseg_metadata_t seg_def = requiredSegments[j];
if(strcmp(seg_def.name, devSpec->memSpecs[i]->name) == 0)
{
const uint8_t seg_id = seg_def.seg_id;
devMem->memsegShifts[seg_id] = memSegToGlobal(devSpec, i, 0);
devMem->memsegSizes[seg_id] = devSpec->memSpecs[i]->len;
devMem->memsegCellSizes[seg_id] = devSpec->memSpecs[i]->wordLen;
printf("set mem segment %d meta: +%lu/%lu[%d] \n", seg_id, devMem->memsegShifts[seg_id], devMem->memsegSizes[seg_id], devMem->memsegCellSizes[seg_id]);
}
}
}
// for(uint8_t i = 0; i < devSpec->memSpecsCount; i++)
// {
// devMem->memsegShifts[i] = memSegToGlobal(devSpec, i, 0);
// devMem->memsegSizes[i] = devSpec->memSpecs[i]->len;
// }
setOpcodeSizes((uint8_t*)(devMem->cells[MEMDATA_OPSIZE]));
return devMem;
}
uint8_t makeDeviceTick(device_public_context_t* devContext)
{
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
prog_counter_t _pc;
READ_MEM(_pc, devInfo->deviceMem, MEMSEG_PC_SEG_NUM, MEMSEG_PC_ADDR, prog_counter_t)
// printf("old PC is %d\n", _pc);
if(_pc >= devInfo->deviceMem->memsegSizes[MEMSEG_PS])
{
_pc = 0;
}
uint8_t ticks = makeTick(&_pc, devInfo->instr, devInfo->deviceMem);
WRITE_MEM(devInfo->deviceMem, MEMSEG_PC_SEG_NUM, MEMSEG_PC_ADDR, prog_counter_t, _pc);
// printf("new PC is %d\n", _pc);
return ticks;
}
device_info_t* initSpecs(device_specs_t* specs, char* errbuf)
{
device_info_t* devInfo = calloc(1, sizeof(device_info_t));
if (devInfo == NULL)
{
sprintf(errbuf, "unable to allocate dev info");
return NULL;
}
char genErrBuf[200];
device_mem_t* devMem = genDevMem(specs, genErrBuf);
if (devMem == NULL)
{
sprintf(errbuf, "unable to generate device memory: %s", genErrBuf);
free(devInfo);
return NULL;
}
devInfo->pc = &((prog_counter_t*)(devMem->cells[MEMSEG_PC_SEG_NUM]))[MEMSEG_PC_ADDR];
*(devInfo->pc) = 0;
devInfo->specs = specs;
devInfo->deviceMem = devMem;
instr_h_func* instrs = genInstrArray(genErrBuf);
if (instrs == NULL)
{
sprintf(errbuf, "unable to initialize instructions: %s", genErrBuf);
freeDevMem(devInfo->deviceMem);
free(devInfo);
return NULL;
}
devInfo->instr = instrs;
return devInfo;
}
void error(const char* err, char* errbuf)
{
strcpy(errbuf, err);
}
device_specs_t* parseSpecsFromConfig(const conf_dev_t* devConf, char* errbuf)
{
conf_mem_seg_t** segments = devConf->memConf->memSegConfs;
device_specs_t* specs = calloc(1, sizeof(device_specs_t));
if (specs == NULL)
{
error("unable to allocate mem specs struct", errbuf);
return NULL;
}
memseg_metadata_t requiredSegments[] = MEMSEG_DEFINES;
uint8_t* requiredSegmentsFoundMap = (uint8_t*)calloc(sizeof(requiredSegments) / sizeof(memseg_metadata_t), sizeof(uint8_t));
if(requiredSegmentsFoundMap == NULL)
{
error("unable to allocate found map", errbuf);
free(specs);
return NULL;
}
uint8_t specCount = 0;
specs->executableSegmentsCount = 0;
while (segments[specCount] != NULL)
{
if(segments[specCount]->isExecutable)
{
specs->executableSegmentsCount++;
}
specCount++;
}
const uint8_t providedSpecCount = specCount;
if(specCount < sizeof(requiredSegments)/sizeof(memseg_metadata_t))
{
specCount = sizeof(requiredSegments)/sizeof(memseg_metadata_t);
}
specs->memSpecsCount = specCount;
specs->memSpecs = calloc(specCount, sizeof(memseg_spec_t*));
if (specs->memSpecs == NULL)
{
free(specs);
free(requiredSegmentsFoundMap);
error("unable to allocate mem segment specs", errbuf);
return NULL;
}
specs->executableSegments = calloc(specs->executableSegmentsCount, sizeof(uint8_t));
if (specs->executableSegments == NULL)
{
error("unable to allocate mem executable specs", errbuf);
free(specs->memSpecs);
free(specs);
free(requiredSegmentsFoundMap);
return NULL;
}
for (uint8_t i = 0; i < specCount; i++)
{
memseg_spec_t* spec = (memseg_spec_t*)calloc(1, sizeof(memseg_spec_t));
if (spec == NULL)
{
sprintf(errbuf, "unable to allocate spec %d", i);
for (uint8_t j = 0; j < i; j++)
{
free(specs->memSpecs[j]);
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
free(requiredSegmentsFoundMap);
return NULL;
}
spec->name = NULL;
specs->memSpecs[i] = spec;
}
uint8_t executableSegmentsFound = 0;
for (uint8_t i = 0; i < providedSpecCount; i++)
{
uint8_t specNum = 0xFF;
uint8_t is_error = 0;
uint8_t seek_found = 0;
for(uint8_t j = 0; j < sizeof(requiredSegments) / sizeof(memseg_metadata_t); j++)
{
const memseg_metadata_t seg_def = requiredSegments[j];
if(strcmp(seg_def.name, segments[i]->name) == 0)
{
printf("found seg \"%s\"\n", seg_def.name);
const uint8_t is_found = requiredSegmentsFoundMap[j];
if(is_found)
{
is_error = 1;
sprintf(errbuf, "duplicate segment %s", seg_def.name);
}
else
{
if(seg_def.is_executable == segments[i]->isExecutable || segments[i]->isExecutable)
{
printf("seg executable req satisfyed (%d)\n", seg_def.is_executable);
requiredSegmentsFoundMap[j] = 1;
specNum = seg_def.seg_id;
seek_found = 1;
}
else
{
is_error = 1;
sprintf(errbuf, "segment %s must be executable", seg_def.name);
}
}
break;
}
}
if(seek_found == 0)
{
is_error = 1;
sprintf(errbuf, "unsupported memory segment: %s", segments[i]->name);
}
if(is_error)
{
freeDevSpec(specs);
free(requiredSegmentsFoundMap);
return NULL;
}
specs->memSpecs[specNum]->name = calloc(strlen(segments[i]->name) + 1, sizeof(char));
if(specs->memSpecs[specNum]->name == NULL)
{
sprintf(errbuf, "unable to allocate spec %d name", i);
freeDevSpec(specs);
free(requiredSegmentsFoundMap);
return NULL;
}
strcpy(specs->memSpecs[specNum]->name, segments[i]->name);
specs->memSpecs[specNum]->start = segments[i]->start;
specs->memSpecs[specNum]->len = segments[i]->len;
specs->memSpecs[specNum]->wordLen = segments[i]->wordLen;
printf("set segment %s :%lu->%lu/%d\n", segments[i]->name, segments[i]->start, segments[i]->len, segments[i]->wordLen);
if(segments[i]->isExecutable)
{
specs->executableSegments[executableSegmentsFound] = specNum;
executableSegmentsFound++;
}
}
for(uint8_t i = 0; i < sizeof(requiredSegments)/sizeof(memseg_metadata_t); i++)
{
if(requiredSegmentsFoundMap[i] == 0)
{
const memseg_metadata_t seg_def = requiredSegments[i];
printf("found segment that not specified: \"%s\"\n", seg_def.name);
specs->memSpecs[seg_def.seg_id]->start = seg_def.default_addr;
specs->memSpecs[seg_def.seg_id]->len = seg_def.default_size;
specs->memSpecs[seg_def.seg_id]->wordLen = seg_def.word_len;
specs->memSpecs[seg_def.seg_id]->name = calloc(strlen(seg_def.name) + 1, sizeof(char));
if(specs->memSpecs[seg_def.seg_id]->name == NULL)
{
sprintf(errbuf, "unable to allocate spec %d name", i);
freeDevSpec(specs);
free(requiredSegmentsFoundMap);
return NULL;
}
strcpy(specs->memSpecs[seg_def.seg_id]->name, seg_def.name);
if(seg_def.is_executable)
{
specs->executableSegments[executableSegmentsFound] = seg_def.seg_id;
executableSegmentsFound++;
}
}
}
if(executableSegmentsFound < specs->executableSegmentsCount)
{
sprintf(errbuf, "Not all executable segments found");
freeDevSpec(specs);
free(requiredSegmentsFoundMap);
return NULL;
}
printf("set all segments\n");
#ifdef MEMSEG_PC_SEG_NUM
#ifndef MEMSEG_PC_ADDR
specs->pcAddr = memSegToGlobal(specs, MEMSEG_PC_SEG_NUM, 0);
#else
specs->pcAddr = memSegToGlobal(specs, MEMSEG_PC_SEG_NUM, MEMSEG_PC_ADDR);
#endif
#endif
free(requiredSegmentsFoundMap);
printf("parse specs done\n");
return specs;
}
void freeDevSpecs(device_specs_t* specs)
{
for(size_t k = 0; k < specs->memSpecsCount; k++)
{
if(specs->memSpecs[k]->name != NULL)
{
free(specs->memSpecs[k]->name);
}
free(specs->memSpecs[k]);
}
free(specs->executableSegments);
free(specs->memSpecs);
free(specs);
}
void fillSmartReadSpecs(device_specs_t* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount)
{
specs->smartReadSpecs = smartReadSpecs;
specs->smartReadSpecsCount = smartReadSpecsCount;
}
void fillSmartWriteSpecs(device_specs_t* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount)
{
specs->smartWriteSpecs = smartWriteSpecs;
specs->smartWriteSpecsCount = smartWriteSpecsCount;
}
device_public_context_t* initDefault(smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount, char* errbuf)
{
return NULL;
//TODO
device_specs_t* specs = calloc(1, sizeof(device_specs_t));
if (specs == NULL)
{
return NULL;
}
specs->memSpecsCount = 4;
specs->memSpecs = calloc(specs->memSpecsCount, sizeof(memseg_spec_t*));
if (specs->memSpecs == NULL)
{
sprintf(errbuf, "unable to allocate default mem segment specs");
free(specs);
return NULL;
}
specs->executableSegmentsCount = 1;
specs->executableSegments = calloc(1, sizeof(uint8_t));
if (specs->executableSegments == NULL)
{
sprintf(errbuf, "unable to allocate default executable segments");
free(specs->memSpecs);
free(specs);
return NULL;
}
specs->executableSegments[0] = 0;
for (uint8_t i = 0; i < specs->memSpecsCount; i++)
{
specs->memSpecs[i] = calloc(1, sizeof(memseg_spec_t));
if (specs->memSpecs[i] == NULL)
{
sprintf(errbuf, "unable to allocate default mem seg spec %u", i);
for (uint8_t j = 0; j < i; j++)
{
free(specs->memSpecs[j]);
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
return NULL;
}
}
specs->smartReadSpecs = smartReadSpecs;
specs->smartReadSpecsCount = smartReadSpecsCount;
specs->smartWriteSpecs = smartWriteSpecs;
specs->smartWriteSpecsCount = smartWriteSpecsCount;
specs->memSpecs[0]->len = 1024;
specs->memSpecs[0]->start = 0;
specs->memSpecs[0]->wordLen = OPCODE_WORDSIZE;
specs->memSpecs[1]->len = 32;
specs->memSpecs[1]->start = (1024 * OPCODE_WORDSIZE);
specs->memSpecs[1]->wordLen = GP_REG_CELL_WORDS;
specs->memSpecs[2]->len = 0xFF;
specs->memSpecs[2]->start = (1024 * OPCODE_WORDSIZE) + (32 * GP_REG_CELL_WORDS);
specs->memSpecs[2]->wordLen = IO_REG_CELL_WORDS;
specs->memSpecs[3]->len = 0xFFFF;
specs->memSpecs[3]->start = (1024 * OPCODE_WORDSIZE) + (32 * GP_REG_CELL_WORDS) + (0xFF * IO_REG_CELL_WORDS);
specs->memSpecs[3]->wordLen = RAM_CELL_WORDS;
char initErrbuf[200];
device_public_context_t* ret = init(specs, initErrbuf);
if (ret == NULL)
{
sprintf(errbuf, "unable to init default: %s", initErrbuf);
freeDevSpec(specs);
return NULL;
}
free(specs->memSpecs);
free(specs->executableSegments);
free(specs);
return ret;
}
device_public_context_t* init(device_specs_t* specs, char* errbuf)
{
device_public_context_t* pubDevContext = calloc(1, sizeof(device_public_context_t));
if (pubDevContext == NULL)
{
sprintf(errbuf, "unable to allocate public context");
return NULL;
}
char initErrbuf[200];
device_info_t* devInfo = initSpecs(specs, initErrbuf);
if (devInfo == NULL)
{
sprintf(errbuf, "unable to init specs: %s", initErrbuf);
free(pubDevContext);
return NULL;
}
pubDevContext->deviceInfo = (void*)devInfo;
pubDevContext->deviceMem = devInfo->deviceMem;
return pubDevContext;
}
uint8_t pubDeviceType()
{
return DEVICE_TYPE;
}
size_t pubExtractPcounter(device_public_context_t* devContext)
{
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
return (size_t)(*devInfo->pc);
}
void* pubExtractPcounterPtr(device_public_context_t* devContext)
{
device_info_t* devInfo = (device_info_t*)devContext->deviceInfo;
return (void*)(devInfo->pc);
}
uint8_t pubExtractPcounterSizeWords()
{
return sizeof(prog_counter_t);
}
void reset (device_specs_t* specs, device_public_context_t* devInfo)
{
// printf("reset device\n");
// uint8_t fuck = 0;
for(size_t i = 0; i < specs->memSpecsCount; i++)
{
if(i != MEMDATA_OPSIZE)
{
const memseg_spec_t* spec = specs->memSpecs[i];
for(size_t j = 0; j < spec->len; j++)
{
((uint8_t*)devInfo->deviceMem->cells[i])[j] = 0;
// fuck++;
}
}
}
devInfo->deviceMem->memwriteLen = 0;
devInfo->deviceMem->memreadLen = 0;
}

0
flamegraph.svg Normal file
View File

11
flatbuffers/auth/auth.fbs Normal file
View File

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

12
flatbuffers/compacted.fbs Normal file
View File

@@ -0,0 +1,12 @@
namespace hmmmm;
// A single serialized ServerMessage frame packed into a byte vector.
table CompactedFrame {
data: [ubyte];
}
// A batch of ServerMessage frames sent as a single WebSocket frame to reduce
// per-frame syscall overhead when message rate exceeds the batching threshold.
table CompactedMessage {
frames: [CompactedFrame];
}

View File

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

View File

@@ -0,0 +1,25 @@
include "device.fbs";
include "mem_config.fbs";
include "clock.fbs";
namespace hmmmm.config;
// Top-level emulation configuration sent by the client via ConfigCtrlMessage.
// Contains the root composite device and simulation parameters.
table EmulationConfig {
sim_rate_limit: uint64; // max virtual-seconds per real-second (0 = unlimited)
root_id: string; // ID of the root compose device
root: ComposeDeviceConfig; // the root device tree
mem_setup: [MemSetup]; // firmware / memory init instructions
}
// Legacy: flat system configuration for standalone config files.
table SystemConfig {
devices: [DeviceEntry];
clock: ClockConfig;
projections: [Projection];
intercepts: [Intercept];
mem_setup: [MemSetup];
}
root_type EmulationConfig;

View File

@@ -0,0 +1,50 @@
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];
}
// A composite device that contains other devices.
table ComposeDeviceConfig {
devices: [DeviceEntry]; // child devices (base or compose)
projections: [Projection]; // memory projections between children
intercepts: [Intercept]; // memory intercepts between children
}
// A device can be either a leaf (base) or a container (compose).
union DeviceConfig {
BaseDeviceConfig,
ComposeDeviceConfig,
}
// Override for one memory segment within a child device.
// Only fields that need changing from the base config must be set.
table MemSegOverride {
segment: string;
addr: uint32;
len: uint32;
word_len: uint8;
executable: bool;
}
// One device entry in a device tree.
table DeviceEntry {
// Local identifier, unique within this composite (e.g. "core", "gpio_a").
id: string;
// Inline device configuration — either base or compose.
config: DeviceConfig;
//clock: DeviceClockConfig;
clock_divider: uint64 = 1;
overrides: [MemSegOverride];
}
// Root type when serialising a standalone device config to a binary file.
root_type BaseDeviceConfig;

View File

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

View File

@@ -0,0 +1,10 @@
include "config/config.fbs";
namespace hmmmm.ctrl.config_ctrl;
// Client → Server: load a new emulation configuration.
// Only allowed when the emulator is in STILL state.
// Completely replaces any previously loaded configuration.
table ConfigCtrlMessage {
config: hmmmm.config.EmulationConfig;
}

View File

@@ -0,0 +1,43 @@
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. ["sys", "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];
}
// Sent when a config load request fails.
table ConfigLoadError {
message: string;
}
union ConfigNotifPayload {
DeviceConfigUpdateNotif,
DeviceIdMappingNotif,
ConfigLoadError,
}
table ConfigNotifMessage {
tclk: uint64;
payload: ConfigNotifPayload;
}

View File

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

View File

@@ -0,0 +1,15 @@
namespace hmmmm.ctrl.exec;
enum ExecPrompt: byte {
_invalid = 0,
start = 1,
pause = 2,
resume = 3,
stop = 4,
reset = 5,
}
table ExecCtrlMessage {
prompt: ExecPrompt;
tick_count: uint64 = 0; // 0 = run indefinitely, >0 = auto-pause after N ticks
}

View File

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

View File

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

View File

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

View File

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

View File

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

46
flatbuffers/mem/mem.fbs Normal file
View File

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

44
flatbuffers/proto.fbs Normal file
View File

@@ -0,0 +1,44 @@
include "auth/auth.fbs";
include "control/control.fbs";
include "stream/stream.fbs";
include "mem/mem.fbs";
include "compacted.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,
CompactedMessage,
}
// 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;

View File

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

View File

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

View File

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

120
flatbuffers_example_main.c Normal file
View File

@@ -0,0 +1,120 @@
#include <stdio.h>
#include <stdlib.h>
#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;
}

View File

@@ -1,60 +0,0 @@
[dev]
core = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc.toml"
# gpio_a = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_gpio/gpio.toml"
[config.core.mem.ps]
len = 128
[clock]
limiter = 16000000
[clock.core]
src = ""
divider = 1
[clock.gpio_a]
src = "core"
divider = 2
# [mem.projection.gpio_a.ext_reg_io]
# base_at = "core"
# base_seg = "reg_io"
# projection_shift = 0
[mem.intercept.PORTA]
base_at = "core"
point_at = "gpio_a"
addr = 0
seg = "reg_io"
[mem.intercept.PINA]
base_at = "core"
point_at = "gpio_a"
base_addr = 1
point_addr = 1
base_seg = "reg_io"
point_seg = "ext_reg_io"
[mem.setup.core.ps]
filepath = "/home/nikto_b/Documents/avr_selftests/test.bin"
[mem.setup.core.ds]
filepath = "/dev/urandom"
overflow_behaviour = "ignore"
# - почему си (адресная арифметика, отсутствие мешающих фичей языка, широкое распространение среди разработчеков-железячников)
# - подход к модульности (всё базовое устройство, запускаем комбинированные устройства из базовых и комбинированных)
# - структура базового модуля эмулятора (какие есть варианты полей конфига)
# - структура комбинированного модуля эмулятора (какие есть варианты полей конфига)
# - структура комбинированного модуля эмулятора (какие конфиг вырождается в эмулятор)
# - процесс инициализации модуля (6 этапов инициализации)
# - процесс исполнения модуля (чисто дергаем "makeTick" у каждой dlib'ы)
# - реализация перехватчиков доступа (пример с sbi, диаграмма с тестовым перехватчиком)
# - реализация проецирования памяти (диаграммы с колбасами)
# - тестовая прошивка (селфтест сложения-вычитания)
# - пример исполнения (?)

1
hmmmm_scripts Submodule

Submodule hmmmm_scripts added at 6a52cfbb3c

23
inc/base_device.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef __BASE_DEVICE_H__
#define __BASE_DEVICE_H__
#include "hmmmm.h"
#include <stdint.h>
typedef struct {
void* specs;
device_lib_t* lib;
device_public_context_t* ctx;
uint64_t clockCycleLimit;
uint64_t clockCycleCounter;
uint64_t clockDivider;
} device_handle_t;
device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf);
device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf);
conf_dev_t* openBaseDeviceConfig(const char* configPath, char* errbuf);
void closeBaseDevice(device_handle_t* devHandle);
#endif // ifndef __BASE_DEVICE_H__

View File

@@ -2,25 +2,17 @@
#define __CLIENT_H__ #define __CLIENT_H__
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include "sized_ptr.h"
#include "wsServer/include/ws.h" #include "wsServer/include/ws.h"
#include "ptQueue/inc/ptQueue.h" #include "ptQueue/inc/spsc.h"
typedef struct { typedef struct {
ws_cli_conn_t clientId; ws_cli_conn_t clientId;
uint64_t seatId;
uint8_t isAuthed; uint8_t isAuthed;
ptQueue* incomeQ; queue_spsc_t* incomeQ;
// ptQueue* outcomeQ; queue_spsc_t* outcomeQ;
uint64_t connectedAt; uint64_t connectedAt;
uint32_t streamRegIterator; uint32_t streamRegIterator;
uint64_t orphanedAt;
uint64_t orphanedDeadTimeout;
SizedPtr* fallbackOutcomeQ;
size_t fallbackOutcomeQPadding;
} ClientContext; } ClientContext;
#endif //ifndef __CLIENT_H__ #endif //ifndef __CLIENT_H__

40
inc/compose_device.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef __COMPOSE_DEVICE_H__
#define __COMPOSE_DEVICE_H__
#include "base_device.h"
typedef struct {
uint64_t limiter;
} clock_conf_t;
typedef struct {
char** baseAt;
char* baseSeg;
char** target;
uint64_t projectionShift;
} projection_conf_t;
typedef struct {
char** baseAt;
char** pointAt;
uint64_t addr;
char* seg;
} intercept_conf_t;
typedef struct {
clock_conf_t clockConf;
conf_dev_t** baseConfigs;
projection_conf_t** projections;
intercept_conf_t** intercepts;
} compose_dev_conf_t;
typedef struct {
device_handle_t** devHandlers;
} compose_dev_handle_t;
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf);
#endif // ifndef __COMPOSE_DEVICE_H__

12
inc/config.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __HMMMM_CONFIG_H__
#define __HMMMM_CONFIG_H__
#include "pub/libhmmmm/config.h"
// void freeMemSegConf(conf_mem_seg_t* memSegConf);
// void freeMemConf(conf_mem_t* memConf);
void freeConf(conf_dev_t* conf);
void freeComposeId(char** id);
// uint8_t compareComposeId(char** idA, char** idB);
#endif

View File

@@ -2,43 +2,60 @@
#define __CONTEXT_H__ #define __CONTEXT_H__
#include <pthread.h> #include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include "ptQueue/inc/ptQueue.h" #include "ptQueue/inc/spsc.h"
#include "wsServer/include/ws.h" #include "wsServer/include/ws.h"
#include "ptQueue/inc/mpsc.h"
#include "linkedlist.h" #include "linkedlist.h"
#include "sized_ptr.h" #include "sized_ptr.h"
#include "streamed.h" #include "streamed.h"
#include "flatcc/flatcc_builder.h"
typedef struct { typedef struct {
SizedPtr* bufs; SizedPtr* bufs;
uint8_t buffersCount; uint8_t buffersCount;
_Atomic (uint8_t) readRequestIdx; CACHE_ALIGN _Atomic (uint8_t) readRequestIdx;
_Atomic (uint8_t) currWritingIdx; CACHE_ALIGN _Atomic (uint8_t) currWritingIdx;
} OutgoingBuffers; } OutgoingBuffers;
typedef struct { typedef struct {
uint8_t* resetRequest;
uint8_t* emulState; uint8_t* emulState;
uint64_t* clockCounter; uint64_t* clockCounter;
uint64_t* tickTarget; // 0 = run indefinitely, >0 = auto-pause at this tick
LinkedListEntry** clientsHead; LinkedListEntry** clientsHead;
uint8_t* utilizedFlag; uint8_t* utilizedFlag;
OutgoingBuffers* outBufs; OutgoingBuffers* outBufs;
DeviceSegStreamReg** deviceStreamRegs; DeviceSegStreamReg** deviceStreamRegs;
uint8_t** devicesMem; uint8_t** devicesMem;
size_t** devicesMemSegAddrs;
size_t devicesCount; size_t devicesCount;
void** deviceHandles; // device_handle_t** — dynamically loaded devices
void* interceptCtxs; // contiguous intercept_ctx_t array (freed as single block)
size_t interceptCtxCount;
flatcc_builder_t stream_builder;
uint64_t simRateLimit;
// Cached DeviceIdMappingNotif broadcast message (sent to newly authed clients)
uint8_t* deviceIdMappingMsg;
size_t deviceIdMappingMsgLen;
queue_mpsc_t* inMsgQueue;
queue_spsc_t* outMsgQueue;
} EmulContext; } EmulContext;
typedef struct { typedef struct {
pthread_mutex_t registerMutex; pthread_mutex_t registerMutex;
ptQueue* regQueue; queue_spsc_t* regQueue;
queue_mpsc_t* inMsgQueue;
uint8_t* accessToken; uint8_t* accessToken;
EmulContext* emulContext; EmulContext* emulContext;
_Atomic (uint64_t) seatCounter;
} ServerContext; } ServerContext;

0
inc/executor/executor.h Normal file
View File

34
inc/hmmmm.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef __HMMMMM__
#define __HMMMMM__
#include "pub/libhmmmm/device.h"
#include "config.h"
typedef struct {
void* (*extractPcounterPtr)(device_public_context_t* devContext);
size_t (*extractPcounter)(device_public_context_t* devContext);
size_t (*extractOpcode)(device_mem_t* devMem, size_t _programCounter);
uint8_t (*extractPcounterSizeWords)();
} instruction_simul_handlers_t;
typedef struct {
uint8_t devType;
device_public_context_t* (*init)(void*, char* errbuf);
uint8_t (*makeDeviceTick)(device_public_context_t* devInfo);
void* extendedHandlers;
device_public_context_t* devContext;
void* _dlhandl;
void* (*parseSpecsFromConfig)(const conf_dev_t* devConf, char* errbuf);
void (*freeSpecs)(void* specs);
void (*freeDevMem)(device_mem_t* mem);
void (*fillSmartReadSpecs)(void* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount);
void (*fillSmartWriteSpecs)(void* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount);
void (*reset)(void* specs, device_public_context_t* devInfo);
} device_lib_t;
device_lib_t* loadDeviceLib(const char *libpath, char* errbuf);
#endif // ifndef __HMMMMM__

View File

@@ -4,8 +4,9 @@
#include <stdint.h> #include <stdint.h>
#include "context.h" #include "context.h"
#include "ptQueue/inc/spsc.h"
void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen); void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen);
void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ClientContext* client, uint8_t* msg, size_t msgLen); void dispatchOutgoingMessage(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen);
#endif #endif

View File

@@ -16,9 +16,6 @@
#define CTRL_TYPE_EXEC 0b0001 #define CTRL_TYPE_EXEC 0b0001
#define CTRL_TYPE_NOTIF_STATE 0b0010 #define CTRL_TYPE_NOTIF_STATE 0b0010
#define CTRL_TYPE_LIST_ORPHANED 0b1001
#define CTRL_TYPE_LOAD_FAILED 0b1010
#define CTRL_TYPE_SETUP_CONNECTION 0b1011
#define NOTIF_TYPE_EXEC 0b0000 #define NOTIF_TYPE_EXEC 0b0000

View File

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

View File

@@ -1,10 +1,16 @@
#ifndef __PROTO_HANDLERS_CONTROL_H__ #ifndef __PROTO_HANDLERS_CONTROL_H__
#define __PROTO_HANDLERS_CONTROL_H__ #define __PROTO_HANDLERS_CONTROL_H__
#include "proto/msg.h" #include <stdint.h>
#include "client.h"
#include "context.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "control_reader.h"
void handleIncomingControlMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext); void handleIncomingCtrlMessage(
hmmmm_ctrl_CtrlClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_CONTROL_H__
#endif //ifndef __PROTO_HANDLERS_CONTROL_H__

View File

@@ -1,10 +1,16 @@
#ifndef __PROTO_HANDLERS_MEM_H__ #ifndef __PROTO_HANDLERS_MEM_H__
#define __PROTO_HANDLERS_MEM_H__ #define __PROTO_HANDLERS_MEM_H__
#include "proto/msg.h" #include <stdint.h>
#include "context.h"
#include "client.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "mem_reader.h"
void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext); void handleIncomingMemMessage(
hmmmm_mem_MemClientMessage_table_t msg,
#endif //ifndef __PROTO_HANDLERS_MEM_H__ uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_MEM_H__

View File

@@ -1,12 +1,19 @@
#ifndef __PROTO_HANDLERS_STREAM_H__ #ifndef __PROTO_HANDLERS_STREAM_H__
#define __PROTO_HANDLERS_STREAM_H__ #define __PROTO_HANDLERS_STREAM_H__
#include "proto/msg.h" #include <stdint.h>
#include "context.h"
#include "client.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "stream_reader.h"
// static void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId);
void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId);
void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx); void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx);
void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext);
#endif //ifndef __PROTO_HANDLERS_STREAM_H__ void handleIncomingStreamMessage(
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_STREAM_H__

View File

@@ -1,29 +1,82 @@
#ifndef __PROTO_MSG_H__ #ifndef __PROTO_MSG_H__
#define __PROTO_MSG_H__ #define __PROTO_MSG_H__
#include "wsServer/include/ws.h"
#include <stdlib.h>
#include "client.h" #include "client.h"
#include "wsServer/include/ws.h"
#include <stdint.h>
#include <stdlib.h>
#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;
ClientContext* ctx;
} FbMessage;
typedef struct { typedef struct {
ClientContext* client; ws_cli_conn_t clientIdx;
uint8_t* msg; uint8_t* msg;
size_t msgLen; size_t msgLen;
uint64_t dispatch_us; // microsecond timestamp when queued (for batching window)
} OutgoingMessage; } OutgoingMessage;
typedef struct { // Build a ServerMessage{AuthResponse} frame. Returns a malloc'd buffer;
uint64_t nonce; // ownership is transferred to the caller (dispatchOutgoingMessage will free it).
uint8_t packetType; uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out);
uint8_t payloadHeader;
const void* payload;
size_t payloadLen;
} BaseMessage;
BaseMessage* parseMessage(const uint8_t* bytes, size_t size); // Build a ServerMessage{CtrlServerMessage{ExecNotifyMessage}} frame.
void fillHead(uint64_t nonce, uint8_t packetType, uint8_t payloadHeader, uint8_t* outmsg); uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out);
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);
uint8_t* createClientSetup(uint64_t nonce, ClientContext* ctx, size_t* lenOut);
#endif //ifndef __PROTO_MSG_H__ // 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);
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{DeviceIdMappingNotif}}} frame.
// paths: array of NULL-terminated string arrays; path_lens: component count per path.
// seg_names[i]: array of seg_counts[i] segment name strings for device i.
uint8_t* fb_build_config_device_id_mapping(
uint64_t nonce,
char** paths[], size_t path_lens[],
char* seg_names[][64], size_t seg_counts[],
size_t device_count, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{ConfigLoadError}}} frame.
uint8_t* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out);
// Build a ServerMessage{CompactedMessage{frames=[...]}} frame.
// msgs / msg_lens: array of `count` pre-built ServerMessage byte buffers to pack.
// The caller retains ownership of the source buffers; this function copies their bytes.
// Nonce is set to UINT64_MAX (no-nonce / fire-and-forget).
uint8_t* fb_build_compacted_message(
uint8_t* const* msgs, const size_t* msg_lens, size_t count, size_t* len_out);
#endif // __PROTO_MSG_H__

29
inc/pub/libhmmmm/config.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef __HMMMM_PUB_LIB_CONFIG_H__
#define __HMMMM_PUB_LIB_CONFIG_H__
#include <unistd.h>
#include <stdint.h>
typedef struct {
char* name;
size_t start;
size_t len;
uint8_t wordLen;
uint8_t isExecutable;
} conf_mem_seg_t;
typedef struct {
conf_mem_seg_t** memSegConfs;
} conf_mem_t;
typedef struct {
char** id;
char** clockId;
uint64_t clockDivider;
uint64_t clockMultipler;
conf_mem_t* memConf;
char* libPath;
} conf_dev_t;
#endif // ifndef __HMMMM_PUB_LIB_CONFIG_H__

42
inc/pub/libhmmmm/device.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef __LIBDEVICE_H__
#define __LIBDEVICE_H__
#include <stdint.h>
#include "mem.h"
#define SMART_ADDR_TYPE_GLOBAL 1
#define SMART_ADDR_TYPE_SEGMENTED 2
#define EXTENDED_DEVICE_TYPE_DUMMY 0
#define EXTENDED_DEVICE_TYPE_INSTR_SIMUL 1
typedef struct
{
uint64_t addr;
uint8_t segno;
uint64_t localAddr;
uint8_t addrType;
mem_h_read_func* handler;
uint64_t ident;
} smart_read_spec_t;
typedef struct
{
uint64_t addr;
uint8_t segno;
uint64_t localAddr;
uint8_t addrType;
mem_h_write_func handler;
uint64_t ident;
} smart_write_spec_t;
typedef struct {
device_mem_t* deviceMem;
void* deviceInfo;
} device_public_context_t;
#endif // ifndef __LIBDEVICE_H__

95
inc/pub/libhmmmm/mem.h Normal file
View File

@@ -0,0 +1,95 @@
#ifndef __HMMMM_PUB_MEM_H__
#define __HMMMM_PUB_MEM_H__
#include <stdint.h>
#include <stdlib.h>
#define GET_BIT(n, b) ((uint8_t)((n >> b) & 1))
#define MEM_ACCESS_INTERCEPT_BUF_SIZE 64
// Internal mem handlers
typedef void* (*mem_h_read_func)(uint64_t ident, uint64_t addr, void* rawCells);
typedef void (*mem_h_write_func)(uint64_t ident, uint64_t addr, void* rawCells, void* data);
typedef struct {
mem_h_read_func func;
uint64_t ident;
} mem_h_read_handler;
typedef struct {
mem_h_write_func func;
uint64_t ident;
} mem_h_write_handler;
typedef struct
{
void* rawCells;
void** cells;
uint64_t smartAddrReadMask;
uint64_t smartAddrWriteMask;
mem_h_read_handler* smartAddrReadHandlers;
mem_h_write_handler* smartAddrWriteHandlers;
char** memsegNames;
uint64_t* memsegShifts;
uint64_t* memsegSizes;
uint8_t* memsegCellSizes;
uint64_t* memreadCellAddrs;
uint8_t* memwriteCellSegments;
uint64_t* memwriteCellAddrs;
uint64_t* memwriteValues;
uint8_t memreadLen;
uint8_t memwriteLen;
} device_mem_t;
typedef struct
{
uint64_t start;
uint64_t len;
uint8_t wordLen;
char* name;
} memseg_spec_t;
// External handlers
typedef void* (*ext_h_read_func)(uint64_t addr, void* rawCells, void* devContext);
typedef void (*ext_h_write_func)(uint64_t addr, void* rawCells, void* data, void* devContext);
#define READ_MEM(__tgt, __mem, __segno, __addr, __cell_t) \
{\
uint64_t __globalAddr = __mem->memsegShifts[__segno] + __addr; \
if (__mem->smartAddrReadHandlers[__globalAddr].func != NULL) \
{ \
/* __tgt = *(__cell_t*)__mem->smartAddrReadHandlers[__globalAddr].func(__mem->smartAddrReadHandlers[__globalAddr].ident, __addr, __mem->rawCells + __mem->memsegShifts[__segno]);*/ \
__tgt = 0;\
} \
else \
{ \
__tgt = ((__cell_t*)__mem->cells[__segno])[__addr]; \
} \
__mem->memreadCellAddrs[__mem->memreadLen] = __globalAddr; \
__mem->memreadLen += 1; \
}
#define WRITE_MEM(__mem, __segno, __addr, __cell_t, __val) \
{ \
uint64_t __globalAddr = ((uint64_t)(__mem->memsegShifts[__segno]) + (uint64_t)(__addr)); \
if (0 && __mem->smartAddrWriteHandlers[__globalAddr].func != NULL) \
{ \
/*__cell_t __dat = (__cell_t)(__val);*/ \
/*__mem->smartAddrWriteHandlers[__globalAddr].func(__mem->smartAddrWriteHandlers[__globalAddr].ident, __addr, __mem->rawCells + __mem->memsegShifts[__segno], (void*)&__dat);*/ \
} \
else if(0) \
{ \
((__cell_t*)__mem->cells[__segno])[__addr] = (__cell_t)(__val); \
} \
__mem->memwriteCellAddrs[__mem->memwriteLen] = __addr; \
__mem->memwriteCellSegments[__mem->memwriteLen] = __segno; \
/*__mem->memwriteWordLengths[__mem->memwriteLen] = sizeof(__cell_t);*/ \
(__mem->memwriteValues[__mem->memwriteLen]) = (__cell_t)(__val); \
__mem->memwriteLen += 1; \
}
#endif // ifndef __HMMMM_PUB_MEM_H__

View File

@@ -12,7 +12,9 @@
typedef struct { typedef struct {
ClientContext* clientContext; ClientContext* clientContext;
uint32_t regId; uint32_t regId;
uint32_t segId;
uint64_t startAddr; uint64_t startAddr;
uint64_t startGlobalAddr;
uint64_t segLen; uint64_t segLen;
uint8_t mode; uint8_t mode;
} StreamReg; } StreamReg;

BIN
src/.main.c.swp Normal file

Binary file not shown.

75
src/base_device.c Normal file
View File

@@ -0,0 +1,75 @@
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include "base_device.h"
void closeBaseDevice(device_handle_t* devHandle)
{
if(devHandle == NULL)
{
return;
}
if(devHandle->ctx != NULL)
{
if(devHandle->specs != NULL && devHandle->lib != NULL)
{
devHandle->lib->freeSpecs(devHandle->specs);
}
if(devHandle->ctx->deviceMem != NULL && devHandle->lib != NULL)
{
devHandle->lib->freeDevMem(devHandle->ctx->deviceMem);
}
free(devHandle->ctx);
}
if(devHandle->lib != NULL)
{
if(devHandle->lib->extendedHandlers != NULL)
{
free(devHandle->lib->extendedHandlers);
}
dlclose(devHandle->lib->_dlhandl);
free(devHandle->lib);
}
}
device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
{
device_handle_t* ret = malloc(sizeof(device_handle_t));
if (ret == NULL)
{
snprintf(errbuf, 1024, "unable to allocate device handle struct");
return NULL;
}
char intErrbuf[512];
device_lib_t* devLib = loadDeviceLib(devConf->libPath, intErrbuf);
if (devLib == NULL)
{
snprintf(errbuf, 1024, "unable to load device library %s: %s", devConf->libPath, intErrbuf);
free(ret);
return NULL;
}
void* devSpecs = devLib->parseSpecsFromConfig(devConf, intErrbuf);
if (devSpecs == NULL)
{
snprintf(errbuf, 1024, "device config parse error: %s", intErrbuf);
dlclose(devLib->_dlhandl);
free(ret);
free(devLib);
return NULL;
}
ret->lib = devLib;
ret->specs = devSpecs;
ret->clockCycleCounter = 0;
ret->clockCycleLimit = 0;
ret->clockDivider = 1;
return ret;
}

122
src/compose_device.c Normal file
View File

@@ -0,0 +1,122 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "base_device.h"
#include "compose_device.h"
// char** appendId(char** prev, const char* cur, char* errbuf)
// {
// if(prev == NULL)
// {
// prev = malloc(sizeof(char*) * 2);
// if(prev == NULL)
// {
// snprintf(errbuf, 1024, "unable to allocate id");
// return NULL;
// }
// prev[0] = NULL;
// prev[1] = NULL;
// }
// size_t clockIdLen = 0;
// while (prev[clockIdLen] != NULL){clockIdLen++;}
// clockIdLen++;
// char** new = realloc(prev, sizeof(char*) * (clockIdLen + 1));
// if(new == NULL)
// {
// snprintf(errbuf, 1024, "unable to reallocate id");
// freeComposeId(prev);
// return NULL;
// }
// prev = new;
// prev[clockIdLen] = NULL;
// size_t idLen = strlen(cur);
// prev[clockIdLen - 1] = malloc(sizeof(char) * (idLen + 1));
// if(prev[clockIdLen - 1] == NULL)
// {
// snprintf(errbuf, 1024, "unable to allocate new id entry");
// freeComposeId(prev);
// return NULL;
// }
// strcpy(prev[clockIdLen - 1], cur);
// prev[clockIdLen - 1][idLen] = '\0';
// return prev;
// }
// static void freeProjectionConfig(projection_conf_t* conf)
// {
// if(conf == NULL)
// {
// return;
// }
// freeComposeId(conf->target);
// freeComposeId(conf->baseAt);
// if(conf->baseSeg != NULL)
// {
// free(conf->baseSeg);
// }
// conf->target = NULL;
// conf->baseAt = NULL;
// conf->baseSeg = NULL;
// free(conf);
// }
// void freeProjectionConfigs(projection_conf_t** confs)
// {
// if(confs == NULL)
// {
// return;
// }
// size_t i = 0;
// while(confs[i] != NULL)
// {
// freeProjectionConfig(confs[i]);
// confs[i] = NULL;
// }
// free(confs);
// }
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
{
size_t devIdx = 0;
while(conf->baseConfigs[devIdx] != NULL){devIdx++;}
device_handle_t** devHandlers = malloc(sizeof(device_handle_t*) * (devIdx + 1));
if(devHandlers == NULL)
{
snprintf(errbuf, 1024, "unable to allocate dev handlers");
return NULL;
}
devHandlers[devIdx] = NULL;
for(size_t i = 0; i < devIdx; i++)
{
conf_dev_t* devConf = conf->baseConfigs[i];
char intErrbuf[255] = {0};
device_handle_t* devHandle = openBaseDevice(devConf, intErrbuf);
if(devHandle == NULL)
{
snprintf(errbuf, 1024, "unable to open base device %s: %s", devConf->id[0], intErrbuf);
for(size_t j = 0; j < i; j++)
{
closeBaseDevice(devHandlers[j]);
free(devHandlers[j]);
}
free(devHandlers);
return NULL;
}
devHandlers[i] = devHandle;
}
return devHandlers;
}

86
src/config.c Normal file
View File

@@ -0,0 +1,86 @@
#include <stdlib.h>
#include <string.h>
#include "config.h"
static void xfree(void* p)
{
if(p != NULL)
{
free(p);
}
}
void freeComposeId(char** id)
{
if(id == NULL)
{
return;
}
size_t i = 0;
while(id[i] != NULL)
{
free(id[i]);
i++;
}
free(id);
}
// static uint8_t compareComposeId(char** idA, char** idB)
// {
// size_t i = 0;
// while(idA[i] != NULL && idB[i] != NULL)
// {
// if(strcmp(idA[i], idB[i]) != 0)
// {
// return 0;
// }
// i++;
// }
// return idA[i] == NULL && idB[i] == NULL;
// }
static void freeMemSegConf(conf_mem_seg_t* memSegConf)
{
if(memSegConf == NULL)
{
return;
}
xfree(memSegConf->name);
}
static void freeMemConf(conf_mem_t* memConf)
{
if(memConf == NULL)
{
return;
}
for(size_t i = 0; i < 0xFF && memConf->memSegConfs[i] != NULL; i++)
{
freeMemSegConf(memConf->memSegConfs[i]);
}
xfree(memConf->memSegConfs);
free(memConf);
}
void freeConf(conf_dev_t* conf)
{
if(conf == NULL)
{
return;
}
if(conf->clockId != NULL)
{
size_t i = 0;
while(conf->clockId[i] != NULL)
{
free(conf->clockId[i]);
i++;
}
free(conf->clockId);
}
freeComposeId(conf->id);
xfree(conf->libPath);
freeMemConf(conf->memConf);
free(conf);
}

262
src/hmmmm.c Normal file
View File

@@ -0,0 +1,262 @@
#include "hmmmm.h"
#include <dlfcn.h>
#include <stdio.h>
typedef size_t (*_dlib_pubExtractPcounter_t)(device_public_context_t* devContext);
typedef size_t (*_dlib_pubExtractOpcode_t)(device_mem_t* devMem, size_t _programCounter);
typedef uint8_t(*_dlib_pubExtractPcounterSizeWords_t)();
typedef device_public_context_t* (*_dlib_dev_init_t)(void* specs, char* errbuf);
typedef void (*_dlib_dev_reset_t)(void* specs, device_public_context_t* devInfo);
typedef uint8_t (*_dlib_makeDeviceTick_t)(device_public_context_t* devInfo);
typedef uint8_t (*_dlib_deviceType_t)();
typedef void* (*_dlib_pubExtractPcounterPtr_t)(device_public_context_t* devContext);
typedef void* (*_dlib_parseSpecsFromConfig_t)(const conf_dev_t* devConf, char* errbuf);
typedef void (*_dlib_freeSpecs_t)(void* specs);
typedef void (*_dlib_freeDevMem_t)(device_mem_t* mem);
typedef void (*_dlib_fillSmartReadSpecs_t)(void* specs, smart_read_spec_t* smartReadSpecs, uint64_t smartReadSpecsCount);
typedef void (*_dlib_fillSmartWriteSpecs_t)(void* specs, smart_write_spec_t* smartWriteSpecs, uint64_t smartWriteSpecsCount);
static instruction_simul_handlers_t* _fillInstructionSimul(void* handle)
{
instruction_simul_handlers_t* ret = malloc(sizeof(instruction_simul_handlers_t));
if (ret == NULL)
{
return NULL;
}
_dlib_pubExtractPcounterPtr_t _dlib_pubExtractPcounterPtr = (_dlib_pubExtractPcounterPtr_t)(uintptr_t)dlsym(handle, "pubExtractPcounterPtr");
const char *dlib_pubExtractPcounterPtr_error = dlerror();
if (dlib_pubExtractPcounterPtr_error) {
dlclose(handle);
free(ret);
return NULL;
}
ret->extractPcounterPtr = _dlib_pubExtractPcounterPtr;
_dlib_pubExtractPcounter_t _dlib_pubExtractPcounter = (_dlib_pubExtractPcounter_t)(uintptr_t)dlsym(handle, "pubExtractPcounter");
const char *dlib_pubExtractPcounter_error = dlerror();
if (dlib_pubExtractPcounter_error) {
dlclose(handle);
free(ret);
return NULL;
}
ret->extractPcounter = _dlib_pubExtractPcounter;
_dlib_pubExtractOpcode_t _dlib_pubExtractOpcode = (_dlib_pubExtractOpcode_t)(uintptr_t)dlsym(handle, "pubExtractOpcode");
const char *dlib_pubExtractOpcode_error = dlerror();
if (dlib_pubExtractOpcode_error) {
dlclose(handle);
free(ret);
return NULL;
}
ret->extractOpcode = _dlib_pubExtractOpcode;
_dlib_pubExtractPcounterSizeWords_t _dlib_pubExtractPcounterSizeWords = (_dlib_pubExtractPcounterSizeWords_t)(uintptr_t)dlsym(handle, "pubExtractPcounterSizeWords");
const char *dlib_pubExtractPcounterSizeWords_error = dlerror();
if (dlib_pubExtractPcounterSizeWords_error) {
dlclose(handle);
free(ret);
return NULL;
}
ret->extractPcounterSizeWords = _dlib_pubExtractPcounterSizeWords;
return ret;
}
device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
{
device_lib_t* dev = malloc(sizeof(device_lib_t));
if (dev == NULL)
{
snprintf(errbuf, 1024, "unable to allocate device lib struct");
return NULL;
}
void *handle = dlopen(libpath, RTLD_NOW);
if (!handle)
{
const char *dlerr = dlerror();
snprintf(errbuf, 1024, "unable to open dl handle: %s", dlerr);
free(dev);
return NULL;
}
dlerror();
_dlib_dev_init_t _dlib_dev_init = (_dlib_dev_init_t)(uintptr_t)dlsym(handle, "init");
const char *dlsym_init_error = dlerror();
if (dlsym_init_error)
{
snprintf(errbuf, 1024, "unable to find init symbol: %s", dlsym_init_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_makeDeviceTick_t _dlib_makeDeviceTick = (_dlib_makeDeviceTick_t)(uintptr_t)dlsym(handle, "makeDeviceTick");
const char *dlsym_maketick_error = dlerror();
if (dlsym_maketick_error)
{
snprintf(errbuf, 1024, "unable to find makeDeviceTick symbol: %s", dlsym_maketick_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_parseSpecsFromConfig_t _dlib_parseSpecsFromConfig = (_dlib_parseSpecsFromConfig_t)(uintptr_t)dlsym(handle, "parseSpecsFromConfig");
const char *_dlib_parseSpecsFromConfig_error = dlerror();
if (_dlib_parseSpecsFromConfig_error)
{
snprintf(errbuf, 1024, "unable to find parseSpecsFromConfig symbol: %s", _dlib_parseSpecsFromConfig_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_fillSmartReadSpecs_t _dlib_fillSmartReadSpecs = (_dlib_fillSmartReadSpecs_t)(uintptr_t)dlsym(handle, "fillSmartReadSpecs");
const char *_dlib_fillSmartReadSpecs_error = dlerror();
if (_dlib_fillSmartReadSpecs_error)
{
snprintf(errbuf, 1024, "unable to find fillSmartReadSpecs symbol: %s", _dlib_fillSmartReadSpecs_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_fillSmartWriteSpecs_t _dlib_fillSmartWriteSpecs = (_dlib_fillSmartWriteSpecs_t)(uintptr_t)dlsym(handle, "fillSmartWriteSpecs");
const char *_dlib_fillSmartWriteSpecs_error = dlerror();
if (_dlib_fillSmartWriteSpecs_error)
{
snprintf(errbuf, 1024, "unable to find fillSmartWriteSpecs symbol: %s", _dlib_fillSmartWriteSpecs_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_deviceType_t _dlib_deviceType = (_dlib_deviceType_t)(uintptr_t)dlsym(handle, "pubDeviceType");
const char *dlib_deviceType_error = dlerror();
if (dlib_deviceType_error)
{
snprintf(errbuf, 1024, "unable to find pubDeviceType symbol: %s", dlib_deviceType_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_freeSpecs_t _dlib_freeSpecs = (_dlib_freeSpecs_t)(uintptr_t)dlsym(handle, "freeDevSpecs");
const char *dlib_freeSpecs_error = dlerror();
if (dlib_freeSpecs_error)
{
snprintf(errbuf, 1024, "unable to find freeSpecs symbol: %s", dlib_freeSpecs_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_dev_reset_t _dlib_reset = (_dlib_dev_reset_t)(uintptr_t)dlsym(handle, "reset");
const char *dlib_dev_reset_t_error = dlerror();
if (dlib_dev_reset_t_error)
{
snprintf(errbuf, 1024, "unable to find reset symbol: %s", dlib_dev_reset_t_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevMem");
const char *dlib_freeDevMem_error = dlerror();
if (dlib_freeDevMem_error)
{
snprintf(errbuf, 1024, "unable to find freeDevMem symbol: %s", dlib_freeDevMem_error);
dlclose(handle);
free(dev);
return NULL;
}
dev->devContext = NULL;
dev->init = _dlib_dev_init;
dev->makeDeviceTick = _dlib_makeDeviceTick;
dev->parseSpecsFromConfig = _dlib_parseSpecsFromConfig;
dev->fillSmartReadSpecs = _dlib_fillSmartReadSpecs;
dev->fillSmartWriteSpecs = _dlib_fillSmartWriteSpecs;
dev->freeSpecs = _dlib_freeSpecs;
dev->freeDevMem = _dlib_freeDevMem;
dev->reset = _dlib_reset;
uint8_t devType = _dlib_deviceType();
dev->devType = devType;
if (devType == EXTENDED_DEVICE_TYPE_DUMMY)
{
dev->extendedHandlers = NULL;
}
else if (devType == EXTENDED_DEVICE_TYPE_INSTR_SIMUL)
{
dev->extendedHandlers = _fillInstructionSimul(handle);
if (dev->extendedHandlers == NULL)
{
free(dev);
dlclose(handle);
return NULL;
}
}
else
{
free(dev);
dlclose(handle);
return NULL;
}
dev->_dlhandl = handle;
return dev;
}

View File

@@ -4,10 +4,6 @@
void removeLinkedListEntry(LinkedListEntry** head, LinkedListEntry* entry) void removeLinkedListEntry(LinkedListEntry** head, LinkedListEntry* entry)
{ {
if(entry == NULL)
{
return;
}
// check for head // check for head
if(entry->prevEntry != NULL) if(entry->prevEntry != NULL)
{ {

1638
src/main.c

File diff suppressed because it is too large Load Diff

1
src/main.h Normal file
View File

@@ -0,0 +1 @@
#pragma once

View File

@@ -1,10 +1,20 @@
#include "proto/dial.h" #include "proto/dial.h"
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/time.h>
#include "context.h" #include "context.h"
#include "panic.h" #include "panic.h"
#include "proto/msg.h" #include "proto/msg.h"
#include "ptQueue/inc/spsc.h"
static uint64_t dial_now_us(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
}
void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen) void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
@@ -17,7 +27,7 @@ void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
memcpy(newMsg, msg, msgLen); memcpy(newMsg, msg, msgLen);
ClientContext* ctx = clientsHead->payload; ClientContext* ctx = clientsHead->payload;
dispatchOutgoingMessage(emulContext->outBufs, ctx, newMsg, msgLen); dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, newMsg, msgLen);
clientsHead = clientsHead->nextEntry; clientsHead = clientsHead->nextEntry;
} }
free(msg); free(msg);
@@ -27,20 +37,25 @@ void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ClientContext* client, uint8_t* msg, size_t msgLen) void dispatchOutgoingMessage(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen)
{ {
SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx]; // OutgoingBuffers* outBufs = emulContext->outBufs;
if(p->size + 1 >= p->allocatedSize) // SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx];
{ // if(p->size + 1 >= p->allocatedSize)
printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx); // {
OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 2); // // printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx);
NULL_GUARD(newPtr); // OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 5);
p->ptr = newPtr; // NULL_GUARD(newPtr);
p->allocatedSize = p->allocatedSize * 2; // p->ptr = newPtr;
} // p->allocatedSize = p->allocatedSize * 5;
OutgoingMessage* outmsg = &((OutgoingMessage*)p->ptr)[p->size]; // }
OutgoingMessage* outmsg = malloc(sizeof(OutgoingMessage));
// OutgoingMessage* outmsg = &((OutgoingMessage*)p->ptr)[p->size];
NULL_GUARD(outmsg);
outmsg->msg = msg; outmsg->msg = msg;
outmsg->msgLen = msgLen; outmsg->msgLen = msgLen;
outmsg->client = client; outmsg->clientIdx = clientIdx;
p->size++; outmsg->dispatch_us = dial_now_us();
queue_spsc_push(outMsgQueue, outmsg);
// p->size++;
} }

View File

@@ -1,71 +1,57 @@
#include "proto/handlers.h" #include "proto/handlers.h"
#include <stdio.h> #include <stdio.h>
#include "proto/enums.h"
#include "panic.h" #include "panic.h"
#include "proto/enums.h"
#include "proto/handlers/auth.h" #include "proto/handlers/auth.h"
#include "proto/handlers/stream.h" #include "proto/handlers/stream.h"
#include "proto/handlers/control.h" #include "proto/handlers/control.h"
#include "proto/handlers/mem.h" #include "proto/handlers/mem.h"
#include "state.h" #include "proto/msg.h"
#include "proto_reader.h"
#include "proto_verifier.h"
#include "control_reader.h"
#include "ptQueue/inc/mpsc.h"
#include "ptQueue/inc/spsc.h"
#include "stream_reader.h"
#include "mem_reader.h"
void handleCloseClient(EmulContext* emulContext, LinkedListEntry* clientEntry) static void handleCloseClient(EmulContext* emulContext, ClientContext* ctx)
{ {
ClientContext* ctx = clientEntry->payload; // if (ctx->streamRegIterator > 0) {
unregisterClientStreams(emulContext, ctx);
if(ctx->orphanedAt == 0)
{
ctx->orphanedAt = (uint64_t)time(NULL);
}
// if(ctx->streamRegIterator > 0)
// {
// unregisterClientStreams(emulContext, ctx);
// } // }
// if(ctx->streamRegIterator == 0)
// {
// removeLinkedListEntry(emulContext->clientsHead, clientEntry);
// ctx->clientId = 0;
// free(ctx->fallbackOutcomeQ->ptr);
// free(ctx->fallbackOutcomeQ);
// ptQueueFree(ctx->incomeQ);
// free(ctx);
// }
} }
void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev) 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->seatId); printf("open client %lu\n", ev->ctx->clientId);
LinkedListEntry* newClientsLinkedListHead = malloc(sizeof(LinkedListEntry)); LinkedListEntry* entry = malloc(sizeof(LinkedListEntry));
NULL_GUARD(newClientsLinkedListHead); NULL_GUARD(entry);
newClientsLinkedListHead->payload = ev->ctx; entry->payload = ev->ctx;
if(*emulContext->clientsHead != NULL) if (*emulContext->clientsHead != NULL) {
{ (*emulContext->clientsHead)->prevEntry = entry;
(*emulContext->clientsHead)->prevEntry = newClientsLinkedListHead;
} }
newClientsLinkedListHead->prevEntry = NULL; entry->prevEntry = NULL;
newClientsLinkedListHead->nextEntry = *emulContext->clientsHead; entry->nextEntry = *emulContext->clientsHead;
*emulContext->clientsHead = newClientsLinkedListHead; *emulContext->clientsHead = entry;
} }
else if (ev->regType == REG_EVTYPE_CLOSE) else if (ev->regType == REG_EVTYPE_CLOSE)
{ {
LinkedListEntry* clientEntry = *emulContext->clientsHead; LinkedListEntry* clientEntry = *emulContext->clientsHead;
while (clientEntry != NULL) {
while(clientEntry != NULL) if (clientEntry->payload == ev->ctx)
{
if(clientEntry->payload == ev->ctx)
{ {
printf("close client %lu\n", ev->ctx->seatId); printf("close client %lu\n", ev->ctx->clientId);
handleCloseClient(emulContext, clientEntry); handleCloseClient(emulContext, ev->ctx);
removeLinkedListEntry(emulContext->clientsHead, clientEntry);
break; break;
} }
clientEntry = clientEntry->nextEntry; clientEntry = clientEntry->nextEntry;
@@ -73,37 +59,54 @@ void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev)
} }
else if (ev->regType == REG_EVTYPE_AUTH) else if (ev->regType == REG_EVTYPE_AUTH)
{ {
printf("auth client %lu\n", ev->ctx->seatId); printf("auth client %lu\n", ev->ctx->clientId);
ev->ctx->isAuthed = 1; ev->ctx->isAuthed = 1;
handleOnClientAuthDone(ev->ctx, emulContext); handleOnClientAuthDone(ev->ctx, emulContext);
} }
} }
static void handleIncomingMessage(
hmmmm_ClientMessage_table_t cm, ClientContext* ctx, EmulContext* emulContext)
void handleIncomingMessage(BaseMessage* msg, 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:
{ {
handleIncomingControlMessage(msg, ctx, emulContext); hmmmm_ctrl_CtrlClientMessage_table_t ctrl =
(hmmmm_ctrl_CtrlClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingCtrlMessage(ctrl, nonce, ctx, emulContext);
break; break;
} }
case PACKET_TYPE_STREAM: case hmmmm_ClientPayload_StreamClientMessage:
{ {
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; break;
} }
case PACKET_TYPE_MEM: case hmmmm_ClientPayload_MemClientMessage:
{ {
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; break;
} }
default: default:
{ {
printf("Unsupported packet type: %u\n", msg->packetType); printf("client %lu: unknown payload type %u\n", ctx->clientId, ptype);
break; break;
} }
} }
@@ -112,88 +115,77 @@ void handleIncomingMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* em
void handleAllClients(EmulContext* emulContext) void handleAllClients(EmulContext* emulContext)
{ {
LinkedListEntry* clientEntry = *emulContext->clientsHead;
size_t handleLimit = 128; queue_mpsc_t* inMsgQueue = emulContext->inMsgQueue;
uint8_t hasAliveClients = 0;
while(clientEntry != NULL && handleLimit > 0) FbMessage* fbmsg = NULL;
for(size_t handleLimit = 0; handleLimit < 32; handleLimit++)
{ {
LinkedListEntry* currEntry = clientEntry; fbmsg = NULL;
handleLimit--; if(queue_mpsc_pop(inMsgQueue, (void**)&fbmsg))
ClientContext* ctx = clientEntry->payload;
if(ctx->orphanedDeadTimeout != 0 && ctx->orphanedAt != 0 && ctx->isAuthed)
{ {
// printf("Found orphaned client\n"); if(fbmsg != NULL)
if((uint64_t)time(NULL) - ctx->orphanedAt > ctx->orphanedDeadTimeout || ctx->streamRegIterator == 0)
{ {
printf("Orphaned client dead timeout exceeded\n"); *emulContext->utilizedFlag = 1;
if(ctx->streamRegIterator > 0)
{ // if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
unregisterClientStreams(emulContext, ctx); hmmmm_ClientMessage_table_t cm =
} hmmmm_ClientMessage_as_root(fbmsg->data);
clientEntry = clientEntry->nextEntry; // printf("read message len %lu\n", fbmsg->size);
removeLinkedListEntry(emulContext->clientsHead, currEntry); handleIncomingMessage(cm, fbmsg->ctx, emulContext);
dispatchOutgoingMessage(emulContext->outBufs, ctx, NULL, 0); // } else {
// printf("client %lu: dropped malformed FlatBuffer\n", fbmsg->ctx->clientId);
// }
free(fbmsg->data);
free(fbmsg);
continue;
} }
} else
if(!ctx->isAuthed)
{
clientEntry = disconnectDueTimeout(emulContext, clientEntry);
if(clientEntry == NULL)
{ {
break; break;
} }
if(*emulContext->utilizedFlag)
{
continue;
}
} }
else else
{ {
if(ctx->orphanedAt == 0) break;
{
hasAliveClients = 1;
}
void* payload = ctx->incomeQ->head->payload;
if(payload != 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);
free(payload);
ctx->incomeQ->head = ctx->incomeQ->head->nextEl;
}
clientEntry = clientEntry->nextEntry;
} }
} }
if(hasAliveClients == 0)
{
uint8_t newEmulState = switchNewEmulState(*emulContext->emulState, EMUL_STATE_OP_PAUSE);
if(newEmulState != *emulContext->emulState)
{
printf("No alive clients, pausing execution\n");
*emulContext->emulState = newEmulState;
size_t len = 0; // LinkedListEntry* clientEntry = *emulContext->clientsHead;
uint8_t* notify = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, newEmulState, &len); // size_t handleLimit = 128;
broadcastClients(emulContext, notify, len);
} // while (clientEntry != NULL && handleLimit > 0) {
} // handleLimit--;
// ClientContext* ctx = clientEntry->payload;
// if (!ctx->isAuthed) {
// clientEntry = disconnectDueTimeout(emulContext, clientEntry);
// if (clientEntry == NULL) break;
// if (*emulContext->utilizedFlag) continue;
// } else {
// FbMessage* fbmsg = NULL;
// queue_spsc_pop(ctx->incomeQ, (void**)&fbmsg);
// // FbMessage* fbmsg = ctx->incomeQ->head->payload;
// if (fbmsg != NULL) {
// *emulContext->utilizedFlag = 1;
// // if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
// printf("read message len %lu\n", fbmsg->size);
// 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(fbmsg->data);
// free(fbmsg);
// // ctx->incomeQ->head = ctx->incomeQ->head->nextEl;
// }
// clientEntry = clientEntry->nextEntry;
// }
// }
} }

View File

@@ -1,143 +1,159 @@
#include "proto/handlers/auth.h" #include "proto/handlers/auth.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include "my_mutex.h" #include "my_mutex.h"
#include "proto/enums.h"
#include "events.h" #include "events.h"
#include "panic.h" #include "panic.h"
#include "proto/enums.h"
#include "proto/msg.h"
#include "proto/pack.h"
#include "proto/dial.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"
#include "ptQueue/inc/spsc.h"
static uint8_t validateAccessTokenDeterministic(
const uint8_t* data, const uint8_t* token, uint64_t timestamp)
{ {
char buf[1024]; char buf[1024];
sprintf(buf, "%s%lu", token, timestamp); (void)snprintf(buf, sizeof(buf), "%s%lu", token, timestamp);
uint8_t hash[SHA512_DIGEST_LENGTH]; 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; uint8_t valid = 1;
for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) {
for(size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) if (data[i] != hash[i]) {
{
if (data[i] != hash[i])
{
valid = 0; valid = 0;
} }
} }
return valid; 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; uint64_t t = (uint64_t)time(NULL) / 30;
return validateAccessTokenDeterministic(data, access_token, t)
uint8_t valid1 = validateAccessTokenDeterministic(data, access_token, t); || validateAccessTokenDeterministic(data, access_token, t - 1);
uint8_t valid2 = validateAccessTokenDeterministic(data, access_token, t - 1);
return valid1 || valid2;
} }
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); ServerContext* ctx = ws_get_server_context(client);
if(msgSize != SHA512_DIGEST_LENGTH)
{ // Verify it's a valid ClientMessage FlatBuffer
int ret = ws_close_client(client); if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)msgSize)) {
if(ret == -1) printf("Auth: invalid FlatBuffer from client %lu\n", client);
{ ws_close_client(client);
printf("Unable to close client %lu\n", client);
}
return 0; return 0;
} }
uint8_t isValid = validateAccessToken(msg, (const uint8_t*)ctx->accessToken);
if(!isValid) hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_as_root(msg);
{
printf("Auth token invalid\n"); if (hmmmm_ClientMessage_payload_type(cm) != hmmmm_ClientPayload_AuthRequest) {
int ret = ws_close_client(client); printf("Auth: expected AuthRequest, got type %u from client %lu\n",
if(ret == -1) hmmmm_ClientMessage_payload_type(cm), client);
{ ws_close_client(client);
printf("Unable to close client %lu\n", client);
}
return 0; 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]; char errbuf[1024];
ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
printf("Auth token is valid\n"); if (ev == NULL) {
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
if(ev == NULL)
{
panic("Unable to allocate register event"); panic("Unable to allocate register event");
} }
ev->regType = REG_EVTYPE_AUTH; ev->regType = REG_EVTYPE_AUTH;
ev->ctx = cctx; ev->ctx = cctx;
ws_set_connection_context(client, cctx);
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
printf("Writing auth event\n"); printf("Writing auth event\n");
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); // int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
if(exitCode)
{ queue_spsc_push(ctx->regQueue, ev);
panic("Unable to push to reg queue: %s\n", errbuf); // if (!queue_spsc_push(ctx->regQueue, ev)) {
} // panic("Unable to push to reg queue: %s\n", errbuf);
// }
} }
return 1; return 1;
} }
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); uint64_t now = (uint64_t)time(NULL);
if(now - ctx->connectedAt <= 30) if (now - cctx->connectedAt <= 30) {
{
return clientEntry->nextEntry; return clientEntry->nextEntry;
} }
printf("Timeout on connection %lu\n", ctx->clientId); printf("Timeout on connection %lu\n", cctx->clientId);
int ret = ws_close_client(ctx->clientId); int ret = ws_close_client(cctx->clientId);
if(ret == -1) if (ret == -1) {
{
printf("Unable to close client\n"); printf("Unable to close client\n");
} }
LinkedListEntry* nextEntry = clientEntry->nextEntry; LinkedListEntry* nextEntry = clientEntry->nextEntry;
removeLinkedListEntry(emulContext->clientsHead, clientEntry); removeLinkedListEntry(emulContext->clientsHead, clientEntry);
clientEntry = nextEntry;
*emulContext->utilizedFlag = 1; *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); // Send AuthResponse with assigned seat_id
NULL_GUARD(framedata);
encodeUintToBytes(ctx->seatId, framedata);
dispatchOutgoingMessage(emulContext->outBufs, ctx, framedata, 8);
size_t len = 0; size_t len = 0;
uint8_t* msg = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, *emulContext->emulState, &len); uint8_t* authResp = fb_build_auth_response(UINT64_MAX, cctx->clientId, &len);
dispatchOutgoingMessage(emulContext->outBufs, ctx, msg, len); dispatchOutgoingMessage(emulContext->outMsgQueue, cctx->clientId, authResp, len);
// Send current execution state
len = 0;
uint8_t* stateMsg = fb_build_exec_notify(
UINT64_MAX, *emulContext->clockCounter, *emulContext->emulState, &len);
dispatchOutgoingMessage(emulContext->outMsgQueue, cctx->clientId, stateMsg, len);
uint8_t* outMsg = createClientSetup((uint64_t)~0, ctx, &len); // Send cached DeviceIdMappingNotif if a config has been loaded
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, len); if (emulContext->deviceIdMappingMsg && emulContext->deviceIdMappingMsgLen > 0) {
uint8_t* mappingCopy = malloc(emulContext->deviceIdMappingMsgLen);
if (mappingCopy) {
memcpy(mappingCopy, emulContext->deviceIdMappingMsg, emulContext->deviceIdMappingMsgLen);
dispatchOutgoingMessage(
emulContext->outMsgQueue, cctx->clientId,
mappingCopy, emulContext->deviceIdMappingMsgLen);
}
}
} }

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

@@ -0,0 +1,758 @@
#include "proto/handlers/config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "state.h"
#include "base_device.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "config_ctrl_reader.h"
#include "config_reader.h"
#include "device_reader.h"
#include "mem_config_reader.h"
#define MAX_DEVICES 64
#define MAX_DEPTH 16
#define MAX_SEGMENTS 64
// Converts a FlatBuffer BaseDeviceConfig into a heap-allocated conf_dev_t
// suitable for openBaseDevice(). Caller must call freeConf() when done.
static conf_dev_t* conf_from_fb(
hmmmm_config_BaseDeviceConfig_table_t base,
char* errbuf)
{
conf_dev_t* dc = calloc(1, sizeof(conf_dev_t));
if (!dc) { snprintf(errbuf, 1024, "alloc conf_dev_t"); return NULL; }
dc->clockDivider = 1;
dc->clockMultipler = 1;
flatbuffers_string_t lp = hmmmm_config_BaseDeviceConfig_libpath(base);
if (!lp) { snprintf(errbuf, 1024, "missing libpath"); free(dc); return NULL; }
dc->libPath = strdup(lp);
hmmmm_config_MemSegment_vec_t segs = hmmmm_config_BaseDeviceConfig_mem_segments(base);
size_t seg_n = segs ? hmmmm_config_MemSegment_vec_len(segs) : 0;
dc->memConf = calloc(1, sizeof(conf_mem_t));
if (!dc->memConf) { free(dc->libPath); free(dc); snprintf(errbuf, 1024, "alloc memConf"); return NULL; }
dc->memConf->memSegConfs = calloc(seg_n + 1, sizeof(conf_mem_seg_t*));
if (!dc->memConf->memSegConfs) {
free(dc->memConf); free(dc->libPath); free(dc);
snprintf(errbuf, 1024, "alloc memSegConfs");
return NULL;
}
for (size_t i = 0; i < seg_n; i++) {
hmmmm_config_MemSegment_table_t seg = hmmmm_config_MemSegment_vec_at(segs, i);
conf_mem_seg_t* s = calloc(1, sizeof(conf_mem_seg_t));
if (!s) { snprintf(errbuf, 1024, "alloc seg %zu", i); goto fail; }
flatbuffers_string_t sname = hmmmm_config_MemSegment_name(seg);
s->name = strdup(sname ? sname : "");
s->start = hmmmm_config_MemSegment_addr(seg);
s->len = hmmmm_config_MemSegment_len(seg);
s->wordLen = hmmmm_config_MemSegment_word_len(seg);
s->isExecutable = hmmmm_config_MemSegment_executable(seg);
dc->memConf->memSegConfs[i] = s;
}
dc->memConf->memSegConfs[seg_n] = NULL;
return dc;
fail:
freeConf(dc); // freeConf() already calls free(dc)
return NULL;
}
typedef struct {
device_handle_t* handles[MAX_DEVICES];
const char* path_stack[MAX_DEPTH]; // current path components
size_t depth;
// For building the DeviceIdMappingNotif response:
char** paths[MAX_DEVICES]; // hierarchical path per device (heap-alloc'd)
size_t path_lens[MAX_DEVICES]; // number of path components per device
size_t seg_counts[MAX_DEVICES]; // number of memory segments per device
char* seg_names[MAX_DEVICES][MAX_SEGMENTS]; // segment names (heap-alloc'd)
size_t count;
} LoadState;
static int load_devices_recursive(
hmmmm_config_ComposeDeviceConfig_table_t compose,
LoadState* st,
char* errbuf)
{
hmmmm_config_DeviceEntry_vec_t entries =
hmmmm_config_ComposeDeviceConfig_devices(compose);
size_t n = entries ? hmmmm_config_DeviceEntry_vec_len(entries) : 0;
for (size_t i = 0; i < n; i++) {
hmmmm_config_DeviceEntry_table_t entry =
hmmmm_config_DeviceEntry_vec_at(entries, i);
flatbuffers_string_t id = hmmmm_config_DeviceEntry_id(entry);
if (!id) { snprintf(errbuf, 1024, "device entry missing id"); return -1; }
if (st->depth >= MAX_DEPTH) {
snprintf(errbuf, 1024, "device tree too deep");
return -1;
}
st->path_stack[st->depth] = id;
st->depth++;
hmmmm_config_DeviceConfig_union_type_t ctype =
hmmmm_config_DeviceEntry_config_type(entry);
if (ctype == hmmmm_config_DeviceConfig_BaseDeviceConfig) {
if (st->count >= MAX_DEVICES) {
snprintf(errbuf, 1024, "too many devices");
return -1;
}
hmmmm_config_BaseDeviceConfig_table_t base =
(hmmmm_config_BaseDeviceConfig_table_t)
hmmmm_config_DeviceEntry_config(entry);
conf_dev_t* dc = conf_from_fb(base, errbuf);
if (!dc) return -1;
device_handle_t* dev = openBaseDevice(dc, errbuf);
if (!dev) {
freeConf(dc);
return -1;
}
const uint64_t clockDivider = hmmmm_config_DeviceEntry_clock_divider(entry);
if(clockDivider > 0)
{
dev->clockDivider = clockDivider;
}
dev->lib->fillSmartReadSpecs(dev->specs, NULL, 0);
dev->lib->fillSmartWriteSpecs(dev->specs, NULL, 0);
char initErrBuf[256];
dev->ctx = dev->lib->init(dev->specs, initErrBuf);
if (!dev->ctx)
{
snprintf(errbuf, 1024, "error while loading device %lu: %s", st->count, initErrBuf);
closeBaseDevice(dev);
free(dev);
freeConf(dc);
return -1;
}
dev->lib->reset(dev->specs, dev->ctx);
freeConf(dc);
size_t idx = st->count;
st->handles[idx] = dev;
// Store segment count and names from FlatBuffer config
hmmmm_config_MemSegment_vec_t dev_segs =
hmmmm_config_BaseDeviceConfig_mem_segments(base);
size_t nseg = dev_segs ? hmmmm_config_MemSegment_vec_len(dev_segs) : 0;
// if (nseg > MAX_SEGMENTS) nseg = MAX_SEGMENTS;
st->seg_counts[idx] = nseg;
for (size_t si = 0; si < nseg; si++)
{
hmmmm_config_MemSegment_table_t seg =
hmmmm_config_MemSegment_vec_at(dev_segs, si);
flatbuffers_string_t sname = hmmmm_config_MemSegment_name(seg);
st->seg_names[idx][si] = strdup(sname ? sname : "");
}
// Copy path
st->path_lens[idx] = st->depth;
st->paths[idx] = calloc(st->depth + 1, sizeof(char*));
if (!st->paths[idx])
{
snprintf(errbuf, 1024, "alloc path array");
return -1;
}
for (size_t p = 0; p < st->depth; p++)
{
st->paths[idx][p] = strdup(st->path_stack[p]);
}
st->paths[idx][st->depth] = NULL;
st->count++;
}
else if (ctype == hmmmm_config_DeviceConfig_ComposeDeviceConfig)
{
hmmmm_config_ComposeDeviceConfig_table_t child =
(hmmmm_config_ComposeDeviceConfig_table_t)
hmmmm_config_DeviceEntry_config(entry);
if (load_devices_recursive(child, st, errbuf) != 0) {
return -1;
}
}
else
{
snprintf(errbuf, 1024, "unknown device config type %u", ctype);
return -1;
}
st->depth--;
}
return 0;
}
static void free_load_state_paths(LoadState* st)
{
for (size_t i = 0; i < st->count; i++) {
if (st->paths[i]) {
for (size_t j = 0; j < st->path_lens[i]; j++) {
free(st->paths[i][j]);
}
free(st->paths[i]);
}
for (size_t si = 0; si < st->seg_counts[i]; si++) {
free(st->seg_names[i][si]);
}
}
}
// Find a device index by its leaf ID (last path component)
static size_t find_device_by_id(LoadState* st, const char* id)
{
for (size_t i = 0; i < st->count; i++) {
if (st->path_lens[i] == 0) continue;
// Match by last path component (the device's own ID)
const char* leaf = st->paths[i][st->path_lens[i] - 1];
if (strcmp(leaf, id) == 0) return i;
}
return (size_t)~0;
}
// Find a segment index by name in a device
static size_t find_seg_by_name(device_handle_t* dev, size_t seg_count, const char* name)
{
const device_mem_t* mem = dev->ctx->deviceMem;
for (size_t i = 0; i < seg_count; i++) {
if (strcmp(mem->memsegNames[i], name) == 0) return i;
}
return (size_t)~0;
}
static int apply_projections(
hmmmm_config_ComposeDeviceConfig_table_t compose,
LoadState* st,
char* errbuf)
{
hmmmm_config_Projection_vec_t proj_vec =
hmmmm_config_ComposeDeviceConfig_projections(compose);
size_t proj_n = proj_vec ? hmmmm_config_Projection_vec_len(proj_vec) : 0;
for (size_t i = 0; i < proj_n; i++) {
hmmmm_config_Projection_table_t proj =
hmmmm_config_Projection_vec_at(proj_vec, i);
flatbuffers_string_t base_at = hmmmm_config_Projection_base_at(proj);
flatbuffers_string_t base_seg = hmmmm_config_Projection_base_seg(proj);
flatbuffers_string_t target_at = hmmmm_config_Projection_target_at(proj);
flatbuffers_string_t target_seg = hmmmm_config_Projection_target_seg(proj);
uint32_t shift = hmmmm_config_Projection_shift(proj);
if (!base_at || !base_seg || !target_at || !target_seg) {
snprintf(errbuf, 1024, "projection %zu: missing field", i);
return -1;
}
size_t base_idx = find_device_by_id(st, base_at);
if (base_idx == (size_t)~0) {
snprintf(errbuf, 1024, "projection %zu: base device '%s' not found", i, base_at);
return -1;
}
size_t target_idx = find_device_by_id(st, target_at);
if (target_idx == (size_t)~0) {
snprintf(errbuf, 1024, "projection %zu: target device '%s' not found", i, target_at);
return -1;
}
size_t base_seg_idx = find_seg_by_name(
st->handles[base_idx], st->seg_counts[base_idx], base_seg);
if (base_seg_idx == (size_t)~0) {
snprintf(errbuf, 1024, "projection %zu: base segment '%s' not found in '%s'",
i, base_seg, base_at);
return -1;
}
size_t target_seg_idx = find_seg_by_name(
st->handles[target_idx], st->seg_counts[target_idx], target_seg);
if (target_seg_idx == (size_t)~0) {
snprintf(errbuf, 1024, "projection %zu: target segment '%s' not found in '%s'",
i, target_seg, target_at);
return -1;
}
// Remap: target device's segment now points to base device's segment memory + shift
void* base_ptr = st->handles[base_idx]->ctx->deviceMem->cells[base_seg_idx];
st->handles[target_idx]->ctx->deviceMem->cells[target_seg_idx] =
(uint8_t*)base_ptr + shift;
printf("[CTRL/CONFIG] projection: %s.%s -> %s.%s (shift=%u)\n",
base_at, base_seg, target_at, target_seg, shift);
}
return 0;
}
// ── Intercept handler context and functions ─────────────────────────────────
typedef struct {
uint8_t* point_cell; // direct pointer to point device's cell
uint8_t* base_cell; // direct pointer to base device's cell (shadow_copy)
uint8_t word_len;
} intercept_ctx_t;
// shadow_replace: read from point device instead of base
static void* intercept_replace_read(uint64_t ident, uint64_t addr, void* rawCells)
{
const intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
return ctx->point_cell;
}
// shadow_replace: write to point device instead of base
static void intercept_replace_write(uint64_t ident, uint64_t addr, void* rawCells, void* data)
{
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
memcpy(ctx->point_cell, data, ctx->word_len);
}
// shadow_copy: write to both base and point
static void intercept_copy_write(uint64_t ident, uint64_t addr, void* rawCells, void* data)
{
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
memcpy(ctx->base_cell, data, ctx->word_len);
memcpy(ctx->point_cell, data, ctx->word_len);
}
#define MAX_INTERCEPTS 256
static int apply_intercepts(
hmmmm_config_ComposeDeviceConfig_table_t compose,
LoadState* st,
intercept_ctx_t** out_ctxs,
size_t* out_ctx_count,
char* errbuf)
{
hmmmm_config_Intercept_vec_t icpt_vec =
hmmmm_config_ComposeDeviceConfig_intercepts(compose);
size_t icpt_n = icpt_vec ? hmmmm_config_Intercept_vec_len(icpt_vec) : 0;
*out_ctx_count = 0;
if (icpt_n == 0) { *out_ctxs = NULL; return 0; }
if (icpt_n > MAX_INTERCEPTS) {
snprintf(errbuf, 1024, "too many intercepts (%zu)", icpt_n);
return -1;
}
// Allocate array of intercept contexts (one per intercept)
intercept_ctx_t* ctxs = calloc(icpt_n, sizeof(intercept_ctx_t));
if (!ctxs) { snprintf(errbuf, 1024, "alloc intercept contexts"); return -1; }
for (size_t i = 0; i < icpt_n; i++) {
hmmmm_config_Intercept_table_t icpt =
hmmmm_config_Intercept_vec_at(icpt_vec, i);
flatbuffers_string_t name = hmmmm_config_Intercept_name(icpt);
int8_t op = hmmmm_config_Intercept_op(icpt);
int8_t mode = hmmmm_config_Intercept_mode(icpt);
flatbuffers_string_t base_at = hmmmm_config_Intercept_base_at(icpt);
flatbuffers_string_t base_seg = hmmmm_config_Intercept_base_seg(icpt);
uint32_t base_addr = hmmmm_config_Intercept_base_addr(icpt);
flatbuffers_string_t point_at = hmmmm_config_Intercept_point_at(icpt);
flatbuffers_string_t point_seg = hmmmm_config_Intercept_point_seg(icpt);
uint32_t point_addr = hmmmm_config_Intercept_point_addr(icpt);
if (!base_at || !base_seg || !point_at || !point_seg) {
snprintf(errbuf, 1024, "intercept %zu: missing field", i);
free(ctxs);
return -1;
}
// Resolve base device + segment
size_t base_idx = find_device_by_id(st, base_at);
if (base_idx == (size_t)~0) {
snprintf(errbuf, 1024, "intercept %zu: base device '%s' not found", i, base_at);
free(ctxs); return -1;
}
size_t base_seg_idx = find_seg_by_name(
st->handles[base_idx], st->seg_counts[base_idx], base_seg);
if (base_seg_idx == (size_t)~0) {
snprintf(errbuf, 1024, "intercept %zu: base segment '%s' not found in '%s'",
i, base_seg, base_at);
free(ctxs); return -1;
}
// Resolve point device + segment
size_t point_idx = find_device_by_id(st, point_at);
if (point_idx == (size_t)~0) {
snprintf(errbuf, 1024, "intercept %zu: point device '%s' not found", i, point_at);
free(ctxs); return -1;
}
size_t point_seg_idx = find_seg_by_name(
st->handles[point_idx], st->seg_counts[point_idx], point_seg);
if (point_seg_idx == (size_t)~0) {
snprintf(errbuf, 1024, "intercept %zu: point segment '%s' not found in '%s'",
i, point_seg, point_at);
free(ctxs); return -1;
}
device_mem_t* base_mem = st->handles[base_idx]->ctx->deviceMem;
device_mem_t* point_mem = st->handles[point_idx]->ctx->deviceMem;
// Compute global address on base device (for handler array index)
uint64_t base_global = base_mem->memsegShifts[base_seg_idx] + base_addr;
// Get word length from base segment
uint8_t wl = 1; // default
// memsegShifts difference gives segment size; use rawCells layout
// We can get wordLen from the point segment shift or use 1 for byte-addressable
// Fill intercept context
intercept_ctx_t* ctx = &ctxs[i];
ctx->word_len = wl;
ctx->base_cell = (uint8_t*)base_mem->rawCells + base_global;
ctx->point_cell = (uint8_t*)point_mem->rawCells
+ point_mem->memsegShifts[point_seg_idx] + point_addr;
uint64_t ident = (uint64_t)(uintptr_t)ctx;
if (mode == hmmmm_config_InterceptMode_shadow_replace) {
if (op == hmmmm_config_InterceptOp_op_read ||
op == hmmmm_config_InterceptOp_op_both) {
base_mem->smartAddrReadHandlers[base_global].func = intercept_replace_read;
base_mem->smartAddrReadHandlers[base_global].ident = ident;
}
if (op == hmmmm_config_InterceptOp_op_write ||
op == hmmmm_config_InterceptOp_op_both) {
base_mem->smartAddrWriteHandlers[base_global].func = intercept_replace_write;
base_mem->smartAddrWriteHandlers[base_global].ident = ident;
}
} else if (mode == hmmmm_config_InterceptMode_shadow_copy) {
if (op == hmmmm_config_InterceptOp_op_write ||
op == hmmmm_config_InterceptOp_op_both) {
base_mem->smartAddrWriteHandlers[base_global].func = intercept_copy_write;
base_mem->smartAddrWriteHandlers[base_global].ident = ident;
}
} else if (mode == hmmmm_config_InterceptMode_callback) {
// Callback mode: not implemented yet — skip with warning
printf("[CTRL/CONFIG] intercept %zu '%s': callback mode not yet implemented\n",
i, name ? name : "(unnamed)");
} else {
snprintf(errbuf, 1024, "intercept %zu: unknown mode %d", i, mode);
free(ctxs); return -1;
}
printf("[CTRL/CONFIG] intercept: %s.%s[%u] -> %s.%s[%u] mode=%s op=%s\n",
base_at, base_seg, base_addr,
point_at, point_seg, point_addr,
hmmmm_config_InterceptMode_name(mode),
hmmmm_config_InterceptOp_name(op));
}
*out_ctxs = ctxs;
*out_ctx_count = icpt_n;
return 0;
}
static void free_old_config(EmulContext* emulContext)
{
if (emulContext->devicesCount == 0)
{
return;
}
// Free intercept context array (single contiguous allocation)
if (emulContext->interceptCtxs)
{
free(emulContext->interceptCtxs);
emulContext->interceptCtxs = NULL;
emulContext->interceptCtxCount = 0;
}
for (size_t i = 0; i < emulContext->devicesCount; i++)
{
device_handle_t* dev = (device_handle_t*)emulContext->deviceHandles[i];
closeBaseDevice(dev);
free(dev);
if (emulContext->deviceStreamRegs[i])
{
free(emulContext->deviceStreamRegs[i]->regs);
free(emulContext->deviceStreamRegs[i]);
}
}
free(emulContext->deviceHandles);
free(emulContext->devicesMem);
free(emulContext->deviceStreamRegs);
emulContext->deviceHandles = NULL;
emulContext->devicesMem = NULL;
emulContext->deviceStreamRegs = NULL;
emulContext->devicesCount = 0;
emulContext->simRateLimit = 0;
if (emulContext->deviceIdMappingMsg)
{
free(emulContext->deviceIdMappingMsg);
emulContext->deviceIdMappingMsg = NULL;
emulContext->deviceIdMappingMsgLen = 0;
}
}
void handleConfigCtrlMessage(
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{
printf("[CTRL/CONFIG] received config load request\n");
char errbuf[1024];
// Block config load only while actively executing (ticking)
if (*emulContext->emulState != EMUL_STATE_STILL) {
snprintf(errbuf, 1024, "reset emulator before loading config, current state is %d", *emulContext->emulState);
printf("[CTRL/CONFIG] error: %s\n", errbuf);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
hmmmm_config_EmulationConfig_table_t econf =
hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_config(msg);
if (!econf) {
snprintf(errbuf, 1024, "missing EmulationConfig");
printf("[CTRL/CONFIG] error: %s\n", errbuf);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
flatbuffers_string_t root_id = hmmmm_config_EmulationConfig_root_id(econf);
hmmmm_config_ComposeDeviceConfig_table_t root =
hmmmm_config_EmulationConfig_root(econf);
if (!root) {
snprintf(errbuf, 1024, "missing root ComposeDeviceConfig");
printf("[CTRL/CONFIG] error: %s\n", errbuf);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
const uint64_t simRateLimit = hmmmm_config_EmulationConfig_sim_rate_limit(econf);
// Free old config
free_old_config(emulContext);
// Load new config
LoadState st = {0};
if (root_id) {
st.path_stack[0] = root_id;
st.depth = 1;
}
if (load_devices_recursive(root, &st, errbuf) != 0) {
printf("[CTRL/CONFIG] load error: %s\n", errbuf);
// Close any devices that were opened before the failure
for (size_t i = 0; i < st.count; i++) {
closeBaseDevice(st.handles[i]);
free(st.handles[i]);
}
free_load_state_paths(&st);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
printf("[CTRL/CONFIG] loaded %zu devices\n", st.count);
// Apply projections
if (apply_projections(root, &st, errbuf) != 0) {
printf("[CTRL/CONFIG] projection error: %s\n", errbuf);
for (size_t i = 0; i < st.count; i++) {
closeBaseDevice(st.handles[i]);
free(st.handles[i]);
}
free_load_state_paths(&st);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
// Apply intercepts
intercept_ctx_t* icpt_ctxs = NULL;
size_t icpt_count = 0;
if (apply_intercepts(root, &st, &icpt_ctxs, &icpt_count, errbuf) != 0) {
printf("[CTRL/CONFIG] intercept error: %s\n", errbuf);
for (size_t i = 0; i < st.count; i++) {
closeBaseDevice(st.handles[i]);
free(st.handles[i]);
}
free_load_state_paths(&st);
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
// Store intercept context array for later cleanup (single contiguous allocation)
emulContext->interceptCtxs = icpt_ctxs;
emulContext->interceptCtxCount = icpt_count;
// Allocate new arrays for EmulContext
size_t dc = st.count;
emulContext->devicesCount = dc;
emulContext->simRateLimit = simRateLimit;
emulContext->deviceHandles = calloc(dc, sizeof(void*));
emulContext->devicesMem = calloc(dc, sizeof(uint8_t*));
emulContext->devicesMemSegAddrs = calloc(dc, sizeof(size_t*));
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*));
if (!emulContext->deviceHandles || !emulContext->devicesMem ||
!emulContext->deviceStreamRegs) {
free(emulContext->deviceHandles);
free(emulContext->devicesMem);
free(emulContext->deviceStreamRegs);
emulContext->deviceHandles = NULL;
emulContext->devicesMem = NULL;
emulContext->deviceStreamRegs = NULL;
emulContext->devicesCount = 0;
for (size_t i = 0; i < st.count; i++) {
closeBaseDevice(st.handles[i]);
free(st.handles[i]);
}
free_load_state_paths(&st);
snprintf(errbuf, sizeof(errbuf), "failed to allocate device arrays");
size_t msg_len;
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
return;
}
for (size_t i = 0; i < dc; i++)
{
emulContext->deviceHandles[i] = st.handles[i];
emulContext->devicesMem[i] = st.handles[i]->ctx->deviceMem->rawCells;
emulContext->devicesMemSegAddrs[i] = calloc(st.seg_counts[i], sizeof(size_t));
for(size_t seg_id = 0; seg_id < st.seg_counts[i]; seg_id++)
{
// size_t segShift = (size_t)(st.handles[i]->ctx->deviceMem->cells[seg_id]) - (size_t)(st.handles[i]->ctx->deviceMem->cells[0]);
size_t segShift = st.handles[i]->ctx->deviceMem->memsegShifts[seg_id];
emulContext->devicesMemSegAddrs[i][seg_id] = segShift;
}
DeviceSegStreamReg* dsr = calloc(1, sizeof(DeviceSegStreamReg));
if (!dsr)
{
emulContext->deviceStreamRegs[i] = NULL;
continue;
}
dsr->allocatedSize = 4;
dsr->regCount = 0;
dsr->regs = calloc(4, sizeof(StreamReg));
if (!dsr->regs)
{
free(dsr);
emulContext->deviceStreamRegs[i] = NULL;
continue;
}
emulContext->deviceStreamRegs[i] = dsr;
}
// Reset emulation state to STILL and notify all clients
if (*emulContext->emulState != EMUL_STATE_STILL)
{
*emulContext->emulState = EMUL_STATE_STILL;
size_t notify_len;
uint8_t* notify = fb_build_exec_notify(0, *emulContext->clockCounter, EMUL_STATE_STILL, &notify_len);
broadcastClients(emulContext, notify, notify_len);
}
// Build DeviceIdMappingNotif and broadcast to all connected clients
size_t msg_len;
printf("fuck!\n");
for(size_t i = 0; i < emulContext->devicesCount; i++)
{
const device_handle_t* handl = emulContext->deviceHandles[i];
for(size_t j = 0; j < st.seg_counts[i]; j++)
{
if(st.seg_names[i][j])
{
free(st.seg_names[i][j]);
}
printf("setting device %lu segment %lu name: \"%s\"\n", i, j, handl->ctx->deviceMem->memsegNames[j]);
st.seg_names[i][j] = strdup(handl->ctx->deviceMem->memsegNames[j]);
}
}
//*emulContext->resetRequest = 1;
uint8_t* out = fb_build_config_device_id_mapping(
nonce, st.paths, st.path_lens, st.seg_names, st.seg_counts, dc, &msg_len);
free_load_state_paths(&st);
// Cache a copy for newly connecting clients
if (emulContext->deviceIdMappingMsg) {
free(emulContext->deviceIdMappingMsg);
}
emulContext->deviceIdMappingMsg = malloc(msg_len);
if (emulContext->deviceIdMappingMsg) {
memcpy(emulContext->deviceIdMappingMsg, out, msg_len);
emulContext->deviceIdMappingMsgLen = msg_len;
}
// Broadcast to all clients (broadcastClients takes ownership of out)
broadcastClients(emulContext, out, msg_len);
}

View File

@@ -1,199 +1,135 @@
#include "proto/handlers/control.h" #include "proto/handlers/control.h"
#include "proto/handlers/config.h"
#include "proto/pack.h" #include <stdio.h>
#include "proto/enums.h"
#include "panic.h"
#include "state.h" #include "state.h"
#include "proto/msg.h"
#include "proto/dial.h"
// #include <alloca.h> #include "control_reader.h"
#include "exec_ctrl_reader.h"
#include "config_ctrl_reader.h"
#include "setup_buf_reader.h"
#include "orphaned_reader.h"
#include "lost_reader.h"
void handleIncomingExecControl(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)
{ {
printf("ctrl exec\n"); switch (prompt) {
uint8_t stateOp = ((const uint8_t*)(msg->payload))[0]; case hmmmm_ctrl_exec_ExecPrompt_start: return EMUL_STATE_OP_START;
printf("state operation: %u\n", stateOp); case hmmmm_ctrl_exec_ExecPrompt_pause: return EMUL_STATE_OP_PAUSE;
uint8_t newEmulState = switchNewEmulState(*emulContext->emulState, stateOp); case hmmmm_ctrl_exec_ExecPrompt_resume: return EMUL_STATE_OP_RESUME;
if(newEmulState != *emulContext->emulState) case hmmmm_ctrl_exec_ExecPrompt_stop: return EMUL_STATE_OP_STOP;
{ case hmmmm_ctrl_exec_ExecPrompt_reset: return EMUL_STATE_OP_RESET;
printf("Switch state %u -> %u\n", *emulContext->emulState, newEmulState); default: return 0;
*emulContext->emulState = newEmulState;
size_t len = 0;
uint8_t* notify = createControlNotifyMessage(msg->nonce, *emulContext->clockCounter, newEmulState, &len);
broadcastClients(emulContext, notify, len);
} }
} }
void handleIncomingEditConnectionSettingsControl(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) void handleIncomingCtrlMessage(
hmmmm_ctrl_CtrlClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
uint64_t fallbackQLen = decodeBytesToU64(msg->payload); hmmmm_ctrl_CtrlClientPayload_union_type_t ptype =
uint64_t orphanedDeadTimeout = decodeBytesToU64(msg->payload + 8); hmmmm_ctrl_CtrlClientMessage_payload_type(msg);
ctx->orphanedDeadTimeout = orphanedDeadTimeout; printf("[CTRL] client=%lu nonce=%lu type=%s\n",
uint8_t* newFallbackQPtr = realloc(ctx->fallbackOutcomeQ->ptr, sizeof(OutgoingMessage) * fallbackQLen); ctx->clientId, nonce,
NULL_GUARD(newFallbackQPtr); hmmmm_ctrl_CtrlClientPayload_type_name(ptype));
ctx->fallbackOutcomeQ->ptr = newFallbackQPtr;
ctx->fallbackOutcomeQ->allocatedSize = fallbackQLen;
size_t len = 0; if (ptype == hmmmm_ctrl_CtrlClientPayload_ExecCtrlMessage)
uint8_t* outMsg = createClientSetup(msg->nonce, ctx, &len);
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, len);
}
size_t fillOrphanedClientReportItem(uint8_t* outmsg, ClientContext* ctx)
{
printf("filling orphaned %lu with %lu packets\n", ctx->seatId, ctx->fallbackOutcomeQ->size - ctx->fallbackOutcomeQPadding);
encodeUintToBytes(ctx->seatId, outmsg);
encodeUintToBytes(ctx->orphanedAt, outmsg + 8);
encodeUintToBytes((uint64_t)(ctx->fallbackOutcomeQ->size - ctx->fallbackOutcomeQPadding), outmsg + 8 + 8);
return 8 * 3;
}
void handleIncomingListOrphaned(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
{
LinkedListEntry* clientEntry = *emulContext->clientsHead;
size_t orphanedCount = 0;
size_t orphanedPackets = 0;
while(clientEntry != NULL)
{ {
ClientContext* orphanedCtx = clientEntry->payload; hmmmm_ctrl_exec_ExecCtrlMessage_table_t exec =
if(orphanedCtx->orphanedAt != 0) (hmmmm_ctrl_exec_ExecCtrlMessage_table_t)
{ hmmmm_ctrl_CtrlClientMessage_payload(msg);
orphanedCount++;
orphanedPackets += orphanedCtx->fallbackOutcomeQ->size - ctx->fallbackOutcomeQPadding; hmmmm_ctrl_exec_ExecPrompt_enum_t prompt =
hmmmm_ctrl_exec_ExecCtrlMessage_prompt(exec);
uint64_t tick_count =
hmmmm_ctrl_exec_ExecCtrlMessage_tick_count(exec);
printf("[CTRL/EXEC] prompt=%s tick_count=%lu\n",
hmmmm_ctrl_exec_ExecPrompt_name(prompt), tick_count);
uint8_t state_op = prompt_to_state_op(prompt);
if (state_op == 0) {
printf("[CTRL/EXEC] unknown prompt, ignoring\n");
return;
} }
clientEntry = clientEntry->nextEntry;
}
printf("found %lu orphaned clients with %lu packets\n", orphanedCount, orphanedPackets); if(state_op == EMUL_STATE_OP_RESET && emulContext->emulState != EMUL_STATE_STILL)
const size_t orphanedListReportItemSize = 8 * 3;
const size_t msgLen = ((orphanedListReportItemSize * orphanedCount) + 9 + 8);
uint8_t* outMsg = malloc(sizeof(uint8_t) * msgLen);
NULL_GUARD(outMsg);
fillHead(msg->nonce, PACKET_TYPE_CTRL, CTRL_TYPE_LIST_ORPHANED, outMsg);
clientEntry = *emulContext->clientsHead;
size_t filledCount = 0;
uint8_t* outMsgCursor = outMsg + 9;
encodeUintToBytes((uint64_t)orphanedCount, outMsgCursor);
outMsgCursor += 8;
while(clientEntry != NULL && filledCount < orphanedCount)
{
ClientContext* orphanedCtx = clientEntry->payload;
if(orphanedCtx->orphanedAt != 0)
{ {
size_t filled = fillOrphanedClientReportItem(outMsgCursor, orphanedCtx); *emulContext->resetRequest = 1;
outMsgCursor += filled;
filledCount++;
} }
clientEntry = clientEntry->nextEntry; else
}
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, msgLen);
}
void handleIncomingLoadFailed(const BaseMessage* msg, ClientContext* ctx, const EmulContext* emulContext)
{
const uint8_t readMode = ((const uint8_t*)msg->payload)[0];
const uint64_t seatId = decodeBytesToU64(((const uint8_t*)msg->payload) + 1);
LinkedListEntry* clientEntry = *emulContext->clientsHead;
ClientContext* orphanedCtx = NULL;
while(clientEntry != NULL)
{
orphanedCtx = clientEntry->payload;
if(orphanedCtx->orphanedAt != 0)
{ {
if(orphanedCtx->seatId == seatId) uint8_t new_state = switchNewEmulState(*emulContext->emulState, state_op);
{ *emulContext->emulState = new_state;
break; printf("[CTRL/EXEC] state -> %u\n", new_state);
if (new_state == EMUL_STATE_EXEC && tick_count > 0) {
*emulContext->tickTarget = *emulContext->clockCounter + tick_count;
printf("set ticket target at %lu\n", *emulContext->tickTarget);
} else if (new_state == EMUL_STATE_EXEC) {
*emulContext->tickTarget = 0;
printf("reset tick target\n");
} }
size_t msg_len;
uint8_t* out = fb_build_exec_notify(0, *emulContext->clockCounter, new_state, &msg_len);
broadcastClients(emulContext, out, msg_len);
} }
clientEntry = clientEntry->nextEntry;
} }
else if (ptype == hmmmm_ctrl_CtrlClientPayload_SetupBuf)
if(clientEntry == NULL || orphanedCtx == NULL)
{ {
return; 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->outMsgQueue, ctx->clientId, out, msg_len);
} }
else if (ptype == hmmmm_ctrl_CtrlClientPayload_OrphanedRequest)
size_t payloadSize = 0;
OutgoingMessage* failedQueue = (OutgoingMessage*)orphanedCtx->fallbackOutcomeQ->ptr;
for(size_t i = orphanedCtx->fallbackOutcomeQPadding; i < orphanedCtx->fallbackOutcomeQ->size; i++)
{ {
OutgoingMessage* failedMessage = &failedQueue[i]; printf("[CTRL/ORPHANED] returning empty list\n");
payloadSize += 8;
payloadSize += failedMessage->msgLen; size_t msg_len;
uint8_t* out = fb_build_orphaned_response(nonce, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
} }
else if (ptype == hmmmm_ctrl_CtrlClientPayload_ConfigCtrlMessage)
const size_t outLen = 9 + payloadSize;
printf("preparing failed messages report: %lu messages, %lu payload size, %lu message size\n",
orphanedCtx->fallbackOutcomeQ->size - orphanedCtx->fallbackOutcomeQPadding,
payloadSize,
outLen);
uint8_t* outMsg = malloc(sizeof(uint8_t) * outLen);
fillHead(msg->nonce, PACKET_TYPE_CTRL, CTRL_TYPE_LOAD_FAILED, outMsg);
uint8_t* outMsgCursor = outMsg + 9;
for(size_t i = orphanedCtx->fallbackOutcomeQPadding; i < orphanedCtx->fallbackOutcomeQ->size; i++)
{ {
OutgoingMessage* failedMessage = &failedQueue[i]; handleConfigCtrlMessage(
encodeUintToBytes(failedMessage->msgLen, outMsgCursor); (hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t)
outMsgCursor += 8; hmmmm_ctrl_CtrlClientMessage_payload(msg),
for(size_t j = 0; j < failedMessage->msgLen; j++) nonce, ctx, emulContext);
{
outMsgCursor[j] = failedMessage->msg[j];
}
outMsgCursor += failedMessage->msgLen;
} }
else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest)
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, outLen);
if(readMode == 1)
{ {
dispatchOutgoingMessage(emulContext->outBufs, ctx, NULL, 0); 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);
void handleIncomingControlMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
{ printf("[CTRL/LOST] seat_id=%lu returning empty list\n", seat_id);
switch (msg->payloadHeader)
{ size_t msg_len;
case CTRL_TYPE_EXEC: uint8_t* out = fb_build_lost_messages_response(nonce, seat_id, &msg_len);
{ dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
handleIncomingExecControl(msg, emulContext);
break;
}
case CTRL_TYPE_LIST_ORPHANED:
{
handleIncomingListOrphaned(msg, ctx, emulContext);
break;
}
case CTRL_TYPE_LOAD_FAILED:
{
handleIncomingLoadFailed(msg, ctx, emulContext);
break;
}
case CTRL_TYPE_SETUP_CONNECTION:
{
handleIncomingEditConnectionSettingsControl(msg, ctx, emulContext);
break;
}
default:
break;
} }
} }

View File

@@ -1,84 +1,171 @@
#include "proto/handlers/mem.h" #include "proto/handlers/mem.h"
#include "proto/enums.h"
#include "proto/pack.h" #include <stdio.h>
#include "panic.h"
#include <string.h> #include <string.h>
#include "base_device.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "mem_reader.h"
#include "pub/libhmmmm/mem.h"
// #define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
uint8_t* createMemReadResponseMessage(uint64_t nonce, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut) static void printMemory(void* cells, uint64_t cellsCount)
{ {
*lenOut = 9 + 8 + payloadLen;
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
NULL_GUARD(outmsg);
encodeUintToBytes(nonce, outmsg); for(uint64_t i = 0; i < cellsCount; i++)
outmsg[8] = PACKET_TYPE_MEM << 4;
outmsg[8] |= MEM_TYPE_READ_RESP;
encodeUintToBytes(clockCounter, outmsg + 9);
memcpy(outmsg + 9 + 8, payload, payloadLen);
return outmsg;
}
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; printf("%lu: 0x%04X", i, ((uint8_t*)cells)[i]);
}
// uint8_t wasRead = 0;
// for (uint8_t j = 0; j < mem->memreadLen; j++)
// {
// if(mem->memreadCellAddrs[j] == i)
// {
// wasRead = 1;
// break;
// }
// }
// uint8_t wasWrite = 0;
// for (uint8_t j = 0; j < mem->memwriteLen; j++)
// {
// if(mem->memwriteCellAddrs[j] == i)
// {
// wasWrite = 1;
// break;
// }
// }
//TODO: lookup config for global addr conversion // if (wasRead == 1)
uint64_t globalAddr = startAddr + segId * 128; // {
// printf("\t[was read]");
// }
// else
// {
// printf("\t[was not read]");
// }
uint8_t* readPtr = emulContext->devicesMem[devId] + globalAddr; // if (wasWrite == 1)
// {
size_t outLen = 0; // printf("\t[was written]");
uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, readPtr, readLen, &outLen); // }
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, outLen); // else
} // {
// printf("\t[was not written]");
// }
void handleIncomingMemWrite(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) printf("\n");
{ }
uint64_t devId = decodeBytesToU64(msg->payload); }
uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16); void handleIncomingMemMessage(
hmmmm_mem_MemClientMessage_table_t msg,
if(devId >= emulContext->devicesCount) uint64_t nonce,
{ ClientContext* ctx,
return; EmulContext* emulContext)
} {
hmmmm_mem_MemClientPayload_union_type_t ptype =
//TODO: lookup config for global addr conversion hmmmm_mem_MemClientMessage_payload_type(msg);
uint64_t globalAddr = startAddr + segId * 128;
// printf("[MEM] client=%lu nonce=%lu type=%s\n",
uint8_t* writePtr = emulContext->devicesMem[devId] + globalAddr; // ctx->clientId, nonce,
// hmmmm_mem_MemClientPayload_type_name(ptype));
memcpy(writePtr, msg->payload + 24, msg->payloadLen - 24);
if (ptype == hmmmm_mem_MemClientPayload_MemReadRequest)
size_t outLen = 0; {
uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, writePtr, msg->payloadLen - 24, &outLen); hmmmm_mem_MemReadRequest_table_t req =
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, outLen); (hmmmm_mem_MemReadRequest_table_t)
} hmmmm_mem_MemClientMessage_payload(msg);
uint32_t dev_id = hmmmm_mem_MemReadRequest_device_id(req);
void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext) volatile uint32_t seg_id = hmmmm_mem_MemReadRequest_seg_id(req);
{ uint32_t offset = hmmmm_mem_MemReadRequest_offset(req);
if(msg->payloadHeader == MEM_TYPE_READ_REQ) uint32_t length = hmmmm_mem_MemReadRequest_length(req);
{
handleIncomingMemReadReq(msg, ctx, emulContext); // printf("[MEM/READ] device=%u seg=%u offset=%u len=%u\n",
} // dev_id, seg_id, offset, length);
else if(msg->payloadHeader == MEM_TYPE_WRITE_PUSH)
{ if (dev_id >= (uint32_t)emulContext->devicesCount) {
handleIncomingMemWrite(msg, ctx, emulContext); printf("[MEM/READ] invalid device %u\n", dev_id);
return;
}
// offset = (uint32_t)(((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
// if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
// printf("[MEM/READ] out of bounds\n");
// return;
// }
// printf("[MEM/READ] from %d/%d+%d:%d\n", dev_id, seg_id, offset, length);
device_handle_t* handl = emulContext->deviceHandles[dev_id];
const uint8_t* base = handl->ctx->deviceMem->cells[seg_id]; //emulContext->devicesMem[dev_id] + handl->ctx->deviceMem->memsegShifts[seg_id];
// for(size_t i = 0; i < length; i++)
// {
// printf("%02X ", (base + offset)[i]);
// }
// printf("\n");
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->outMsgQueue, 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(!data) {
printf("[MEM/WRITE] invalid data\n");
return;
}
// if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
// printf("[MEM/WRITE] out of bounds\n");
// return;
// }
// device_mem_t* devMem = emulContext->devicesMem[dev_id];
device_handle_t* handl = emulContext->deviceHandles[dev_id];
// printf("[MEM/WRITE] from %d/%d+%d:%lu\n", dev_id, seg_id, offset, data_len);
uint8_t* base = handl->ctx->deviceMem->cells[seg_id]; // emulContext->devicesMem[dev_id] + handl->ctx->deviceMem->memsegShifts[seg_id];
memcpy(base + offset, data, data_len);
// for(size_t i = 0; i < data_len; i++)
// {
// printf("%02X ", (base + offset)[i]);
// }
// printf("\n");
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->outMsgQueue, ctx->clientId, out, out_len);
} }
} }

View File

@@ -1,26 +1,32 @@
#include "proto/handlers/stream.h" #include "proto/handlers/stream.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "streamed.h" #include "streamed.h"
#include "proto/pack.h"
#include "proto/enums.h"
#include "panic.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) static 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]; 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]; 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); printf("Discard stream %u for client %lu\n", regId, ctx->clientId);
deviceRegs->regCount -= 1; deviceRegs->regCount--;
for(size_t j = i; j < deviceRegs->regCount; j++) for (size_t j = i; j < deviceRegs->regCount; j++) {
{
deviceRegs->regs[j] = deviceRegs->regs[j + 1]; deviceRegs->regs[j] = deviceRegs->regs[j + 1];
} }
break; break;
@@ -31,94 +37,121 @@ void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32
void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx) 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]; DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId];
StreamReg* newStreamRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount); StreamReg* newRegs = calloc(deviceRegs->allocatedSize, sizeof(StreamReg));
NULL_GUARD(newStreamRegs); NULL_GUARD(newRegs);
size_t newStreamRegsId = 0; 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]; StreamReg* reg = &deviceRegs->regs[i];
if(reg->clientContext->clientId != ctx->clientId) if (reg->clientContext->clientId != ctx->clientId)
{ {
newStreamRegs[newStreamRegsId] = *reg; newRegs[newCount++] = *reg;
newStreamRegsId++;
} }
else else
{ {
printf("Removing reg %d mode stream for %lu/%lu+%lu: [%u]\n", reg->mode, deviceId, reg->startAddr, reg->segLen, reg->regId); printf("Removing stream reg [%u] for client %lu\n",
reg->regId, ctx->clientId);
} }
} }
free(deviceRegs->regs); StreamReg* oldRegs = deviceRegs->regs;
deviceRegs->regCount = newStreamRegsId; deviceRegs->regCount = newCount;
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 * 2;
if(newAllocatedSize <= deviceRegs->regCount + 1)
{
newAllocatedSize = deviceRegs->regCount + 1;
}
StreamReg* newRegs = realloc(deviceRegs->regs, sizeof(StreamReg) * newAllocatedSize);
NULL_GUARD(newRegs);
deviceRegs->allocatedSize = newAllocatedSize;
deviceRegs->regs = newRegs; deviceRegs->regs = newRegs;
free(oldRegs);
} }
//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, notifMsg, len);
} }
void handleIncomingStreamMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
// ── Incoming stream messages ──────────────────────────────────────────────────
void handleIncomingStreamMessage(
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
uint8_t X = msg->payloadHeader >> 3; hmmmm_stream_StreamClientPayload_union_type_t ptype =
uint8_t streamType = msg->payloadHeader & 0b111; hmmmm_stream_StreamClientMessage_payload_type(msg);
if(streamType == STREAM_TYPE_REG_REQUEST) 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;
}
uint64_t globalOffset = (((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
uint32_t reg_id = ctx->streamRegIterator++;
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++];
reg->clientContext = ctx;
reg->regId = reg_id;
reg->segId = seg_id;
reg->startAddr = (uint64_t)offset;
reg->startGlobalAddr = globalOffset;
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->outMsgQueue, 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); hmmmm_stream_StreamDeregRequest_table_t req =
unregisterClientStream(emulContext, ctx, regId); (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);
}
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
} }
} }

View File

@@ -1,59 +1,69 @@
#include "proto/handlers/ws.h" #include "proto/handlers/ws.h"
#include "context.h"
#include "proto/handlers.h" #include "proto/handlers.h"
#include <stdio.h>
#include <stdlib.h>
#include "panic.h" #include "panic.h"
#include "proto/enums.h"
#include "my_mutex.h" #include "my_mutex.h"
#include "proto/enums.h"
#include "proto/handlers/auth.h" #include "proto/handlers/auth.h"
#include "proto/msg.h" #include "proto/msg.h"
#include "proto_verifier.h"
#include "proto_reader.h"
#include "ptQueue/inc/spsc.h"
#include "wsServer/include/ws.h"
void onWsOpen(ws_cli_conn_t client) void onWsOpen(ws_cli_conn_t client)
{ {
char errbuf[1024]; char errbuf[1024];
ptQueue* incomeQueue = ptQueueCreate(errbuf); queue_spsc_t *incomeQueue = malloc(sizeof(queue_spsc_t));
NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf); // queue_spsc_t incomeQueue, outcomeQueue;
if(incomeQueue == NULL || !queue_spsc_init(incomeQueue, 65536))
{
panic("Unable to create income queue");
}
queue_spsc_t *outcomeQueue = malloc(sizeof(queue_spsc_t));
if(outcomeQueue == NULL || !queue_spsc_init(outcomeQueue, 65536))
{
panic("Unable to create outcome queue");
}
// ptQueue* outcomeQueue = ptQueueCreate(errbuf); // = ptQueueCreate(errbuf);
// NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf);
// queue_spsc_t* outcomeQueue = ptQueueCreate(errbuf);
// NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf); // NULL_GUARD(outcomeQueue, "Unable to create outcome queue: %s\n", errbuf);
ClientContext* cctx = malloc(sizeof(ClientContext)); ClientContext* cctx = malloc(sizeof(ClientContext));
if(cctx == NULL) if (cctx == NULL) {
{ queue_spsc_destroy(incomeQueue);
ptQueueFree(incomeQueue); queue_spsc_destroy(outcomeQueue);
// ptQueueFree(incomeQueue);
// ptQueueFree(outcomeQueue); // ptQueueFree(outcomeQueue);
panic("Unable to allocate client context\n"); panic("Unable to allocate client context\n");
} }
cctx->clientId = client; cctx->clientId = client;
cctx->isAuthed = 0; cctx->isAuthed = 0;
cctx->streamRegIterator = 0; cctx->streamRegIterator = 0;
cctx->incomeQ = incomeQueue; cctx->incomeQ = incomeQueue;
// cctx->outcomeQ = outcomeQueue; cctx->outcomeQ = outcomeQueue;
cctx->connectedAt = (uint64_t)time(NULL); cctx->connectedAt = (uint64_t)time(NULL);
cctx->orphanedAt = 0;
cctx->orphanedDeadTimeout = 60;
cctx->fallbackOutcomeQPadding = 0;
cctx->fallbackOutcomeQ = malloc(sizeof(SizedPtr));
NULL_GUARD(cctx->fallbackOutcomeQ);
cctx->fallbackOutcomeQ->ptr = (void*) calloc(1024, sizeof(OutgoingMessage));
NULL_GUARD(cctx->fallbackOutcomeQ->ptr);
cctx->fallbackOutcomeQ->allocatedSize = 1024;
cctx->fallbackOutcomeQ->size = 0;
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
if(ev == NULL) if (ev == NULL) {
{ queue_spsc_destroy(incomeQueue);
ptQueueFree(incomeQueue); queue_spsc_destroy(outcomeQueue);
// ptQueueFree(incomeQueue);
// ptQueueFree(outcomeQueue); // ptQueueFree(outcomeQueue);
free(cctx->fallbackOutcomeQ->ptr);
free(cctx->fallbackOutcomeQ);
free(cctx); free(cctx);
panic("Unable to allocate register event"); panic("Unable to allocate register event");
} }
ev->regType = REG_EVTYPE_CONNECT; ev->regType = REG_EVTYPE_CONNECT;
ev->ctx = cctx; ev->ctx = cctx;
ws_set_connection_context(client, cctx); ws_set_connection_context(client, cctx);
@@ -61,96 +71,112 @@ void onWsOpen(ws_cli_conn_t client)
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
cctx->seatId = ctx->seatCounter; // int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
ctx->seatCounter++;
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); queue_spsc_push(ctx->regQueue, ev);
if(exitCode) // if (!queue_spsc_push(ctx->regQueue, ev)) {
{ // queue_spsc_destroy(incomeQueue);
ptQueueFree(incomeQueue); // queue_spsc_destroy(outcomeQueue);
// ptQueueFree(outcomeQueue); // // ptQueueFree(incomeQueue);
free(cctx->fallbackOutcomeQ->ptr); // // ptQueueFree(outcomeQueue);
free(cctx->fallbackOutcomeQ); // free(cctx);
free(cctx); // panic("Unable to push to reg queue: %s\n", errbuf);
// }
panic("Unable to push to reg queue: %s\n", errbuf);
}
} }
char *cli; const char* cli = ws_getaddress(client);
cli = ws_getaddress(client);
printf("Connection %lu opened, addr: %s\n", client, cli); printf("Connection %lu opened, addr: %s\n", client, cli);
} }
void onWsClose(ws_cli_conn_t client) void onWsClose(ws_cli_conn_t client)
{ {
pthread_t tid = pthread_self(); const char* cli = ws_getaddress(client);
char *cli; printf("Connection %lu closed, addr: %s\n", client, cli);
cli = ws_getaddress(client);
printf("Connection %lu closed, addr: %s, thread id: %lu\n", client, cli, tid);
char errbuf[1024]; char errbuf[1024];
ClientContext* cctx = ws_get_connection_context(client); ClientContext* cctx = ws_get_connection_context(client);
if(cctx == NULL) if (cctx == NULL) {
{ printf("Unable to get client context for %lu\n", client);
cctx = NULL;
printf("Unable to get client context for client %lu\n", client);
} }
ServerContext* ctx = ws_get_server_context(client); ServerContext* ctx = ws_get_server_context(client);
if(ctx == NULL) if (ctx == NULL) {
{ printf("Unable to get server context for %lu\n", client);
printf("Unable to get server context for client %lu\n", client);
return; return;
} }
ClientRegistrationEvent* ev = malloc(sizeof(ClientContext)); ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
NULL_GUARD(ev); NULL_GUARD(ev);
ev->regType = REG_EVTYPE_CLOSE; ev->regType = REG_EVTYPE_CLOSE;
ev->ctx = cctx; ev->ctx = cctx;
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); queue_spsc_push(ctx->regQueue, ev);
if(exitCode) // int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
{ // if (!queue_spsc_push(ctx->regQueue, ev))
panic("Unable to push to reg queue: %s\n", errbuf); // {
} // 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); ClientContext* cctx = ws_get_connection_context(client);
if(cctx == NULL) if (cctx == NULL) {
{
panic("Unable to get client context\n"); panic("Unable to get client context\n");
} }
if (cctx->isAuthed == 0)
{ if (!cctx->isAuthed) {
handle_auth(cctx, client, msg, size, type); handle_auth(cctx, client, msg, size, type);
return;
} }
else
{ // Verify the FlatBuffer before queuing
char errbuf[1024]; // if (hmmmm_ClientMessage_verify_as_root(msg, size)) {
BaseMessage* baseMsg = parseMessage(msg, size); // printf("Client %lu sent invalid FlatBuffer, dropping\n", client);
printf("msg\n");
int isErr = ptQueuePush(cctx->incomeQ, baseMsg, errbuf); // FILE *write_ptr;
if(isErr)
{ // write_ptr = fopen("/tmp/last_failed_packet.bin","wb"); // w for write, b for binary
panic("Unable to dispatch client message: %s\n", errbuf); // NULL_GUARD(write_ptr);
}
// fwrite(msg,size,1,write_ptr); // write 10 bytes from our buffer
// // free(write_ptr);
// fclose(write_ptr);
// 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;
fbmsg->ctx = cctx;
memcpy(fbmsg->data, msg, (size_t)size);
char errbuf[1024];
// int isErr = ptQueuePush(cctx->incomeQ, fbmsg, errbuf);
ServerContext* sctx = ws_get_server_context(client);
// printf("got message with len %lu\n", size);
queue_mpsc_push(sctx->inMsgQueue, fbmsg);
// if (!queue_spsc_push(cctx->incomeQ, fbmsg)) {
// if (!queue_mpsc_push(sctx->inMsgQueue, fbmsg)) {
// free(fbmsg->data);
// free(fbmsg);
// panic("Unable to queue client message: %s\n", errbuf);
// }
} }

View File

@@ -1,107 +1,404 @@
#include "proto/msg.h" #include "proto/msg.h"
#include "panic.h" #include "panic.h"
#include "proto/enums.h" #include "flatcc/flatcc_builder.h"
#include "proto/pack.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 "compacted_builder.h"
#include <string.h>
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; flatcc_builder_t B;
BaseMessage* msg = malloc(sizeof(BaseMessage)); if (flatcc_builder_init(&B)) {
NULL_GUARD(msg); panic("flatcc_builder_init failed\n");
uint64_t nonce = decodeBytesToU64(bytes); }
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; hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
msg->payloadHeader = bytes[8] & 0b1111;
msg->payloadLen = size - headerSize; uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
uint8_t* payload = malloc(sizeof(uint8_t) * (msg->payloadLen)); NULL_GUARD(buf);
NULL_GUARD(payload); return buf;
memcpy(payload, bytes + headerSize, msg->payloadLen);
msg->payload = payload;
return msg;
} }
void fillHead(uint64_t nonce, uint8_t packetType, uint8_t payloadHeader, uint8_t* outmsg) uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out)
{ {
encodeUintToBytes(nonce, outmsg); flatcc_builder_t B;
outmsg[8] = (uint8_t)((packetType & 0b1111) << 4); if (flatcc_builder_init(&B)) {
outmsg[8] |= payloadHeader & 0b1111; panic("flatcc_builder_init failed\n");
} }
uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut) hmmmm_ctrl_exec_notif_ExecNotifyMessage_ref_t notif =
{ hmmmm_ctrl_exec_notif_ExecNotifyMessage_create(
*lenOut = 9 + 10; &B, tclk, (hmmmm_ctrl_exec_notif_ExecState_enum_t)(int8_t)state);
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
NULL_GUARD(outmsg, "Unable to allocate message");
fillHead(nonce, PACKET_TYPE_CTRL, CTRL_TYPE_NOTIF_STATE, outmsg); hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_ExecNotifyMessage(notif);
outmsg[9] = NOTIF_TYPE_EXEC; hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
encodeUintToBytes(clockCounter, outmsg + 10); hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
outmsg[18] = newEmulState;
return outmsg; 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; flatcc_builder_reset(B);
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
NULL_GUARD(outmsg);
flatbuffers_uint8_vec_ref_t data_vec =
flatbuffers_uint8_vec_create(B, data, data_len);
fillHead(nonce, PACKET_TYPE_STREAM, (uint8_t)(X << 3) | STREAM_TYPE_REG_CONFIRM, outmsg); hmmmm_stream_StreamDataPush_ref_t push =
hmmmm_stream_StreamDataPush_create(B, stream_id, tclk, data_vec);
encodeUintToBytes(devId, outmsg + 9); hmmmm_stream_StreamServerPayload_union_ref_t stream_payload =
encodeUintToBytes(segId, outmsg + 9 + 8); hmmmm_stream_StreamServerPayload_as_StreamDataPush(push);
encodeUintToBytes(startAddr, outmsg + 9 + 8 + 8);
encodeUintToBytes(segLength, outmsg + 9 + 8 + 8 + 8); hmmmm_stream_StreamServerMessage_ref_t stream_msg =
encodeUintToBytes(regId, outmsg + 9 + 8 + 8 + 8 + 8); hmmmm_stream_StreamServerMessage_create(B, stream_payload);
return outmsg;
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)); hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb =
NULL_GUARD(outmsg); hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks);
uint64_t nonce = (uint64_t)~0; hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
encodeUintToBytes(nonce, outmsg); hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb);
outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (mode << 3) | STREAM_TYPE_SEND);
fillHead(nonce, PACKET_TYPE_STREAM, (uint8_t)((mode << 3) | STREAM_TYPE_SEND), outmsg); hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
encodeUintToBytes(regId, outmsg + 9); hmmmm_ServerPayload_union_ref_t setup_outer =
encodeUintToBytes(clockCounter, outmsg + 9 + 4); hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
memcpy(outmsg + 9 + 4 + 8, payload, payloadLen);
return outmsg; 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* createClientSetup(uint64_t nonce, ClientContext* ctx, size_t* lenOut) uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out)
{ {
*lenOut = 9 + 8 + 8; flatcc_builder_t B;
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut)); if (flatcc_builder_init(&B)) {
NULL_GUARD(outmsg); panic("flatcc_builder_init failed\n");
}
fillHead(nonce, PACKET_TYPE_CTRL, CTRL_TYPE_SETUP_CONNECTION, outmsg); // Use start/end directly: passing 0 to _create causes entries_add to return -1
uint8_t* payload = outmsg + 9; hmmmm_ctrl_orphaned_OrphanedResponse_start(&B);
encodeUintToBytes((uint64_t)ctx->fallbackOutcomeQ->allocatedSize, payload); hmmmm_ctrl_orphaned_OrphanedResponse_ref_t resp =
encodeUintToBytes((uint64_t)ctx->orphanedDeadTimeout, payload + 8); hmmmm_ctrl_orphaned_OrphanedResponse_end(&B);
return outmsg; 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[],
char* seg_names[][64], size_t seg_counts[],
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);
// Build SegIdEntry refs for this device's segments
size_t nseg = seg_counts[i];
hmmmm_ctrl_config_notif_SegIdEntry_ref_t seg_refs[nseg];
for (size_t si = 0; si < nseg; si++) {
flatbuffers_string_ref_t name_ref =
flatbuffers_string_create_str(&B, seg_names[i][si] ? seg_names[i][si] : "");
seg_refs[si] = hmmmm_ctrl_config_notif_SegIdEntry_create(
&B, name_ref, (uint32_t)si);
}
hmmmm_ctrl_config_notif_SegIdEntry_vec_start(&B);
for (size_t si = 0; si < nseg; si++) {
hmmmm_ctrl_config_notif_SegIdEntry_vec_push(&B, seg_refs[si]);
}
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;
}
uint8_t* fb_build_compacted_message(
uint8_t* const* msgs, const size_t* msg_lens, size_t count, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Build CompactedFrame refs bottom-up (FlatBuffers requires nested objects
// to be created before the table that references them).
hmmmm_CompactedFrame_ref_t frame_refs[count];
for (size_t i = 0; i < count; i++) {
flatbuffers_uint8_vec_ref_t data_vec =
flatbuffers_uint8_vec_create(&B, msgs[i], msg_lens[i]);
frame_refs[i] = hmmmm_CompactedFrame_create(&B, data_vec);
}
hmmmm_CompactedFrame_vec_start(&B);
for (size_t i = 0; i < count; i++) {
hmmmm_CompactedFrame_vec_push(&B, frame_refs[i]);
}
hmmmm_CompactedFrame_vec_ref_t frames_vec = hmmmm_CompactedFrame_vec_end(&B);
hmmmm_CompactedMessage_ref_t cm = hmmmm_CompactedMessage_create(&B, frames_vec);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CompactedMessage(cm);
// UINT64_MAX = no-nonce (fire-and-forget batch frame)
hmmmm_ServerMessage_create_as_root(&B, UINT64_MAX, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
} }