mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Compile selected firmware translation units unmodified under host gcc with AddressSanitizer and UBSan, force-including a shim header that erases the SDCC 8051 keywords, and link them against a mocked serial ring and console. A first suite covers the CLI line editor: line entry, backspace editing, and both long-input overflow cases (length cap and ring-pointer stall). The only firmware-source change is one #ifndef RTLP_HOST_TEST block in rtl837x_common.h grouping the libc-named prototypes (sleep/memcpy/memset/ strlen/strcpy/strcmp) so host builds take them from the C library instead; on-target builds are unaffected. Run with: make -C test
104 lines
3.8 KiB
C
104 lines
3.8 KiB
C
/*
|
|
* support.c — mock hardware edges + test environment for the host harness.
|
|
*
|
|
* Everything the firmware expects the platform/other-TUs to provide, but which
|
|
* has no business touching real hardware in a unit test, lives here: the serial
|
|
* ring, the command/history buffers, the character-output sink, and stubs for
|
|
* the print helpers. The buffers are sized EXACTLY as on target so that
|
|
* AddressSanitizer redzones catch the same off-by-one overflows the 8051 hits.
|
|
*/
|
|
#include "sdcc_shim.h"
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
/* Declared by hand instead of <unistd.h>, whose sleep() prototype clashes with
|
|
* the firmware's own sleep() declaration in rtl837x_common.h. */
|
|
extern unsigned int alarm(unsigned int);
|
|
extern void _exit(int);
|
|
#include "rtl837x_common.h" /* SBUF_SIZE, CMD_BUF_SIZE, CMD_HISTORY_SIZE, decls */
|
|
#include "support.h"
|
|
|
|
/* --- symbols the editor/parser reference (extern in the firmware headers) --- */
|
|
__xdata volatile uint8_t sbuf_ptr;
|
|
__xdata uint8_t sbuf[SBUF_SIZE];
|
|
uint8_t cmd_buffer[CMD_BUF_SIZE]; /* ASan-instrumented: OOB caught */
|
|
uint8_t cmd_available;
|
|
uint8_t err_status;
|
|
uint8_t cmd_history[CMD_HISTORY_SIZE];
|
|
uint16_t cmd_history_ptr;
|
|
|
|
/* editor-internal read pointer (defined in cmd_editor.c, external linkage) */
|
|
extern __xdata uint8_t l;
|
|
|
|
/* the editor entry points under test */
|
|
void cmd_editor_init(void);
|
|
void cmd_edit(void);
|
|
|
|
/* ------------------------------- output sink ------------------------------- */
|
|
char out_buf[8192];
|
|
size_t out_len;
|
|
|
|
void out_reset(void) { out_len = 0; out_buf[0] = 0; }
|
|
|
|
static void out_putc(char c) {
|
|
if (out_len < sizeof(out_buf) - 1) { out_buf[out_len++] = c; out_buf[out_len] = 0; }
|
|
}
|
|
|
|
/* --- mocked print helpers (signatures match rtl837x_common.h post-shim) ---- */
|
|
void write_char(char c) { out_putc(c); }
|
|
void print_string(char *p) { while (*p) out_putc(*p++); }
|
|
void print_string_x(char *p) { while (*p) out_putc(*p++); }
|
|
void print_cmd_prompt(void) { print_string((char *)"> "); }
|
|
void itoa(uint8_t v) { /* firmware prints a decimal number */
|
|
char t[4]; int n = 0;
|
|
if (!v) { out_putc('0'); return; }
|
|
while (v) { t[n++] = '0' + v % 10; v /= 10; }
|
|
while (n) out_putc(t[--n]);
|
|
}
|
|
|
|
/* ------------------------------ environment -------------------------------- */
|
|
void env_reset(void) {
|
|
sbuf_ptr = 0;
|
|
t_memset(sbuf, 0, sizeof(sbuf));
|
|
t_memset(cmd_buffer, 0, sizeof(cmd_buffer));
|
|
t_memset(cmd_history, 0, sizeof(cmd_history));
|
|
cmd_history_ptr = 0;
|
|
cmd_available = 0;
|
|
err_status = 0;
|
|
out_reset();
|
|
cmd_editor_init(); /* sets l = sbuf_ptr, clears cursor/len */
|
|
}
|
|
|
|
/* Push one byte into the ring, draining first if it is full (leave 1 slot). */
|
|
static void ring_push(uint8_t b) {
|
|
while (((sbuf_ptr + 1) & SBUF_MASK) == l)
|
|
cmd_edit(); /* drain; under the C4 bug this spins */
|
|
sbuf[sbuf_ptr] = b;
|
|
sbuf_ptr = (sbuf_ptr + 1) & SBUF_MASK;
|
|
}
|
|
|
|
void feed_n(const char *s, size_t n) {
|
|
for (size_t i = 0; i < n; i++) ring_push((uint8_t)s[i]);
|
|
cmd_edit();
|
|
}
|
|
void feed(const char *s) { feed_n(s, t_strlen(s)); }
|
|
|
|
/* -------------------------------- watchdog --------------------------------- */
|
|
static const char *wd_what;
|
|
static void wd_fire(int sig) {
|
|
(void)sig;
|
|
/* async-signal-unsafe write is acceptable: we abort immediately after. */
|
|
fprintf(stderr, "\n HANG DETECTED (watchdog): %s\n", wd_what ? wd_what : "?");
|
|
_exit(2);
|
|
}
|
|
void watchdog_arm(unsigned seconds, const char *what) {
|
|
wd_what = what;
|
|
signal(SIGALRM, wd_fire);
|
|
alarm(seconds);
|
|
}
|
|
void watchdog_disarm(void) { alarm(0); }
|
|
|
|
/* -------------------------------- framework -------------------------------- */
|
|
int tests_run, tests_failed;
|