ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_sd_image.c
Go to the documentation of this file.
1
13
14#ifndef _GNU_SOURCE
16#define _GNU_SOURCE
17#endif
18
19#include <errno.h>
20#include <fcntl.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#ifdef __linux__
27#include <linux/falloc.h>
28#endif
29
30#include "board_periph_sd.h"
33
35typedef enum : uint32_t {
36 k_sd_io_chunk = 4096U,
38
50RA8_INTERNAL static void internal_close_fd(int* fd)
51{
52 if ((fd != nullptr) && (*fd >= 0)) {
53 (void)close(*fd);
54 *fd = -1;
55 }
56}
57
69{
71 g_board_sd.image_len = 0U;
72 g_board_sd.attached = false;
73}
74
91RA8_INTERNAL static bool internal_create_sparse(uint64_t bytes, int* out_fd)
92{
93 if ((out_fd == nullptr) || (bytes == 0U) || (bytes > (uint64_t)INT64_MAX)) {
94 return false;
95 }
96 char path[] = "/tmp/ra8_emulator_sd.XXXXXX";
97 int fd = mkstemp(path);
98 if (fd < 0) {
99 return false;
100 }
101 (void)unlink(path);
102 if (ftruncate(fd, (off_t)bytes) != 0) {
104 return false;
105 }
106 *out_fd = fd;
107 return true;
108}
109
126RA8_INTERNAL static bool internal_copy_bytes(int source_fd, int target_fd, uint64_t bytes)
127{
128 uint8_t scratch[k_sd_io_chunk];
129 uint64_t offset = 0U;
130 while (offset < bytes) {
131 const uint64_t remaining = bytes - offset;
132 const size_t count =
133 (remaining < (uint64_t)sizeof(scratch)) ? (size_t)remaining : sizeof(scratch);
134 const emu_io_result_t read_result =
135 priv_emu_io_pread_exact(source_fd, scratch, count, (off_t)offset);
136 if (read_result.status != k_emu_io_ok) {
137 return false;
138 }
139 const emu_io_result_t write_result =
140 priv_emu_io_pwrite_exact(target_fd, scratch, count, (off_t)offset);
141 if (write_result.status != k_emu_io_ok) {
142 return false;
143 }
144 offset += (uint64_t)count;
145 }
146 return true;
147}
148
161RA8_INTERNAL static void internal_adopt_image(int image_fd, uint64_t bytes)
162{
164 g_board_sd = (board_sd_state_t){.image_fd = image_fd};
165 g_board_sd.image_len = bytes;
166 g_board_sd.attached = true;
167}
168
184RA8_INTERNAL static bool internal_range_valid(uint64_t offset, size_t count)
185{
186 if (!g_board_sd.attached || (g_board_sd.image_fd < 0) || (offset > g_board_sd.image_len) ||
187 (offset > (uint64_t)INT64_MAX)) {
188 return false;
189 }
190 return (uint64_t)count <= (g_board_sd.image_len - offset);
191}
192
193bool priv_board_sd_storage_read(uint64_t offset, void* dst, size_t count)
194{
195 if (((dst == nullptr) && (count != 0U)) || !internal_range_valid(offset, count)) {
196 return false;
197 }
198 const emu_io_result_t result =
199 priv_emu_io_pread_exact(g_board_sd.image_fd, dst, count, (off_t)offset);
200 return result.status == k_emu_io_ok;
201}
202
203bool priv_board_sd_storage_write(uint64_t offset, const void* src, size_t count)
204{
205 if (((src == nullptr) && (count != 0U)) || !internal_range_valid(offset, count)) {
206 return false;
207 }
208 const emu_io_result_t result =
209 priv_emu_io_pwrite_exact(g_board_sd.image_fd, src, count, (off_t)offset);
210 return result.status == k_emu_io_ok;
211}
212
228RA8_INTERNAL static bool internal_write_zeros(uint64_t offset, uint64_t count)
229{
230 const uint8_t zeros[k_sd_io_chunk] = {};
231 uint64_t done = 0U;
232 while (done < count) {
233 const uint64_t remaining = count - done;
234 const size_t chunk = (remaining < (uint64_t)sizeof(zeros)) ? (size_t)remaining : sizeof(zeros);
235 if (!priv_board_sd_storage_write(offset + done, zeros, chunk)) {
236 return false;
237 }
238 done += (uint64_t)chunk;
239 }
240 return true;
241}
242
243bool priv_board_sd_storage_zero(uint64_t offset, uint64_t count)
244{
245 if (!g_board_sd.attached || (g_board_sd.image_fd < 0) || (offset > g_board_sd.image_len) ||
246 (count > (g_board_sd.image_len - offset)) || (offset > (uint64_t)INT64_MAX) ||
247 (count > (uint64_t)INT64_MAX)) {
248 return false;
249 }
250 if (count == 0U) {
251 return true;
252 }
253#ifdef __linux__
254 if (fallocate(g_board_sd.image_fd,
255 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
256 (off_t)offset,
257 (off_t)count) == 0) {
258 return true;
259 }
260 if ((errno != EOPNOTSUPP) && (errno != ENOSYS) && (errno != EINVAL)) {
261 return false;
262 }
263#endif
264 return internal_write_zeros(offset, count);
265}
266
267bool board_sd_attach(const char* path)
268{
269 if (path == nullptr) {
270 return false;
271 }
272 emu_io_file_t source = {.fd = -1, .size = 0};
273 if (priv_emu_io_open_read(path, &source).status != k_emu_io_ok) {
274 (void)priv_emu_io_errf("ra8_emulator: --sd: cannot open '%s'\n", path);
275 return false;
276 }
277 if (source.size <= 0) {
278 (void)priv_emu_io_close(&source);
279 (void)priv_emu_io_errf("ra8_emulator: --sd: empty image '%s'\n", path);
280 return false;
281 }
282 const uint64_t bytes = (uint64_t)source.size;
283 int working_fd = -1;
284 const bool prepared =
285 internal_create_sparse(bytes, &working_fd) && internal_copy_bytes(source.fd, working_fd, bytes);
286 (void)priv_emu_io_close(&source);
287 if (!prepared) {
288 internal_close_fd(&working_fd);
289 (void)priv_emu_io_errf("ra8_emulator: --sd: cannot prepare private image '%s'\n", path);
290 return false;
291 }
292 internal_adopt_image(working_fd, bytes);
293 (void)priv_emu_io_errf("ra8_emulator: SD card attached (%llu bytes) from %s\n",
294 (unsigned long long)g_board_sd.image_len,
295 path);
296 return true;
297}
298
300{
301 return g_board_sd.attached;
302}
303
317RA8_INTERNAL static void internal_report_created(uint64_t bytes, uint8_t fat_bits, uint32_t spc)
318{
319 if (bytes >= ((uint64_t)k_unit_kib * (uint64_t)k_unit_kib * (uint64_t)k_unit_kib)) {
320 (void)priv_emu_io_errf(
321 "ra8_emulator: SD card created (%llu GiB FAT%u, %u sec/clus) sparse + attached\n",
322 (unsigned long long)(bytes /
323 ((uint64_t)k_unit_kib * (uint64_t)k_unit_kib * (uint64_t)k_unit_kib)),
324 (unsigned)fat_bits,
325 (unsigned)spc);
326 } else {
327 (void)priv_emu_io_errf(
328 "ra8_emulator: SD card created (%llu MiB FAT%u, %u sec/clus) sparse + attached\n",
329 (unsigned long long)(bytes / ((uint64_t)k_unit_kib * (uint64_t)k_unit_kib)),
330 (unsigned)fat_bits,
331 (unsigned)spc);
332 }
333}
334
335bool board_sd_attach_blank(uint32_t total_sectors, uint8_t fat_bits, const char* label)
336{
337 if (total_sectors < (uint32_t)k_sd_min_sectors) {
338 (void)priv_emu_io_errf("ra8_emulator: --sd-new: size too small (need >= 32 KiB)\n");
339 return false;
340 }
341 const uint64_t bytes = (uint64_t)total_sectors * (uint64_t)k_fmt_sec_bytes;
342 int image_fd = -1;
343 if (!internal_create_sparse(bytes, &image_fd)) {
344 (void)priv_emu_io_errf("ra8_emulator: --sd-new: cannot create %llu-byte sparse image\n",
345 (unsigned long long)bytes);
346 return false;
347 }
348 uint32_t spc = 0U;
349 const bool formatted = (fat_bits == (uint8_t)k_fat32_bits)
350 ? priv_board_sd_format_fat32(image_fd, total_sectors, label, &spc)
351 : priv_board_sd_format_fat16(image_fd, total_sectors, label, &spc);
352 if (!formatted) {
353 internal_close_fd(&image_fd);
354 (void)priv_emu_io_errf("ra8_emulator: --sd-new: FAT format write failed\n");
355 return false;
356 }
357 internal_adopt_image(image_fd, bytes);
358 g_board_sd.fat_bits =
359 (fat_bits == (uint8_t)k_fat32_bits) ? (uint8_t)k_fat32_bits : (uint8_t)k_fat16_bits;
360 priv_board_sd_label_field((uint8_t*)g_board_sd.label, label);
361 g_board_sd.label[k_fmt_label_len] = '\0';
362 internal_report_created(bytes, g_board_sd.fat_bits, spc);
363 return true;
364}
365
366bool board_sd_save(const char* path)
367{
368 if ((path == nullptr) || !g_board_sd.attached || (g_board_sd.image_fd < 0)) {
369 return false;
370 }
371 if (g_board_sd.image_len > (uint64_t)k_sd_save_max_bytes) {
372 (void)priv_emu_io_errf(
373 "ra8_emulator: --save-sd: card is %llu MiB (> %u MiB cap) -- skipped\n",
374 (unsigned long long)(g_board_sd.image_len / ((uint64_t)k_unit_kib * (uint64_t)k_unit_kib)),
375 (unsigned)((uint64_t)k_sd_save_max_bytes / ((uint64_t)k_unit_kib * (uint64_t)k_unit_kib)));
376 return false;
377 }
378 emu_io_txn_t txn = {.fd = -1};
379 if (priv_emu_io_txn_begin(path, &txn).status != k_emu_io_ok) {
380 (void)priv_emu_io_errf("ra8_emulator: --save-sd: cannot write '%s'\n", path);
381 return false;
382 }
383 if (!internal_copy_bytes(g_board_sd.image_fd, txn.fd, g_board_sd.image_len) ||
384 (priv_emu_io_txn_commit(&txn).status != k_emu_io_ok)) {
386 return false;
387 }
388 (void)priv_emu_io_errf("ra8_emulator: SD card image saved (%llu bytes) to %s\n",
389 (unsigned long long)g_board_sd.image_len,
390 path);
391 return true;
392}
393
394void board_sd_info(bool* attached, uint64_t* bytes, uint8_t* fat_bits, const char** label)
395{
396 if (attached != nullptr) {
397 *attached = g_board_sd.attached;
398 }
399 if (bytes != nullptr) {
400 *bytes = g_board_sd.image_len;
401 }
402 if (fat_bits != nullptr) {
403 *fat_bits = g_board_sd.fat_bits;
404 }
405 if (label != nullptr) {
406 *label = g_board_sd.label;
407 }
408}
SD-card-over-SPI device model for ra8_emulator (attached to SPI_B).
bool priv_board_sd_storage_write(uint64_t offset, const void *src, size_t count)
Write exactly to the current sparse backend at a checked byte offset.
bool priv_board_sd_storage_zero(uint64_t offset, uint64_t count)
Make a checked byte range read as zero while preserving sparse storage.
static RA8_INTERNAL void internal_close_fd(int *fd)
Close a descriptor when owned and invalidate the caller's slot.
static RA8_INTERNAL void internal_adopt_image(int image_fd, uint64_t bytes)
Replace the active card with a prepared working file.
static RA8_INTERNAL void internal_release_image(void)
Release the currently attached anonymous working file.
bool board_sd_save(const char *path)
Transactionally publish the current SD-card image to a file.
bool board_sd_attached(void)
Report whether an SD-card image is currently attached.
static RA8_INTERNAL bool internal_copy_bytes(int source_fd, int target_fd, uint64_t bytes)
Copy a descriptor range with one fixed scratch buffer.
bool board_sd_attach(const char *path)
Attach an SD-card image from a host file.
bool priv_board_sd_storage_read(uint64_t offset, void *dst, size_t count)
Read exactly from the current sparse backend at a checked byte offset.
bool board_sd_attach_blank(uint32_t total_sectors, uint8_t fat_bits, const char *label)
Create a blank, FAT-formatted sparse SD card and attach it.
static RA8_INTERNAL bool internal_create_sparse(uint64_t bytes, int *out_fd)
Create, unlink, and size an anonymous sparse working file.
static RA8_INTERNAL bool internal_write_zeros(uint64_t offset, uint64_t count)
Portably zero a range with bounded memory when hole punching is unavailable.
board_sd_io_const_t
Fixed transfer size for card-image copies and portable zeroing.
@ k_sd_io_chunk
Bounded stack scratch; independent of card size.
void board_sd_info(bool *attached, uint64_t *bytes, uint8_t *fat_bits, const char **label)
Read back the attached card's summary (for the status view / report).
static RA8_INTERNAL bool internal_range_valid(uint64_t offset, size_t count)
Validate a positioned operation against descriptor and card bounds.
static RA8_INTERNAL void internal_report_created(uint64_t bytes, uint8_t fat_bits, uint32_t spc)
Emit the stable blank-card geometry diagnostic.
Module-private FAT formatter shared by the board_periph_sd TUs.
board_sd_state_t g_board_sd
The single modelled SD card (defined in board_periph_sd.c).
@ k_fat32_bits
FAT32 selector / label width.
@ k_fat16_bits
FAT16 selector.
@ k_sd_min_sectors
–sd-new floor: 32 KiB of sectors.
@ k_sd_save_max_bytes
–save-sd cap: 2 GiB.
@ k_unit_kib
Binary kilo multiplier (KiB).
@ k_fmt_sec_bytes
Bytes per sector.
bool priv_board_sd_format_fat16(int image_fd, uint32_t total_sectors, const char *label, uint32_t *out_spc)
Format the attached sparse backend as a FAT16 volume.
void priv_board_sd_label_field(uint8_t *dst, const char *label)
Copy a volume label into an 11-char space-padded 8.3 field.
bool priv_board_sd_format_fat32(int image_fd, uint32_t total_sectors, const char *label, uint32_t *out_spc)
Format the attached sparse backend as a FAT32 volume.
Bounded raw-descriptor I/O seam for the RA8 emulator.
@ k_emu_io_ok
The complete operation succeeded.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
emu_io_result_t priv_emu_io_pread_exact(int fd, void *buf, size_t count, off_t offset)
Read exactly count bytes at offset without changing the cursor.
emu_io_result_t priv_emu_io_pwrite_exact(int fd, const void *buf, size_t count, off_t offset)
Write exactly count bytes at offset without changing the cursor.
void priv_emu_io_txn_abort(emu_io_txn_t *txn)
Close and unlink an active sibling transaction without publication.
emu_io_result_t priv_emu_io_txn_commit(emu_io_txn_t *txn)
Sync, close, and atomically rename an active sibling transaction.
emu_io_result_t priv_emu_io_close(emu_io_file_t *file)
Close and invalidate an owned raw file descriptor.
emu_io_result_t priv_emu_io_txn_begin(const char *path, emu_io_txn_t *txn)
Create a sibling temporary output for failure-atomic publication.
emu_io_result_t priv_emu_io_open_read(const char *path, emu_io_file_t *file)
Open and stat a regular file for bounded raw reading.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_fmt_label_len
Volume-label field width (bytes).
The modelled card: backing image + command/response framing.
Raw descriptor plus its stat-derived byte length.
int64_t size
Non-negative regular-file length.
int fd
Owned descriptor, or -1 when closed.
Complete, caller-visible result of a raw host-I/O operation.
emu_io_status_t status
Semantic completion status.
Sibling temporary file used for failure-atomic publication.
int fd
Owned temporary descriptor, or -1 when inactive.