ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_host_io.c
Go to the documentation of this file.
1
11
12#include <errno.h>
13#include <fcntl.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/stat.h>
18#include <unistd.h>
19
21
22enum : size_t {
25};
26
42RA8_INTERNAL static ssize_t internal_read(int fd, void* buf, size_t count)
43{
44 return read(fd, buf, count);
45}
46
62RA8_INTERNAL static ssize_t internal_write(int fd, const void* buf, size_t count)
63{
64 return write(fd, buf, count);
65}
66
83RA8_INTERNAL static ssize_t internal_pread(int fd, void* buf, size_t count, off_t offset)
84{
85 return pread(fd, buf, count, offset);
86}
87
104RA8_INTERNAL static ssize_t internal_pwrite(int fd, const void* buf, size_t count, off_t offset)
105{
106 return pwrite(fd, buf, count, offset);
107}
108
110 .read_fn = internal_read,
111 .write_fn = internal_write,
112 .pread_fn = internal_pread,
113 .pwrite_fn = internal_pwrite,
114};
116static int s_out_fd = STDOUT_FILENO;
117static int s_err_fd = STDERR_FILENO;
118
135internal_result(emu_io_status_t status, size_t transferred, int os_error)
136{
137 return (emu_io_result_t){.status = status, .transferred = transferred, .os_error = os_error};
138}
139
140void priv_emu_io_configure(int out_fd, int err_fd, const emu_io_ops_t* ops)
141{
142 s_out_fd = out_fd;
143 s_err_fd = err_fd;
144 s_ops = (ops == nullptr) ? &s_k_default_ops : ops;
145}
146
147typedef ssize_t (*internal_transfer_fn_t)(int fd, void* buf, size_t count, off_t offset);
148
168 void* buf,
169 size_t count,
170 off_t offset,
172 bool input)
173{
174 if ((fd < 0) || ((buf == nullptr) && (count != 0U)) || (fn == nullptr)) {
175 return internal_result(k_emu_io_invalid, 0U, 0);
176 }
177 size_t done = 0U;
178 size_t interrupted = 0U;
179 while (done < count) {
180 ssize_t amount = fn(fd, (uint8_t*)buf + done, count - done, offset + (off_t)done);
181 if (amount > 0) {
182 done += (size_t)amount;
183 interrupted = 0U;
184 continue;
185 }
186 if (amount == 0) {
187 return internal_result(input ? k_emu_io_eof : k_emu_io_error, done, input ? 0 : EIO);
188 }
189 if ((errno == EINTR) && (interrupted < k_emu_io_eintr_max)) {
190 interrupted++;
191 continue;
192 }
193 return internal_result(k_emu_io_error, done, errno);
194 }
195 return internal_result(k_emu_io_ok, done, 0);
196}
197
214RA8_INTERNAL static ssize_t internal_read_adapter(int fd, void* buf, size_t count, off_t offset)
215{
216 (void)offset;
217 return s_ops->read_fn(fd, buf, count);
218}
219
236RA8_INTERNAL static ssize_t internal_write_adapter(int fd, void* buf, size_t count, off_t offset)
237{
238 (void)offset;
239 return s_ops->write_fn(fd, buf, count);
240}
241
258RA8_INTERNAL static ssize_t internal_pread_adapter(int fd, void* buf, size_t count, off_t offset)
259{
260 return s_ops->pread_fn(fd, buf, count, offset);
261}
262
279RA8_INTERNAL static ssize_t internal_pwrite_adapter(int fd, void* buf, size_t count, off_t offset)
280{
281 return s_ops->pwrite_fn(fd, buf, count, offset);
282}
283
284emu_io_result_t priv_emu_io_read_exact(int fd, void* buf, size_t count)
285{
286 return internal_transfer(fd, buf, count, 0, internal_read_adapter, true);
287}
288
289emu_io_result_t priv_emu_io_write_exact(int fd, const void* buf, size_t count)
290{
291 return internal_transfer(fd, (void*)buf, count, 0, internal_write_adapter, false);
292}
293
294emu_io_result_t priv_emu_io_pread_exact(int fd, void* buf, size_t count, off_t offset)
295{
296 return internal_transfer(fd, buf, count, offset, internal_pread_adapter, true);
297}
298
299emu_io_result_t priv_emu_io_pwrite_exact(int fd, const void* buf, size_t count, off_t offset)
300{
301 return internal_transfer(fd, (void*)buf, count, offset, internal_pwrite_adapter, false);
302}
303
319RA8_INTERNAL static emu_io_result_t internal_vformat(int fd, const char* format, va_list args)
320{
321 if ((fd < 0) || (format == nullptr)) {
322 return internal_result(k_emu_io_invalid, 0U, 0);
323 }
324 char text[k_emu_io_format_cap];
325 va_list copy;
326 va_copy(copy, args);
327 const int needed = vsnprintf(text, sizeof(text), format, copy);
328 va_end(copy);
329 if (needed < 0) {
330 return internal_result(k_emu_io_error, 0U, EILSEQ);
331 }
332 if ((size_t)needed >= sizeof(text)) {
333 return internal_result(k_emu_io_truncated, 0U, 0);
334 }
335 return priv_emu_io_write_exact(fd, text, (size_t)needed);
336}
337
339{
340 return (text == nullptr) ? internal_result(k_emu_io_invalid, 0U, 0)
342}
343
345{
346 return (text == nullptr) ? internal_result(k_emu_io_invalid, 0U, 0)
348}
349
350emu_io_result_t priv_emu_io_err_bytes(const void* bytes, size_t length)
351{
352 return priv_emu_io_write_exact(s_err_fd, bytes, length);
353}
354
355emu_io_result_t priv_emu_io_outf(const char* format, ...)
356{
357 va_list args;
358 va_start(args, format);
359 const emu_io_result_t result = internal_vformat(s_out_fd, format, args);
360 va_end(args);
361 return result;
362}
363
364emu_io_result_t priv_emu_io_errf(const char* format, ...)
365{
366 va_list args;
367 va_start(args, format);
368 const emu_io_result_t result = internal_vformat(s_err_fd, format, args);
369 va_end(args);
370 return result;
371}
372
373emu_io_result_t priv_emu_io_file_text(int fd, const char* text)
374{
375 return (text == nullptr) ? internal_result(k_emu_io_invalid, 0U, 0)
376 : priv_emu_io_write_exact(fd, text, strlen(text));
377}
378
380{
381 return priv_emu_io_write_exact(fd, &value, 1U);
382}
383
384emu_io_result_t priv_emu_io_filef(int fd, const char* format, ...)
385{
386 va_list args;
387 va_start(args, format);
388 const emu_io_result_t result = internal_vformat(fd, format, args);
389 va_end(args);
390 return result;
391}
392
394{
395 if ((path == nullptr) || (path[0] == '\0') || (file == nullptr)) {
396 return internal_result(k_emu_io_invalid, 0U, 0);
397 }
398 file->fd = -1;
399 file->size = 0;
400 const int fd = open(path, O_RDONLY | O_CLOEXEC);
401 if (fd < 0) {
402 return internal_result(k_emu_io_error, 0U, errno);
403 }
404 struct stat info = {};
405 if ((fstat(fd, &info) != 0) || !S_ISREG(info.st_mode) || (info.st_size < 0)) {
406 const int saved = (errno == 0) ? EINVAL : errno;
407 (void)close(fd);
408 return internal_result(k_emu_io_error, 0U, saved);
409 }
410 file->fd = fd;
411 file->size = (int64_t)info.st_size;
412 return internal_result(k_emu_io_ok, 0U, 0);
413}
414
416{
417 if ((file == nullptr) || (file->fd < 0)) {
418 return internal_result(k_emu_io_invalid, 0U, 0);
419 }
420 const int fd = file->fd;
421 file->fd = -1;
422 file->size = 0;
423 return (close(fd) == 0) ? internal_result(k_emu_io_ok, 0U, 0)
424 : internal_result(k_emu_io_error, 0U, errno);
425}
426
428{
429 if ((path == nullptr) || (txn == nullptr)) {
430 return internal_result(k_emu_io_invalid, 0U, 0);
431 }
432 const int final_n = snprintf(txn->final, sizeof(txn->final), "%s", path);
433 const int temp_n = snprintf(txn->temp, sizeof(txn->temp), "%s.tmp.XXXXXX", path);
434 if ((final_n < 1) || ((size_t)final_n >= sizeof(txn->final)) || (temp_n < 1) ||
435 ((size_t)temp_n >= sizeof(txn->temp))) {
436 txn->fd = -1;
437 return internal_result(k_emu_io_truncated, 0U, 0);
438 }
439 txn->fd = mkstemp(txn->temp);
440 if (txn->fd < 0) {
441 return internal_result(k_emu_io_error, 0U, errno);
442 }
443 (void)fcntl(txn->fd, F_SETFD, FD_CLOEXEC);
444 return internal_result(k_emu_io_ok, 0U, 0);
445}
446
448{
449 if ((txn == nullptr) || (txn->fd < 0)) {
450 return internal_result(k_emu_io_invalid, 0U, 0);
451 }
452 if (fsync(txn->fd) != 0) {
453 return internal_result(k_emu_io_error, 0U, errno);
454 }
455 if (close(txn->fd) != 0) {
456 txn->fd = -1;
457 return internal_result(k_emu_io_error, 0U, errno);
458 }
459 txn->fd = -1;
460 if (rename(txn->temp, txn->final) != 0) {
461 return internal_result(k_emu_io_error, 0U, errno);
462 }
463 txn->temp[0] = '\0';
464 return internal_result(k_emu_io_ok, 0U, 0);
465}
466
468{
469 if (txn == nullptr) {
470 return;
471 }
472 if (txn->fd >= 0) {
473 (void)close(txn->fd);
474 txn->fd = -1;
475 }
476 if (txn->temp[0] != '\0') {
477 (void)unlink(txn->temp);
478 txn->temp[0] = '\0';
479 }
480}
static int s_out_fd
static RA8_INTERNAL ssize_t internal_write_adapter(int fd, void *buf, size_t count, off_t offset)
Adapt sequential output to the common positioned callback shape.
emu_io_result_t priv_emu_io_err_text(const char *text)
Write a NUL-terminated literal to the injected error descriptor.
emu_io_result_t priv_emu_io_out_text(const char *text)
Write a NUL-terminated literal to the injected output descriptor.
static int s_err_fd
emu_io_result_t priv_emu_io_write_exact(int fd, const void *buf, size_t count)
Write exactly count sequential bytes or report fault progress.
static RA8_INTERNAL ssize_t internal_write(int fd, const void *buf, size_t count)
Call the default sequential output primitive.
Definition emu_host_io.c:62
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.
static RA8_INTERNAL ssize_t internal_pread_adapter(int fd, void *buf, size_t count, off_t offset)
Dispatch positioned input through the configured operation table.
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.
emu_io_result_t priv_emu_io_outf(const char *format,...)
Format bounded text and write it to the injected output descriptor.
emu_io_result_t priv_emu_io_filef(int fd, const char *format,...)
Format bounded text and write it to an explicit raw descriptor.
static RA8_INTERNAL emu_io_result_t internal_transfer(int fd, void *buf, size_t count, off_t offset, internal_transfer_fn_t fn, bool input)
Complete one exact raw transfer with bounded EINTR retries.
emu_io_result_t priv_emu_io_read_exact(int fd, void *buf, size_t count)
Read exactly count sequential bytes or report EOF/fault progress.
static const emu_io_ops_t * s_ops
void priv_emu_io_configure(int out_fd, int err_fd, const emu_io_ops_t *ops)
Inject process output descriptors and optional raw transfer hooks.
emu_io_result_t priv_emu_io_err_bytes(const void *bytes, size_t length)
Write an exact non-NUL-terminated byte fragment to the error sink.
static RA8_INTERNAL ssize_t internal_pread(int fd, void *buf, size_t count, off_t offset)
Call the default positioned input primitive.
Definition emu_host_io.c:83
static RA8_INTERNAL ssize_t internal_pwrite_adapter(int fd, void *buf, size_t count, off_t offset)
Dispatch positioned output through the configured operation table.
void priv_emu_io_txn_abort(emu_io_txn_t *txn)
Close and unlink an active sibling transaction without publication.
static RA8_INTERNAL emu_io_result_t internal_result(emu_io_status_t status, size_t transferred, int os_error)
Construct one complete public operation result.
static RA8_INTERNAL ssize_t internal_pwrite(int fd, const void *buf, size_t count, off_t offset)
Call the default positioned output primitive.
@ k_emu_io_eintr_max
Consecutive EINTR retry bound.
Definition emu_host_io.c:24
@ k_emu_io_format_cap
Per-call stack formatting capacity.
Definition emu_host_io.c:23
static const emu_io_ops_t s_k_default_ops
static RA8_INTERNAL emu_io_result_t internal_vformat(int fd, const char *format, va_list args)
Render one bounded format operation and write it exactly.
ssize_t(* internal_transfer_fn_t)(int fd, void *buf, size_t count, off_t offset)
emu_io_result_t priv_emu_io_file_char(int fd, char value)
Write one byte to an explicit raw descriptor.
emu_io_result_t priv_emu_io_txn_commit(emu_io_txn_t *txn)
Sync, close, and atomically rename an active sibling transaction.
static RA8_INTERNAL ssize_t internal_read_adapter(int fd, void *buf, size_t count, off_t offset)
Adapt sequential input to the common positioned callback shape.
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_file_text(int fd, const char *text)
Write a NUL-terminated literal to an explicit raw 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.
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_status_t
Result status for an emulator host-I/O operation.
@ k_emu_io_eof
Input ended before the requested byte count.
@ k_emu_io_ok
The complete operation succeeded.
@ k_emu_io_error
A host syscall failed; os_error carries errno.
@ k_emu_io_invalid
A pointer, descriptor, size, or path was invalid.
@ k_emu_io_truncated
Formatted text exceeded the bounded scratch buffer.
static uint32_t internal_read(void)
Read current GPT counter ticks, or return UINT32_MAX on err.
Definition main.c:150
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
static ra8_err_t internal_write(void *ctx, void *file_state, const uint8_t *src, uint32_t len, uint32_t *out_written)
static ra8_err_t internal_read(void *ctx, void *file_state, uint8_t *dst, uint32_t cap, uint32_t *out_read)
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
size_t strlen(const char *s)
Calculate string length.
Raw descriptor plus its stat-derived byte length.
Injectable raw operations used by the exact-transfer loops.
Complete, caller-visible result of a raw host-I/O operation.
Sibling temporary file used for failure-atomic publication.
char temp[544]
Sibling mkstemp template / active temporary path.
char final[512]
Final publication path.
int fd
Owned temporary descriptor, or -1 when inactive.