1 Commits

68 changed files with 1116 additions and 6066 deletions

1
.gitignore vendored
View File

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

View File

@@ -4,28 +4,13 @@ SRC_DIR=src
INC_DIR=inc INC_DIR=inc
CC=gcc CC=gcc
OBJDUMP=objdump OBJDUMP=objdump
LIBS=deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.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 #3 -march=native OPTIMIZE=-Og
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
@@ -35,43 +20,32 @@ 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 -Wno-unused-function -Wno-unused-but-set-variable DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith -Wno-analyzer-infinite-loop
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 -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 $(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-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 -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline -fno-lto LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fuse-linker-plugin $(LSECTIONS) -lm
# 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 proto python-proto target build: date deps Dir target compile_commands
rebuild: clean | build rebuild: clean | build
#$(info $(DEP)) $(info $(DEP))
-include $(DEP) -include $(DEP)
@@ -80,30 +54,20 @@ $(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 proto $(BUILD_DIR)/$(TARGET).elf target: date $(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) $(LDLIBS) -o $(BUILD_DIR)/$(TARGET).elf @$(CC) $(LFLAGS) $(OBJECTS) -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)
@@ -111,36 +75,21 @@ SrcDir:
IncDir: IncDir:
@mkdir -p $(INC_DIR) @mkdir -p $(INC_DIR)
ProtoDir: Dir: BuildDir SrcDir IncDir
@mkdir -p $(PROTO_INC_DIR)
@mkdir -p $(PROTO_SRC_DIR)
Dir: BuildDir SrcDir IncDir ProtoDir
python-proto: $(PY_PROTO_STAMP) .PHONY: clean deps
$(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)/* $(BUILD_DIR)/.* $(PROTO_INC_DIR)/* $(PROTO_SRC_DIR)/* @rm -rf $(BUILD_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 proto .NOTPARALLEL: date target rebuild deps
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
View File

@@ -1,250 +0,0 @@
# 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,5 +2,3 @@ tomlc99
ptQueue ptQueue
tomlc99 tomlc99
wsServer wsServer
flatcc
flatbuffers

38
deps/Makefile vendored
View File

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

1
deps/flatbuffers vendored

Submodule deps/flatbuffers deleted from e223d69b36

View File

@@ -1,72 +0,0 @@
#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

@@ -1,833 +0,0 @@
#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;
}

View File

View File

@@ -1,11 +0,0 @@
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;
}

View File

@@ -1,12 +0,0 @@
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

@@ -1,16 +0,0 @@
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

@@ -1,25 +0,0 @@
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

@@ -1,50 +0,0 @@
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

@@ -1,73 +0,0 @@
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

@@ -1,10 +0,0 @@
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

@@ -1,43 +0,0 @@
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

@@ -1,39 +0,0 @@
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

@@ -1,15 +0,0 @@
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

@@ -1,13 +0,0 @@
namespace hmmmm.ctrl.exec_notif;
enum ExecState: byte {
still = 0,
executing = 1,
paused = 2,
stopped = 3,
}
table ExecNotifyMessage {
tclk: uint64;
state: ExecState;
}

View File

@@ -1,19 +0,0 @@
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

@@ -1,16 +0,0 @@
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

@@ -1,10 +0,0 @@
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

@@ -1,8 +0,0 @@
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
}

View File

@@ -1,46 +0,0 @@
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;
}

View File

@@ -1,44 +0,0 @@
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

@@ -1,27 +0,0 @@
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

@@ -1,18 +0,0 @@
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

@@ -1,32 +0,0 @@
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;
}

View File

@@ -1,120 +0,0 @@
#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;
}

60
glob.toml Normal file
View File

@@ -0,0 +1,60 @@
[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, диаграмма с тестовым перехватчиком)
# - реализация проецирования памяти (диаграммы с колбасами)
# - тестовая прошивка (селфтест сложения-вычитания)
# - пример исполнения (?)

Submodule hmmmm_scripts deleted from 6a52cfbb3c

View File

@@ -1,23 +0,0 @@
#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,17 +2,25 @@
#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/spsc.h" #include "ptQueue/inc/ptQueue.h"
typedef struct { typedef struct {
ws_cli_conn_t clientId; ws_cli_conn_t clientId;
uint64_t seatId;
uint8_t isAuthed; uint8_t isAuthed;
queue_spsc_t* incomeQ; ptQueue* incomeQ;
queue_spsc_t* outcomeQ; // ptQueue* 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__

View File

@@ -1,40 +0,0 @@
#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__

View File

@@ -1,12 +0,0 @@
#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,60 +2,43 @@
#define __CONTEXT_H__ #define __CONTEXT_H__
#include <pthread.h> #include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include "ptQueue/inc/spsc.h" #include "ptQueue/inc/ptQueue.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;
CACHE_ALIGN _Atomic (uint8_t) readRequestIdx; _Atomic (uint8_t) readRequestIdx;
CACHE_ALIGN _Atomic (uint8_t) currWritingIdx; _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;
queue_spsc_t* regQueue; ptQueue* regQueue;
queue_mpsc_t* inMsgQueue;
uint8_t* accessToken; uint8_t* accessToken;
EmulContext* emulContext; EmulContext* emulContext;
_Atomic (uint64_t) seatCounter;
} ServerContext; } ServerContext;

View File

@@ -1,34 +0,0 @@
#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,9 +4,8 @@
#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(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen); void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ClientContext* client, uint8_t* msg, size_t msgLen);
#endif #endif

View File

@@ -16,6 +16,9 @@
#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

@@ -1,14 +0,0 @@
#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,16 +1,10 @@
#ifndef __PROTO_HANDLERS_CONTROL_H__ #ifndef __PROTO_HANDLERS_CONTROL_H__
#define __PROTO_HANDLERS_CONTROL_H__ #define __PROTO_HANDLERS_CONTROL_H__
#include <stdint.h> #include "proto/msg.h"
#include "client.h"
#include "context.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "control_reader.h"
void handleIncomingCtrlMessage( void handleIncomingControlMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext);
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,16 +1,10 @@
#ifndef __PROTO_HANDLERS_MEM_H__ #ifndef __PROTO_HANDLERS_MEM_H__
#define __PROTO_HANDLERS_MEM_H__ #define __PROTO_HANDLERS_MEM_H__
#include <stdint.h> #include "proto/msg.h"
#include "context.h"
#include "client.h"
#include "proto/dial.h" #include "proto/dial.h"
#include "mem_reader.h"
void handleIncomingMemMessage( void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext);
hmmmm_mem_MemClientMessage_table_t msg,
uint64_t nonce, #endif //ifndef __PROTO_HANDLERS_MEM_H__
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_MEM_H__

View File

@@ -1,19 +1,12 @@
#ifndef __PROTO_HANDLERS_STREAM_H__ #ifndef __PROTO_HANDLERS_STREAM_H__
#define __PROTO_HANDLERS_STREAM_H__ #define __PROTO_HANDLERS_STREAM_H__
#include <stdint.h> #include "proto/msg.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);
void handleIncomingStreamMessage( #endif //ifndef __PROTO_HANDLERS_STREAM_H__
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext);
#endif // __PROTO_HANDLERS_STREAM_H__

View File

@@ -1,82 +1,29 @@
#ifndef __PROTO_MSG_H__ #ifndef __PROTO_MSG_H__
#define __PROTO_MSG_H__ #define __PROTO_MSG_H__
#include "client.h"
#include "wsServer/include/ws.h" #include "wsServer/include/ws.h"
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "flatcc/flatcc_builder.h" #include "client.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 {
ws_cli_conn_t clientIdx; ClientContext* client;
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;
// Build a ServerMessage{AuthResponse} frame. Returns a malloc'd buffer; typedef struct {
// ownership is transferred to the caller (dispatchOutgoingMessage will free it). uint64_t nonce;
uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out); uint8_t packetType;
uint8_t payloadHeader;
const void* payload;
size_t payloadLen;
} BaseMessage;
// Build a ServerMessage{CtrlServerMessage{ExecNotifyMessage}} frame. BaseMessage* parseMessage(const uint8_t* bytes, size_t size);
uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out); void fillHead(uint64_t nonce, uint8_t packetType, uint8_t payloadHeader, uint8_t* outmsg);
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);
// Build a ServerMessage{StreamServerMessage{StreamDataPush}} frame. #endif //ifndef __PROTO_MSG_H__
// 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__

View File

@@ -1,29 +0,0 @@
#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__

View File

@@ -1,42 +0,0 @@
#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__

View File

@@ -1,95 +0,0 @@
#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,9 +12,7 @@
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;

Binary file not shown.

View File

@@ -1,75 +0,0 @@
#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;
}

View File

@@ -1,122 +0,0 @@
#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;
}

View File

@@ -1,86 +0,0 @@
#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);
}

View File

@@ -1,262 +0,0 @@
#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,6 +4,10 @@
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)
{ {

1634
src/main.c

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,20 +1,10 @@
#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)
@@ -27,7 +17,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->outMsgQueue, ctx->clientId, newMsg, msgLen); dispatchOutgoingMessage(emulContext->outBufs, ctx, newMsg, msgLen);
clientsHead = clientsHead->nextEntry; clientsHead = clientsHead->nextEntry;
} }
free(msg); free(msg);
@@ -37,25 +27,20 @@ void broadcastClients(EmulContext* emulContext, uint8_t* msg, size_t msgLen)
void dispatchOutgoingMessage(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen) void dispatchOutgoingMessage(OutgoingBuffers* outBufs, ClientContext* client, uint8_t* msg, size_t msgLen)
{ {
// OutgoingBuffers* outBufs = emulContext->outBufs; SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx];
// SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx]; if(p->size + 1 >= p->allocatedSize)
// if(p->size + 1 >= p->allocatedSize) {
// { printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx);
// // printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx); OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 2);
// OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 5); NULL_GUARD(newPtr);
// NULL_GUARD(newPtr); p->ptr = newPtr;
// p->ptr = newPtr; p->allocatedSize = p->allocatedSize * 2;
// 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->clientIdx = clientIdx; outmsg->client = client;
outmsg->dispatch_us = dial_now_us(); p->size++;
queue_spsc_push(outMsgQueue, outmsg);
// p->size++;
} }

View File

@@ -1,57 +1,71 @@
#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 "proto/msg.h" #include "state.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"
static void handleCloseClient(EmulContext* emulContext, ClientContext* ctx) void handleCloseClient(EmulContext* emulContext, LinkedListEntry* clientEntry)
{ {
// if (ctx->streamRegIterator > 0) { ClientContext* ctx = clientEntry->payload;
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->clientId); printf("open client %lu\n", ev->ctx->seatId);
LinkedListEntry* entry = malloc(sizeof(LinkedListEntry)); LinkedListEntry* newClientsLinkedListHead = malloc(sizeof(LinkedListEntry));
NULL_GUARD(entry); NULL_GUARD(newClientsLinkedListHead);
entry->payload = ev->ctx; newClientsLinkedListHead->payload = ev->ctx;
if (*emulContext->clientsHead != NULL) { if(*emulContext->clientsHead != NULL)
(*emulContext->clientsHead)->prevEntry = entry; {
(*emulContext->clientsHead)->prevEntry = newClientsLinkedListHead;
} }
entry->prevEntry = NULL; newClientsLinkedListHead->prevEntry = NULL;
entry->nextEntry = *emulContext->clientsHead; newClientsLinkedListHead->nextEntry = *emulContext->clientsHead;
*emulContext->clientsHead = entry; *emulContext->clientsHead = newClientsLinkedListHead;
} }
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) {
if (clientEntry->payload == ev->ctx) while(clientEntry != NULL)
{
if(clientEntry->payload == ev->ctx)
{ {
printf("close client %lu\n", ev->ctx->clientId); printf("close client %lu\n", ev->ctx->seatId);
handleCloseClient(emulContext, ev->ctx); handleCloseClient(emulContext, clientEntry);
removeLinkedListEntry(emulContext->clientsHead, clientEntry);
break; break;
} }
clientEntry = clientEntry->nextEntry; clientEntry = clientEntry->nextEntry;
@@ -59,54 +73,37 @@ 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->clientId); printf("auth client %lu\n", ev->ctx->seatId);
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)
{ {
uint64_t nonce = hmmmm_ClientMessage_nonce(cm); switch (msg->packetType)
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 hmmmm_ClientPayload_CtrlClientMessage: case PACKET_TYPE_CTRL:
{ {
hmmmm_ctrl_CtrlClientMessage_table_t ctrl = handleIncomingControlMessage(msg, ctx, emulContext);
(hmmmm_ctrl_CtrlClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingCtrlMessage(ctrl, nonce, ctx, emulContext);
break; break;
} }
case hmmmm_ClientPayload_StreamClientMessage: case PACKET_TYPE_STREAM:
{ {
hmmmm_stream_StreamClientMessage_table_t stream = handleIncomingStreamMessage(msg, ctx, emulContext);
(hmmmm_stream_StreamClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingStreamMessage(stream, nonce, ctx, emulContext);
break; break;
} }
case hmmmm_ClientPayload_MemClientMessage: case PACKET_TYPE_MEM:
{ {
hmmmm_mem_MemClientMessage_table_t mem = handleIncomingMemMessage(msg, ctx, emulContext);
(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("client %lu: unknown payload type %u\n", ctx->clientId, ptype); printf("Unsupported packet type: %u\n", msg->packetType);
break; break;
} }
} }
@@ -115,77 +112,88 @@ static void handleIncomingMessage(
void handleAllClients(EmulContext* emulContext) void handleAllClients(EmulContext* emulContext)
{ {
LinkedListEntry* clientEntry = *emulContext->clientsHead;
queue_mpsc_t* inMsgQueue = emulContext->inMsgQueue; size_t handleLimit = 128;
uint8_t hasAliveClients = 0;
FbMessage* fbmsg = NULL; while(clientEntry != NULL && handleLimit > 0)
for(size_t handleLimit = 0; handleLimit < 32; handleLimit++)
{ {
fbmsg = NULL; LinkedListEntry* currEntry = clientEntry;
if(queue_mpsc_pop(inMsgQueue, (void**)&fbmsg)) handleLimit--;
ClientContext* ctx = clientEntry->payload;
if(ctx->orphanedDeadTimeout != 0 && ctx->orphanedAt != 0 && ctx->isAuthed)
{ {
if(fbmsg != NULL) // printf("Found orphaned client\n");
if((uint64_t)time(NULL) - ctx->orphanedAt > ctx->orphanedDeadTimeout || ctx->streamRegIterator == 0)
{ {
*emulContext->utilizedFlag = 1; printf("Orphaned client dead timeout exceeded\n");
if(ctx->streamRegIterator > 0)
// if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) { {
hmmmm_ClientMessage_table_t cm = unregisterClientStreams(emulContext, ctx);
hmmmm_ClientMessage_as_root(fbmsg->data); }
// printf("read message len %lu\n", fbmsg->size); clientEntry = clientEntry->nextEntry;
handleIncomingMessage(cm, fbmsg->ctx, emulContext); removeLinkedListEntry(emulContext->clientsHead, currEntry);
// } else { dispatchOutgoingMessage(emulContext->outBufs, ctx, NULL, 0);
// 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
{ {
break; if(ctx->orphanedAt == 0)
{
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;
// LinkedListEntry* clientEntry = *emulContext->clientsHead; size_t len = 0;
// size_t handleLimit = 128; uint8_t* notify = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, newEmulState, &len);
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,159 +1,143 @@
#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"
#include "proto_reader.h" uint8_t validateAccessTokenDeterministic(const uint8_t* data, const uint8_t* token, uint64_t timestamp)
#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];
(void)snprintf(buf, sizeof(buf), "%s%lu", token, timestamp); sprintf(buf, "%s%lu", token, timestamp);
uint8_t hash[SHA512_DIGEST_LENGTH]; uint8_t hash[SHA512_DIGEST_LENGTH];
SHA512((const uint8_t*)buf, strlen(buf), hash); SHA512((uint8_t*)buf, strlen(buf), hash);
uint8_t valid = 1; uint8_t valid = 1;
for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) {
if (data[i] != hash[i]) { for(size_t i = 0; i < SHA512_DIGEST_LENGTH; i++)
{
if (data[i] != hash[i])
{
valid = 0; valid = 0;
} }
} }
return valid; return valid;
} }
static uint8_t validateAccessToken(const uint8_t* data, const uint8_t* access_token) 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)
|| validateAccessTokenDeterministic(data, access_token, t - 1); uint8_t valid1 = validateAccessTokenDeterministic(data, access_token, t);
uint8_t valid2 = validateAccessTokenDeterministic(data, access_token, t - 1);
return valid1 || valid2;
} }
uint8_t handle_auth( uint8_t handle_auth(ClientContext* cctx, ws_cli_conn_t client, const uint8_t* msg, uint64_t msgSize, int msgType)
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 {
if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)msgSize)) { int ret = ws_close_client(client);
printf("Auth: invalid FlatBuffer from client %lu\n", client); if(ret == -1)
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);
hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_as_root(msg); if(!isValid)
{
if (hmmmm_ClientMessage_payload_type(cm) != hmmmm_ClientPayload_AuthRequest) { printf("Auth token invalid\n");
printf("Auth: expected AuthRequest, got type %u from client %lu\n", int ret = ws_close_client(client);
hmmmm_ClientMessage_payload_type(cm), client); if(ret == -1)
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));
if (ev == NULL) { printf("Auth token is valid\n");
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); {
// 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); }
// }
} }
return 1; return 1;
} }
LinkedListEntry* disconnectDueTimeout( LinkedListEntry* disconnectDueTimeout(EmulContext* emulContext, LinkedListEntry* clientEntry)
EmulContext* emulContext, LinkedListEntry* clientEntry)
{ {
ClientContext* cctx = clientEntry->payload; ClientContext* ctx = clientEntry->payload;
uint64_t now = (uint64_t)time(NULL); uint64_t now = (uint64_t)time(NULL);
if (now - cctx->connectedAt <= 30) { if(now - ctx->connectedAt <= 30)
{
return clientEntry->nextEntry; return clientEntry->nextEntry;
} }
printf("Timeout on connection %lu\n", cctx->clientId); printf("Timeout on connection %lu\n", ctx->clientId);
int ret = ws_close_client(cctx->clientId); int ret = ws_close_client(ctx->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 nextEntry; return clientEntry;
} }
void handleOnClientAuthDone(ClientContext* ctx, EmulContext* emulContext)
void handleOnClientAuthDone(ClientContext* cctx, EmulContext* emulContext)
{ {
// Send AuthResponse with assigned seat_id uint8_t* framedata = malloc(sizeof(uint8_t) * 8);
NULL_GUARD(framedata);
encodeUintToBytes(ctx->seatId, framedata);
dispatchOutgoingMessage(emulContext->outBufs, ctx, framedata, 8);
size_t len = 0; size_t len = 0;
uint8_t* authResp = fb_build_auth_response(UINT64_MAX, cctx->clientId, &len); uint8_t* msg = createControlNotifyMessage((uint64_t)~0, *emulContext->clockCounter, *emulContext->emulState, &len);
dispatchOutgoingMessage(emulContext->outMsgQueue, cctx->clientId, authResp, len); dispatchOutgoingMessage(emulContext->outBufs, ctx, msg, 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);
// Send cached DeviceIdMappingNotif if a config has been loaded uint8_t* outMsg = createClientSetup((uint64_t)~0, ctx, &len);
if (emulContext->deviceIdMappingMsg && emulContext->deviceIdMappingMsgLen > 0) { dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, len);
uint8_t* mappingCopy = malloc(emulContext->deviceIdMappingMsgLen);
if (mappingCopy) {
memcpy(mappingCopy, emulContext->deviceIdMappingMsg, emulContext->deviceIdMappingMsgLen);
dispatchOutgoingMessage(
emulContext->outMsgQueue, cctx->clientId,
mappingCopy, emulContext->deviceIdMappingMsgLen);
}
}
} }

View File

@@ -1,758 +0,0 @@
#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,135 +1,199 @@
#include "proto/handlers/control.h" #include "proto/handlers/control.h"
#include "proto/handlers/config.h"
#include <stdio.h> #include "proto/pack.h"
#include "proto/enums.h"
#include "panic.h"
#include "state.h" #include "state.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "control_reader.h" // #include <alloca.h>
#include "exec_ctrl_reader.h"
#include "config_ctrl_reader.h"
#include "setup_buf_reader.h"
#include "orphaned_reader.h"
#include "lost_reader.h"
// Maps FlatBuffers ExecPrompt values to EMUL_STATE_OP_* constants. void handleIncomingExecControl(BaseMessage* msg, EmulContext* emulContext)
// 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)
{ {
switch (prompt) { printf("ctrl exec\n");
case hmmmm_ctrl_exec_ExecPrompt_start: return EMUL_STATE_OP_START; uint8_t stateOp = ((const uint8_t*)(msg->payload))[0];
case hmmmm_ctrl_exec_ExecPrompt_pause: return EMUL_STATE_OP_PAUSE; printf("state operation: %u\n", stateOp);
case hmmmm_ctrl_exec_ExecPrompt_resume: return EMUL_STATE_OP_RESUME; uint8_t newEmulState = switchNewEmulState(*emulContext->emulState, stateOp);
case hmmmm_ctrl_exec_ExecPrompt_stop: return EMUL_STATE_OP_STOP; if(newEmulState != *emulContext->emulState)
case hmmmm_ctrl_exec_ExecPrompt_reset: return EMUL_STATE_OP_RESET; {
default: return 0; printf("Switch state %u -> %u\n", *emulContext->emulState, newEmulState);
*emulContext->emulState = newEmulState;
size_t len = 0;
uint8_t* notify = createControlNotifyMessage(msg->nonce, *emulContext->clockCounter, newEmulState, &len);
broadcastClients(emulContext, notify, len);
} }
} }
void handleIncomingCtrlMessage( void handleIncomingEditConnectionSettingsControl(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
hmmmm_ctrl_CtrlClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
hmmmm_ctrl_CtrlClientPayload_union_type_t ptype = uint64_t fallbackQLen = decodeBytesToU64(msg->payload);
hmmmm_ctrl_CtrlClientMessage_payload_type(msg); uint64_t orphanedDeadTimeout = decodeBytesToU64(msg->payload + 8);
printf("[CTRL] client=%lu nonce=%lu type=%s\n", ctx->orphanedDeadTimeout = orphanedDeadTimeout;
ctx->clientId, nonce, uint8_t* newFallbackQPtr = realloc(ctx->fallbackOutcomeQ->ptr, sizeof(OutgoingMessage) * fallbackQLen);
hmmmm_ctrl_CtrlClientPayload_type_name(ptype)); NULL_GUARD(newFallbackQPtr);
ctx->fallbackOutcomeQ->ptr = newFallbackQPtr;
ctx->fallbackOutcomeQ->allocatedSize = fallbackQLen;
if (ptype == hmmmm_ctrl_CtrlClientPayload_ExecCtrlMessage) size_t len = 0;
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)
{ {
hmmmm_ctrl_exec_ExecCtrlMessage_table_t exec = ClientContext* orphanedCtx = clientEntry->payload;
(hmmmm_ctrl_exec_ExecCtrlMessage_table_t) if(orphanedCtx->orphanedAt != 0)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
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;
}
if(state_op == EMUL_STATE_OP_RESET && emulContext->emulState != EMUL_STATE_STILL)
{ {
*emulContext->resetRequest = 1; orphanedCount++;
orphanedPackets += orphanedCtx->fallbackOutcomeQ->size - ctx->fallbackOutcomeQPadding;
} }
else clientEntry = clientEntry->nextEntry;
}
printf("found %lu orphaned clients with %lu packets\n", orphanedCount, orphanedPackets);
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)
{ {
uint8_t new_state = switchNewEmulState(*emulContext->emulState, state_op); size_t filled = fillOrphanedClientReportItem(outMsgCursor, orphanedCtx);
*emulContext->emulState = new_state; outMsgCursor += filled;
printf("[CTRL/EXEC] state -> %u\n", new_state); filledCount++;
}
clientEntry = clientEntry->nextEntry;
}
if (new_state == EMUL_STATE_EXEC && tick_count > 0) { dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, msgLen);
*emulContext->tickTarget = *emulContext->clockCounter + tick_count; }
printf("set ticket target at %lu\n", *emulContext->tickTarget); void handleIncomingLoadFailed(const BaseMessage* msg, ClientContext* ctx, const EmulContext* emulContext)
} else if (new_state == EMUL_STATE_EXEC) { {
*emulContext->tickTarget = 0; const uint8_t readMode = ((const uint8_t*)msg->payload)[0];
printf("reset tick target\n"); 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)
{
break;
} }
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)
{ {
hmmmm_ctrl_setup_buf_SetupBuf_table_t sb = return;
(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)
{
printf("[CTRL/ORPHANED] returning empty list\n");
size_t msg_len; size_t payloadSize = 0;
uint8_t* out = fb_build_orphaned_response(nonce, &msg_len); OutgoingMessage* failedQueue = (OutgoingMessage*)orphanedCtx->fallbackOutcomeQ->ptr;
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len); for(size_t i = orphanedCtx->fallbackOutcomeQPadding; i < orphanedCtx->fallbackOutcomeQ->size; i++)
{
OutgoingMessage* failedMessage = &failedQueue[i];
payloadSize += 8;
payloadSize += failedMessage->msgLen;
} }
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++)
{ {
handleConfigCtrlMessage( OutgoingMessage* failedMessage = &failedQueue[i];
(hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t) encodeUintToBytes(failedMessage->msgLen, outMsgCursor);
hmmmm_ctrl_CtrlClientMessage_payload(msg), outMsgCursor += 8;
nonce, ctx, emulContext); for(size_t j = 0; j < failedMessage->msgLen; j++)
{
outMsgCursor[j] = failedMessage->msg[j];
}
outMsgCursor += failedMessage->msgLen;
} }
else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest)
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, outLen);
if(readMode == 1)
{ {
hmmmm_ctrl_lost_LostMessagesRequest_table_t req = dispatchOutgoingMessage(emulContext->outBufs, ctx, NULL, 0);
(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; {
uint8_t* out = fb_build_lost_messages_response(nonce, seat_id, &msg_len); case CTRL_TYPE_EXEC:
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,171 +1,84 @@
#include "proto/handlers/mem.h" #include "proto/handlers/mem.h"
#include "proto/enums.h"
#include <stdio.h> #include "proto/pack.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))
static void printMemory(void* cells, uint64_t cellsCount) uint8_t* createMemReadResponseMessage(uint64_t nonce, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut)
{ {
*lenOut = 9 + 8 + payloadLen;
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
NULL_GUARD(outmsg);
for(uint64_t i = 0; i < cellsCount; i++) encodeUintToBytes(nonce, outmsg);
{
printf("%lu: 0x%04X", i, ((uint8_t*)cells)[i]);
// uint8_t wasRead = 0; outmsg[8] = PACKET_TYPE_MEM << 4;
// for (uint8_t j = 0; j < mem->memreadLen; j++) outmsg[8] |= MEM_TYPE_READ_RESP;
// {
// 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;
// }
// }
// if (wasRead == 1) encodeUintToBytes(clockCounter, outmsg + 9);
// {
// printf("\t[was read]");
// }
// else
// {
// printf("\t[was not read]");
// }
// if (wasWrite == 1) memcpy(outmsg + 9 + 8, payload, payloadLen);
// {
// printf("\t[was written]"); return outmsg;
// }
// else
// {
// printf("\t[was not written]");
// }
printf("\n");
}
} }
void handleIncomingMemMessage( void handleIncomingMemReadReq(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
hmmmm_mem_MemClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
hmmmm_mem_MemClientPayload_union_type_t ptype = uint64_t devId = decodeBytesToU64(msg->payload);
hmmmm_mem_MemClientMessage_payload_type(msg); uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
uint64_t readLen = decodeBytesToU64(msg->payload + 24);
// printf("[MEM] client=%lu nonce=%lu type=%s\n", if(devId >= emulContext->devicesCount)
// ctx->clientId, nonce,
// hmmmm_mem_MemClientPayload_type_name(ptype));
if (ptype == hmmmm_mem_MemClientPayload_MemReadRequest)
{ {
hmmmm_mem_MemReadRequest_table_t req = return;
(hmmmm_mem_MemReadRequest_table_t)
hmmmm_mem_MemClientMessage_payload(msg);
uint32_t dev_id = hmmmm_mem_MemReadRequest_device_id(req);
volatile uint32_t seg_id = hmmmm_mem_MemReadRequest_seg_id(req);
uint32_t offset = hmmmm_mem_MemReadRequest_offset(req);
uint32_t length = hmmmm_mem_MemReadRequest_length(req);
// printf("[MEM/READ] device=%u seg=%u offset=%u len=%u\n",
// dev_id, seg_id, offset, length);
if (dev_id >= (uint32_t)emulContext->devicesCount) {
printf("[MEM/READ] invalid device %u\n", dev_id);
return;
}
// 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)
//TODO: lookup config for global addr conversion
uint64_t globalAddr = startAddr + segId * 128;
uint8_t* readPtr = emulContext->devicesMem[devId] + globalAddr;
size_t outLen = 0;
uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, readPtr, readLen, &outLen);
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, outLen);
}
void handleIncomingMemWrite(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
{
uint64_t devId = decodeBytesToU64(msg->payload);
uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
if(devId >= emulContext->devicesCount)
{ {
hmmmm_mem_MemWriteRequest_table_t req = return;
(hmmmm_mem_MemWriteRequest_table_t) }
hmmmm_mem_MemClientMessage_payload(msg);
uint32_t dev_id = hmmmm_mem_MemWriteRequest_device_id(req); //TODO: lookup config for global addr conversion
uint32_t seg_id = hmmmm_mem_MemWriteRequest_seg_id(req); uint64_t globalAddr = startAddr + segId * 128;
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", uint8_t* writePtr = emulContext->devicesMem[devId] + globalAddr;
// dev_id, seg_id, offset, data_len);
memcpy(writePtr, msg->payload + 24, msg->payloadLen - 24);
size_t outLen = 0;
uint8_t* outMsg = createMemReadResponseMessage(msg->nonce, *emulContext->clockCounter, writePtr, msg->payloadLen - 24, &outLen);
dispatchOutgoingMessage(emulContext->outBufs, ctx, outMsg, outLen);
}
if (dev_id >= (uint32_t)emulContext->devicesCount) { void handleIncomingMemMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext)
printf("[MEM/WRITE] invalid device %u\n", dev_id); {
return; if(msg->payloadHeader == MEM_TYPE_READ_REQ)
} {
if(!data) { handleIncomingMemReadReq(msg, ctx, emulContext);
printf("[MEM/WRITE] invalid data\n"); }
return; else if(msg->payloadHeader == MEM_TYPE_WRITE_PUSH)
} {
// if ((size_t)offset + data_len > DEVICE_MEM_SIZE) { handleIncomingMemWrite(msg, ctx, emulContext);
// 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,32 +1,26 @@
#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) ────────────────────────
static void unregisterClientStream( void unregisterClientStream(EmulContext* emulContext, ClientContext* ctx, uint32_t regId)
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 if(iReg->clientContext->clientId == ctx->clientId && iReg->regId == regId)
&& iReg->regId == regId)
{ {
printf("Discard stream %u for client %lu\n", regId, ctx->clientId); printf("Discard stream %u register for client %lu\n", regId, ctx->clientId);
deviceRegs->regCount--; deviceRegs->regCount -= 1;
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;
@@ -37,121 +31,94 @@ static void unregisterClientStream(
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* newRegs = calloc(deviceRegs->allocatedSize, sizeof(StreamReg)); StreamReg* newStreamRegs = malloc(sizeof(StreamReg) * deviceRegs->regCount);
NULL_GUARD(newRegs); NULL_GUARD(newStreamRegs);
size_t newCount = 0; size_t newStreamRegsId = 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)
{ {
newRegs[newCount++] = *reg; newStreamRegs[newStreamRegsId] = *reg;
newStreamRegsId++;
} }
else else
{ {
printf("Removing stream reg [%u] for client %lu\n", printf("Removing reg %d mode stream for %lu/%lu+%lu: [%u]\n", reg->mode, deviceId, reg->startAddr, reg->segLen, reg->regId);
reg->regId, ctx->clientId);
} }
} }
StreamReg* oldRegs = deviceRegs->regs; free(deviceRegs->regs);
deviceRegs->regCount = newCount; deviceRegs->regCount = newStreamRegsId;
deviceRegs->regs = newRegs; deviceRegs->regs = newStreamRegs;
free(oldRegs);
} }
} }
// ── Incoming stream messages ────────────────────────────────────────────────── void handleStreamRegMessage(BaseMessage* msg, ClientContext* ctx, EmulContext* emulContext, uint8_t X)
void handleIncomingStreamMessage(
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{ {
hmmmm_stream_StreamClientPayload_union_type_t ptype = uint64_t devId = decodeBytesToU64(msg->payload);
hmmmm_stream_StreamClientMessage_payload_type(msg); uint64_t segId = decodeBytesToU64(msg->payload + 8);
uint64_t startAddr = decodeBytesToU64(msg->payload + 16);
uint64_t segLen = decodeBytesToU64(msg->payload + 24);
printf("[STREAM] client=%lu nonce=%lu type=%s\n", if(devId >= emulContext->devicesCount)
ctx->clientId, nonce,
hmmmm_stream_StreamClientPayload_type_name(ptype));
if (ptype == hmmmm_stream_StreamClientPayload_StreamRegRequest)
{ {
hmmmm_stream_StreamRegRequest_table_t req = return;
(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 (ptype == hmmmm_stream_StreamClientPayload_StreamDeregRequest)
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[devId];
if(deviceRegs->regCount + 1 >= deviceRegs->allocatedSize)
{ {
hmmmm_stream_StreamDeregRequest_table_t req = size_t newAllocatedSize = deviceRegs->allocatedSize * 2;
(hmmmm_stream_StreamDeregRequest_table_t) if(newAllocatedSize <= deviceRegs->regCount + 1)
hmmmm_stream_StreamClientMessage_payload(msg); {
newAllocatedSize = deviceRegs->regCount + 1;
uint32_t stream_id = hmmmm_stream_StreamDeregRequest_stream_id(req); }
printf("[STREAM/DEREG] stream_id=%u\n", stream_id); StreamReg* newRegs = realloc(deviceRegs->regs, sizeof(StreamReg) * newAllocatedSize);
NULL_GUARD(newRegs);
unregisterClientStream(emulContext, ctx, stream_id); deviceRegs->allocatedSize = newAllocatedSize;
deviceRegs->regs = newRegs;
} }
else if (ptype == hmmmm_stream_StreamClientPayload_StreamWritePush)
//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)
{
uint8_t X = msg->payloadHeader >> 3;
uint8_t streamType = msg->payloadHeader & 0b111;
if(streamType == STREAM_TYPE_REG_REQUEST)
{ {
hmmmm_stream_StreamWritePush_table_t push = handleStreamRegMessage(msg, ctx, emulContext, X);
(hmmmm_stream_StreamWritePush_table_t) }
hmmmm_stream_StreamClientMessage_payload(msg); else if(streamType == STREAM_TYPE_REG_DISCARD)
{
printf("[STREAM/WRITE] stream_id=%u offset=%u len=%zu\n", uint32_t regId = decodeBytesToU32(msg->payload);
hmmmm_stream_StreamWritePush_stream_id(push), unregisterClientStream(emulContext, ctx, regId);
hmmmm_stream_StreamWritePush_offset(push),
flatbuffers_uint8_vec_len(
hmmmm_stream_StreamWritePush_data(push)));
// TODO: apply write to device memory
} }
} }

View File

@@ -1,69 +1,59 @@
#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 "my_mutex.h"
#include "proto/enums.h" #include "proto/enums.h"
#include "my_mutex.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];
queue_spsc_t *incomeQueue = malloc(sizeof(queue_spsc_t)); ptQueue* incomeQueue = ptQueueCreate(errbuf);
// queue_spsc_t incomeQueue, outcomeQueue; NULL_GUARD(incomeQueue, "Unable to create income queue: %s\n", errbuf);
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");
}
// = ptQueueCreate(errbuf); // ptQueue* outcomeQueue = 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); {
queue_spsc_destroy(outcomeQueue); ptQueueFree(incomeQueue);
// 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(ClientRegistrationEvent)); ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
if (ev == NULL) { if(ev == NULL)
queue_spsc_destroy(incomeQueue); {
queue_spsc_destroy(outcomeQueue); ptQueueFree(incomeQueue);
// 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);
@@ -71,112 +61,96 @@ void onWsOpen(ws_cli_conn_t client)
with_lock(&ctx->registerMutex) with_lock(&ctx->registerMutex)
{ {
// int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); cctx->seatId = ctx->seatCounter;
ctx->seatCounter++;
queue_spsc_push(ctx->regQueue, ev); int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
// if (!queue_spsc_push(ctx->regQueue, ev)) { if(exitCode)
// queue_spsc_destroy(incomeQueue); {
// queue_spsc_destroy(outcomeQueue); ptQueueFree(incomeQueue);
// // ptQueueFree(incomeQueue); // ptQueueFree(outcomeQueue);
// // ptQueueFree(outcomeQueue); free(cctx->fallbackOutcomeQ->ptr);
// free(cctx); free(cctx->fallbackOutcomeQ);
// panic("Unable to push to reg queue: %s\n", errbuf); free(cctx);
// }
panic("Unable to push to reg queue: %s\n", errbuf);
}
} }
const char* cli = ws_getaddress(client); char *cli;
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)
{ {
const char* cli = ws_getaddress(client); pthread_t tid = pthread_self();
printf("Connection %lu closed, addr: %s\n", client, cli); char *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(ClientRegistrationEvent)); ClientRegistrationEvent* ev = malloc(sizeof(ClientContext));
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)
{ {
queue_spsc_push(ctx->regQueue, ev); int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
// int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf); if(exitCode)
// 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 {
// if (hmmmm_ClientMessage_verify_as_root(msg, size)) { char errbuf[1024];
// printf("Client %lu sent invalid FlatBuffer, dropping\n", client); BaseMessage* baseMsg = parseMessage(msg, size);
printf("msg\n");
// FILE *write_ptr; int isErr = ptQueuePush(cctx->incomeQ, baseMsg, errbuf);
if(isErr)
// write_ptr = fopen("/tmp/last_failed_packet.bin","wb"); // w for write, b for binary {
// NULL_GUARD(write_ptr); panic("Unable to dispatch client message: %s\n", errbuf);
}
// 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,404 +1,107 @@
#include "proto/msg.h" #include "proto/msg.h"
#include "panic.h" #include "panic.h"
#include "flatcc/flatcc_builder.h" #include "proto/enums.h"
#include "proto_builder.h" #include "proto/pack.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>
uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out) BaseMessage* parseMessage(const uint8_t* bytes, size_t size)
{ {
flatcc_builder_t B; const uint8_t headerSize = 9;
if (flatcc_builder_init(&B)) { BaseMessage* msg = malloc(sizeof(BaseMessage));
panic("flatcc_builder_init failed\n"); NULL_GUARD(msg);
} uint64_t nonce = decodeBytesToU64(bytes);
hmmmm_auth_AuthResponse_ref_t ar =
hmmmm_auth_AuthResponse_create(&B, seat_id);
hmmmm_ServerPayload_union_ref_t payload = msg->nonce = nonce;
hmmmm_ServerPayload_as_AuthResponse(ar);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload); msg->packetType = bytes[8] >> 4;
msg->payloadHeader = bytes[8] & 0b1111;
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out); msg->payloadLen = size - headerSize;
flatcc_builder_clear(&B);
NULL_GUARD(buf); uint8_t* payload = malloc(sizeof(uint8_t) * (msg->payloadLen));
return buf; NULL_GUARD(payload);
memcpy(payload, bytes + headerSize, msg->payloadLen);
msg->payload = payload;
return msg;
} }
uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out) void fillHead(uint64_t nonce, uint8_t packetType, uint8_t payloadHeader, uint8_t* outmsg)
{ {
flatcc_builder_t B; encodeUintToBytes(nonce, outmsg);
if (flatcc_builder_init(&B)) { outmsg[8] = (uint8_t)((packetType & 0b1111) << 4);
panic("flatcc_builder_init failed\n"); outmsg[8] |= payloadHeader & 0b1111;
} }
hmmmm_ctrl_exec_notif_ExecNotifyMessage_ref_t notif = uint8_t* createControlNotifyMessage(uint64_t nonce, uint64_t clockCounter, uint8_t newEmulState, size_t* lenOut)
hmmmm_ctrl_exec_notif_ExecNotifyMessage_create( {
&B, tclk, (hmmmm_ctrl_exec_notif_ExecState_enum_t)(int8_t)state); *lenOut = 9 + 10;
uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
NULL_GUARD(outmsg, "Unable to allocate message");
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload = fillHead(nonce, PACKET_TYPE_CTRL, CTRL_TYPE_NOTIF_STATE, outmsg);
hmmmm_ctrl_CtrlServerPayload_as_ExecNotifyMessage(notif);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg = outmsg[9] = NOTIF_TYPE_EXEC;
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload); encodeUintToBytes(clockCounter, outmsg + 10);
outmsg[18] = newEmulState;
hmmmm_ServerPayload_union_ref_t payload = return outmsg;
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
} }
uint8_t* fb_build_stream_data_push( 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)
flatcc_builder_t *B,
uint64_t nonce, uint32_t stream_id, uint64_t tclk,
const uint8_t* data, size_t data_len,
size_t* len_out)
{ {
flatcc_builder_reset(B); *lenOut = 36 + 9;
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);
hmmmm_stream_StreamDataPush_ref_t push = fillHead(nonce, PACKET_TYPE_STREAM, (uint8_t)(X << 3) | STREAM_TYPE_REG_CONFIRM, outmsg);
hmmmm_stream_StreamDataPush_create(B, stream_id, tclk, data_vec);
hmmmm_stream_StreamServerPayload_union_ref_t stream_payload = encodeUintToBytes(devId, outmsg + 9);
hmmmm_stream_StreamServerPayload_as_StreamDataPush(push); encodeUintToBytes(segId, outmsg + 9 + 8);
encodeUintToBytes(startAddr, outmsg + 9 + 8 + 8);
hmmmm_stream_StreamServerMessage_ref_t stream_msg = encodeUintToBytes(segLength, outmsg + 9 + 8 + 8 + 8);
hmmmm_stream_StreamServerMessage_create(B, stream_payload); encodeUintToBytes(regId, outmsg + 9 + 8 + 8 + 8 + 8);
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* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size, uint8_t* createStreamSegmentPush(uint8_t mode, uint32_t regId, uint64_t clockCounter, uint8_t* payload, size_t payloadLen, size_t* lenOut)
uint64_t client_lifetime_ticks, size_t* len_out)
{ {
flatcc_builder_t B; *lenOut = 9 + 4 + 8 + payloadLen;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb = uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks); NULL_GUARD(outmsg);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload = uint64_t nonce = (uint64_t)~0;
hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb); encodeUintToBytes(nonce, outmsg);
outmsg[8] = (uint8_t)((PACKET_TYPE_STREAM << 4) | (mode << 3) | STREAM_TYPE_SEND);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg = fillHead(nonce, PACKET_TYPE_STREAM, (uint8_t)((mode << 3) | STREAM_TYPE_SEND), outmsg);
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t setup_outer = encodeUintToBytes(regId, outmsg + 9);
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg); encodeUintToBytes(clockCounter, outmsg + 9 + 4);
memcpy(outmsg + 9 + 4 + 8, payload, payloadLen);
hmmmm_ServerMessage_create_as_root(&B, nonce, setup_outer); return outmsg;
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
} }
uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out) uint8_t* createClientSetup(uint64_t nonce, ClientContext* ctx, size_t* lenOut)
{ {
flatcc_builder_t B; *lenOut = 9 + 8 + 8;
if (flatcc_builder_init(&B)) { uint8_t* outmsg = malloc(sizeof(uint8_t) * (*lenOut));
panic("flatcc_builder_init failed\n"); NULL_GUARD(outmsg);
}
// Use start/end directly: passing 0 to _create causes entries_add to return -1 fillHead(nonce, PACKET_TYPE_CTRL, CTRL_TYPE_SETUP_CONNECTION, outmsg);
hmmmm_ctrl_orphaned_OrphanedResponse_start(&B); uint8_t* payload = outmsg + 9;
hmmmm_ctrl_orphaned_OrphanedResponse_ref_t resp = encodeUintToBytes((uint64_t)ctx->fallbackOutcomeQ->allocatedSize, payload);
hmmmm_ctrl_orphaned_OrphanedResponse_end(&B); encodeUintToBytes((uint64_t)ctx->orphanedDeadTimeout, payload + 8);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload = return outmsg;
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;
} }