Generate README.md, add clock dividers and limiters, load configs from WS client, fix stream per-segment subscriptions according to memory projections mechanism

This commit is contained in:
2026-04-05 17:13:53 +03:00
parent 71ec472510
commit d8804c9ae2
19 changed files with 396 additions and 109 deletions

1
.gitignore vendored
View File

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

View File

@@ -11,7 +11,7 @@ LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE)
SYSTEM_INCLUDES=-isystem deps/flatcc/include/ -isystem $(PROTO_INC_DIR)/ SYSTEM_INCLUDES=-isystem deps/flatcc/include/ -isystem $(PROTO_INC_DIR)/
STATIC_LIBS=crypto STATIC_LIBS=crypto
STANDART=c23 STANDART=c23
OPTIMIZE=-O3 OPTIMIZE=-Og
TARGET=main TARGET=main
FLATCC = deps/flatcc/bin/flatcc FLATCC = deps/flatcc/bin/flatcc
@@ -48,7 +48,7 @@ LFLAGS=$(OPTIMIZE) -g $(PEDANTIC_FLAGS) $(DEFINES) $(STATICLIBS_FLAGS) -flto -fu
OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES)) OBJECTS = $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES))
DEP = $(filter %.d, $(OBJECTS:.o=.d)) DEP = $(filter %.d, $(OBJECTS:.o=.d))
$(info $(DEP)) #$(info $(DEP))
OBJECTS += $(LIBS) OBJECTS += $(LIBS)
vpath %.c $(sort $(dir $(C_SOURCES))) vpath %.c $(sort $(dir $(C_SOURCES)))
@@ -56,11 +56,11 @@ vpath %.c $(sort $(dir $(C_SOURCES)))
all: build all: build
build: date deps Dir proto target compile_commands build: date deps Dir proto python-proto target compile_commands
rebuild: clean | build rebuild: clean | build
$(info $(DEP)) #$(info $(DEP))
-include $(DEP) -include $(DEP)
@@ -92,7 +92,7 @@ $(PROTO_STAMP): $(FBS_SOURCES) | ProtoDir BuildDir
BuildDir: BuildDir:
@mkdir -p $(BUILD_DIR) @mkdir -p $(BUILD_DIR)
$(shell mkdir -p $(dir $(OBJECTS))) @$(shell mkdir -p $(dir $(OBJECTS)))
SrcDir: SrcDir:
@mkdir -p $(SRC_DIR) @mkdir -p $(SRC_DIR)

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/flatbuffers vendored

Submodule deps/flatbuffers updated: 4e582b0c1d...e223d69b36

View File

@@ -72,7 +72,7 @@ device_mem_t* genDevMem(device_specs_t* devSpec, char* errbuf)
void* rawCells = (void*)malloc(memTotalSize); void* rawCells = (void*)malloc(memTotalSize);
if (rawCells == NULL) if (rawCells == NULL)
{ {
sprintf(errbuf, "unable to allocate raw memory buf"); sprintf(errbuf, "unable to allocate raw memory buf %lu bytes", memTotalSize);
free(devMem); free(devMem);
return NULL; return NULL;
} }

View File

@@ -41,7 +41,8 @@ table DeviceEntry {
// Inline device configuration — either base or compose. // Inline device configuration — either base or compose.
config: DeviceConfig; config: DeviceConfig;
clock: DeviceClockConfig; //clock: DeviceClockConfig;
clock_divider: uint64 = 1;
overrides: [MemSegOverride]; overrides: [MemSegOverride];
} }

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

View File

@@ -2,6 +2,7 @@
#define __CONTEXT_H__ #define __CONTEXT_H__
#include <pthread.h> #include <pthread.h>
#include <stdint.h>
#include "ptQueue/inc/ptQueue.h" #include "ptQueue/inc/ptQueue.h"
#include "wsServer/include/ws.h" #include "wsServer/include/ws.h"
@@ -30,11 +31,13 @@ typedef struct {
OutgoingBuffers* outBufs; OutgoingBuffers* outBufs;
DeviceSegStreamReg** deviceStreamRegs; DeviceSegStreamReg** deviceStreamRegs;
uint8_t** devicesMem; uint8_t** devicesMem;
size_t** devicesMemSegAddrs;
size_t devicesCount; size_t devicesCount;
void** deviceHandles; // device_handle_t** — dynamically loaded devices void** deviceHandles; // device_handle_t** — dynamically loaded devices
void* interceptCtxs; // contiguous intercept_ctx_t array (freed as single block) void* interceptCtxs; // contiguous intercept_ctx_t array (freed as single block)
size_t interceptCtxCount; size_t interceptCtxCount;
flatcc_builder_t stream_builder; flatcc_builder_t stream_builder;
uint64_t simRateLimit;
} EmulContext; } EmulContext;

View File

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

View File

@@ -69,6 +69,7 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
ret->specs = devSpecs; ret->specs = devSpecs;
ret->clockCycleCounter = 0; ret->clockCycleCounter = 0;
ret->clockCycleLimit = 0; ret->clockCycleLimit = 0;
ret->clockDivider = 1;
return ret; return ret;
} }

View File

@@ -100,7 +100,7 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
for(size_t i = 0; i < devIdx; i++) for(size_t i = 0; i < devIdx; i++)
{ {
conf_dev_t* devConf = conf->baseConfigs[i]; conf_dev_t* devConf = conf->baseConfigs[i];
char intErrbuf[1024] = {0}; char intErrbuf[255] = {0};
device_handle_t* devHandle = openBaseDevice(devConf, intErrbuf); device_handle_t* devHandle = openBaseDevice(devConf, intErrbuf);
if(devHandle == NULL) if(devHandle == NULL)
{ {

View File

@@ -245,17 +245,17 @@ void mockDevice1(uint8_t* mem, uint64_t* readAddrs, size_t* readAddrsLen, uint64
} }
void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, uint8_t* mem) void dispatchStreamSegment(EmulContext* emulContext, StreamReg* reg, device_mem_t* mem)
{ {
size_t mlen = 0; size_t mlen = 0;
uint8_t* msg = fb_build_stream_data_push( uint8_t* msg = fb_build_stream_data_push(
&emulContext->stream_builder, &emulContext->stream_builder,
UINT64_MAX, reg->regId, *emulContext->clockCounter, UINT64_MAX, reg->regId, *emulContext->clockCounter,
mem + reg->startAddr, reg->segLen, &mlen); (uint8_t*)(((uint64_t)(mem->cells[reg->segId])) + (uint64_t)reg->startAddr), reg->segLen, &mlen);
dispatchOutgoingMessage(emulContext->outBufs, reg->clientContext->clientId, msg, mlen); dispatchOutgoingMessage(emulContext->outBufs, reg->clientContext->clientId, msg, mlen);
} }
void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg* deviceRegs, uint8_t* mem, uint64_t* addrs, size_t addrsLen, uint8_t mode) void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg* deviceRegs, device_mem_t* mem, uint64_t* addrs, size_t addrsLen, uint8_t mode)
{ {
if(deviceRegs->regCount == 0) if(deviceRegs->regCount == 0)
{ {
@@ -274,7 +274,7 @@ void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg
StreamReg* reg = &deviceRegs->regs[regIdx]; StreamReg* reg = &deviceRegs->regs[regIdx];
if(reg->mode == mode) if(reg->mode == mode)
{ {
if(reg->startAddr <= addr && reg->startAddr + reg->segLen >= addr) if(reg->startGlobalAddr <= addr && reg->startGlobalAddr + reg->segLen >= addr)
{ {
uint8_t isDuplicate = 0; uint8_t isDuplicate = 0;
for(size_t j = 0; j < dispatchRegsCnt; j++) for(size_t j = 0; j < dispatchRegsCnt; j++)
@@ -307,6 +307,14 @@ void dispatchMemAccessNotifications(EmulContext* emulContext, DeviceSegStreamReg
free(dispatchRegs); free(dispatchRegs);
} }
uint64_t getCurrentUsec()
{
struct timeval tv;
gettimeofday(&tv,NULL);
return (1000000 * (uint64_t)tv.tv_sec) + (uint64_t)tv.tv_usec;
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
(void)argc; (void)argv; (void)argc; (void)argv;
@@ -320,6 +328,7 @@ int main(int argc, char** argv)
size_t deviceCount = 0; size_t deviceCount = 0;
DeviceSegStreamReg** deviceStreamRegs = NULL; DeviceSegStreamReg** deviceStreamRegs = NULL;
uint8_t** devicesMem = NULL; uint8_t** devicesMem = NULL;
size_t** devicesMemSegmentAddrs = NULL;
void** deviceHandlesArr = NULL; void** deviceHandlesArr = NULL;
uint8_t emulState = EMUL_STATE_STILL; uint8_t emulState = EMUL_STATE_STILL;
@@ -366,11 +375,13 @@ int main(int argc, char** argv)
&outBufs, &outBufs,
deviceStreamRegs, deviceStreamRegs,
devicesMem, devicesMem,
devicesMemSegmentAddrs,
deviceCount, deviceCount,
deviceHandlesArr, deviceHandlesArr,
NULL, /* interceptCtxs */ NULL, /* interceptCtxs */
0, /* interceptCtxCount */ 0, /* interceptCtxCount */
{0} /* stream_builder — initialized below */ {0}, /* stream_builder — initialized below */
0,
}; };
if (flatcc_builder_init(&emulContext.stream_builder)) { if (flatcc_builder_init(&emulContext.stream_builder)) {
panic("flatcc_builder_init failed\n"); panic("flatcc_builder_init failed\n");
@@ -409,6 +420,8 @@ int main(int argc, char** argv)
uint8_t clients_try_timer = 100; uint8_t clients_try_timer = 100;
uint64_t lastTickAt = getCurrentUsec();
while(1) while(1)
{ {
ClientRegistrationEvent* payload = regQueueTail->payload; ClientRegistrationEvent* payload = regQueueTail->payload;
@@ -443,7 +456,7 @@ int main(int argc, char** argv)
} }
while(readReqIdx == newWriteIdx) while(readReqIdx == newWriteIdx)
{ {
my_sleep(1000); my_sleep(100000);
readReqIdx = atomic_load(&outBufs.readRequestIdx); readReqIdx = atomic_load(&outBufs.readRequestIdx);
} }
atomic_store(&outBufs.currWritingIdx, newWriteIdx); atomic_store(&outBufs.currWritingIdx, newWriteIdx);
@@ -451,22 +464,35 @@ int main(int argc, char** argv)
} }
} }
if(emulState == EMUL_STATE_EXEC && emulContext.devicesCount > 0) uint64_t now = emulContext.simRateLimit > 0? getCurrentUsec() : 0;
if(emulState == EMUL_STATE_EXEC && emulContext.devicesCount > 0 && (emulContext.simRateLimit == 0 || now - lastTickAt >= 1000000 / emulContext.simRateLimit))
{ {
for (size_t di = 0; di < emulContext.devicesCount; di++) for (size_t di = 0; di < emulContext.devicesCount; di++)
{ {
device_handle_t* dev = (device_handle_t*)emulContext.deviceHandles[di]; device_handle_t* dev = (device_handle_t*)emulContext.deviceHandles[di];
device_mem_t* devMem = dev->ctx->deviceMem;
devMem->memreadLen = 0;
devMem->memwriteLen = 0;
dev->lib->makeDeviceTick(dev->ctx); if (clockCounter % dev->clockDivider == 0)
{
// printf("clock device %lu\n", di);
device_mem_t* devMem = dev->ctx->deviceMem;
devMem->memreadLen = 0;
devMem->memwriteLen = 0;
dev->lib->makeDeviceTick(dev->ctx);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem, devMem->memreadCellAddrs, (size_t)devMem->memreadLen, STREAM_MODE_READ);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem, devMem->memwriteCellAddrs, (size_t)devMem->memwriteLen, STREAM_MODE_WRITE);
}
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memreadCellAddrs, (size_t)devMem->memreadLen, STREAM_MODE_READ);
dispatchMemAccessNotifications(&emulContext, emulContext.deviceStreamRegs[di], devMem->rawCells, devMem->memwriteCellAddrs, (size_t)devMem->memwriteLen, STREAM_MODE_WRITE);
} }
clockCounter++; clockCounter++;
if(emulContext.simRateLimit > 0)
{
lastTickAt = getCurrentUsec();
}
} }
else if(!utilizedFlag) else if(!utilizedFlag)
{ {

View File

@@ -128,13 +128,13 @@ void handleAllClients(EmulContext* emulContext)
if (fbmsg != NULL) { if (fbmsg != NULL) {
*emulContext->utilizedFlag = 1; *emulContext->utilizedFlag = 1;
if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) { // if (hmmmm_ClientMessage_verify_as_root(fbmsg->data, fbmsg->size) == 0) {
hmmmm_ClientMessage_table_t cm = hmmmm_ClientMessage_table_t cm =
hmmmm_ClientMessage_as_root(fbmsg->data); hmmmm_ClientMessage_as_root(fbmsg->data);
handleIncomingMessage(cm, ctx, emulContext); handleIncomingMessage(cm, ctx, emulContext);
} else { // } else {
printf("client %lu: dropped malformed FlatBuffer\n", ctx->clientId); // printf("client %lu: dropped malformed FlatBuffer\n", ctx->clientId);
} // }
free(fbmsg->data); free(fbmsg->data);
free(fbmsg); free(fbmsg);

View File

@@ -1,5 +1,6 @@
#include "proto/handlers/config.h" #include "proto/handlers/config.h"
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -122,19 +123,35 @@ static int load_devices_recursive(
if (!dc) return -1; if (!dc) return -1;
device_handle_t* dev = openBaseDevice(dc, errbuf); device_handle_t* dev = openBaseDevice(dc, errbuf);
freeConf(dc); // freeConf() already calls free(dc) if (!dev) {
if (!dev) return -1; 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->fillSmartReadSpecs(dev->specs, NULL, 0);
dev->lib->fillSmartWriteSpecs(dev->specs, NULL, 0); dev->lib->fillSmartWriteSpecs(dev->specs, NULL, 0);
dev->ctx = dev->lib->init(dev->specs, errbuf); char initErrBuf[256];
dev->ctx = dev->lib->init(dev->specs, initErrBuf);
if (!dev->ctx) { if (!dev->ctx) {
snprintf(errbuf, 1024, "error while loading device %lu: %s", st->count, initErrBuf);
closeBaseDevice(dev); closeBaseDevice(dev);
free(dev); free(dev);
freeConf(dc);
return -1; return -1;
} }
freeConf(dc);
size_t idx = st->count; size_t idx = st->count;
st->handles[idx] = dev; st->handles[idx] = dev;
@@ -471,6 +488,7 @@ static void free_old_config(EmulContext* emulContext)
emulContext->devicesMem = NULL; emulContext->devicesMem = NULL;
emulContext->deviceStreamRegs = NULL; emulContext->deviceStreamRegs = NULL;
emulContext->devicesCount = 0; emulContext->devicesCount = 0;
emulContext->simRateLimit = 0;
} }
@@ -522,6 +540,8 @@ void handleConfigCtrlMessage(
return; return;
} }
const uint64_t simRateLimit = hmmmm_config_EmulationConfig_sim_rate_limit(econf);
// Free old config // Free old config
free_old_config(emulContext); free_old_config(emulContext);
@@ -591,10 +611,12 @@ void handleConfigCtrlMessage(
// Allocate new arrays for EmulContext // Allocate new arrays for EmulContext
size_t dc = st.count; size_t dc = st.count;
emulContext->devicesCount = dc; emulContext->devicesCount = dc;
emulContext->simRateLimit = simRateLimit;
emulContext->deviceHandles = calloc(dc, sizeof(void*)); emulContext->deviceHandles = calloc(dc, sizeof(void*));
emulContext->devicesMem = calloc(dc, sizeof(uint8_t*)); emulContext->devicesMem = calloc(dc, sizeof(uint8_t*));
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*)); emulContext->devicesMemSegAddrs = calloc(dc, sizeof(size_t*));
emulContext->deviceStreamRegs = calloc(dc, sizeof(DeviceSegStreamReg*));
if (!emulContext->deviceHandles || !emulContext->devicesMem || if (!emulContext->deviceHandles || !emulContext->devicesMem ||
!emulContext->deviceStreamRegs) { !emulContext->deviceStreamRegs) {
@@ -619,16 +641,36 @@ void handleConfigCtrlMessage(
return; return;
} }
for (size_t i = 0; i < dc; i++) { for (size_t i = 0; i < dc; i++)
{
emulContext->deviceHandles[i] = st.handles[i]; emulContext->deviceHandles[i] = st.handles[i];
emulContext->devicesMem[i] = st.handles[i]->ctx->deviceMem->rawCells; 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)); DeviceSegStreamReg* dsr = calloc(1, sizeof(DeviceSegStreamReg));
if (!dsr) { emulContext->deviceStreamRegs[i] = NULL; continue; } if (!dsr)
{
emulContext->deviceStreamRegs[i] = NULL;
continue;
}
dsr->allocatedSize = 4; dsr->allocatedSize = 4;
dsr->regCount = 0; dsr->regCount = 0;
dsr->regs = calloc(4, sizeof(StreamReg)); dsr->regs = calloc(4, sizeof(StreamReg));
if (!dsr->regs) { free(dsr); emulContext->deviceStreamRegs[i] = NULL; continue; } if (!dsr->regs)
{
free(dsr);
emulContext->deviceStreamRegs[i] = NULL;
continue;
}
emulContext->deviceStreamRegs[i] = dsr; emulContext->deviceStreamRegs[i] = dsr;
} }

View File

@@ -6,7 +6,7 @@
#include "proto/dial.h" #include "proto/dial.h"
#include "mem_reader.h" #include "mem_reader.h"
#define DEVICE_MEM_SIZE ((size_t)(256 * 1024)) // #define DEVICE_MEM_SIZE ((size_t)(256 * 1024))
void handleIncomingMemMessage( void handleIncomingMemMessage(
@@ -40,10 +40,13 @@ void handleIncomingMemMessage(
printf("[MEM/READ] invalid device %u\n", dev_id); printf("[MEM/READ] invalid device %u\n", dev_id);
return; return;
} }
if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
printf("[MEM/READ] out of bounds\n"); offset = (uint32_t)(((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
return;
} // if ((size_t)offset + (size_t)length > DEVICE_MEM_SIZE) {
// printf("[MEM/READ] out of bounds\n");
// return;
// }
const uint8_t* base = emulContext->devicesMem[dev_id]; const uint8_t* base = emulContext->devicesMem[dev_id];
size_t out_len; size_t out_len;
@@ -73,10 +76,10 @@ void handleIncomingMemMessage(
printf("[MEM/WRITE] invalid device %u\n", dev_id); printf("[MEM/WRITE] invalid device %u\n", dev_id);
return; return;
} }
if ((size_t)offset + data_len > DEVICE_MEM_SIZE) { // if ((size_t)offset + data_len > DEVICE_MEM_SIZE) {
printf("[MEM/WRITE] out of bounds\n"); // printf("[MEM/WRITE] out of bounds\n");
return; // return;
} // }
uint8_t* base = emulContext->devicesMem[dev_id]; uint8_t* base = emulContext->devicesMem[dev_id];
memcpy(base + offset, data, data_len); memcpy(base + offset, data, data_len);

View File

@@ -1,5 +1,6 @@
#include "proto/handlers/stream.h" #include "proto/handlers/stream.h"
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "streamed.h" #include "streamed.h"
@@ -104,11 +105,15 @@ void handleIncomingStreamMessage(
deviceRegs->allocatedSize = new_size; deviceRegs->allocatedSize = new_size;
} }
uint64_t globalOffset = (((size_t)offset) + emulContext->devicesMemSegAddrs[dev_id][seg_id]);
uint32_t reg_id = ctx->streamRegIterator++; uint32_t reg_id = ctx->streamRegIterator++;
StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++]; StreamReg* reg = &deviceRegs->regs[deviceRegs->regCount++];
reg->clientContext = ctx; reg->clientContext = ctx;
reg->regId = reg_id; reg->regId = reg_id;
reg->segId = seg_id;
reg->startAddr = (uint64_t)offset; reg->startAddr = (uint64_t)offset;
reg->startGlobalAddr = globalOffset;
reg->segLen = (uint64_t)length; reg->segLen = (uint64_t)length;
reg->mode = (uint8_t)mode; reg->mode = (uint8_t)mode;

View File

@@ -2,6 +2,7 @@
#include "proto/handlers.h" #include "proto/handlers.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include "panic.h" #include "panic.h"
#include "my_mutex.h" #include "my_mutex.h"
#include "proto/enums.h" #include "proto/enums.h"
@@ -112,10 +113,20 @@ void onWsMessage(
} }
// Verify the FlatBuffer before queuing // Verify the FlatBuffer before queuing
if (hmmmm_ClientMessage_verify_as_root(msg, (size_t)size)) { // if (hmmmm_ClientMessage_verify_as_root(msg, size)) {
printf("Client %lu sent invalid FlatBuffer, dropping\n", client); // printf("Client %lu sent invalid FlatBuffer, dropping\n", client);
return;
} // 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 // Copy bytes — the WS buffer is only valid for this callback's duration
FbMessage* fbmsg = malloc(sizeof(FbMessage)); FbMessage* fbmsg = malloc(sizeof(FbMessage));