From 9452d47324dbd0f32c746e27afaf4a6bf6d2876a Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Sat, 13 Sep 2025 03:59:40 +0800 Subject: [PATCH 1/6] Implement Goldfish RTC The system date after boot is not synchronized with the host's wall clock, resulting in incorrect timestamps shown by commands such as date. When the system has the hwclock utility, it cannot function because the /dev/rtc? device file is missing. In addition, programs might rely on RTC alarm events for wake-up operations, which require access to /dev/rtc? device file. This commit implements an RTC device using the Goldfish RTC model, chosen for its simplicity and native support in the Linux kernel. By adding RTC support, the following capabilities are enabled: - System boot-up synchronization: The RTC driver initializes the system time from the at boot, ensuring the guestOS starts with an accurate and synchronized timestamp. - Wakeup events: The RTC can generate alarm interrupts to wake up the process. - The date command, hwclock utility, and alarm configuration via ioctl (see the rtc(4) man page) are now functional. - Provide accurate timestamps for filesystem operations such as when writing or modifying files on a virtio block device. The #605 hardcoded vblk_mmio_base_hi to 0x41, which limits adding new subnodes (e.g., RTC) under the SoC node. This value should instead be dynamically set during load_dtb(). Another limitation is in the next_addr probing logic, which currently uses subnode's name + 7. This assumes a fixed-length name and fails for variable-length names (e.g., rtc@4100000, should be name + 4). The fix is to locate the '@' character in the subnode name and use its position + 1 as the offset. Additionally, when no virtio block device is specified, virtio_mmio_base_hi will default to 0. In this case, use vblk_cnt for condition checking during MMIO handling to avoid incorrect behavior. --- src/common.h | 2 +- src/devices/minimal.dts | 13 ++- src/devices/rtc.c | 120 +++++++++++++++++++++++++ src/devices/rtc.h | 58 ++++++++++++ src/emulate.c | 9 ++ src/riscv.c | 15 +++- src/riscv.h | 4 + src/system.c | 10 +++ src/system.h | 193 ++++++++++++++++++++++------------------ 9 files changed, 329 insertions(+), 95 deletions(-) create mode 100644 src/devices/rtc.c create mode 100644 src/devices/rtc.h diff --git a/src/common.h b/src/common.h index bce9f0f24..d6719754d 100644 --- a/src/common.h +++ b/src/common.h @@ -29,7 +29,7 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) -#define MASK(n) (~((~0U << (n)))) +#define MASK(n) (~((~0UL << (n)))) #if defined(_MSC_VER) #include diff --git a/src/devices/minimal.dts b/src/devices/minimal.dts index 1b0b09572..ef4446cd5 100644 --- a/src/devices/minimal.dts +++ b/src/devices/minimal.dts @@ -66,14 +66,21 @@ clock-frequency = <5000000>; /* the baudrate divisor is ignored */ }; + rtc0: rtc@4100000 { + compatible = "google,goldfish-rtc"; + reg = <0x4100000 0x1000>; + interrupts = <2>; + interrupt-parent = <&plic0>; + }; + /* * Virtio block example subnode * The actual subnode are generated dynamically depends on the CLI -x vblk option */ - /*blk0: virtio@4100000 { + /*blk0: virtio@4200000 { compatible = "virtio,mmio"; - reg = <0x4100000 0x200>; - interrupts = <2>; + reg = <0x4200000 0x200>; + interrupts = <3>; };*/ }; }; diff --git a/src/devices/rtc.c b/src/devices/rtc.c new file mode 100644 index 000000000..0a87d7e6a --- /dev/null +++ b/src/devices/rtc.c @@ -0,0 +1,120 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtc.h" + +static uint64_t now_nsec; + +uint64_t get_now_nsec(rtc_t *rtc) +{ + /* TODO: + * - detects timezone and use the correct UTC offset + * - a new CLI option should be added to main.c to let user to select + * [UTC] or [UTC + offset](localtime) time. E.g., -x rtc:utc or -x + * rtc:localtime + */ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (uint64_t) (ts.tv_sec * 1e9) + ts.tv_nsec + rtc->clock_offset; +} + +uint32_t rtc_read(rtc_t *rtc, uint32_t addr) +{ + uint32_t rtc_read_val = 0; + + switch (addr) { + case RTC_TIME_LOW: + now_nsec = get_now_nsec(rtc); + rtc->time_low = (uint32_t) (now_nsec & MASK(32)); + rtc_read_val = rtc->time_low; + break; + case RTC_TIME_HIGH: + /* reuse the now_nsec when reading RTC_TIME_LOW */ + rtc->time_high = (uint32_t) (now_nsec >> 32); + rtc_read_val = rtc->time_high; + break; + case RTC_ALARM_LOW: + rtc_read_val = rtc->alarm_low; + break; + case RTC_ALARM_HIGH: + rtc_read_val = rtc->alarm_high; + break; + case RTC_ALARM_STATUS: + rtc_read_val = rtc->alarm_status; + break; + default: + rv_log_error("Unsupported RTC read operation, 0x%x", addr); + break; + } + + return rtc_read_val; +} + +void rtc_write(rtc_t *rtc, uint32_t addr, uint32_t value) +{ + switch (addr) { + case RTC_TIME_LOW: + now_nsec = get_now_nsec(rtc); + rtc->clock_offset += (uint64_t) (value) - (now_nsec & MASK(32)); + break; + case RTC_TIME_HIGH: + /* reuse the now_nsec when writing RTC_TIME_LOW */ + rtc->clock_offset += ((uint64_t) (value) << 32) - + (now_nsec & ((uint64_t) (MASK(32)) << 32)); + break; + case RTC_ALARM_LOW: + rtc->alarm_low = value; + break; + case RTC_ALARM_HIGH: + rtc->alarm_high = value; + break; + case RTC_IRQ_ENABLED: + rtc->irq_enabled = value; + break; + case RTC_CLEAR_ALARM: + rtc->clear_alarm = value; + break; + case RTC_CLEAR_INTERRUPT: + rtc->clear_interrupt = value; + rtc->interrupt_status = 0; + break; + default: + rv_log_error("Unsupported RTC write operation, 0x%x", addr); + break; + } + return; +} + +rtc_t *rtc_new() +{ + rtc_t *rtc = calloc(1, sizeof(rtc_t)); + assert(rtc); + + /* + * The rtc->time_low/high values can be updated through the RTC_SET_TIME + * ioctl operation. Therefore, they should be initialized to match the + * host OS time during initialization. + */ + now_nsec = get_now_nsec(rtc); + rtc->time_low = (uint32_t) (now_nsec & MASK(32)); + rtc->time_high = (uint32_t) (now_nsec >> 32); + + return rtc; +} + +void rtc_delete(rtc_t *rtc) +{ + free(rtc); +} diff --git a/src/devices/rtc.h b/src/devices/rtc.h new file mode 100644 index 000000000..f95e62c0b --- /dev/null +++ b/src/devices/rtc.h @@ -0,0 +1,58 @@ +/* + * rv32emu is freely redistributable under the MIT License. See the file + * "LICENSE" for information on usage and redistribution of this file. + */ + +#pragma once + +/* Check: https://github.com/torvalds/linux/blob/v6.1/drivers/rtc/rtc-goldfish.c + */ + +/* Google Goldfish RTC MMIO registers */ +#define RTC_REG_LIST \ + _(TIME_LOW, 0x00) /* R/W */ \ + _(TIME_HIGH, 0x04) /* R/W */ \ + _(ALARM_LOW, 0x08) /* R/W */ \ + _(ALARM_HIGH, 0x0c) /* R/W */ \ + _(IRQ_ENABLED, 0x10) /* W */ \ + _(CLEAR_ALARM, 0x14) /* W */ \ + _(ALARM_STATUS, 0x18) /* R */ \ + _(CLEAR_INTERRUPT, 0x1c) /* W */ + +enum { +#define _(reg, addr) RTC_##reg = addr, + RTC_REG_LIST +#undef _ +}; + +typedef struct { + uint32_t time_low; + uint32_t time_high; + uint32_t alarm_low; + uint32_t alarm_high; + uint32_t irq_enabled; + uint32_t clear_alarm; + uint32_t alarm_status; + uint32_t clear_interrupt; + + /* Ensure the clock always progresses so RTC_SET_TIME ioctl can set any + * arbitrary time */ + uint64_t clock_offset; + + uint32_t interrupt_status; +} rtc_t; + +#define IRQ_RTC_SHIFT 2 +#define IRQ_RTC_BIT (1 << IRQ_RTC_SHIFT) + +#define RTC_ALARM_FIRE(rtc, now_nsec) \ + rtc->irq_enabled && \ + (now_nsec) >= ((((uint64_t) rtc->alarm_high) << 32) | rtc->alarm_low) + +uint32_t rtc_read(rtc_t *rtc, uint32_t addr); + +void rtc_write(rtc_t *rtc, uint32_t addr, uint32_t value); + +rtc_t *rtc_new(); + +void rtc_delete(rtc_t *rtc); diff --git a/src/emulate.c b/src/emulate.c index d9c72c816..e92d38d84 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -370,6 +370,8 @@ static bool has_loops = false; #if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) extern void emu_update_uart_interrupts(riscv_t *rv); +extern void emu_update_rtc_interrupts(riscv_t *rv); +extern uint64_t get_now_nsec(rtc_t *rtc); static uint32_t peripheral_update_ctr = 64; #endif @@ -1015,6 +1017,13 @@ static void rv_check_interrupt(riscv_t *rv) u8250_check_ready(PRIV(rv)->uart); if (PRIV(rv)->uart->in_ready) emu_update_uart_interrupts(rv); + + uint64_t now_nsec = get_now_nsec(PRIV(rv)->rtc); + if (RTC_ALARM_FIRE(PRIV(rv)->rtc, now_nsec)) { + PRIV(rv)->rtc->alarm_status = 1; + PRIV(rv)->rtc->interrupt_status = 1; + emu_update_rtc_interrupts(rv); + } } if (rv->timer > attr->timer) diff --git a/src/riscv.c b/src/riscv.c index 5b43114d4..2fcde71f5 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -350,7 +350,10 @@ static void load_dtb(char **ram_loc, vm_attr_t *attr) const char *name = fdt_get_name(dtb_buf, subnode, NULL); assert(name); - uint32_t addr = strtoul(name + 7, NULL, 16); + char *at_pos = strchr(name, '@'); + assert(at_pos); + + uint32_t addr = strtoul(at_pos + 1, NULL, 16); if (addr == next_addr) next_addr = addr + addr_offset; @@ -365,6 +368,10 @@ static void load_dtb(char **ram_loc, vm_attr_t *attr) /* set IRQ for virtio block, see devices/virtio.h */ attr->vblk_irq_base = next_irq; + /* set the VBLK MMIO valid range */ + attr->vblk_mmio_base_hi = next_addr >> 20; + attr->vblk_mmio_max_hi = attr->vblk_mmio_base_hi + attr->vblk_cnt; + /* adding new virtio block nodes */ for (int i = 0; i < attr->vblk_cnt; i++) { uint32_t new_addr = next_addr + i * addr_offset; @@ -665,9 +672,8 @@ riscv_t *rv_create(riscv_user_t rv_attr) attr->uart->in_fd = attr->fd_stdin; attr->uart->out_fd = attr->fd_stdout; - /* setup virtio-blk */ - attr->vblk_mmio_base_hi = 0x41; - attr->vblk_mmio_max_hi = attr->vblk_mmio_base_hi + attr->vblk_cnt; + /* setup rtc */ + attr->rtc = rtc_new(); attr->vblk = malloc(sizeof(virtio_blk_state_t *) * attr->vblk_cnt); assert(attr->vblk); @@ -848,6 +854,7 @@ void rv_delete(riscv_t *rv) #if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) u8250_delete(attr->uart); plic_delete(attr->plic); + rtc_delete(attr->rtc); /* sync device, cleanup inside the callee */ rv_fsync_device(); #endif diff --git a/src/riscv.h b/src/riscv.h index 63f375be7..471e1f875 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -15,6 +15,7 @@ #if RV32_HAS(SYSTEM) #include "devices/plic.h" +#include "devices/rtc.h" #include "devices/uart.h" #include "devices/virtio.h" #endif /* RV32_HAS(SYSTEM) */ @@ -483,6 +484,9 @@ typedef struct { /* plic object */ plic_t *plic; + /* rtc object */ + rtc_t *rtc; + /* virtio-blk device */ uint32_t **disk; virtio_blk_state_t **vblk; diff --git a/src/system.c b/src/system.c index 4dd66f224..3d5550267 100644 --- a/src/system.c +++ b/src/system.c @@ -30,6 +30,16 @@ void emu_update_vblk_interrupts(riscv_t *rv) plic_update_interrupts(attr->plic); } } + +void emu_update_rtc_interrupts(riscv_t *rv) +{ + vm_attr_t *attr = PRIV(rv); + if (attr->rtc->interrupt_status) + attr->plic->active |= IRQ_RTC_BIT; + else + attr->plic->active &= ~IRQ_RTC_BIT; + plic_update_interrupts(attr->plic); +} #endif static bool ppn_is_valid(riscv_t *rv, uint32_t ppn) diff --git a/src/system.h b/src/system.h index ace503599..529ffa531 100644 --- a/src/system.h +++ b/src/system.h @@ -25,107 +25,126 @@ enum SUPPORTED_MMIO { MMIO_PLIC, MMIO_UART, MMIO_VIRTIOBLK, + MMIO_RTC, }; /* clang-format off */ -#define MMIO_OP(io, rw) \ - switch(io){ \ - case MMIO_PLIC: \ - IIF(rw)( /* read */ \ - mmio_read_val = plic_read(PRIV(rv)->plic, addr & 0x3FFFFFF); \ - plic_update_interrupts(PRIV(rv)->plic); \ - return mmio_read_val; \ - , /* write */ \ - plic_write(PRIV(rv)->plic, addr & 0x3FFFFFF, val); \ - plic_update_interrupts(PRIV(rv)->plic); \ - return; \ - ) \ - break; \ - case MMIO_UART: \ - IIF(rw)( /* read */ \ - mmio_read_val = u8250_read(PRIV(rv)->uart, addr & 0xFFFFF); \ - emu_update_uart_interrupts(rv); \ - return mmio_read_val; \ - , /* write */ \ - u8250_write(PRIV(rv)->uart, addr & 0xFFFFF, val); \ - emu_update_uart_interrupts(rv); \ - return; \ - ) \ - break; \ - case MMIO_VIRTIOBLK: \ - IIF(rw)( /* read */ \ +#define MMIO_OP(io, rw) \ + switch(io){ \ + case MMIO_PLIC: \ + IIF(rw)( /* read */ \ + mmio_read_val = plic_read(PRIV(rv)->plic, addr & 0x3FFFFFF); \ + plic_update_interrupts(PRIV(rv)->plic); \ + return mmio_read_val; \ + , /* write */ \ + plic_write(PRIV(rv)->plic, addr & 0x3FFFFFF, val); \ + plic_update_interrupts(PRIV(rv)->plic); \ + return; \ + ) \ + break; \ + case MMIO_UART: \ + IIF(rw)( /* read */ \ + mmio_read_val = u8250_read(PRIV(rv)->uart, addr & 0xFFFFF); \ + emu_update_uart_interrupts(rv); \ + return mmio_read_val; \ + , /* write */ \ + u8250_write(PRIV(rv)->uart, addr & 0xFFFFF, val); \ + emu_update_uart_interrupts(rv); \ + return; \ + ) \ + break; \ + case MMIO_VIRTIOBLK: \ + IIF(rw)( /* read */ \ mmio_read_val = virtio_blk_read(PRIV(rv)->vblk_curr, addr & 0xFFFFF); \ - emu_update_vblk_interrupts(rv); \ - return mmio_read_val; \ - , /* write */ \ + emu_update_vblk_interrupts(rv); \ + return mmio_read_val; \ + , /* write */ \ virtio_blk_write(PRIV(rv)->vblk_curr, addr & 0xFFFFF, val); \ - emu_update_vblk_interrupts(rv); \ - return; \ - ) \ - break; \ - default: \ - rv_log_error("unknown MMIO type %d\n", io); \ - break; \ + emu_update_vblk_interrupts(rv); \ + return; \ + ) \ + break; \ + case MMIO_RTC: \ + IIF(rw)( /* read */ \ + mmio_read_val = rtc_read(PRIV(rv)->rtc, addr & 0xFFFFF); \ + emu_update_rtc_interrupts(rv); \ + return mmio_read_val; \ + , /* write */ \ + rtc_write(PRIV(rv)->rtc, addr & 0xFFFFF, val); \ + emu_update_rtc_interrupts(rv); \ + return; \ + ) \ + break; \ + default: \ + rv_log_error("unknown MMIO type %d\n", io); \ + break; \ } /* clang-format on */ -#define MMIO_READ() \ - do { \ - uint32_t mmio_read_val; \ - if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ - /* 256 regions of 1MiB */ \ - uint32_t hi = (addr >> 20) & MASK(8); \ - if (hi >= PRIV(rv)->vblk_mmio_base_hi && \ - hi <= PRIV(rv)->vblk_mmio_max_hi) { \ - PRIV(rv)->vblk_curr = \ - PRIV(rv)->vblk[hi - PRIV(rv)->vblk_mmio_base_hi]; \ - MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \ - } else { \ - switch (hi) { \ - case 0x0: \ - case 0x2: /* PLIC (0 - 0x3F) */ \ - MMIO_OP(MMIO_PLIC, MMIO_R); \ - break; \ - case 0x40: /* UART */ \ - MMIO_OP(MMIO_UART, MMIO_R); \ - break; \ - default: \ - __UNREACHABLE; \ - break; \ - } \ - } \ - } \ +#define MMIO_READ() \ + do { \ + uint32_t mmio_read_val; \ + if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ + /* 256 regions of 1MiB */ \ + uint32_t hi = (addr >> 20) & MASK(8); \ + if (PRIV(rv)->vblk_cnt && hi >= PRIV(rv)->vblk_mmio_base_hi && \ + hi <= PRIV(rv)->vblk_mmio_max_hi) { \ + PRIV(rv)->vblk_curr = \ + PRIV(rv)->vblk[hi - PRIV(rv)->vblk_mmio_base_hi]; \ + MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \ + } else { \ + switch (hi) { \ + case 0x0: \ + case 0x2: /* PLIC (0 - 0x3F) */ \ + MMIO_OP(MMIO_PLIC, MMIO_R); \ + break; \ + case 0x40: /* UART */ \ + MMIO_OP(MMIO_UART, MMIO_R); \ + break; \ + case 0x41: /* RTC */ \ + MMIO_OP(MMIO_RTC, MMIO_R); \ + break; \ + default: \ + __UNREACHABLE; \ + break; \ + } \ + } \ + } \ } while (0) -#define MMIO_WRITE() \ - do { \ - if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ - /* 256 regions of 1MiB */ \ - uint32_t hi = (addr >> 20) & MASK(8); \ - if (hi >= PRIV(rv)->vblk_mmio_base_hi && \ - hi <= PRIV(rv)->vblk_mmio_max_hi) { \ - PRIV(rv)->vblk_curr = \ - PRIV(rv)->vblk[hi - PRIV(rv)->vblk_mmio_base_hi]; \ - MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \ - } else { \ - switch (hi) { \ - case 0x0: \ - case 0x2: /* PLIC (0 - 0x3F) */ \ - MMIO_OP(MMIO_PLIC, MMIO_W); \ - break; \ - case 0x40: /* UART */ \ - MMIO_OP(MMIO_UART, MMIO_W); \ - break; \ - default: \ - __UNREACHABLE; \ - break; \ - } \ - } \ - } \ +#define MMIO_WRITE() \ + do { \ + if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ + /* 256 regions of 1MiB */ \ + uint32_t hi = (addr >> 20) & MASK(8); \ + if (PRIV(rv)->vblk_cnt && hi >= PRIV(rv)->vblk_mmio_base_hi && \ + hi <= PRIV(rv)->vblk_mmio_max_hi) { \ + PRIV(rv)->vblk_curr = \ + PRIV(rv)->vblk[hi - PRIV(rv)->vblk_mmio_base_hi]; \ + MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \ + } else { \ + switch (hi) { \ + case 0x0: \ + case 0x2: /* PLIC (0 - 0x3F) */ \ + MMIO_OP(MMIO_PLIC, MMIO_W); \ + break; \ + case 0x40: /* UART */ \ + MMIO_OP(MMIO_UART, MMIO_W); \ + break; \ + case 0x41: /* RTC */ \ + MMIO_OP(MMIO_RTC, MMIO_W); \ + break; \ + default: \ + __UNREACHABLE; \ + break; \ + } \ + } \ + } \ } while (0) void emu_update_uart_interrupts(riscv_t *rv); void emu_update_vblk_interrupts(riscv_t *rv); +void emu_update_rtc_interrupts(riscv_t *rv); /* * Linux kernel might create signal frame when returning from trap From cb8e07e33b69d47bf7198a56b382444aeb99145f Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Tue, 16 Sep 2025 00:55:00 +0800 Subject: [PATCH 2/6] Enable Goldfish RTC driver in linux config --- assets/system/configs/linux.config | 61 +++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/assets/system/configs/linux.config b/assets/system/configs/linux.config index fc3584e0e..1c6911809 100644 --- a/assets/system/configs/linux.config +++ b/assets/system/configs/linux.config @@ -1026,7 +1026,66 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_ACCESSIBILITY is not set # CONFIG_INFINIBAND is not set CONFIG_EDAC_SUPPORT=y -# CONFIG_RTC_CLASS is not set + +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_SYSTOHC is not set +# CONFIG_RTC_DEBUG is not set +# CONFIG_RTC_NVMEM is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# + +# +# SPI RTC drivers +# + +# +# SPI and I2C RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_ZYNQMP is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_CADENCE is not set +# CONFIG_RTC_DRV_FTRTC010 is not set +# CONFIG_RTC_DRV_R7301 is not set + +# +# HID Sensor RTC drivers +# +CONFIG_RTC_DRV_GOLDFISH=y # CONFIG_DMADEVICES is not set # From 1b91d35a3978bc91dab1e822177692fb807f91a1 Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Sun, 21 Sep 2025 05:20:50 +0800 Subject: [PATCH 3/6] Enable hwclock utility in buildroot config --- assets/system/configs/buildroot.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/system/configs/buildroot.config b/assets/system/configs/buildroot.config index df36a1759..f2eb4d4b7 100644 --- a/assets/system/configs/buildroot.config +++ b/assets/system/configs/buildroot.config @@ -45,3 +45,5 @@ BR2_TARGET_ROOTFS_CPIO_NONE=y BR2_PACKAGE_BUSYBOX_CONFIG="busybox.config" BR2_PACKAGE_COREMARK=y BR2_PACKAGE_DHRYSTONE=y +BR2_PACKAGE_UTIL_LINUX=y +BR2_PACKAGE_UTIL_LINUX_HWCLOCK=y From 0ae1bb2a6e3ea44f7a90fb8d2891eaa5e6b06f9c Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Sun, 21 Sep 2025 19:30:40 +0800 Subject: [PATCH 4/6] Enable package patching for Buildroot The do_buildroot process now supports patching from scratch, applying the desired packages while building rootfs.cpio with the make build-linux-image target. The desired packages should be stored in the tests/system/br_pkgs/ . The package patching from submodule might be supported in the future. --- tools/build-linux-image.sh | 115 +++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 10 deletions(-) diff --git a/tools/build-linux-image.sh b/tools/build-linux-image.sh index b6ee4e203..0ac6c340c 100755 --- a/tools/build-linux-image.sh +++ b/tools/build-linux-image.sh @@ -23,32 +23,127 @@ PARALLEL="-j$(nproc)" OUTPUT_DIR=./build/linux-image/ +BR_PKG_DIR=./tests/system/br_pkgs + +function create_br_pkg_config() +{ + local pkg_name=$1 + local output_path=$2 + + cat << EOF > "${output_path}" +config BR2_PACKAGE_${pkg_name^^} + bool "${pkg_name}" + help + ${pkg_name} package. +EOF +} + +function create_br_pkg_makefile() +{ + local pkg_name=$1 + local output_path=$2 + + cat << EOF > "${output_path}" +################################################################################ +# +# ${pkg_name} package +# +################################################################################ + +${pkg_name^^}_VERSION = 1.0 +${pkg_name^^}_SITE = package/${pkg_name}/src +${pkg_name^^}_SITE_METHOD = local + +define ${pkg_name^^}_BUILD_CMDS + \$(MAKE) CC="\$(TARGET_CC)" LD="\$(TARGET_LD)" -C \$(@D) +endef + +define ${pkg_name^^}_INSTALL_TARGET_CMDS + \$(INSTALL) -D -m 0755 \$(@D)/${pkg_name} \$(TARGET_DIR)/usr/bin +endef + +\$(eval \$(generic-package)) +EOF +} + +function create_br_pkg_src() +{ + local pkg_name=$1 + local src_c_file=$2 + local output_path=$3 + local mk_output_path=$3/Makefile + local src_output_path=$3/$pkg_name.c + + # Create src directory + mkdir -p ${output_path} + + # Create makefile + # the output binary is in lowercase + cat << EOF > "${mk_output_path}" +all: + \$(CC) ${pkg_name}.c -o ${pkg_name} +EOF + + # moving C source file + cp -f ${src_c_file} ${src_output_path} +} + +function update_br_pkg_config() +{ + local pkg_name=$1 + + cat << EOF >> "${SRC_DIR}/buildroot/package/Config.in" +menu "${pkg_name}" + source "package/${pkg_name}/Config.in" +endmenu + +EOF +} + +# This function patches the packages when building the rootfs.cpio from scratch +function do_patch_buildroot +{ + for c in $(find ${BR_PKG_DIR} -type f); do + local basename="$(basename ${c})" + local pkg_name="${basename%.*}" + + mkdir -p ${SRC_DIR}/buildroot/package/${pkg_name} + + create_br_pkg_config ${pkg_name} ${SRC_DIR}/buildroot/package/${pkg_name}/Config.in + create_br_pkg_makefile ${pkg_name} ${SRC_DIR}/buildroot/package/${pkg_name}/${pkg_name}.mk + create_br_pkg_src ${pkg_name} ${c} ${SRC_DIR}/buildroot/package/${pkg_name}/src + + update_br_pkg_config ${pkg_name} + done +} + function do_buildroot { - cp -f assets/system/configs/buildroot.config $SRC_DIR/buildroot/.config - cp -f assets/system/configs/busybox.config $SRC_DIR/buildroot/busybox.config + cp -f assets/system/configs/buildroot.config ${SRC_DIR}/buildroot/.config + cp -f assets/system/configs/busybox.config ${SRC_DIR}/buildroot/busybox.config # Otherwise, the error below raises: # You seem to have the current working directory in your # LD_LIBRARY_PATH environment variable. This doesn't work. unset LD_LIBRARY_PATH - pushd $SRC_DIR/buildroot + do_patch_buildroot + pushd ${SRC_DIR}/buildroot ASSERT make olddefconfig - ASSERT make $PARALLEL + ASSERT make ${PARALLEL} popd - cp -f $SRC_DIR/buildroot/output/images/rootfs.cpio $OUTPUT_DIR + cp -f ${SRC_DIR}/buildroot/output/images/rootfs.cpio ${OUTPUT_DIR} } function do_linux { - cp -f assets/system/configs/linux.config $SRC_DIR/linux/.config - export PATH="$SRC_DIR/buildroot/output/host/bin:$PATH" + cp -f assets/system/configs/linux.config ${SRC_DIR}/linux/.config + export PATH="${SRC_DIR}/buildroot/output/host/bin:${PATH}" export CROSS_COMPILE=riscv32-buildroot-linux-gnu- export ARCH=riscv - pushd $SRC_DIR/linux + pushd ${SRC_DIR}/linux ASSERT make olddefconfig - ASSERT make $PARALLEL + ASSERT make ${PARALLEL} popd - cp -f $SRC_DIR/linux/arch/riscv/boot/Image $OUTPUT_DIR + cp -f ${SRC_DIR}/linux/arch/riscv/boot/Image ${OUTPUT_DIR} } do_buildroot && OK From 4e7ed84ea229b03f00bfab3b14a23d3319b6087f Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Sun, 21 Sep 2025 19:32:44 +0800 Subject: [PATCH 5/6] Add userspace programs for /dev/rtc? rtc_alarm.c: The newly modified Linux configuration enables the procfs interface for the /dev/rtc? real-time clock device. This example demonstrates how to interact with the RTC device step by step. The program: 1. Opens the /dev/rtc? device. 2. Reads and displays its current status via the procfs interface. 3. Sets an alarm to trigger 5 seconds later. 4. Enables the alarm interrupt. 5. Reads the status again. 6. Waits for the alarm to trigger. 7. Finally reads the status once more and disables the alarm interrupt. During this process, will observe the alarm_IRQ field transition from no -> yes -> no, indicating that the RTC interrupt has been successfully asserted and then handled by the emulated real time clock. rtc_settime.c: A simple user-space tool to test the RTC_SET_TIME ioctl on /dev/rtc?. It sets the RTC to the current UTC system time by default or to a custom year provided via command-line arguments. This helps verify that the real time clock driver correctly handles time updates. The buildroot.config is also modified to enable them by default. The example packages are stored in the tests/system/br_pkgs/ . --- assets/system/configs/buildroot.config | 3 + tests/system/br_pkgs/rtc/rtc_alarm.c | 110 +++++++++++++++++++++++++ tests/system/br_pkgs/rtc/rtc_settime.c | 93 +++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 tests/system/br_pkgs/rtc/rtc_alarm.c create mode 100644 tests/system/br_pkgs/rtc/rtc_settime.c diff --git a/assets/system/configs/buildroot.config b/assets/system/configs/buildroot.config index f2eb4d4b7..65365a798 100644 --- a/assets/system/configs/buildroot.config +++ b/assets/system/configs/buildroot.config @@ -47,3 +47,6 @@ BR2_PACKAGE_COREMARK=y BR2_PACKAGE_DHRYSTONE=y BR2_PACKAGE_UTIL_LINUX=y BR2_PACKAGE_UTIL_LINUX_HWCLOCK=y + +BR2_PACKAGE_RTC_ALARM=y +BR2_PACKAGE_RTC_SETTIME=y diff --git a/tests/system/br_pkgs/rtc/rtc_alarm.c b/tests/system/br_pkgs/rtc/rtc_alarm.c new file mode 100644 index 000000000..92ed02080 --- /dev/null +++ b/tests/system/br_pkgs/rtc/rtc_alarm.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROC_RTC_PATH "/proc/driver/rtc" + +/* Helper function to dump /proc/driver/rtc */ +void read_proc_rtc(const char *msg) +{ + FILE *fp = fopen(PROC_RTC_PATH, "r"); + if (!fp) { + perror("Failed to open /proc/driver/rtc"); + return; + } + + printf("\n=== %s ===\n", msg); + char line[256]; + while (fgets(line, sizeof(line), fp)) { + printf("%s", line); + } + printf("===========================\n\n"); + + fclose(fp); +} + +int main() +{ + int fd; + struct rtc_time rtc_tm; + unsigned long data; + + printf("Opening /dev/rtc0...\n"); + fd = open("/dev/rtc0", O_RDONLY); + if (fd == -1) { + perror("Failed to open /dev/rtc0"); + return 1; + } + + /* Step 1. Read /proc/driver/rtc before setting alarm */ + read_proc_rtc("Initial /proc/driver/rtc"); + + /* Step 2. Read current RTC time via ioctl */ + if (ioctl(fd, RTC_RD_TIME, &rtc_tm) == -1) { + perror("RTC_RD_TIME ioctl failed"); + close(fd); + return 1; + } + + printf("Current RTC time: %04d-%02d-%02d %02d:%02d:%02d (UTC)\n", + rtc_tm.tm_year + 1900, rtc_tm.tm_mon + 1, rtc_tm.tm_mday, + rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec); + + /* Step 3. Set alarm 5 seconds from now */ + int delay = 5; + rtc_tm.tm_sec += delay; + if (rtc_tm.tm_sec >= 60) { + rtc_tm.tm_sec -= 60; + rtc_tm.tm_min++; + if (rtc_tm.tm_min >= 60) { + rtc_tm.tm_min = 0; + rtc_tm.tm_hour = (rtc_tm.tm_hour + 1) % 24; + } + } + + printf("Setting alarm for %d seconds later...\n", delay); + if (ioctl(fd, RTC_ALM_SET, &rtc_tm) == -1) { + perror("RTC_ALM_SET ioctl failed"); + close(fd); + return 1; + } + + /* Step 4. Enable alarm interrupt */ + if (ioctl(fd, RTC_AIE_ON, 0) == -1) { + perror("RTC_AIE_ON ioctl failed"); + close(fd); + return 1; + } + + /* Step 5. Read /proc/driver/rtc right after enabling alarm */ + read_proc_rtc("After enabling alarm"); + + printf("Alarm enabled. Waiting for it to fire...\n"); + + /* Step 6. Block until the alarm interrupt occurs */ + if (read(fd, &data, sizeof(unsigned long)) == -1) { + perror("read() failed"); + close(fd); + return 1; + } + + printf(">>> Alarm Fired! <<<\n"); + + /* Step 7. Read /proc/driver/rtc after alarm fired */ + read_proc_rtc("After alarm fired"); + + /* Step 8. Disable the alarm interrupt */ + if (ioctl(fd, RTC_AIE_OFF, 0) == -1) { + perror("RTC_AIE_OFF ioctl failed"); + close(fd); + return 1; + } + + close(fd); + return 0; +} diff --git a/tests/system/br_pkgs/rtc/rtc_settime.c b/tests/system/br_pkgs/rtc/rtc_settime.c new file mode 100644 index 000000000..06ea4deda --- /dev/null +++ b/tests/system/br_pkgs/rtc/rtc_settime.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd; + struct rtc_time new_time; + int year = 0; + + /* TODO: support month and date */ + /* Handle CLI argument */ + if (argc == 2) { + year = atoi(argv[1]); + } else if (argc > 2) { + fprintf(stderr, "Usage: %s [year]\n", argv[0]); + fprintf(stderr, "Example: %s 1972\n", argv[0]); + return 1; + } + + printf("Opening /dev/rtc0...\n"); + fd = open("/dev/rtc0", O_RDWR); + if (fd == -1) { + perror("Failed to open /dev/rtc0"); + return 1; + } + + if (year > 0) { + /* Manually set RTC to -01-01 00:00:00 */ + new_time.tm_sec = 0; + new_time.tm_min = 0; + new_time.tm_hour = 0; + new_time.tm_mday = 1; + new_time.tm_mon = 0; /* January = 0 */ + new_time.tm_year = year - 1900; /* tm_year is years since 1900 */ + new_time.tm_wday = 0; + new_time.tm_yday = 0; + new_time.tm_isdst = 0; + + printf("Setting RTC time to: %04d-01-01 00:00:00 (UTC)\n", year); + } else { + /* No year provided, set RTC to current UTC time */ + time_t now = time(NULL); + struct tm *utc_tm = gmtime(&now); + + new_time.tm_sec = utc_tm->tm_sec; + new_time.tm_min = utc_tm->tm_min; + new_time.tm_hour = utc_tm->tm_hour; + new_time.tm_mday = utc_tm->tm_mday; + new_time.tm_mon = utc_tm->tm_mon; /* 0-11 */ + new_time.tm_year = utc_tm->tm_year; /* years since 1900 */ + new_time.tm_wday = utc_tm->tm_wday; + new_time.tm_yday = utc_tm->tm_yday; + new_time.tm_isdst = 0; + + printf( + "Setting RTC time to current UTC: %04d-%02d-%02d %02d:%02d:%02d\n", + new_time.tm_year + 1900, new_time.tm_mon + 1, new_time.tm_mday, + new_time.tm_hour, new_time.tm_min, new_time.tm_sec); + } + + /* Trigger the goldfish_rtc_set_time kernel driver */ + if (ioctl(fd, RTC_SET_TIME, &new_time) == -1) { + perror("RTC_SET_TIME ioctl failed"); + close(fd); + return 1; + } + + printf("RTC time successfully updated!\n\n"); + close(fd); + + /* Immediately read and print /proc/driver/rtc */ + printf("Reading /proc/driver/rtc to verify...\n\n"); + FILE *proc_file = fopen("/proc/driver/rtc", "r"); + if (!proc_file) { + perror("Failed to open /proc/driver/rtc"); + return 1; + } + + char buffer[256]; + while (fgets(buffer, sizeof(buffer), proc_file)) { + printf("%s", buffer); + } + + fclose(proc_file); + return 0; +} From 5ee534aa40dbeb4dc1cc1c0e3cf20582ae016226 Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Sun, 21 Sep 2025 19:33:53 +0800 Subject: [PATCH 6/6] CI: Add RTC test coverage This change adds CI tests to verify RTC functionality, covering the following cases: 1. Verify that /dev/rtc0 is present and logged in dmesg. 2. Ensure /dev/rtc0 is interactable: - Use rtc_settime to set the year to both before(1980) and after(2030) the current year, then verify that the RTC retains the changes. - Use rtc_alarm to confirm that the alarm_IRQ status transitions correctly: no -> yes -> no. 3. Validate alignment of the UTC year between host and guest using the date command. --- .ci/boot-linux.sh | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/.ci/boot-linux.sh b/.ci/boot-linux.sh index 9a91abd95..1a88f62fc 100755 --- a/.ci/boot-linux.sh +++ b/.ci/boot-linux.sh @@ -22,6 +22,9 @@ function ASSERT cleanup +# To test RTC clock +HOST_UTC_YEAR=$(LC_ALL=C date -u +%Y) + ENABLE_VBLK=1 VBLK_IMG=build/disk.img [ -f "${VBLK_IMG}" ] || ENABLE_VBLK=0 @@ -33,10 +36,93 @@ OPTS_BASE+=" -i build/linux-image/rootfs.cpio" TEST_OPTIONS=("base (${OPTS_BASE})") EXPECT_CMDS=(' expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } expect "# " { send "uname -a\n" } timeout { exit 2 } expect "riscv32 GNU/Linux" { send "\x01"; send "x" } timeout { exit 3 } ') +# RTC alarm and settime tests +TEST_OPTIONS+=("${OPTS_BASE}") +EXPECT_CMDS+=(' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { } timeout { exit 3 } + expect "# " { send "rtc_alarm\n" } timeout { exit 3 } + expect "alarm_IRQ : no" { } timeout { exit 3 } + expect "alarm_IRQ : yes" { } timeout { exit 3 } + expect "alarm_IRQ : no" { } timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } +') +year1=1980 +year2=2030 +TEST_OPTIONS+=("${OPTS_BASE}") +EXPECT_CMDS+=(' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { } timeout { exit 3 } + expect "# " { send "rtc_settime ${year1}\n" } timeout { exit 3 } + expect "rtc_date : ${year1}-01-01" { } timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } +') +TEST_OPTIONS+=("${OPTS_BASE}") +EXPECT_CMDS+=(' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { } timeout { exit 3 } + expect "# " { send "rtc_settime ${year2}\n" } timeout { exit 3 } + expect "rtc_date : ${year2}-01-01" { } timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } +') +TEST_OPTIONS+=("${OPTS_BASE}") +EXPECT_CMDS+=(' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { } timeout { exit 3 } + expect "# " { send "rtc_settime ${year1}\n" } timeout { exit 3 } + expect "rtc_date : ${year1}-01-01" { } timeout { exit 3 } + expect "# " { send "rtc_alarm\n" } timeout { exit 3 } + expect "alarm_IRQ : no" { } timeout { exit 3 } + expect "alarm_IRQ : yes" { } timeout { exit 3 } + expect "alarm_IRQ : no" { } timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } +') +TEST_OPTIONS+=("${OPTS_BASE}") +EXPECT_CMDS+=(' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { } timeout { exit 3 } + expect "# " { send "rtc_settime ${year2}\n" } timeout { exit 3 } + expect "rtc_date : ${year2}-01-01" { } timeout { exit 3 } + expect "# " { send "rtc_alarm\n" } timeout { exit 3 } + expect "alarm_IRQ : no" { } timeout { exit 3 } + expect "alarm_IRQ : yes" { } timeout { exit 3 } + expect "alarm_IRQ : no" { } timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } +') + COLOR_G='\e[32;01m' # Green COLOR_R='\e[31;01m' # Red COLOR_Y='\e[33;01m' # Yellow @@ -54,6 +140,10 @@ if [ "${ENABLE_VBLK}" -eq "1" ]; then TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG},readonly") EXPECT_CMDS+=(' expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } expect "# " { send "uname -a\n" } timeout { exit 2 } expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 } expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 } @@ -65,6 +155,10 @@ if [ "${ENABLE_VBLK}" -eq "1" ]; then TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG},readonly -x vblk:${BLK_DEV},readonly") EXPECT_CMDS+=(' expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } expect "# " { send "uname -a\n" } timeout { exit 2 } expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 } expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 } @@ -79,6 +173,10 @@ if [ "${ENABLE_VBLK}" -eq "1" ]; then TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG}") VBLK_EXPECT_CMDS=' expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } expect "# " { send "uname -a\n" } timeout { exit 2 } expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 } expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 } @@ -96,6 +194,10 @@ if [ "${ENABLE_VBLK}" -eq "1" ]; then TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG} -x vblk:${BLK_DEV}") VBLK_EXPECT_CMDS=' expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "dmesg | grep rtc\n" } timeout { exit 2 } + expect "rtc0" { } timeout { exit 3 } + expect "# " { send "date -u +%Y\n" } timeout { exit 2 } + expect "${HOST_UTC_YEAR}" { } timeout { exit 3 } expect "# " { send "uname -a\n" } timeout { exit 2 } expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 } expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 } @@ -121,6 +223,9 @@ for i in "${!TEST_OPTIONS[@]}"; do RUN_LINUX="build/rv32emu ${OPTS}" ASSERT expect <<- DONE + set HOST_UTC_YEAR ${HOST_UTC_YEAR} + set year1 ${year1} + set year2 ${year2} set timeout ${TIMEOUT} spawn ${RUN_LINUX} ${EXPECT_CMDS[$i]}