13 Commits

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

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

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

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

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

1
.gitignore vendored
View File

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

107
Makefile
View File

@@ -1,67 +1,109 @@
OPENSSL_INCLUDE ?= /usr/include/openssl/
BUILD_DIR=out
SRC_DIR=src
INC_DIR=inc
CC=gcc
OBJDUMP=objdump
LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a
LIBS_HEADERS=deps/
LIBS=deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a
LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE)
# flatcc runtime and generated reader headers use const-dropping casts;
# include both as system headers so -Wcast-qual doesn't apply to them
SYSTEM_INCLUDES=-isystem deps/flatcc/include/ -isystem $(PROTO_INC_DIR)/
STATIC_LIBS=crypto
STANDART=c23
OPTIMIZE=-Og
# OPTIMIZE=-Og #3 -march=native
OPTIMIZE=-O3 -march=native
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
C_SOURCES=$(wildcard $(SRC_DIR)/*.c)
# C_SOURCES=$(wildcard $(SRC_DIR)/*.c)
C_HEADERS=$(wildcard $(INC_DIR)/*.h)
C_SOURCES := $(shell find $(SRC_DIR) -type f -name '*.c')
C_INCLUDES=-I$(INC_DIR)/ $(addprefix -I,$(LIBS_HEADERS))
C_DEFS=-D_POSIX_C_SOURCE=199309L
DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith
DISABLE_FLAGS=-Wno-unused-variable -Wno-unused-parameter -Wno-write-strings -Wno-pointer-arith -Wno-analyzer-infinite-loop -Wno-unused-function -Wno-unused-but-set-variable
PEDANTIC_FLAGS=-pedantic -pedantic-errors $(DISABLE_FLAGS) -Wall -Wcast-align -Wcast-qual -Wconversion -Wduplicated-branches -Wduplicated-cond -Werror -Wextra -Wfloat-equal -Wlogical-op -Wpedantic -Wredundant-decls -Wsign-conversion
ANALYZER_FLAGS=-fanalyzer -fdiagnostics-show-option -fdiagnostics-color=always
LSECTIONS=-ffunction-sections -fdata-sections -Wl,--gc-sections
CFLAGS=$(C_DEFS) -g $(C_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS)
LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) -flto -fuse-linker-plugin $(LSECTIONS) -lm
# CFLAGS=$(C_DEFS) -g -pthread $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline # -fno-omit-frame-pointer -fno-inline -fno-lto
CFLAGS=$(C_DEFS) -g -pthread $(C_INCLUDES) $(SYSTEM_INCLUDES) $(DEFINES) $(OPTIMIZE) --std=$(STANDART) $(PEDANTIC_FLAGS) $(ANALYZER_FLAGS) -MMD -MP # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline # -fno-omit-frame-pointer -fno-inline -fno-lto
STATICLIBS_FLAGS=$(addprefix -l,$(STATIC_LIBS))
# LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) $(LSECTIONS) -lm -fno-omit-frame-pointer -fno-inline -fno-lto
# LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -fuse-linker-plugin $(LSECTIONS) -lm -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fno-omit-frame-pointer -fno-inline -flto -lgcov # -fno-omit-frame-pointer -fno-inline # #-fprofile-generate # -flto
LFLAGS=$(OPTIMIZE) -g -pthread $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -fuse-linker-plugin $(LSECTIONS) -lm # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fprofile-use # -fprofile-generate -fprofile-arcs -ftest-coverage # -fno-omit-frame-pointer -fno-inline -fno-lto # -fno-omit-frame-pointer -fno-inline -flto -lgcov # -fno-omit-frame-pointer -fno-inline # #-fprofile-generate # -flto
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) $(LIBS)
OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
DEP = $(filter %.d, $(OBJECTS:.o=.d))
#$(info $(DEP))
OBJECTS += $(LIBS)
vpath %.c $(sort $(dir $(C_SOURCES)))
vpath %.h $(sort $(dir $(C_HEADERS)))
# $(info SOURCES=$(C_SOURCES))
# $(info HEADERS=$(C_HEADERS))
# $(info OBJECTS=$(OBJECTS))
# 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
build: date deps Dir $(C_HEADERS) target compile_commands
build: date deps Dir proto python-proto target
rebuild: clean | build
#$(info $(DEP))
-include $(DEP)
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
@echo -e '\033[1;32mCC\t'$<'\t->\t'$@'\033[0m'
@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
@$(OBJDUMP) -d -S $@ > $(BUILD_DIR)/$(notdir $(<:.c=.casm))
target: date $(BUILD_DIR)/$(TARGET).elf
target: date proto $(BUILD_DIR)/$(TARGET).elf
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
@echo -e '\033[1;32mELF\t'$(OBJECTS)'\n\t\t\t->\t'$@'\033[0m'
@$(CC) $(LFLAGS) $(OBJECTS) -o $(BUILD_DIR)/$(TARGET).elf
@$(CC) $(LFLAGS) $(OBJECTS) $(LDLIBS) -o $(BUILD_DIR)/$(TARGET).elf
deps:
@make -C deps all
proto: $(PROTO_STAMP)
$(PROTO_STAMP): $(FBS_SOURCES) | ProtoDir BuildDir
@echo -e '\033[1;32mFLATCC\tGenerating FlatBuffers headers\033[0m'
@for fbs in $(FBS_SOURCES); do \
echo -e '\033[1;32mFLATCC\t'$$fbs'\033[0m'; \
$(FLATCC) -a -I $(FBS_DIR) -o $(PROTO_INC_DIR) $$fbs || exit 1; \
done
@touch $@
BuildDir:
@mkdir -p $(BUILD_DIR)
@$(shell mkdir -p $(dir $(OBJECTS)))
SrcDir:
@mkdir -p $(SRC_DIR)
@@ -69,21 +111,36 @@ SrcDir:
IncDir:
@mkdir -p $(INC_DIR)
Dir: BuildDir SrcDir IncDir
ProtoDir:
@mkdir -p $(PROTO_INC_DIR)
@mkdir -p $(PROTO_SRC_DIR)
Dir: BuildDir SrcDir IncDir ProtoDir
.PHONY: clean deps
python-proto: $(PY_PROTO_STAMP)
$(PY_PROTO_STAMP): $(FBS_SOURCES) $(FLATC) | $(PY_PROTO_DIR) BuildDir
@echo -e '\033[1;32mFLATC\tGenerating Python FlatBuffers bindings\033[0m'
@$(FLATC) --python --gen-all -I $(FBS_DIR) -o $(PY_PROTO_DIR) $(FBS_DIR)/proto.fbs
@touch $@
$(PY_PROTO_DIR):
@mkdir -p $@
$(FLATC):
@$(MAKE) -C deps flatbuffers/build/flatc
.PHONY: clean deps proto python-proto asan tsan clean
clean:
@rm -rf $(BUILD_DIR)/*
# @rm -f compile_commands.json
@rm -rf $(BUILD_DIR)/* $(BUILD_DIR)/.* $(PROTO_INC_DIR)/* $(PROTO_SRC_DIR)/*
@echo -e '\033[0;31mCleaned\033[0m'
.NOTPARALLEL: date target rebuild deps
.NOTPARALLEL: date target rebuild deps proto
date:
@echo -e '\033[1;32m'"Starting build at " | tr -d '\n'
@date
@echo -e '\033[0m'
compile_commands:
# @bear -- ./.gen_compile_commands.sh $(TARGET) $(CC) "$(CFLAGS)" "$(LFLAGS)" "$(OBJECTS)"

250
README.md Normal file
View File

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

2
deps/.gitignore vendored
View File

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

40
deps/Makefile vendored
View File

@@ -1,26 +1,52 @@
all: tomlc99.a ptQueue.a libws.a
all: tomlc99/libtoml.a ptQueue/out/ptQueue.a wsServer/libws.a flatcc/bin/flatcc flatbuffers/build/flatc
tomlc99:
@git clone https://github.com/cktan/tomlc99
tomlc99.a: tomlc99
tomlc99/libtoml.a: tomlc99
@make -C tomlc99 libtoml.a
ptQueue:
@git clone git@192.168.8.167:nikto_b/ptQueue.git
flatcc:
@git clone https://github.com/dvidelabs/flatcc
ptQueue.a: ptQueue
flatcc_src: flatcc/bin/flatcc
flatcc/lib/libflatccrt.a: flatcc/bin/flatcc
flatcc/bin/flatcc: flatcc
@flatcc/scripts/initbuild.sh make
@flatcc/scripts/build.sh
ptQueue:
@git clone https://git.nikto-b.ru/nikto_b/ptQueue
ptQueue/out/ptQueue.a: ptQueue
@make -C ptQueue
wsServer:
@git clone https://github.com/Theldus/wsServer
libws.a: wsServer
wsServer/libws.a: wsServer
@make -C wsServer
flatbuffers:
@git clone --depth=1 https://github.com/google/flatbuffers
flatbuffers/build/flatc: flatbuffers
@cmake -B flatbuffers/build -S flatbuffers \
-DCMAKE_BUILD_TYPE=Release \
-DFLATBUFFERS_BUILD_TESTS=OFF \
-DFLATBUFFERS_BUILD_FLATLIB=OFF \
-DFLATBUFFERS_BUILD_FLATHASH=OFF \
-DFLATBUFFERS_BUILD_GRPCTEST=OFF \
-DFLATBUFFERS_BUILD_SHAREDLIB=OFF \
-DFLATBUFFERS_INSTALL=OFF
@cmake --build flatbuffers/build --target flatc -j$(shell nproc)
clean:
@rm -rf wsServer ptQueue tomlc99
@rm -rf wsServer ptQueue tomlc99 flatcc flatbuffers
.PHONY: all clean

1
deps/flatbuffers vendored Submodule

Submodule deps/flatbuffers added at e223d69b36

View File

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

View File

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

0
flamegraph.svg Normal file
View File

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

@@ -0,0 +1,11 @@
namespace hmmmm.auth;
// Client → Server: SHA-512(access_token + str(unix_timestamp / 30))
table AuthRequest {
hash: [ubyte]; // 64 bytes
}
// Server → Client: seat assigned, current emulation state echoed
table AuthResponse {
seat_id: uint64;
}

12
flatbuffers/compacted.fbs Normal file
View File

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

View File

@@ -0,0 +1,16 @@
namespace hmmmm.config;
// Global emulator clock constraints.
table ClockConfig {
// Maximum emulated ticks per real second.
// 0 = unlimited (run as fast as possible).
limiter: uint64;
}
// Clock source and frequency relationship for one device.
// Devices form a clock tree: each node derives its frequency from its src.
table DeviceClockConfig {
src: string; // id of the parent device; empty = root clock
divider: uint32 = 1;
multiplier: uint32 = 1;
}

View File

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

View File

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

View File

@@ -0,0 +1,73 @@
namespace hmmmm.config;
// Named offset within a segment — symbolic variable for protocol/tool use
// (e.g. PIN=0, PORT=1, DDR=2 in a GPIO device).
table NamedOffset {
name: string;
offset: uint32;
}
// A contiguous memory segment within a base device.
table MemSegment {
name: string;
addr: uint32; // base address in device flat address space
len: uint32;
word_len: uint8 = 1; // word size in bytes
executable: bool = false;
variables: [NamedOffset]; // optional symbolic offsets within segment
}
// ── Projections ──────────────────────────────────────────────────────────────
// Maps a segment from one device into the flat address space of another.
// After projection, accesses to [base_seg + shift .. shift+len) in base_at
// are transparently forwarded to target_at:target_seg.
table Projection {
base_at: string; // device where the projection appears
base_seg: string; // segment in that device that is the projection window
target_at: string; // device owning the real data
target_seg: string;
shift: uint32 = 0; // offset from base_seg start
}
// ── Intercepts ───────────────────────────────────────────────────────────────
enum InterceptOp: byte {
op_read = 0,
op_write = 1,
op_both = 2,
}
enum InterceptMode: byte {
shadow_copy = 1, // duplicate write to point_at:point_addr as well
shadow_replace = 2, // redirect access to point_at:point_addr instead
callback = 3, // invoke a handler registered by point_at device
}
table Intercept {
name: string;
op: InterceptOp = op_both;
mode: InterceptMode = callback;
base_at: string; // device where the triggering access occurs
base_seg: string;
base_addr: uint32;
point_at: string; // device that handles/receives the intercept
point_seg: string;
point_addr: uint32;
}
// ── Memory initialisation ─────────────────────────────────────────────────────
enum OverflowBehaviour: byte {
overflow_error = 0, // abort if file is larger than segment
overflow_ignore = 1, // stop reading at segment end, no error
overflow_wrap = 2, // wrap around and continue writing from segment start
}
// Load initial segment contents from a file (e.g. firmware binary, /dev/urandom).
table MemSetup {
device: string;
segment: string;
filepath: string;
overflow: OverflowBehaviour = overflow_error;
}

View File

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

View File

@@ -0,0 +1,43 @@
namespace hmmmm.ctrl.config_notif;
// Notification subtype 0001 — a device's configuration was updated.
// Carries the path of the affected device; client should re-request full config.
table DeviceConfigUpdateNotif {
device_path: [string]; // hierarchical path, e.g. ["core"] or ["gpio_a"]
}
// Numeric ID assigned to one named segment within a device.
table SegIdEntry {
name: string;
id: uint32;
}
// Maps a hierarchical device path to a compact numeric device_id + segment IDs.
// Clients use these IDs in stream and mem packets instead of string paths.
table DeviceIdEntry {
path: [string]; // hierarchical device id, e.g. ["sys", "core"]
device_id: uint32;
seg_ids: [SegIdEntry];
}
// Notification subtype 0010 — full device-id / segment-id mapping table.
// Sent after auth and after any config change that affects the device tree.
table DeviceIdMappingNotif {
entries: [DeviceIdEntry];
}
// Sent when a config load request fails.
table ConfigLoadError {
message: string;
}
union ConfigNotifPayload {
DeviceConfigUpdateNotif,
DeviceIdMappingNotif,
ConfigLoadError,
}
table ConfigNotifMessage {
tclk: uint64;
payload: ConfigNotifPayload;
}

View File

@@ -0,0 +1,39 @@
include "exec_ctrl.fbs";
include "exec_notif.fbs";
include "config_ctrl.fbs";
include "config_notif.fbs";
include "lost.fbs";
include "orphaned.fbs";
include "setup_buf.fbs";
include "qos.fbs";
namespace hmmmm.ctrl;
// ── Client → Server ──────────────────────────────────────────────────────────
union CtrlClientPayload {
hmmmm.ctrl.exec.ExecCtrlMessage,
hmmmm.ctrl.config_ctrl.ConfigCtrlMessage,
hmmmm.ctrl.lost.LostMessagesRequest,
hmmmm.ctrl.orphaned.OrphanedRequest,
hmmmm.ctrl.setup_buf.SetupBuf,
}
table CtrlClientMessage {
payload: CtrlClientPayload;
}
// ── Server → Client ──────────────────────────────────────────────────────────
union CtrlServerPayload {
hmmmm.ctrl.exec_notif.ExecNotifyMessage,
hmmmm.ctrl.config_notif.ConfigNotifMessage,
hmmmm.ctrl.lost.LostMessagesResponse,
hmmmm.ctrl.orphaned.OrphanedResponse,
hmmmm.ctrl.setup_buf.SetupBuf,
hmmmm.ctrl.qos.QosReport,
}
table CtrlServerMessage {
payload: CtrlServerPayload;
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
namespace hmmmm.ctrl.lost;
// Client → Server: request buffered (undelivered) messages for a seat.
table LostMessagesRequest {
seat_id: uint64;
clear: bool = false; // if true, drop the buffer after reading
}
// One buffered message that was not delivered before the seat disconnected.
table LostMessage {
original_nonce: uint64;
data: [ubyte]; // raw FlatBuffer bytes of the original ServerMessage
}
// Server → Client: buffered messages for the requested seat.
table LostMessagesResponse {
seat_id: uint64;
messages: [LostMessage];
}

View File

@@ -0,0 +1,16 @@
namespace hmmmm.ctrl.orphaned;
// Client → Server: enumerate disconnected seats that still have buffered messages.
// No body — request is the signal itself.
table OrphanedRequest {}
table OrphanedEntry {
seat_id: uint64;
disconnect_tclk: uint64; // virtual clock tick at time of disconnect
lost_count: uint32; // number of buffered messages waiting
}
// Server → Client: list of orphaned seats with pending buffers.
table OrphanedResponse {
entries: [OrphanedEntry];
}

View File

@@ -0,0 +1,10 @@
namespace hmmmm.ctrl.qos;
// Server → Client: connection quality snapshot.
// Sent periodically or on request (TBD).
table QosReport {
rtt_us: uint64; // measured round-trip time, microseconds
packets_sent: uint64;
packets_lost: uint32;
buf_pressure: float; // outgoing buffer fill level, 0.0 .. 1.0
}

View File

@@ -0,0 +1,8 @@
namespace hmmmm.ctrl.setup_buf;
// Client → Server: configure the lost-message buffer for this seat.
// Server → Client: echoes back the confirmed (possibly clamped) settings.
table SetupBuf {
lost_buf_size: uint32; // max buffered messages per seat
client_lifetime_ticks: uint64; // virtual ticks to keep buffer after disconnect
}

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

@@ -0,0 +1,46 @@
namespace hmmmm.mem;
// Client → Server: read a contiguous memory region. Server responds with
// MemReadResponse carrying the same nonce as the enclosing ClientMessage.
table MemReadRequest {
device_id: uint32;
seg_id: uint32;
offset: uint32;
length: uint32;
}
// Server → Client: memory region contents at the moment of the read.
table MemReadResponse {
tclk: uint64; // virtual clock tick at time of read
device_id: uint32;
seg_id: uint32;
offset: uint32;
data: [ubyte];
}
// Client → Server: write a contiguous memory region. No response.
table MemWriteRequest {
device_id: uint32;
seg_id: uint32;
offset: uint32;
data: [ubyte];
}
// ── Unions ───────────────────────────────────────────────────────────────────
union MemClientPayload {
MemReadRequest,
MemWriteRequest,
}
table MemClientMessage {
payload: MemClientPayload;
}
union MemServerPayload {
MemReadResponse,
}
table MemServerMessage {
payload: MemServerPayload;
}

44
flatbuffers/proto.fbs Normal file
View File

@@ -0,0 +1,44 @@
include "auth/auth.fbs";
include "control/control.fbs";
include "stream/stream.fbs";
include "mem/mem.fbs";
include "compacted.fbs";
namespace hmmmm;
// ── Client → Server ──────────────────────────────────────────────────────────
union ClientPayload {
hmmmm.auth.AuthRequest,
hmmmm.ctrl.CtrlClientMessage,
hmmmm.stream.StreamClientMessage,
hmmmm.mem.MemClientMessage,
}
// Every frame sent by the client is a ClientMessage.
// nonce: client-chosen identifier echoed in the server response.
// Use 0xFFFFFFFFFFFFFFFF for fire-and-forget messages (no reply expected).
table ClientMessage {
nonce: uint64;
payload: ClientPayload;
}
// ── Server → Client ──────────────────────────────────────────────────────────
union ServerPayload {
hmmmm.auth.AuthResponse,
hmmmm.ctrl.CtrlServerMessage,
hmmmm.stream.StreamServerMessage,
hmmmm.mem.MemServerMessage,
CompactedMessage,
}
// Every frame sent by the server is a ServerMessage.
// nonce: echoes the triggering ClientMessage nonce.
// 0xFFFFFFFFFFFFFFFF for unsolicited notifications (state changes, stream pushes).
table ServerMessage {
nonce: uint64;
payload: ServerPayload;
}
root_type ClientMessage;

View File

@@ -0,0 +1,27 @@
include "stream_reg.fbs";
include "stream_data.fbs";
namespace hmmmm.stream;
// ── Client → Server ──────────────────────────────────────────────────────────
union StreamClientPayload {
StreamRegRequest,
StreamDeregRequest,
StreamWritePush,
}
table StreamClientMessage {
payload: StreamClientPayload;
}
// ── Server → Client ──────────────────────────────────────────────────────────
union StreamServerPayload {
StreamRegConfirm,
StreamDataPush,
}
table StreamServerMessage {
payload: StreamServerPayload;
}

View File

@@ -0,0 +1,18 @@
namespace hmmmm.stream;
// Server → Client: memory region snapshot pushed by the server.
// tclk is the virtual clock tick at which this data became valid —
// clients may buffer and apply it at the appropriate simulation point.
table StreamDataPush {
stream_id: uint32;
tclk: uint64;
data: [ubyte];
}
// Client → Server: write into a registered write-mode stream.
// offset is relative to the registered region start.
table StreamWritePush {
stream_id: uint32;
offset: uint32;
data: [ubyte];
}

View File

@@ -0,0 +1,32 @@
namespace hmmmm.stream;
enum StreamMode: byte {
mode_read = 0,
mode_write = 1,
}
// Client → Server: subscribe to a memory region.
table StreamRegRequest {
device_id: uint32; // from DeviceIdMappingNotif
seg_id: uint32; // from DeviceIdMappingNotif
offset: uint32; // start offset within segment, in bytes
length: uint32; // region length, in bytes
mode: StreamMode;
}
// Client → Server: cancel a stream subscription.
// No confirmation is sent by the server.
table StreamDeregRequest {
stream_id: uint32;
}
// Server → Client: subscription confirmed, stream_id assigned.
// Echoes the original request fields for client-side reconciliation.
table StreamRegConfirm {
stream_id: uint32;
device_id: uint32;
seg_id: uint32;
offset: uint32;
length: uint32;
mode: StreamMode;
}

120
flatbuffers_example_main.c Normal file
View File

@@ -0,0 +1,120 @@
#include <stdio.h>
#include <stdlib.h>
#include "monster_reader.h"
#include "monster_verifier.h"
#include "monster_builder.h"
#undef ns
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x)
#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
int main()
{
flatcc_builder_t builder, *B = &builder;
flatcc_builder_init(B);
printf("[+] Builder initialized\n");
flatbuffers_string_ref_t weapon_one_name =
flatbuffers_string_create_str(B, "Sword");
flatbuffers_string_ref_t weapon_two_name =
flatbuffers_string_create_str(B, "Axe");
ns(Weapon_ref_t) sword =
ns(Weapon_create(B, weapon_one_name, 3));
ns(Weapon_ref_t) axe =
ns(Weapon_create(B, weapon_two_name, 5));
ns(Weapon_vec_start(B));
ns(Weapon_vec_push(B, sword));
ns(Weapon_vec_push(B, axe));
ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B));
uint8_t treasure[] = {0,1,2,3,4,5,6,7,8,9};
flatbuffers_uint8_vec_ref_t inventory =
flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure));
flatbuffers_string_ref_t name =
flatbuffers_string_create_str(B, "Orc");
uint16_t hp = 300;
uint16_t mana = 150;
ns(Vec3_t) pos = {1.0f, 2.0f, 3.0f};
ns(Vec3_vec_start(B));
ns(Vec3_vec_push(B, &pos));
ns(Vec3_vec_ref_t) path = ns(Vec3_vec_end(B));
ns(Equipment_union_ref_t) equipped =
ns(Equipment_as_Weapon(axe));
ns(Monster_create_as_root(
B, &pos, mana, hp, name,
inventory, ns(Color_Red),
weapons, equipped, path));
uint8_t *buf;
size_t size;
buf = flatcc_builder_finalize_buffer(B, &size);
printf("[+] Buffer built, size = %zu bytes\n", size);
// 🔍 Верификация
if (ns(Monster_verify_as_root(buf, size))) {
printf("[!] Buffer verification FAILED\n");
return -1;
}
printf("[+] Buffer verification OK\n");
// 📖 Чтение
ns(Monster_table_t) m = ns(Monster_as_root(buf));
printf("\n===== MONSTER DUMP =====\n");
printf("Name: %s\n", ns(Monster_name(m)));
printf("HP: %u\n", ns(Monster_hp(m)));
printf("Mana: %u\n", ns(Monster_mana(m)));
ns(Vec3_struct_t) p = ns(Monster_pos(m));
printf("Position: x=%.2f y=%.2f z=%.2f\n",
ns(Vec3_x(p)), ns(Vec3_y(p)), ns(Vec3_z(p)));
flatbuffers_uint8_vec_t inv = ns(Monster_inventory(m));
size_t inv_len = flatbuffers_uint8_vec_len(inv);
printf("Inventory (%zu): ", inv_len);
for (size_t i = 0; i < inv_len; i++) {
printf("%u ", flatbuffers_uint8_vec_at(inv, i));
}
printf("\n");
ns(Weapon_vec_t) wv = ns(Monster_weapons(m));
size_t wlen = ns(Weapon_vec_len(wv));
printf("Weapons (%zu):\n", wlen);
for (size_t i = 0; i < wlen; i++) {
ns(Weapon_table_t) w = ns(Weapon_vec_at(wv, i));
printf(" - %s (dmg=%u)\n",
ns(Weapon_name(w)),
ns(Weapon_damage(w)));
}
if (ns(Monster_equipped_type(m)) == ns(Equipment_Weapon)) {
ns(Weapon_table_t) w = ns(Monster_equipped(m));
printf("Equipped: %s (dmg=%u)\n",
ns(Weapon_name(w)),
ns(Weapon_damage(w)));
}
printf("========================\n\n");
free(buf);
flatcc_builder_clear(B);
return 0;
}

View File

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

View File

@@ -2,6 +2,7 @@
#define __BASE_DEVICE_H__
#include "hmmmm.h"
#include <stdint.h>
typedef struct {
void* specs;
@@ -9,6 +10,7 @@ typedef struct {
device_public_context_t* ctx;
uint64_t clockCycleLimit;
uint64_t clockCycleCounter;
uint64_t clockDivider;
} device_handle_t;

18
inc/client.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef __CLIENT_H__
#define __CLIENT_H__
#include <stdint.h>
#include "wsServer/include/ws.h"
#include "ptQueue/inc/spsc.h"
typedef struct {
ws_cli_conn_t clientId;
uint8_t isAuthed;
queue_spsc_t* incomeQ;
queue_spsc_t* outcomeQ;
uint64_t connectedAt;
uint32_t streamRegIterator;
} ClientContext;
#endif //ifndef __CLIENT_H__

View File

@@ -35,7 +35,6 @@ typedef struct {
device_handle_t** devHandlers;
} compose_dev_handle_t;
compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf);
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf);
#endif // ifndef __COMPOSE_DEVICE_H__

View File

@@ -1,36 +1,12 @@
#ifndef __HMMMM_CONFIG_H__
#define __HMMMM_CONFIG_H__
#include <unistd.h>
#include <stdint.h>
#include "pub/libhmmmm/config.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;
void freeMemSegConf(conf_mem_seg_t* memSegConf);
void freeMemConf(conf_mem_t* memConf);
// 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);
// uint8_t compareComposeId(char** idA, char** idB);
#endif // ifndef __HMMMM_CONFIG_H__
#endif

63
inc/context.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef __CONTEXT_H__
#define __CONTEXT_H__
#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include "ptQueue/inc/spsc.h"
#include "wsServer/include/ws.h"
#include "ptQueue/inc/mpsc.h"
#include "linkedlist.h"
#include "sized_ptr.h"
#include "streamed.h"
#include "flatcc/flatcc_builder.h"
typedef struct {
SizedPtr* bufs;
uint8_t buffersCount;
CACHE_ALIGN _Atomic (uint8_t) readRequestIdx;
CACHE_ALIGN _Atomic (uint8_t) currWritingIdx;
} OutgoingBuffers;
typedef struct {
uint8_t* resetRequest;
uint8_t* emulState;
uint64_t* clockCounter;
uint64_t* tickTarget; // 0 = run indefinitely, >0 = auto-pause at this tick
LinkedListEntry** clientsHead;
uint8_t* utilizedFlag;
OutgoingBuffers* outBufs;
DeviceSegStreamReg** deviceStreamRegs;
uint8_t** devicesMem;
size_t** devicesMemSegAddrs;
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;
typedef struct {
pthread_mutex_t registerMutex;
queue_spsc_t* regQueue;
queue_mpsc_t* inMsgQueue;
uint8_t* accessToken;
EmulContext* emulContext;
} ServerContext;
#endif //ifndef __CONTEXT_H__

13
inc/events.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef __EVENTS_H__
#define __EVENTS_H__
#include <stdint.h>
#include "context.h"
typedef struct {
uint8_t regType;
ClientContext* ctx;
} ClientRegistrationEvent;
#endif //ifndef __EVENTS_H__

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

View File

@@ -1,6 +1,6 @@
#ifndef __HMMMMM__
#define __HMMMMM__
#include "libhmmmm.h"
#include "pub/libhmmmm/device.h"
#include "config.h"
typedef struct {
@@ -25,6 +25,7 @@ typedef struct {
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);

View File

@@ -1,14 +0,0 @@
#ifndef __LIB_HMMMMM__
#define __LIB_HMMMMM__
#include "libmem.h"
#include "libdevice.h"
#define EXTENDED_DEVICE_TYPE_DUMMY 0
#define EXTENDED_DEVICE_TYPE_INSTR_SIMUL 1
#endif // ifndef __LIB_HMMMMM__

View File

@@ -1,52 +0,0 @@
#ifndef __LIBMEM_H__
#define __LIBMEM_H__
#include <stdint.h>
#include <stdlib.h>
#define GET_BIT(n, b) ((n >> b) & 1)
// 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* memreadCellAddrs;
uint64_t* memwriteCellAddrs;
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);
#endif // ifndef __LIBMEM_H__

13
inc/linkedlist.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef __LINKEDLIST_H__
#define __LINKEDLIST_H__
typedef struct LinkedListEntry {
struct LinkedListEntry* prevEntry;
struct LinkedListEntry* nextEntry;
void* payload;
} LinkedListEntry;
void removeLinkedListEntry(LinkedListEntry** head, LinkedListEntry* entry);
#endif //ifndef __LINKEDLIST_H__

42
inc/panic.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef __PANIC_H__
#define __PANIC_H__
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#ifndef PANIC_FD
#define PANIC_FD stderr
#endif //ifndef PANIC_FD
#ifndef PANIC_STRACE_LEN
#define PANIC_STRACE_LEN 32
#endif //ifndef PANIC_STRACE_LEN
void print_stacktrace(FILE* fd);
#define panic(...) \
do { \
fprintf(PANIC_FD, "Panic at %s:%d\n", __FILE__, __LINE__); \
fprintf(PANIC_FD, "Panic message:\n\t" __VA_ARGS__); \
print_stacktrace(PANIC_FD); \
abort(); \
} while(0)
#define NULL_GUARD(ptr, ...) \
do { \
if((ptr) == NULL) \
{ \
if(sizeof(#__VA_ARGS__) > 1) \
{ \
panic(__VA_ARGS__); \
} \
else \
{ \
panic("NULL detected in %s", #ptr); \
} \
} \
} while(0)
#endif //ifndef __PANIC_H__

12
inc/proto/dial.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __PROTO_DIAL_H__
#define __PROTO_DIAL_H__
#include <stdint.h>
#include "context.h"
#include "ptQueue/inc/spsc.h"
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);
#endif

34
inc/proto/enums.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef __PROTO_ENUMS_H__
#define __PROTO_ENUMS_H__
#define REG_EVTYPE_CONNECT 1
#define REG_EVTYPE_AUTH 2
#define REG_EVTYPE_CLOSE 3
#define PACKET_TYPE_CTRL 0b0001
#define PACKET_TYPE_STREAM 0b0010
#define PACKET_TYPE_MEM 0b0011
#define CTRL_TYPE_EXEC 0b0001
#define CTRL_TYPE_NOTIF_STATE 0b0010
#define NOTIF_TYPE_EXEC 0b0000
#define STREAM_TYPE_REG_REQUEST 0b001
#define STREAM_TYPE_REG_DISCARD 0b011
#define STREAM_TYPE_REG_CONFIRM 0b101
#define STREAM_TYPE_SEND 0b000
#define MEM_TYPE_READ_REQ 0b0000
#define MEM_TYPE_READ_RESP 0b0001
#define MEM_TYPE_WRITE_PUSH 0b0010
#endif //ifndef __PROTO_ENUMS_H__

11
inc/proto/handlers.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __PROTO_HANDLERS_H__
#define __PROTO_HANDLERS_H__
#include "events.h"
void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev);
void handleAllClients(EmulContext* emulContext);
#endif //ifndef __PROTO_HANDLERS_H__

11
inc/proto/handlers/auth.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __PROTO_HANDLERS_AUTH_H__
#define __PROTO_HANDLERS_AUTH_H__
#include <stdint.h>
#include "context.h"
uint8_t handle_auth(ClientContext* cctx, ws_cli_conn_t client, const uint8_t* msg, uint64_t msgSize, int msgType);
LinkedListEntry* disconnectDueTimeout(EmulContext* emulContext, LinkedListEntry* clientEntry);
void handleOnClientAuthDone(ClientContext* ctx, EmulContext* emulContext);
#endif //ifndef __PROTO_HANDLERS_AUTH_H__

View File

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

View File

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

16
inc/proto/handlers/mem.h Normal file
View File

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

View File

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

10
inc/proto/handlers/ws.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __PROTO_HANDLERS_WS_H__
#define __PROTO_HANDLERS_WS_H__
#include "wsServer/include/ws.h"
void onWsMessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type);
void onWsClose(ws_cli_conn_t client);
void onWsOpen(ws_cli_conn_t client);
#endif //ifndef __PROTO_HANDLERS_WS_H__

82
inc/proto/msg.h Normal file
View File

@@ -0,0 +1,82 @@
#ifndef __PROTO_MSG_H__
#define __PROTO_MSG_H__
#include "client.h"
#include "wsServer/include/ws.h"
#include <stdint.h>
#include <stdlib.h>
#include "flatcc/flatcc_builder.h"
// Raw FlatBuffers frame copied from the WS thread, queued to the main loop.
typedef struct {
uint8_t* data;
size_t size;
ClientContext* ctx;
} FbMessage;
typedef struct {
ws_cli_conn_t clientIdx;
uint8_t* msg;
size_t msgLen;
uint64_t dispatch_us; // microsecond timestamp when queued (for batching window)
} OutgoingMessage;
// Build a ServerMessage{AuthResponse} frame. Returns a malloc'd buffer;
// ownership is transferred to the caller (dispatchOutgoingMessage will free it).
uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{ExecNotifyMessage}} frame.
uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out);
// Build a ServerMessage{StreamServerMessage{StreamDataPush}} frame.
// B must be a pre-initialized flatcc_builder_t; it is reset before use
// so the caller does not need to reset it manually between calls.
uint8_t* fb_build_stream_data_push(
flatcc_builder_t *B,
uint64_t nonce, uint32_t stream_id, uint64_t tclk,
const uint8_t* data, size_t data_len,
size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{SetupBuf}} frame (echo back to client).
uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size,
uint64_t client_lifetime_ticks, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{OrphanedResponse{entries=[]}}} frame.
uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{LostMessagesResponse{seat_id, messages=[]}}} frame.
uint8_t* fb_build_lost_messages_response(uint64_t nonce, uint64_t seat_id, size_t* len_out);
// Build a ServerMessage{MemServerMessage{MemReadResponse}} frame.
uint8_t* fb_build_mem_read_response(
uint64_t nonce, uint64_t tclk,
uint32_t device_id, uint32_t seg_id, uint32_t offset,
const uint8_t* data, size_t data_len,
size_t* len_out);
// Build a ServerMessage{StreamServerMessage{StreamRegConfirm}} frame.
uint8_t* fb_build_stream_reg_confirm(
uint64_t nonce, uint32_t stream_id,
uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length,
uint8_t mode, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{DeviceIdMappingNotif}}} frame.
// paths: array of NULL-terminated string arrays; path_lens: component count per path.
// seg_names[i]: array of seg_counts[i] segment name strings for device i.
uint8_t* fb_build_config_device_id_mapping(
uint64_t nonce,
char** paths[], size_t path_lens[],
char* seg_names[][64], size_t seg_counts[],
size_t device_count, size_t* len_out);
// Build a ServerMessage{CtrlServerMessage{ConfigNotifMessage{ConfigLoadError}}} frame.
uint8_t* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out);
// Build a ServerMessage{CompactedMessage{frames=[...]}} frame.
// msgs / msg_lens: array of `count` pre-built ServerMessage byte buffers to pack.
// The caller retains ownership of the source buffers; this function copies their bytes.
// Nonce is set to UINT64_MAX (no-nonce / fire-and-forget).
uint8_t* fb_build_compacted_message(
uint8_t* const* msgs, const size_t* msg_lens, size_t count, size_t* len_out);
#endif // __PROTO_MSG_H__

30
inc/proto/pack.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef __PROTO_PACK_H__
#define __PROTO_PACK_H__
#include <stdint.h>
uint64_t decodeBytesToU64(const uint8_t* bytes);
uint32_t decodeBytesToU32(const uint8_t* bytes);
uint16_t decodeBytesToU16(const uint8_t* bytes);
void encodeUint8ToBytes(uint8_t num, uint8_t* tgt);
void encodeUint16ToBytes(uint16_t num, uint8_t* tgt);
void encodeUint32ToBytes(uint32_t num, uint8_t* tgt);
void encodeUint64ToBytes(uint64_t num, uint8_t* tgt);
#define encodeUintToBytes(num, tgt) _Generic((num), \
uint8_t: encodeUint8ToBytes, \
uint16_t: encodeUint16ToBytes, \
uint32_t: encodeUint32ToBytes, \
uint64_t: encodeUint64ToBytes \
)(num, tgt)
#endif //ifndef __PROTO_PACK_H__

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

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

View File

@@ -2,10 +2,15 @@
#define __LIBDEVICE_H__
#include <stdint.h>
#include "libmem.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;

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

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

13
inc/sized_ptr.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef __SIZED_PTR_H__
#define __SIZED_PTR_H__
#include <stdlib.h>
typedef struct {
void* ptr;
size_t size;
size_t allocatedSize;
} SizedPtr;
#endif //ifndef __SIZED_PTR_H__

20
inc/state.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef __EMUL_STATE_H__
#define __EMUL_STATE_H__
#include <stdint.h>
#define EMUL_STATE_STILL 0
#define EMUL_STATE_EXEC 1
#define EMUL_STATE_PAUSE 2
#define EMUL_STATE_STOP 3
#define EMUL_STATE_OP_START 1
#define EMUL_STATE_OP_PAUSE 2
#define EMUL_STATE_OP_RESUME 3
#define EMUL_STATE_OP_RESET 4
#define EMUL_STATE_OP_STOP 5
uint8_t switchNewEmulState(const uint8_t currentState, const uint8_t controlOp);
#endif //ifndef __EMUL_STATE_H__

29
inc/streamed.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef __STREAMED_H__
#define __STREAMED_H__
#include <stdlib.h>
#include <stdint.h>
#include "client.h"
#define STREAM_MODE_READ 0
#define STREAM_MODE_WRITE 1
typedef struct {
ClientContext* clientContext;
uint32_t regId;
uint32_t segId;
uint64_t startAddr;
uint64_t startGlobalAddr;
uint64_t segLen;
uint8_t mode;
} StreamReg;
typedef struct {
StreamReg* regs;
size_t regCount;
size_t allocatedSize;
} DeviceSegStreamReg;
#endif //ifndef __STREAMED_H__

BIN
src/.main.c.swp Normal file

Binary file not shown.

View File

@@ -2,55 +2,36 @@
#include <string.h>
#include <dlfcn.h>
#include "tomlc99/toml.h"
#include "base_device.h"
device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf)
{
char intErrbuf[1024];
conf_dev_t *devConf = openBaseDeviceConfig(configPath, intErrbuf);
if (devConf == NULL)
{
sprintf(errbuf, "unable to read device config: %s", intErrbuf);
return NULL;
}
device_handle_t* ret = openBaseDevice(devConf, intErrbuf);
if(ret == NULL)
{
freeConf(devConf);
free(devConf);
}
return ret;
}
void closeBaseDevice(device_handle_t* devHandle)
{
if(devHandle == NULL)
{
return;
}
if(devHandle->ctx != NULL)
{
if(devHandle->specs != NULL)
if(devHandle->specs != NULL && devHandle->lib != NULL)
{
devHandle->lib->freeSpecs(devHandle->specs);
}
if(devHandle->ctx->deviceMem != NULL)
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)
@@ -58,16 +39,16 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
device_handle_t* ret = malloc(sizeof(device_handle_t));
if (ret == NULL)
{
sprintf(errbuf, "unable to allocate device handle struct");
snprintf(errbuf, 1024, "unable to allocate device handle struct");
return NULL;
}
char intErrbuf[1024];
char intErrbuf[512];
device_lib_t* devLib = loadDeviceLib(devConf->libPath, intErrbuf);
if (devLib == NULL)
{
sprintf(errbuf, "unable to load device library %s: %s", devConf->libPath, intErrbuf);
snprintf(errbuf, 1024, "unable to load device library %s: %s", devConf->libPath, intErrbuf);
free(ret);
return NULL;
}
@@ -76,7 +57,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
if (devSpecs == NULL)
{
sprintf(errbuf, "device config parse error: %s", intErrbuf);
snprintf(errbuf, 1024, "device config parse error: %s", intErrbuf);
dlclose(devLib->_dlhandl);
free(ret);
free(devLib);
@@ -88,189 +69,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
ret->specs = devSpecs;
ret->clockCycleCounter = 0;
ret->clockCycleLimit = 0;
return ret;
}
conf_mem_seg_t** parseMemToml(const toml_table_t* memTable)
{
uint16_t memSegIdx = 0;
const char* memSegKey = toml_key_in(memTable, 0);
while (memSegKey && memSegIdx < 0xFFFF)
{
memSegIdx++;
memSegKey = toml_key_in(memTable, memSegIdx);
}
conf_mem_seg_t** segConfigs = malloc(sizeof(conf_mem_seg_t*) * ((size_t)memSegIdx + 1));
if (segConfigs == NULL)
{
return NULL;
}
segConfigs[memSegIdx] = NULL;
for (size_t i = 0; i < memSegIdx; i++)
{
segConfigs[i] = (conf_mem_seg_t*)malloc(sizeof(conf_mem_seg_t));
memSegKey = toml_key_in(memTable, (int)i);
if (segConfigs[i] == NULL || memSegKey == NULL)
{
if (segConfigs[i] != NULL)
{
free(segConfigs[i]);
}
else
{
}
for(size_t j = 0; j < i; j++)
{
free(segConfigs[j]->name);
free(segConfigs[j]);
}
free(segConfigs);
return NULL;
}
segConfigs[i]->name = (char*)malloc(strlen(memSegKey) + 1);
if (segConfigs[i]->name == NULL)
{
for(size_t j = 0; j <= i; j++)
{
if (j < i)
free(segConfigs[j]->name);
free(segConfigs[j]);
}
free(segConfigs);
return NULL;
}
strcpy(segConfigs[i]->name, memSegKey);
toml_table_t* segTable = toml_table_in(memTable, memSegKey);
if (segTable == NULL)
{
for(size_t j = 0; j <= i; j++)
{
free(segConfigs[j]->name);
free(segConfigs[j]);
}
free(segConfigs);
return NULL;
}
toml_datum_t startDatum = toml_int_in(segTable, "start");
toml_datum_t lenDatum = toml_int_in(segTable, "len");
toml_datum_t wordLenDatum = toml_int_in(segTable, "wordLen");
toml_datum_t executableDatum = toml_bool_in(segTable, "executable");
if (!startDatum.ok || !lenDatum.ok || !wordLenDatum.ok)
{
for(size_t j = 0; j <= i; j++)
{
free(segConfigs[j]->name);
free(segConfigs[j]);
}
free(segConfigs);
return NULL;
}
segConfigs[i]->start = (size_t)startDatum.u.i;
segConfigs[i]->len = (size_t)lenDatum.u.i;
segConfigs[i]->wordLen = (uint8_t)wordLenDatum.u.i;
segConfigs[i]->isExecutable = 0;
if (executableDatum.ok)
{
segConfigs[i]->isExecutable = executableDatum.u.b != 0;
}
}
return segConfigs;
}
conf_dev_t* openBaseDeviceConfig(const char* configPath, char* errbuf)
{
conf_dev_t* ret = malloc(sizeof(conf_dev_t));
if (ret == NULL)
{
sprintf(errbuf, "unable to allocate device conf struct");
return NULL;
}
ret->id = NULL;
ret->clockDivider = 1;
ret->clockMultipler = 1;
ret->clockId = NULL;
ret->memConf = malloc(sizeof(conf_mem_t));
if (ret->memConf == NULL)
{
sprintf(errbuf, "unable to allocate device memory conf struct");
free(ret);
return NULL;
}
char intErrbuf[1024];
FILE *fp = fopen (configPath, "rb");
if (!fp) {
sprintf(errbuf, "unable to open config file %s", configPath);
free(ret->memConf);
free(ret);
return NULL;
}
toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf));
fclose(fp);
if (!conf) {
sprintf(errbuf, "cannot parse - %s", intErrbuf);
free(ret->memConf);
free(ret);
return NULL;
}
toml_table_t* devBlock = toml_table_in(conf, "dev");
if (!devBlock) {
sprintf(errbuf, "missing [dev]");
free(ret->memConf);
free(ret);
return NULL;
}
toml_table_t* memTable = toml_table_in(conf, "mem");
if (!memTable) {
sprintf(errbuf, "missing [mem]");
free(ret->memConf);
free(ret);
return NULL;
}
toml_datum_t libpathBlock = toml_string_in(devBlock, "libpath");
if (!libpathBlock.ok) {
sprintf(errbuf, "unable to read dev.libpath");
free(ret->memConf);
free(ret);
return NULL;
}
conf_mem_seg_t** memSegConfs = parseMemToml(memTable);
if (memSegConfs == NULL)
{
sprintf(errbuf, "unable to parse mem segments");
free(ret->memConf);
free(ret);
return NULL;
}
ret->memConf->memSegConfs = memSegConfs;
ret->libPath = libpathBlock.u.s;
return ret;
}

View File

@@ -2,488 +2,85 @@
#include <stdlib.h>
#include <string.h>
#include "tomlc99/toml.h"
#include "base_device.h"
#include "compose_device.h"
// void fillDevClockConfig(const toml_table_t* clockTable, conf_dev_t** devConfs)
// char** appendId(char** prev, const char* cur, char* errbuf)
// {
// uint16_t clockSegIdx = 0;
// uint16_t clockSegSkips = 0;
// const char* clockSegKey = toml_key_in(clockTable, 0);
// while (clockSegKey && (clockSegIdx + clockSegSkips) < 0xFFFF)
// if(prev == NULL)
// {
// clockSegKey = toml_key_in(clockTable, clockSegIdx + clockSegSkips + 1);
// if(strcmp(clockSegKey, "limiter") != 0)
// prev = malloc(sizeof(char*) * 2);
// if(prev == NULL)
// {
// clockSegIdx++;
// }
// else
// {
// clockSegSkips++;
// }
// snprintf(errbuf, 1024, "unable to allocate id");
// return NULL;
// }
// prev[0] = NULL;
// prev[1] = NULL;
// }
char** appendId(char** prev, const char* cur, char* errbuf)
{
if(prev == NULL)
{
prev = malloc(sizeof(char*) * 2);
if(prev == NULL)
{
sprintf(errbuf, "unable to allocate id");
return NULL;
}
prev[0] = NULL;
prev[1] = NULL;
}
// size_t clockIdLen = 0;
// while (prev[clockIdLen] != NULL){clockIdLen++;}
size_t clockIdLen = 0;
while (prev[clockIdLen] != NULL){clockIdLen++;}
// clockIdLen++;
clockIdLen++;
// char** new = realloc(prev, sizeof(char*) * (clockIdLen + 1));
char** new = realloc(prev, sizeof(char*) * (clockIdLen + 1));
// if(new == NULL)
// {
// snprintf(errbuf, 1024, "unable to reallocate id");
// freeComposeId(prev);
// return NULL;
// }
// prev = new;
if(new == NULL)
{
sprintf(errbuf, "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;
// }
prev[clockIdLen] = NULL;
size_t idLen = strlen(cur);
prev[clockIdLen - 1] = malloc(sizeof(char) * (idLen + 1));
if(prev[clockIdLen - 1] == NULL)
{
sprintf(errbuf, "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);
// }
conf_dev_t** parseDevToml(const toml_table_t* devTable, const toml_table_t* clockTable, char* errbuf)
{
uint16_t devSegIdx = 0;
const char* devSegKey = toml_key_in(devTable, 0);
while (devSegKey && devSegIdx < 0xFFFF)
{
devSegIdx++;
devSegKey = toml_key_in(devTable, devSegIdx);
}
conf_dev_t** deviceConfigs = malloc(sizeof(conf_dev_t*) * (devSegIdx + 1));
if(deviceConfigs == NULL)
{
return NULL;
}
deviceConfigs[devSegIdx] = NULL;
for(size_t i = 0; i < devSegIdx; i++)
{
devSegKey = toml_key_in(devTable, (int)i);
if(devSegKey == NULL)
{
sprintf(errbuf, "unable to load device key %lu", i);
for(size_t j = 0; j < i; j++)
{
freeConf(deviceConfigs[j]);
free(deviceConfigs[j]);
}
free(deviceConfigs);
return NULL;
}
toml_datum_t devpathDatum = toml_string_in(devTable, devSegKey);
if(!devpathDatum.ok || !devpathDatum.u.s)
{
sprintf(errbuf, "unable to get device path for %s", devSegKey);
for(size_t j = 0; j < i; j++)
{
freeConf(deviceConfigs[j]);
free(deviceConfigs[j]);
}
free(deviceConfigs);
return NULL;
}
char intErrbuf[1024];
conf_dev_t* conf = openBaseDeviceConfig(devpathDatum.u.s, intErrbuf);
if(conf == NULL)
{
sprintf(errbuf, "unable to load config for %s: %s", devSegKey, intErrbuf);
for(size_t j = 0; j < i; j++)
{
freeConf(deviceConfigs[j]);
free(deviceConfigs[j]);
}
free(deviceConfigs);
return NULL;
}
conf->id = NULL;
conf->id = appendId(conf->id, devSegKey, intErrbuf);
if(conf->id == NULL)
{
sprintf(errbuf, "unable to allocate device %s name field", devSegKey);
for(size_t j = 0; j <= i; j++)
{
freeConf(deviceConfigs[j]);
free(deviceConfigs[j]);
}
free(deviceConfigs);
return NULL;
}
deviceConfigs[i] = conf;
}
if(clockTable != NULL)
{
for(size_t i = 0; i < devSegIdx; i++)
{
conf_dev_t* conf = deviceConfigs[i];
toml_table_t* devClockTable = toml_table_in(clockTable, conf->id[0]);
if(devClockTable == NULL)
{
continue;
}
toml_datum_t dividerDatum = toml_int_in(devClockTable, "divider");
if(dividerDatum.ok)
{
conf->clockDivider = (uint64_t)dividerDatum.u.i;
}
toml_datum_t multiplerDatum = toml_int_in(devClockTable, "multipler");
if(multiplerDatum.ok)
{
conf->clockMultipler = (uint64_t)multiplerDatum.u.i;
}
toml_datum_t srcDatum = toml_string_in(devClockTable, "src");
if(srcDatum.ok && srcDatum.u.s != NULL)
{
char intErrbuf[1024] = {0};
conf->clockId = appendId(conf->clockId, srcDatum.u.s, intErrbuf);
if(conf->clockId == NULL)
{
sprintf(errbuf, "unable to append clock id for %s: %s", conf->id[0], intErrbuf);
for(size_t j = 0; j <= i; j++)
{
freeConf(deviceConfigs[j]);
free(deviceConfigs[j]);
}
free(deviceConfigs);
return NULL;
}
}
}
}
return deviceConfigs;
}
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);
}
projection_conf_t* parseDeviceProjectionConf(const toml_table_t* deviceProjectionTable, char* errbuf)
{
toml_datum_t baseAtDatum = toml_string_in(deviceProjectionTable, "base_at");
if(!baseAtDatum.ok || baseAtDatum.u.s == NULL)
{
sprintf(errbuf, "missing 'base_at'");
return NULL;
}
toml_datum_t baseSegDatum = toml_string_in(deviceProjectionTable, "base_seg");
if(!baseSegDatum.ok || baseSegDatum.u.s == NULL)
{
sprintf(errbuf, "missing 'base_seg'");
return NULL;
}
toml_datum_t projectionShiftDatum = toml_int_in(deviceProjectionTable, "projection_shift");
char intErrbuf[1024] = {0};
projection_conf_t* ret = malloc(sizeof(projection_conf_t));
if(ret == NULL)
{
sprintf(errbuf, "unable to allocate projection conf");
return NULL;
}
ret->baseAt = NULL;
ret->baseSeg = NULL;
ret->target = NULL;
ret->projectionShift = 0;
if(projectionShiftDatum.ok)
{
ret->projectionShift = (uint64_t)projectionShiftDatum.u.i;
}
ret->baseAt = appendId(ret->baseAt, baseAtDatum.u.s, intErrbuf);
if(ret->baseAt == NULL)
{
sprintf(errbuf, "unable to append base_at id");
freeProjectionConfig(ret);
return NULL;
}
ret->baseSeg = malloc(strlen(baseSegDatum.u.s));
if(ret->baseSeg == NULL)
{
sprintf(errbuf, "unable to fill base_seg");
freeProjectionConfig(ret);
return NULL;
}
strcpy(ret->baseSeg, baseSegDatum.u.s);
return ret;
}
projection_conf_t** parseDeviceProjectionConfs(const toml_table_t* deviceProjectionsTable, char* errbuf)
{
uint16_t projSegIdx = 0;
const char* projSegKey = toml_key_in(deviceProjectionsTable, 0);
while (projSegKey && projSegIdx < 0xFFFF)
{
projSegIdx++;
projSegKey = toml_key_in(deviceProjectionsTable, projSegIdx);
}
char intErrbuf[1024] = {0};
projection_conf_t** ret = malloc(sizeof(projection_conf_t*) * (projSegIdx + 1));
if(ret == NULL)
{
sprintf(errbuf, "unable to allocate device projections confs");
return NULL;
}
for(size_t i = 0; i <= projSegIdx; i++){ret[i] = NULL;}
for(size_t i = 0; i < projSegIdx; i++)
{
projSegKey = toml_key_in(deviceProjectionsTable, (int)i);
if(projSegKey == NULL)
{
sprintf(errbuf, "unable to load device projection key %lu", i);
freeProjectionConfigs(ret);
return NULL;
}
toml_table_t* projTable = toml_table_in(deviceProjectionsTable, projSegKey);
if(projTable == NULL)
{
sprintf(errbuf, "unable to open device %s projection table", projSegKey);
freeProjectionConfigs(ret);
return NULL;
}
ret[i] = parseDeviceProjectionConf(projTable, intErrbuf);
if(ret[i] == NULL)
{
sprintf(errbuf, "unable to load device %s projection configs: %s", projSegKey, intErrbuf);
freeProjectionConfigs(ret);
return NULL;
}
ret[i]->target = appendId(ret[i]->target, projSegKey, intErrbuf);
if(ret[i]->target == NULL)
{
sprintf(errbuf, "unable to append projection target id: %s", intErrbuf);
freeProjectionConfigs(ret);
return NULL;
}
}
return ret;
}
projection_conf_t** parseProjectionConfs(const toml_table_t* projectionTable, char* errbuf)
{
uint16_t devicesProjSegIdx = 0;
const char* devicesProjSegKey = toml_key_in(projectionTable, 0);
while (devicesProjSegKey && devicesProjSegIdx < 0xFFFF)
{
devicesProjSegIdx++;
devicesProjSegKey = toml_key_in(projectionTable, devicesProjSegIdx);
}
char intErrbuf[1024] = {0};
size_t totalRetSize = 0;
projection_conf_t** ret = malloc(sizeof(projection_conf_t*));
if(ret == NULL)
{
sprintf(errbuf, "unable to allocate base projection conf array");
return NULL;
}
ret[0] = NULL;
for(size_t i = 0; i < devicesProjSegIdx; i++)
{
devicesProjSegKey = toml_key_in(projectionTable, (int)i);
if(devicesProjSegKey == NULL)
{
sprintf(errbuf, "unable to load proj key %lu", i);
freeProjectionConfigs(ret);
return NULL;
}
toml_table_t* deviceProjTable = toml_table_in(projectionTable, devicesProjSegKey);
if(deviceProjTable == NULL)
{
sprintf(errbuf, "unable to open device projection table");
freeProjectionConfigs(ret);
return NULL;
}
projection_conf_t** devProjections = parseDeviceProjectionConfs(deviceProjTable, intErrbuf);
if(devProjections == NULL)
{
sprintf(errbuf, "unable to parse device %s projection rules: %s", devicesProjSegKey, intErrbuf);
freeProjectionConfigs(ret);
return NULL;
}
size_t dlen = 0;
while(devProjections[dlen] != NULL)
{
devProjections[dlen]->target = appendId(devProjections[dlen]->target, devicesProjSegKey, intErrbuf);
if(devProjections[dlen]->target == NULL)
{
freeProjectionConfigs(ret);
freeProjectionConfigs(devProjections);
return NULL;
}
dlen++;
}
ret = realloc(ret, sizeof(projection_conf_t*) * (dlen + totalRetSize + 1));
if(ret == NULL)
{
sprintf(errbuf, "unable to reallocate full projection array");
freeProjectionConfigs(devProjections);
return NULL;
}
for(size_t i = 0; i <= dlen; i++)
{
ret[totalRetSize + i] = devProjections[i];
}
totalRetSize += dlen;
ret[totalRetSize] = NULL;
free(devProjections);
}
return ret;
}
compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf)
{
compose_dev_conf_t* ret = malloc(sizeof(compose_dev_conf_t));
if(ret == NULL)
{
sprintf(errbuf, "unable to allocate device struct");
return NULL;
}
char intErrbuf[1024] = {0};
FILE *fp = fopen (configPath, "rb");
if (!fp) {
sprintf(errbuf, "unable to open config file %s", configPath);
free(ret);
return NULL;
}
toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf));
fclose(fp);
toml_table_t* devTable = toml_table_in(conf, "dev");
if (!devTable) {
sprintf(errbuf, "missing [dev]");
free(ret);
return NULL;
}
ret->projections = NULL;
toml_table_t* memTable = toml_table_in(conf, "mem");
if(memTable)
{
toml_table_t* projectionTable = toml_table_in(memTable, "projection");
if(projectionTable)
{
ret->projections = parseProjectionConfs(projectionTable, intErrbuf);
if(ret->projections == NULL)
{
sprintf(errbuf, "unable to load projections: %s", intErrbuf);
free(ret);
return NULL;
}
}
}
toml_table_t* clockTable = toml_table_in(conf, "clock");
conf_dev_t** devHandlers = parseDevToml(devTable, clockTable, intErrbuf);
if(devHandlers == NULL)
{
sprintf(errbuf, "unable to load devices configs: %s", intErrbuf);
freeProjectionConfigs(ret->projections);
free(ret);
return NULL;
}
ret->baseConfigs = devHandlers;
return ret;
}
// 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)
@@ -495,7 +92,7 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
if(devHandlers == NULL)
{
sprintf(errbuf, "unable to allocate dev handlers");
snprintf(errbuf, 1024, "unable to allocate dev handlers");
return NULL;
}
devHandlers[devIdx] = NULL;
@@ -504,11 +101,11 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
for(size_t i = 0; i < devIdx; i++)
{
conf_dev_t* devConf = conf->baseConfigs[i];
char intErrbuf[1024] = {0};
char intErrbuf[255] = {0};
device_handle_t* devHandle = openBaseDevice(devConf, intErrbuf);
if(devHandle == NULL)
{
sprintf(errbuf, "unable to open base device %s: %s", devConf->id[0], intErrbuf);
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]);
@@ -523,7 +120,3 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
return devHandlers;
}

View File

@@ -4,7 +4,7 @@
#include "config.h"
void xfree(void* p)
static void xfree(void* p)
{
if(p != NULL)
{
@@ -28,21 +28,21 @@ void freeComposeId(char** id)
}
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 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;
// }
void freeMemSegConf(conf_mem_seg_t* memSegConf)
static void freeMemSegConf(conf_mem_seg_t* memSegConf)
{
if(memSegConf == NULL)
{
@@ -50,7 +50,7 @@ void freeMemSegConf(conf_mem_seg_t* memSegConf)
}
xfree(memSegConf->name);
}
void freeMemConf(conf_mem_t* memConf)
static void freeMemConf(conf_mem_t* memConf)
{
if(memConf == NULL)
{

View File

@@ -10,6 +10,7 @@ typedef size_t (*_dlib_pubExtractOpcode_t)(device_mem_t* devMem, size_t _program
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);
@@ -25,7 +26,7 @@ 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);
instruction_simul_handlers_t* _fillInstructionSimul(void* handle)
static instruction_simul_handlers_t* _fillInstructionSimul(void* handle)
{
instruction_simul_handlers_t* ret = malloc(sizeof(instruction_simul_handlers_t));
@@ -92,14 +93,16 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
if (dev == NULL)
{
sprintf(errbuf, "unable to allocate device lib struct");
snprintf(errbuf, 1024, "unable to allocate device lib struct");
return NULL;
}
void *handle = dlopen(libpath, RTLD_NOW);
if (!handle) {
sprintf(errbuf, "unable to open dl handle");
if (!handle)
{
const char *dlerr = dlerror();
snprintf(errbuf, 1024, "unable to open dl handle: %s", dlerr);
free(dev);
return NULL;
}
@@ -109,8 +112,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
_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) {
sprintf(errbuf, "unable to find init symbol: %s", dlsym_init_error);
if (dlsym_init_error)
{
snprintf(errbuf, 1024, "unable to find init symbol: %s", dlsym_init_error);
dlclose(handle);
free(dev);
return NULL;
@@ -120,8 +124,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
const char *dlsym_maketick_error = dlerror();
if (dlsym_maketick_error) {
sprintf(errbuf, "unable to find makeDeviceTick symbol: %s", dlsym_maketick_error);
if (dlsym_maketick_error)
{
snprintf(errbuf, 1024, "unable to find makeDeviceTick symbol: %s", dlsym_maketick_error);
dlclose(handle);
free(dev);
return NULL;
@@ -132,8 +137,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
_dlib_parseSpecsFromConfig_t _dlib_parseSpecsFromConfig = (_dlib_parseSpecsFromConfig_t)(uintptr_t)dlsym(handle, "parseSpecsFromConfig");
const char *_dlib_parseSpecsFromConfig_error = dlerror();
if (_dlib_parseSpecsFromConfig_error) {
sprintf(errbuf, "unable to find parseSpecsFromConfig symbol: %s", _dlib_parseSpecsFromConfig_error);
if (_dlib_parseSpecsFromConfig_error)
{
snprintf(errbuf, 1024, "unable to find parseSpecsFromConfig symbol: %s", _dlib_parseSpecsFromConfig_error);
dlclose(handle);
free(dev);
return NULL;
@@ -144,8 +150,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
_dlib_fillSmartReadSpecs_t _dlib_fillSmartReadSpecs = (_dlib_fillSmartReadSpecs_t)(uintptr_t)dlsym(handle, "fillSmartReadSpecs");
const char *_dlib_fillSmartReadSpecs_error = dlerror();
if (_dlib_fillSmartReadSpecs_error) {
sprintf(errbuf, "unable to find fillSmartReadSpecs symbol: %s", _dlib_fillSmartReadSpecs_error);
if (_dlib_fillSmartReadSpecs_error)
{
snprintf(errbuf, 1024, "unable to find fillSmartReadSpecs symbol: %s", _dlib_fillSmartReadSpecs_error);
dlclose(handle);
free(dev);
return NULL;
@@ -156,8 +163,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
_dlib_fillSmartWriteSpecs_t _dlib_fillSmartWriteSpecs = (_dlib_fillSmartWriteSpecs_t)(uintptr_t)dlsym(handle, "fillSmartWriteSpecs");
const char *_dlib_fillSmartWriteSpecs_error = dlerror();
if (_dlib_fillSmartWriteSpecs_error) {
sprintf(errbuf, "unable to find fillSmartWriteSpecs symbol: %s", _dlib_fillSmartWriteSpecs_error);
if (_dlib_fillSmartWriteSpecs_error)
{
snprintf(errbuf, 1024, "unable to find fillSmartWriteSpecs symbol: %s", _dlib_fillSmartWriteSpecs_error);
dlclose(handle);
free(dev);
return NULL;
@@ -167,8 +175,9 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
_dlib_deviceType_t _dlib_deviceType = (_dlib_deviceType_t)(uintptr_t)dlsym(handle, "pubDeviceType");
const char *dlib_deviceType_error = dlerror();
if (dlib_deviceType_error) {
sprintf(errbuf, "unable to find pubDeviceType symbol: %s", dlib_deviceType_error);
if (dlib_deviceType_error)
{
snprintf(errbuf, 1024, "unable to find pubDeviceType symbol: %s", dlib_deviceType_error);
dlclose(handle);
free(dev);
return NULL;
@@ -180,19 +189,33 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
_dlib_freeSpecs_t _dlib_freeSpecs = (_dlib_freeSpecs_t)(uintptr_t)dlsym(handle, "freeDevSpecs");
const char *dlib_freeSpecs_error = dlerror();
if (dlib_freeSpecs_error) {
sprintf(errbuf, "unable to find freeSpecs symbol: %s", dlib_freeSpecs_error);
if (dlib_freeSpecs_error)
{
snprintf(errbuf, 1024, "unable to find freeSpecs symbol: %s", dlib_freeSpecs_error);
dlclose(handle);
free(dev);
return NULL;
}
_dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevSpecs");
_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) {
sprintf(errbuf, "unable to find freeDevMem symbol: %s", dlib_freeDevMem_error);
if (dlib_freeDevMem_error)
{
snprintf(errbuf, 1024, "unable to find freeDevMem symbol: %s", dlib_freeDevMem_error);
dlclose(handle);
free(dev);
return NULL;
@@ -206,6 +229,7 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
dev->fillSmartWriteSpecs = _dlib_fillSmartWriteSpecs;
dev->freeSpecs = _dlib_freeSpecs;
dev->freeDevMem = _dlib_freeDevMem;
dev->reset = _dlib_reset;
uint8_t devType = _dlib_deviceType();

45
src/linkedlist.c Normal file
View File

@@ -0,0 +1,45 @@
#include <stdlib.h>
#include "linkedlist.h"
void removeLinkedListEntry(LinkedListEntry** head, LinkedListEntry* entry)
{
// check for head
if(entry->prevEntry != NULL)
{
entry->prevEntry->nextEntry = entry->nextEntry;
}
// check for tail
if(entry->nextEntry != NULL)
{
entry->nextEntry->prevEntry = entry->prevEntry;
}
// check for removing head entry
if(entry == *head)
{
if(entry->nextEntry == NULL && entry->prevEntry == NULL)
{
// removing full list
*head = NULL;
}
else if(entry->nextEntry != NULL)
{
// moving head to next entry
*head = entry->nextEntry;
}
else if(entry->prevEntry != NULL)
{
// head that have previous item, must be impossible
*head = entry->prevEntry;
}
if(entry == *head)
{
// if still head => there is a circular list, removing whole
*head = NULL;
}
}
free(entry);
}

1055
src/main.c

File diff suppressed because it is too large Load Diff

1
src/main.h Normal file
View File

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

21
src/panic.c Normal file
View File

@@ -0,0 +1,21 @@
#include "panic.h"
void print_stacktrace(FILE* fd)
{
void *buffer[PANIC_STRACE_LEN];
int size = backtrace(buffer, PANIC_STRACE_LEN);
char **symbols = backtrace_symbols(buffer, size);
if (symbols == NULL) {
fprintf(fd, "backtrace_symbols");
abort();
}
fprintf(fd, "\n\nStack trace:\n");
for (int i = 0; i < size; i++)
{
fprintf(fd, "%s\n", symbols[i]);
}
free(symbols);
}

61
src/proto/dial.c Normal file
View File

@@ -0,0 +1,61 @@
#include "proto/dial.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "context.h"
#include "panic.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)
{
LinkedListEntry* clientsHead = *emulContext->clientsHead;
while(clientsHead != NULL)
{
uint8_t* newMsg = malloc(sizeof(uint8_t) * msgLen);
NULL_GUARD(newMsg);
memcpy(newMsg, msg, msgLen);
ClientContext* ctx = clientsHead->payload;
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, newMsg, msgLen);
clientsHead = clientsHead->nextEntry;
}
free(msg);
}
void dispatchOutgoingMessage(queue_spsc_t* outMsgQueue, ws_cli_conn_t clientIdx, uint8_t* msg, size_t msgLen)
{
// OutgoingBuffers* outBufs = emulContext->outBufs;
// SizedPtr* p = &outBufs->bufs[outBufs->currWritingIdx];
// if(p->size + 1 >= p->allocatedSize)
// {
// // printf("\t>>Reallocating buf %d\n", outBufs->currWritingIdx);
// OutgoingMessage* newPtr = realloc(p->ptr, sizeof(OutgoingMessage) * p->allocatedSize * 5);
// NULL_GUARD(newPtr);
// p->ptr = newPtr;
// p->allocatedSize = p->allocatedSize * 5;
// }
OutgoingMessage* outmsg = malloc(sizeof(OutgoingMessage));
// OutgoingMessage* outmsg = &((OutgoingMessage*)p->ptr)[p->size];
NULL_GUARD(outmsg);
outmsg->msg = msg;
outmsg->msgLen = msgLen;
outmsg->clientIdx = clientIdx;
outmsg->dispatch_us = dial_now_us();
queue_spsc_push(outMsgQueue, outmsg);
// p->size++;
}

191
src/proto/handlers.c Normal file
View File

@@ -0,0 +1,191 @@
#include "proto/handlers.h"
#include <stdio.h>
#include "panic.h"
#include "proto/enums.h"
#include "proto/handlers/auth.h"
#include "proto/handlers/stream.h"
#include "proto/handlers/control.h"
#include "proto/handlers/mem.h"
#include "proto/msg.h"
#include "proto_reader.h"
#include "proto_verifier.h"
#include "control_reader.h"
#include "ptQueue/inc/mpsc.h"
#include "ptQueue/inc/spsc.h"
#include "stream_reader.h"
#include "mem_reader.h"
static void handleCloseClient(EmulContext* emulContext, ClientContext* ctx)
{
// if (ctx->streamRegIterator > 0) {
unregisterClientStreams(emulContext, ctx);
// }
}
void handleRegEvent(EmulContext* emulContext, ClientRegistrationEvent* ev)
{
if (ev->regType == REG_EVTYPE_CONNECT)
{
printf("open client %lu\n", ev->ctx->clientId);
LinkedListEntry* entry = malloc(sizeof(LinkedListEntry));
NULL_GUARD(entry);
entry->payload = ev->ctx;
if (*emulContext->clientsHead != NULL) {
(*emulContext->clientsHead)->prevEntry = entry;
}
entry->prevEntry = NULL;
entry->nextEntry = *emulContext->clientsHead;
*emulContext->clientsHead = entry;
}
else if (ev->regType == REG_EVTYPE_CLOSE)
{
LinkedListEntry* clientEntry = *emulContext->clientsHead;
while (clientEntry != NULL) {
if (clientEntry->payload == ev->ctx)
{
printf("close client %lu\n", ev->ctx->clientId);
handleCloseClient(emulContext, ev->ctx);
removeLinkedListEntry(emulContext->clientsHead, clientEntry);
break;
}
clientEntry = clientEntry->nextEntry;
}
}
else if (ev->regType == REG_EVTYPE_AUTH)
{
printf("auth client %lu\n", ev->ctx->clientId);
ev->ctx->isAuthed = 1;
handleOnClientAuthDone(ev->ctx, emulContext);
}
}
static void handleIncomingMessage(
hmmmm_ClientMessage_table_t cm, ClientContext* ctx, EmulContext* emulContext)
{
uint64_t nonce = hmmmm_ClientMessage_nonce(cm);
hmmmm_ClientPayload_union_type_t ptype = hmmmm_ClientMessage_payload_type(cm);
printf("client %lu: nonce=%lu payload=%s\n",
ctx->clientId, nonce, hmmmm_ClientPayload_type_name(ptype));
switch (ptype)
{
case hmmmm_ClientPayload_CtrlClientMessage:
{
hmmmm_ctrl_CtrlClientMessage_table_t ctrl =
(hmmmm_ctrl_CtrlClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingCtrlMessage(ctrl, nonce, ctx, emulContext);
break;
}
case hmmmm_ClientPayload_StreamClientMessage:
{
hmmmm_stream_StreamClientMessage_table_t stream =
(hmmmm_stream_StreamClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingStreamMessage(stream, nonce, ctx, emulContext);
break;
}
case hmmmm_ClientPayload_MemClientMessage:
{
hmmmm_mem_MemClientMessage_table_t mem =
(hmmmm_mem_MemClientMessage_table_t)hmmmm_ClientMessage_payload(cm);
handleIncomingMemMessage(mem, nonce, ctx, emulContext);
break;
}
case hmmmm_ClientPayload_AuthRequest:
{
// AuthRequest must only arrive before auth; drop if seen here
printf("client %lu: unexpected AuthRequest after auth\n", ctx->clientId);
break;
}
default:
{
printf("client %lu: unknown payload type %u\n", ctx->clientId, ptype);
break;
}
}
}
void handleAllClients(EmulContext* emulContext)
{
queue_mpsc_t* inMsgQueue = emulContext->inMsgQueue;
FbMessage* fbmsg = NULL;
for(size_t handleLimit = 0; handleLimit < 32; handleLimit++)
{
fbmsg = NULL;
if(queue_mpsc_pop(inMsgQueue, (void**)&fbmsg))
{
if(fbmsg != NULL)
{
*emulContext->utilizedFlag = 1;
// if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
hmmmm_ClientMessage_table_t cm =
hmmmm_ClientMessage_as_root(fbmsg->data);
// printf("read message len %lu\n", fbmsg->size);
handleIncomingMessage(cm, fbmsg->ctx, emulContext);
// } else {
// printf("client %lu: dropped malformed FlatBuffer\n", fbmsg->ctx->clientId);
// }
free(fbmsg->data);
free(fbmsg);
}
else
{
break;
}
}
else
{
break;
}
}
// LinkedListEntry* clientEntry = *emulContext->clientsHead;
// size_t handleLimit = 128;
// 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;
// }
// }
}

159
src/proto/handlers/auth.c Normal file
View File

@@ -0,0 +1,159 @@
#include "proto/handlers/auth.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/sha.h>
#include "my_mutex.h"
#include "events.h"
#include "panic.h"
#include "proto/enums.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "proto_reader.h"
#include "proto_verifier.h"
#include "auth_reader.h"
#include "ptQueue/inc/spsc.h"
static uint8_t validateAccessTokenDeterministic(
const uint8_t* data, const uint8_t* token, uint64_t timestamp)
{
char buf[1024];
(void)snprintf(buf, sizeof(buf), "%s%lu", token, timestamp);
uint8_t hash[SHA512_DIGEST_LENGTH];
SHA512((const uint8_t*)buf, strlen(buf), hash);
uint8_t valid = 1;
for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) {
if (data[i] != hash[i]) {
valid = 0;
}
}
return valid;
}
static uint8_t validateAccessToken(const uint8_t* data, const uint8_t* access_token)
{
uint64_t t = (uint64_t)time(NULL) / 30;
return validateAccessTokenDeterministic(data, access_token, t)
|| validateAccessTokenDeterministic(data, access_token, t - 1);
}
uint8_t handle_auth(
ClientContext* cctx, ws_cli_conn_t client,
const uint8_t* msg, uint64_t msgSize, int msgType)
{
(void)msgType;
ServerContext* ctx = ws_get_server_context(client);
// Verify it's a valid ClientMessage FlatBuffer
if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)msgSize)) {
printf("Auth: invalid FlatBuffer from client %lu\n", client);
ws_close_client(client);
return 0;
}
hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_as_root(msg);
if (hmmmm_ClientMessage_payload_type(cm) != hmmmm_ClientPayload_AuthRequest) {
printf("Auth: expected AuthRequest, got type %u from client %lu\n",
hmmmm_ClientMessage_payload_type(cm), client);
ws_close_client(client);
return 0;
}
hmmmm_auth_AuthRequest_table_t ar =
(hmmmm_auth_AuthRequest_table_t)hmmmm_ClientMessage_payload(cm);
flatbuffers_uint8_vec_t hash_vec = hmmmm_auth_AuthRequest_hash(ar);
size_t hash_len = flatbuffers_uint8_vec_len(hash_vec);
if (hash_len != SHA512_DIGEST_LENGTH) {
printf("Auth: wrong hash length %zu from client %lu\n", hash_len, client);
ws_close_client(client);
return 0;
}
if (!validateAccessToken(hash_vec, (const uint8_t*)ctx->accessToken)) {
printf("Auth: token invalid for client %lu\n", client);
ws_close_client(client);
return 0;
}
printf("Auth: token valid for client %lu\n", client);
char errbuf[1024];
ClientRegistrationEvent* ev = malloc(sizeof(ClientRegistrationEvent));
if (ev == NULL) {
panic("Unable to allocate register event");
}
ev->regType = REG_EVTYPE_AUTH;
ev->ctx = cctx;
with_lock(&ctx->registerMutex)
{
printf("Writing auth event\n");
// int exitCode = ptQueuePush(ctx->regQueue, ev, errbuf);
queue_spsc_push(ctx->regQueue, ev);
// if (!queue_spsc_push(ctx->regQueue, ev)) {
// panic("Unable to push to reg queue: %s\n", errbuf);
// }
}
return 1;
}
LinkedListEntry* disconnectDueTimeout(
EmulContext* emulContext, LinkedListEntry* clientEntry)
{
ClientContext* cctx = clientEntry->payload;
uint64_t now = (uint64_t)time(NULL);
if (now - cctx->connectedAt <= 30) {
return clientEntry->nextEntry;
}
printf("Timeout on connection %lu\n", cctx->clientId);
int ret = ws_close_client(cctx->clientId);
if (ret == -1) {
printf("Unable to close client\n");
}
LinkedListEntry* nextEntry = clientEntry->nextEntry;
removeLinkedListEntry(emulContext->clientsHead, clientEntry);
*emulContext->utilizedFlag = 1;
return nextEntry;
}
void handleOnClientAuthDone(ClientContext* cctx, EmulContext* emulContext)
{
// Send AuthResponse with assigned seat_id
size_t len = 0;
uint8_t* authResp = fb_build_auth_response(UINT64_MAX, cctx->clientId, &len);
dispatchOutgoingMessage(emulContext->outMsgQueue, cctx->clientId, authResp, len);
// Send current execution state
len = 0;
uint8_t* stateMsg = fb_build_exec_notify(
UINT64_MAX, *emulContext->clockCounter, *emulContext->emulState, &len);
dispatchOutgoingMessage(emulContext->outMsgQueue, cctx->clientId, stateMsg, len);
// Send cached DeviceIdMappingNotif if a config has been loaded
if (emulContext->deviceIdMappingMsg && emulContext->deviceIdMappingMsgLen > 0) {
uint8_t* mappingCopy = malloc(emulContext->deviceIdMappingMsgLen);
if (mappingCopy) {
memcpy(mappingCopy, emulContext->deviceIdMappingMsg, emulContext->deviceIdMappingMsgLen);
dispatchOutgoingMessage(
emulContext->outMsgQueue, cctx->clientId,
mappingCopy, emulContext->deviceIdMappingMsgLen);
}
}
}

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

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

View File

@@ -0,0 +1,135 @@
#include "proto/handlers/control.h"
#include "proto/handlers/config.h"
#include <stdio.h>
#include "state.h"
#include "proto/msg.h"
#include "proto/dial.h"
#include "control_reader.h"
#include "exec_ctrl_reader.h"
#include "config_ctrl_reader.h"
#include "setup_buf_reader.h"
#include "orphaned_reader.h"
#include "lost_reader.h"
// Maps FlatBuffers ExecPrompt values to EMUL_STATE_OP_* constants.
// ExecPrompt: start=1, pause=2, resume=3, stop=4, reset=5
// EMUL_STATE_OP: START=1, PAUSE=2, RESUME=3, RESET=4, STOP=5
static uint8_t prompt_to_state_op(hmmmm_ctrl_exec_ExecPrompt_enum_t prompt)
{
switch (prompt) {
case hmmmm_ctrl_exec_ExecPrompt_start: return EMUL_STATE_OP_START;
case hmmmm_ctrl_exec_ExecPrompt_pause: return EMUL_STATE_OP_PAUSE;
case hmmmm_ctrl_exec_ExecPrompt_resume: return EMUL_STATE_OP_RESUME;
case hmmmm_ctrl_exec_ExecPrompt_stop: return EMUL_STATE_OP_STOP;
case hmmmm_ctrl_exec_ExecPrompt_reset: return EMUL_STATE_OP_RESET;
default: return 0;
}
}
void handleIncomingCtrlMessage(
hmmmm_ctrl_CtrlClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{
hmmmm_ctrl_CtrlClientPayload_union_type_t ptype =
hmmmm_ctrl_CtrlClientMessage_payload_type(msg);
printf("[CTRL] client=%lu nonce=%lu type=%s\n",
ctx->clientId, nonce,
hmmmm_ctrl_CtrlClientPayload_type_name(ptype));
if (ptype == hmmmm_ctrl_CtrlClientPayload_ExecCtrlMessage)
{
hmmmm_ctrl_exec_ExecCtrlMessage_table_t exec =
(hmmmm_ctrl_exec_ExecCtrlMessage_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
hmmmm_ctrl_exec_ExecPrompt_enum_t prompt =
hmmmm_ctrl_exec_ExecCtrlMessage_prompt(exec);
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;
}
else
{
uint8_t new_state = switchNewEmulState(*emulContext->emulState, state_op);
*emulContext->emulState = new_state;
printf("[CTRL/EXEC] state -> %u\n", new_state);
if (new_state == EMUL_STATE_EXEC && tick_count > 0) {
*emulContext->tickTarget = *emulContext->clockCounter + tick_count;
printf("set ticket target at %lu\n", *emulContext->tickTarget);
} else if (new_state == EMUL_STATE_EXEC) {
*emulContext->tickTarget = 0;
printf("reset tick target\n");
}
size_t msg_len;
uint8_t* out = fb_build_exec_notify(0, *emulContext->clockCounter, new_state, &msg_len);
broadcastClients(emulContext, out, msg_len);
}
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_SetupBuf)
{
hmmmm_ctrl_setup_buf_SetupBuf_table_t sb =
(hmmmm_ctrl_setup_buf_SetupBuf_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
uint32_t lost_buf_size = hmmmm_ctrl_setup_buf_SetupBuf_lost_buf_size(sb);
uint64_t lifetime_ticks = hmmmm_ctrl_setup_buf_SetupBuf_client_lifetime_ticks(sb);
printf("[CTRL/SETUP] lost_buf_size=%u lifetime_ticks=%lu\n",
lost_buf_size, lifetime_ticks);
size_t msg_len;
uint8_t* out = fb_build_setup_buf(nonce, lost_buf_size, lifetime_ticks, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_OrphanedRequest)
{
printf("[CTRL/ORPHANED] returning empty list\n");
size_t msg_len;
uint8_t* out = fb_build_orphaned_response(nonce, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_ConfigCtrlMessage)
{
handleConfigCtrlMessage(
(hmmmm_ctrl_config_ctrl_ConfigCtrlMessage_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg),
nonce, ctx, emulContext);
}
else if (ptype == hmmmm_ctrl_CtrlClientPayload_LostMessagesRequest)
{
hmmmm_ctrl_lost_LostMessagesRequest_table_t req =
(hmmmm_ctrl_lost_LostMessagesRequest_table_t)
hmmmm_ctrl_CtrlClientMessage_payload(msg);
uint64_t seat_id = hmmmm_ctrl_lost_LostMessagesRequest_seat_id(req);
printf("[CTRL/LOST] seat_id=%lu returning empty list\n", seat_id);
size_t msg_len;
uint8_t* out = fb_build_lost_messages_response(nonce, seat_id, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
}
}

171
src/proto/handlers/mem.c Normal file
View File

@@ -0,0 +1,171 @@
#include "proto/handlers/mem.h"
#include <stdio.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)
{
for(uint64_t i = 0; i < cellsCount; i++)
{
printf("%lu: 0x%04X", i, ((uint8_t*)cells)[i]);
// uint8_t wasRead = 0;
// for (uint8_t j = 0; j < mem->memreadLen; j++)
// {
// if(mem->memreadCellAddrs[j] == i)
// {
// wasRead = 1;
// break;
// }
// }
// uint8_t wasWrite = 0;
// for (uint8_t j = 0; j < mem->memwriteLen; j++)
// {
// if(mem->memwriteCellAddrs[j] == i)
// {
// wasWrite = 1;
// break;
// }
// }
// if (wasRead == 1)
// {
// printf("\t[was read]");
// }
// else
// {
// printf("\t[was not read]");
// }
// if (wasWrite == 1)
// {
// printf("\t[was written]");
// }
// else
// {
// printf("\t[was not written]");
// }
printf("\n");
}
}
void handleIncomingMemMessage(
hmmmm_mem_MemClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{
hmmmm_mem_MemClientPayload_union_type_t ptype =
hmmmm_mem_MemClientMessage_payload_type(msg);
// printf("[MEM] client=%lu nonce=%lu type=%s\n",
// ctx->clientId, nonce,
// hmmmm_mem_MemClientPayload_type_name(ptype));
if (ptype == hmmmm_mem_MemClientPayload_MemReadRequest)
{
hmmmm_mem_MemReadRequest_table_t req =
(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)
{
hmmmm_mem_MemWriteRequest_table_t req =
(hmmmm_mem_MemWriteRequest_table_t)
hmmmm_mem_MemClientMessage_payload(msg);
uint32_t dev_id = hmmmm_mem_MemWriteRequest_device_id(req);
uint32_t seg_id = hmmmm_mem_MemWriteRequest_seg_id(req);
uint32_t offset = hmmmm_mem_MemWriteRequest_offset(req);
flatbuffers_uint8_vec_t data = hmmmm_mem_MemWriteRequest_data(req);
size_t data_len = flatbuffers_uint8_vec_len(data);
// printf("[MEM/WRITE] device=%u seg=%u offset=%u len=%zu\n",
// dev_id, seg_id, offset, data_len);
if (dev_id >= (uint32_t)emulContext->devicesCount) {
printf("[MEM/WRITE] invalid device %u\n", dev_id);
return;
}
if(!data) {
printf("[MEM/WRITE] invalid data\n");
return;
}
// if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
// printf("[MEM/WRITE] out of bounds\n");
// return;
// }
// device_mem_t* devMem = emulContext->devicesMem[dev_id];
device_handle_t* handl = emulContext->deviceHandles[dev_id];
// printf("[MEM/WRITE] from %d/%d+%d:%lu\n", dev_id, seg_id, offset, data_len);
uint8_t* base = handl->ctx->deviceMem->cells[seg_id]; // emulContext->devicesMem[dev_id] + handl->ctx->deviceMem->memsegShifts[seg_id];
memcpy(base + offset, data, data_len);
// for(size_t i = 0; i < data_len; i++)
// {
// printf("%02X ", (base + offset)[i]);
// }
// printf("\n");
size_t out_len;
uint8_t* out = fb_build_mem_read_response(
nonce, *emulContext->clockCounter,
dev_id, seg_id, offset,
base + offset, data_len,
&out_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, out_len);
}
}

157
src/proto/handlers/stream.c Normal file
View File

@@ -0,0 +1,157 @@
#include "proto/handlers/stream.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "streamed.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(
EmulContext* emulContext, ClientContext* ctx, uint32_t regId)
{
for (size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) {
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId];
for (size_t i = 0; i < deviceRegs->regCount; i++) {
StreamReg* iReg = &deviceRegs->regs[i];
if (iReg->clientContext->clientId == ctx->clientId
&& iReg->regId == regId)
{
printf("Discard stream %u for client %lu\n", regId, ctx->clientId);
deviceRegs->regCount--;
for (size_t j = i; j < deviceRegs->regCount; j++) {
deviceRegs->regs[j] = deviceRegs->regs[j + 1];
}
break;
}
}
}
}
void unregisterClientStreams(EmulContext* emulContext, ClientContext* ctx)
{
for (size_t deviceId = 0; deviceId < emulContext->devicesCount; deviceId++) {
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[deviceId];
StreamReg* newRegs = calloc(deviceRegs->allocatedSize, sizeof(StreamReg));
NULL_GUARD(newRegs);
size_t newCount = 0;
for (size_t i = 0; i < deviceRegs->regCount; i++)
{
StreamReg* reg = &deviceRegs->regs[i];
if (reg->clientContext->clientId != ctx->clientId)
{
newRegs[newCount++] = *reg;
}
else
{
printf("Removing stream reg [%u] for client %lu\n",
reg->regId, ctx->clientId);
}
}
StreamReg* oldRegs = deviceRegs->regs;
deviceRegs->regCount = newCount;
deviceRegs->regs = newRegs;
free(oldRegs);
}
}
// ── Incoming stream messages ──────────────────────────────────────────────────
void handleIncomingStreamMessage(
hmmmm_stream_StreamClientMessage_table_t msg,
uint64_t nonce,
ClientContext* ctx,
EmulContext* emulContext)
{
hmmmm_stream_StreamClientPayload_union_type_t ptype =
hmmmm_stream_StreamClientMessage_payload_type(msg);
printf("[STREAM] client=%lu nonce=%lu type=%s\n",
ctx->clientId, nonce,
hmmmm_stream_StreamClientPayload_type_name(ptype));
if (ptype == hmmmm_stream_StreamClientPayload_StreamRegRequest)
{
hmmmm_stream_StreamRegRequest_table_t req =
(hmmmm_stream_StreamRegRequest_table_t)
hmmmm_stream_StreamClientMessage_payload(msg);
uint32_t dev_id = hmmmm_stream_StreamRegRequest_device_id(req);
uint32_t seg_id = hmmmm_stream_StreamRegRequest_seg_id(req);
uint32_t offset = hmmmm_stream_StreamRegRequest_offset(req);
uint32_t length = hmmmm_stream_StreamRegRequest_length(req);
hmmmm_stream_StreamMode_enum_t mode = hmmmm_stream_StreamRegRequest_mode(req);
printf("[STREAM/REG] device=%u seg=%u offset=%u len=%u mode=%d\n",
dev_id, seg_id, offset, length, (int)mode);
if (dev_id >= (uint32_t)emulContext->devicesCount) {
printf("[STREAM/REG] invalid device %u\n", dev_id);
return;
}
DeviceSegStreamReg* deviceRegs = emulContext->deviceStreamRegs[dev_id];
if (deviceRegs->regCount >= deviceRegs->allocatedSize) {
size_t new_size = deviceRegs->allocatedSize * 2;
StreamReg* new_regs = realloc(deviceRegs->regs, sizeof(StreamReg) * new_size);
NULL_GUARD(new_regs);
deviceRegs->regs = new_regs;
deviceRegs->allocatedSize = new_size;
}
uint64_t globalOffset = (((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
uint32_t reg_id = ctx->streamRegIterator++;
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++];
reg->clientContext = ctx;
reg->regId = reg_id;
reg->segId = seg_id;
reg->startAddr = (uint64_t)offset;
reg->startGlobalAddr = globalOffset;
reg->segLen = (uint64_t)length;
reg->mode = (uint8_t)mode;
printf("[STREAM/REG] assigned reg_id=%u to client=%lu\n", reg_id, ctx->clientId);
size_t msg_len;
uint8_t* out = fb_build_stream_reg_confirm(
nonce, reg_id, dev_id, seg_id, offset, length, (uint8_t)mode, &msg_len);
dispatchOutgoingMessage(emulContext->outMsgQueue, ctx->clientId, out, msg_len);
}
else if (ptype == hmmmm_stream_StreamClientPayload_StreamDeregRequest)
{
hmmmm_stream_StreamDeregRequest_table_t req =
(hmmmm_stream_StreamDeregRequest_table_t)
hmmmm_stream_StreamClientMessage_payload(msg);
uint32_t stream_id = hmmmm_stream_StreamDeregRequest_stream_id(req);
printf("[STREAM/DEREG] stream_id=%u\n", stream_id);
unregisterClientStream(emulContext, ctx, stream_id);
}
else if (ptype == hmmmm_stream_StreamClientPayload_StreamWritePush)
{
hmmmm_stream_StreamWritePush_table_t push =
(hmmmm_stream_StreamWritePush_table_t)
hmmmm_stream_StreamClientMessage_payload(msg);
printf("[STREAM/WRITE] stream_id=%u offset=%u len=%zu\n",
hmmmm_stream_StreamWritePush_stream_id(push),
hmmmm_stream_StreamWritePush_offset(push),
flatbuffers_uint8_vec_len(
hmmmm_stream_StreamWritePush_data(push)));
// TODO: apply write to device memory
}
}

182
src/proto/handlers/ws.c Normal file
View File

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

404
src/proto/msg.c Normal file
View File

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

57
src/proto/pack.c Normal file
View File

@@ -0,0 +1,57 @@
#include "proto/pack.h"
uint64_t decodeBytesToU64(const uint8_t* bytes)
{
uint64_t ret = 0;
for(uint8_t i = 0; i < 8; i++)
{
ret |= (uint64_t)((uint64_t)(bytes[i]) << ((7 - i) * 8));
}
return ret;
}
uint32_t decodeBytesToU32(const uint8_t* bytes)
{
uint32_t ret = 0;
for(uint8_t i = 0; i < 4; i++)
{
ret |= (uint32_t)((uint32_t)(bytes[i]) << ((3 - i) * 4));
}
return ret;
}
uint16_t decodeBytesToU16(const uint8_t* bytes)
{
return ((uint16_t)(bytes[0] << 8)) | ((uint16_t)bytes[1]);
}
void encodeUint8ToBytes(uint8_t num, uint8_t* tgt)
{
tgt[0] = num;
}
void encodeUint16ToBytes(uint16_t num, uint8_t* tgt)
{
for(uint8_t i = 0; i < 2; i++)
{
tgt[i] = (uint8_t)(((uint32_t)num) >> (2 * (1 - i)));
}
}
void encodeUint32ToBytes(uint32_t num, uint8_t* tgt)
{
for(uint8_t i = 0; i < 4; i++)
{
tgt[i] = (uint8_t)(((uint32_t)num) >> (4 * (3 - i)));
}
}
void encodeUint64ToBytes(uint64_t num, uint8_t* tgt)
{
for(uint8_t i = 0; i < 8; i++)
{
tgt[i] = (uint8_t)(((uint64_t)num) >> (8 * (7 - i)));
}
}

57
src/state.c Normal file
View File

@@ -0,0 +1,57 @@
#include <stdint.h>
#include "state.h"
uint8_t switchNewEmulState(const uint8_t currentState, const uint8_t controlOp)
{
switch (currentState)
{
case EMUL_STATE_STILL:
{
if(controlOp == EMUL_STATE_OP_START)
{
return EMUL_STATE_EXEC;
}
break;
}
case EMUL_STATE_EXEC:
{
if(controlOp == EMUL_STATE_OP_PAUSE)
{
return EMUL_STATE_PAUSE;
}
else if(controlOp == EMUL_STATE_OP_STOP)
{
return EMUL_STATE_STOP;
}
break;
}
case EMUL_STATE_PAUSE:
{
if(controlOp == EMUL_STATE_OP_RESUME)
{
return EMUL_STATE_EXEC;
}
else if (controlOp == EMUL_STATE_OP_RESET)
{
return EMUL_STATE_STILL;
}
break;
}
case EMUL_STATE_STOP:
{
if(controlOp == EMUL_STATE_OP_RESET)
{
return EMUL_STATE_STILL;
}
break;
}
default:
break;
}
return currentState;
}