Skip to content

Commit 0eb4c43

Browse files
committed
feat: flash-mock
1 parent 9c266de commit 0eb4c43

3 files changed

Lines changed: 237 additions & 23 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# CMake Build Directories & Files
2+
archive/
23
build/
34
build-*/
45
CMakeCache.txt

test/mocks/mock_flash.c

Lines changed: 207 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,217 @@
44
* ==============================================================================
55
*
66
* REFERENCED SPECIFICATIONS & GAPS:
7-
*
87
* 1. docs/hals.md (Flash HAL Backend)
9-
* - Erase-Prereq: Ein Write-Command MUSS fehlschlagen (BOOT_ERR_FLASH_NOT_ERASED),
10-
* falls die Ziel-Speicherzellen nicht 'erased_value' (0xFF) aufweisen.
11-
* - Alignment Rules: Writes, die nicht exakt CHIP_FLASH_WRITE_ALIGN einhalten,
12-
* MÜSSEN hart abweisen werden.
13-
*
14-
* 2. docs/concept_fusion.md
15-
* - In-Place Swap Buffer: Die Flash-Implementierung muss asymmetrisches
16-
* Sector-Erasing unterstützen, da der Puffer vom Rest abweichen kann.
17-
*
18-
* 3. docs/merkle_spec.md
19-
* - GAP-08: Stream-Hashing erzwingt, dass wir via flash.read niemals
20-
* "Out-of-Bound" Page-Räume verlassen dürfen.
21-
*
8+
* 2. docs/concept_fusion.md (In-Place Swap Buffer)
9+
* 3. docs/merkle_spec.md (GAP-08: Stream-Hashing)
2210
* 4. docs/sandbox_setup.md & docs/testing_requirements.md
23-
* - File-Simulation: Der "Flash" auf der Sandbox MUSS strikt durch eine
24-
* lokale `flash_sim.bin` via stdio.h abgebildet werden, um Persistenz
25-
* über Power-Loss-Crashes hinaus für Integrationstests aufrecht zu erhalten.
26-
*
27-
* TODO: stdio (fopen, fseek, fread, fwrite) Wrapper schreiben.
2811
*/
2912

3013
#include "mock_flash.h"
14+
#include "chip_config.h"
3115
#include <stdio.h>
16+
#include <stdlib.h>
17+
#include <string.h>
18+
19+
#define DEFAULT_SIM_FILE "flash_sim.bin"
20+
21+
static FILE *flash_file = NULL;
22+
static const char *sim_filename = DEFAULT_SIM_FILE;
23+
static uint32_t write_count_limit = 0;
24+
static uint32_t simulated_writes = 0;
25+
static uint32_t simulated_vendor_error = 0;
26+
27+
/* [TODO] (GAP-F20): Bit-Rot Simulator. Implementiere Umgebungsvariablen wie TOOB_BITROT_ADDR
28+
* und TOOB_BITROT_VALUE, um bei mock_flash_read absichtliche Fehler (0x00 / 0xFF Injektion)
29+
* für SIL-Verifizierungs-Checks zu provozieren (laut testing_requirements.md). */
30+
31+
static void check_fault_injection(void) {
32+
if (write_count_limit > 0 && simulated_writes >= write_count_limit) {
33+
printf("[M-SANDBOX] BROWNOUT SIMULATED! Power loss after %u writes.\n", simulated_writes);
34+
fflush(stdout);
35+
exit(1); /* Crash! */
36+
}
37+
}
38+
39+
static boot_status_t mock_flash_init(void) {
40+
const char *env_file = getenv("TOOB_FLASH_SIM_FILE");
41+
if (env_file != NULL) {
42+
sim_filename = env_file;
43+
}
44+
45+
const char *env_fail = getenv("TOOB_FAIL_AFTER_WRITES");
46+
if (env_fail != NULL) {
47+
write_count_limit = (uint32_t)strtoul(env_fail, NULL, 10);
48+
}
49+
50+
flash_file = fopen(sim_filename, "rb+");
51+
if (!flash_file) {
52+
/* File doesn't exist, create it */
53+
flash_file = fopen(sim_filename, "wb+");
54+
if (!flash_file) {
55+
return BOOT_ERR_STATE;
56+
}
57+
58+
/* Initialisieren mit 0xFF (Erase Data) */
59+
uint8_t buffer[CHIP_FLASH_PAGE_SIZE];
60+
memset(buffer, CHIP_FLASH_ERASURE_MAPPING, sizeof(buffer));
61+
62+
uint32_t pages = CHIP_FLASH_TOTAL_SIZE / CHIP_FLASH_PAGE_SIZE;
63+
for (uint32_t i = 0; i < pages; i++) {
64+
if (fwrite(buffer, 1, sizeof(buffer), flash_file) != sizeof(buffer)) {
65+
fclose(flash_file);
66+
flash_file = NULL;
67+
return BOOT_ERR_STATE;
68+
}
69+
}
70+
}
71+
fflush(flash_file);
72+
return BOOT_OK;
73+
}
74+
75+
static void mock_flash_deinit(void) {
76+
if (flash_file) {
77+
fclose(flash_file);
78+
flash_file = NULL;
79+
}
80+
}
81+
82+
static boot_status_t mock_flash_read(uint32_t addr, void *buf, size_t len) {
83+
if (!flash_file) return BOOT_ERR_STATE;
84+
if (addr + len > CHIP_FLASH_TOTAL_SIZE) return BOOT_ERR_FLASH_BOUNDS;
85+
86+
if (fseek(flash_file, addr, SEEK_SET) != 0) return BOOT_ERR_FLASH;
87+
if (fread(buf, 1, len, flash_file) != len) return BOOT_ERR_FLASH;
88+
89+
return BOOT_OK;
90+
}
91+
92+
static boot_status_t mock_flash_write(uint32_t addr, const void *buf, size_t len) {
93+
if (!flash_file) return BOOT_ERR_STATE;
94+
if (addr % CHIP_FLASH_WRITE_ALIGNMENT != 0 || len % CHIP_FLASH_WRITE_ALIGNMENT != 0) {
95+
return BOOT_ERR_FLASH_ALIGN;
96+
}
97+
if (addr + len > CHIP_FLASH_TOTAL_SIZE) return BOOT_ERR_FLASH_BOUNDS;
98+
99+
#ifndef TOOB_FLASH_DISABLE_BLANK_CHECK
100+
/* [TODO]: Die Spec hals.md fordert aus Performancegründen einen "32-Bit Aligned Word-Check"
101+
* anstatt des hier genutzten Byte-per-Byte Checks. Muss auf uint32_t Casting optimiert werden. */
102+
uint8_t existing[256];
103+
size_t remaining = len;
104+
uint32_t current_addr = addr;
105+
106+
while (remaining > 0) {
107+
size_t chunk = (remaining > sizeof(existing)) ? sizeof(existing) : remaining;
108+
if (fseek(flash_file, current_addr, SEEK_SET) != 0) return BOOT_ERR_FLASH;
109+
if (fread(existing, 1, chunk, flash_file) != chunk) return BOOT_ERR_FLASH;
110+
111+
for (size_t i = 0; i < chunk; i++) {
112+
if (existing[i] != CHIP_FLASH_ERASURE_MAPPING) {
113+
return BOOT_ERR_FLASH_NOT_ERASED;
114+
}
115+
}
116+
remaining -= chunk;
117+
current_addr += chunk;
118+
}
119+
#endif
120+
121+
/* Simuliere NOR-Flash Physik: Man kann Bits nur auf 0 ziehen (Logisches AND) */
122+
/* [TODO]: KRITISCHE P10 VERLETZUNG! uint8_t buffer_to_write[len] ist ein
123+
* Variable Length Array (VLA) und verstößt gegen NASA P10 Regeln (Keine laufzeitabhängigen Stack-Allokationen).
124+
* Dies muss zwingend als Chunked-Loop (z.B. 256 Bytes max) umgeschrieben werden! */
125+
uint8_t buffer_to_write[len];
126+
const uint8_t *src = (const uint8_t *)buf;
127+
128+
if (fseek(flash_file, addr, SEEK_SET) != 0) return BOOT_ERR_FLASH;
129+
if (fread(buffer_to_write, 1, len, flash_file) != len) return BOOT_ERR_FLASH;
130+
131+
for (size_t i = 0; i < len; i++) {
132+
buffer_to_write[i] &= src[i];
133+
}
134+
135+
if (fseek(flash_file, addr, SEEK_SET) != 0) return BOOT_ERR_FLASH;
136+
if (fwrite(buffer_to_write, 1, len, flash_file) != len) return BOOT_ERR_FLASH;
137+
138+
fflush(flash_file);
139+
140+
simulated_writes++;
141+
check_fault_injection();
142+
143+
return BOOT_OK;
144+
}
145+
146+
static boot_status_t mock_flash_erase_sector(uint32_t addr) {
147+
if (!flash_file) return BOOT_ERR_STATE;
148+
if (addr % CHIP_FLASH_PAGE_SIZE != 0) return BOOT_ERR_FLASH_ALIGN;
149+
if (addr >= CHIP_FLASH_TOTAL_SIZE) return BOOT_ERR_FLASH_BOUNDS;
150+
151+
uint8_t erased_block[CHIP_FLASH_PAGE_SIZE];
152+
memset(erased_block, CHIP_FLASH_ERASURE_MAPPING, sizeof(erased_block));
153+
154+
if (fseek(flash_file, addr, SEEK_SET) != 0) return BOOT_ERR_FLASH;
155+
if (fwrite(erased_block, 1, sizeof(erased_block), flash_file) != sizeof(erased_block)) {
156+
return BOOT_ERR_FLASH;
157+
}
158+
159+
fflush(flash_file);
160+
161+
simulated_writes++;
162+
check_fault_injection();
163+
164+
return BOOT_OK;
165+
}
166+
167+
static boot_status_t mock_flash_get_sector_size(uint32_t addr, size_t *size_out) {
168+
if (addr >= CHIP_FLASH_TOTAL_SIZE) return BOOT_ERR_FLASH_BOUNDS;
169+
if (size_out) {
170+
*size_out = CHIP_FLASH_PAGE_SIZE;
171+
}
172+
return BOOT_OK;
173+
}
174+
175+
static boot_status_t mock_flash_set_otfdec_mode(bool enable) {
176+
/* Sandbox emuliert keine On-The-Fly Entschlüsselung in Software */
177+
(void)enable;
178+
return BOOT_ERR_NOT_SUPPORTED;
179+
}
180+
181+
static uint32_t mock_flash_get_last_vendor_error(void) {
182+
uint32_t err = simulated_vendor_error;
183+
simulated_vendor_error = 0; /* Clear on read */
184+
return err;
185+
}
186+
187+
/* --- Public Utilities --- */
188+
189+
void mock_flash_reset_to_factory(void) {
190+
if (flash_file) {
191+
fclose(flash_file);
192+
flash_file = NULL;
193+
}
194+
remove(sim_filename);
195+
simulated_writes = 0;
196+
}
197+
198+
void mock_flash_set_fail_limit(uint32_t limit) {
199+
write_count_limit = limit;
200+
simulated_writes = 0;
201+
}
202+
203+
/* --- Export --- */
204+
205+
const flash_hal_t sandbox_flash_hal = {
206+
.version = 0x01000000,
207+
.init = mock_flash_init,
208+
.deinit = mock_flash_deinit,
209+
.read = mock_flash_read,
210+
.write = mock_flash_write,
211+
.erase_sector = mock_flash_erase_sector,
212+
.get_sector_size = mock_flash_get_sector_size,
213+
.set_otfdec_mode = mock_flash_set_otfdec_mode,
214+
.get_last_vendor_error = mock_flash_get_last_vendor_error,
32215

33-
// TODO: Implementiere Flash Operationen und Environment-basierten Crash-Injection Hook (TOOB_FAIL_AFTER)
216+
.max_sector_size = CHIP_FLASH_MAX_SECTOR_SIZE,
217+
.total_size = CHIP_FLASH_TOTAL_SIZE,
218+
.write_align = CHIP_FLASH_WRITE_ALIGNMENT,
219+
.erased_value = CHIP_FLASH_ERASURE_MAPPING
220+
};

test/mocks/mock_flash.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,34 @@
1414
#define MOCK_FLASH_H
1515

1616
#include <stdint.h>
17-
#include <stddef.h>
17+
#include <stdbool.h>
18+
#include "boot_hal.h"
1819

19-
// TODO: Deklariere init(), deinit(), read(), write(), erase() für flash_hal_t Einbindung
20+
/**
21+
* @brief Die instanziierte Flash HAL für die M-SANDBOX.
22+
* Wird in chip_platform.c der Sandbox der platform->flash zugewiesen.
23+
*/
24+
extern const flash_hal_t sandbox_flash_hal;
25+
26+
/**
27+
* @brief Löscht die Host-SIM-Datei physikalisch für saubere Unit Tests.
28+
*/
29+
void mock_flash_reset_to_factory(void);
30+
31+
/**
32+
* @brief Manuelles Interface für C-Runner, um die ENV-Variable zu überschreiben.
33+
*
34+
* @param write_count_limit Anzahl an wdt/flash writes, nach denen ein
35+
* Brownout Reset simuliert wird. 0 = deaktiviert.
36+
*/
37+
void mock_flash_set_fail_limit(uint32_t write_count_limit);
38+
39+
/**
40+
* @brief Manuelles Interface für statische Bit-Rot Injektion in mock_flash_read.
41+
*
42+
* @param addr Hexadezimale Adresse, an der der Bit-Fehler emuliert werden soll.
43+
* @param value Der injizierte 8-Bit Wert (z.B. 0x00 oder 0xFF).
44+
*/
45+
void mock_flash_set_bitrot(uint32_t addr, uint8_t value);
2046

21-
#endif
47+
#endif /* MOCK_FLASH_H */

0 commit comments

Comments
 (0)