Add FAT file timestamps to USB report saving (RTC on x86, build date elsewhere)

This commit is contained in:
Sam Demeulemeester
2026-07-27 16:47:14 +02:00
parent fca063f6d1
commit 814ee02aea
8 changed files with 174 additions and 70 deletions
+11 -70
View File
@@ -3,11 +3,11 @@
#include "cpuinfo.h"
#include "tsc.h"
#include "io.h"
#include "heap.h"
#include "hwctrl.h"
#include "memctrl.h"
#include "pmem.h"
#include "rtc.h"
#include "serial.h"
#include "smbios.h"
#include "smp.h"
@@ -59,63 +59,6 @@
// Private Functions
//------------------------------------------------------------------------------
// RTC (CMOS) register reading. Ports 0x70/0x71 BCD format.
// ISA-only; LoongArch has no equivalent fixed-port RTC.
#if defined(__i386__) || defined(__x86_64__)
static uint8_t rtc_read(uint8_t reg)
{
outb(reg, 0x70);
return inb(0x71);
}
static uint8_t bcd_to_bin(uint8_t bcd)
{
return (bcd >> 4) * 10 + (bcd & 0x0F);
}
static void rtc_get_datetime(int *year, int *mon, int *day, int *hour, int *min, int *sec)
{
// Wait for any update in progress to complete (UIP bit, status register A).
for (int i = 0; i < 20000 && (rtc_read(0x0A) & 0x80); i++) {}
uint8_t rtc_stb = rtc_read(0x0B);
uint8_t rtc_sec = rtc_read(0x00);
uint8_t rtc_min = rtc_read(0x02);
uint8_t rtc_hour = rtc_read(0x04);
uint8_t rtc_day = rtc_read(0x07);
uint8_t rtc_mon = rtc_read(0x08);
uint8_t rtc_year = rtc_read(0x09);
uint8_t rtc_cent = rtc_read(0x32);
bool rtc_pm = rtc_hour & 0x80;
rtc_hour &= 0x7F;
// Registers are BCD unless the RTC is in binary mode (status register B, DM bit).
if (!(rtc_stb & 0x04)) {
rtc_sec = bcd_to_bin(rtc_sec);
rtc_min = bcd_to_bin(rtc_min);
rtc_hour = bcd_to_bin(rtc_hour);
rtc_day = bcd_to_bin(rtc_day);
rtc_mon = bcd_to_bin(rtc_mon);
rtc_year = bcd_to_bin(rtc_year);
rtc_cent = bcd_to_bin(rtc_cent);
}
// Convert 12-hour mode (hour bit 7 = PM) to 24-hour.
if (!(rtc_stb & 0x02)) {
rtc_hour = rtc_hour % 12 + (rtc_pm ? 12 : 0);
}
*year = (rtc_cent ? rtc_cent * 100 : 2000) + rtc_year;
*mon = rtc_mon;
*day = rtc_day;
*hour = rtc_hour;
*min = rtc_min;
*sec = rtc_sec;
}
#endif
// Minimal buffer printf supporting %s, %i, %u, %x with field width.
// %u and %x consume a uintptr_t argument - cast at every call site: on x86_64
// the upper half of a default-promoted 32-bit vararg register is undefined.
@@ -249,13 +192,12 @@ static int format_results(char *buf, int bufsize)
pos = buf_printf(pos, "=======================\r\n\r\n");
// Date & time from RTC (x86 ISA CMOS only).
#if defined(__i386__) || defined(__x86_64__)
int year, mon, day, hour, min, sec;
rtc_get_datetime(&year, &mon, &day, &hour, &min, &sec);
pos = buf_printf(pos, "Date: %04i-%02i-%02i %02i:%02i:%02i\r\n",
year, mon, day, hour, min, sec);
pos = buf_printf(pos, "\r\n");
#endif
rtc_time_t now;
if (rtc_get_time(&now)) {
pos = buf_printf(pos, "Date: %04i-%02i-%02i %02i:%02i:%02i\r\n",
now.year, now.month, now.day, now.hour, now.min, now.sec);
pos = buf_printf(pos, "\r\n");
}
// System info.
if (cpu_model) {
@@ -570,11 +512,10 @@ void serial_log_run_start(void)
{
if (!enable_tty_log) return;
#if defined(__i386__) || defined(__x86_64__)
int year, mon, day, hour, min, sec;
rtc_get_datetime(&year, &mon, &day, &hour, &min, &sec);
slog("info", " rtc=\"%04i-%02i-%02i %02i:%02i:%02i\"", year, mon, day, hour, min, sec);
#endif
rtc_time_t now;
if (rtc_get_time(&now)) {
slog("info", " rtc=\"%04i-%02i-%02i %02i:%02i:%02i\"", now.year, now.month, now.day, now.hour, now.min, now.sec);
}
if (cpu_model) {
slog("info", " cpu=\"%s\"", cpu_model);
+1
View File
@@ -47,6 +47,7 @@ SYS_OBJS = system/acpi.o \
system/ohci.o \
system/pmem.o \
system/reloc.o \
system/rtc.o \
system/screen.o \
system/serial.o \
system/smbios.o \
+1
View File
@@ -40,6 +40,7 @@ SYS_OBJS = system/acpi.o \
system/pci.o \
system/pmem.o \
system/reloc.o \
system/rtc.o \
system/screen.o \
system/serial.o \
system/smbios.o \
+1
View File
@@ -38,6 +38,7 @@ SYS_OBJS = system/acpi.o \
system/pci_mmio.o \
system/pmem.o \
system/reloc.o \
system/rtc.o \
system/screen.o \
system/serial.o \
system/smbios.o \
+1
View File
@@ -39,6 +39,7 @@ SYS_OBJS = system/acpi.o \
system/pci.o \
system/pmem.o \
system/reloc.o \
system/rtc.o \
system/screen.o \
system/serial.o \
system/smbios.o \
+19
View File
@@ -4,6 +4,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "rtc.h"
#include "string.h"
#include "fat32.h"
@@ -508,6 +510,23 @@ bool fat32_write_file(fat32_fs_t *fs, const char *name_8_3, const void *data, ui
// Attributes.
entry[11] = DIR_ATTR_ARCHIVE;
// Creation/access/write timestamps. DIR_WrtDate is mandatory and 0 encodes an
// invalid date (day/month 0), so fall back to the build date when there is no RTC.
rtc_time_t now;
(void)rtc_get_time(&now);
uint16_t fat_time = now.hour << 11 | now.min << 5 | now.sec / 2;
uint16_t fat_date = (now.year - 1980) << 9 | now.month << 5 | now.day;
entry[14] = fat_time & 0xFF; // creation time (bytes 14-15)
entry[15] = (fat_time >> 8) & 0xFF;
entry[16] = fat_date & 0xFF; // creation date (bytes 16-17)
entry[17] = (fat_date >> 8) & 0xFF;
entry[18] = fat_date & 0xFF; // last access date (bytes 18-19)
entry[19] = (fat_date >> 8) & 0xFF;
entry[22] = fat_time & 0xFF; // last write time (bytes 22-23)
entry[23] = (fat_time >> 8) & 0xFF;
entry[24] = fat_date & 0xFF; // last write date (bytes 24-25)
entry[25] = (fat_date >> 8) & 0xFF;
// First cluster high word (bytes 20-21).
entry[20] = (first_cluster >> 16) & 0xFF;
entry[21] = (first_cluster >> 24) & 0xFF;
+105
View File
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2026 Sam Demeulemeester.
#include <stdbool.h>
#include <stdint.h>
#include "io.h"
#include "string.h"
#include "rtc.h"
//------------------------------------------------------------------------------
// Private Functions
//------------------------------------------------------------------------------
// RTC (CMOS) register reading. Ports 0x70/0x71 BCD format.
// ISA-only; LoongArch has no equivalent fixed-port RTC.
#if defined(__i386__) || defined(__x86_64__)
static uint8_t rtc_read(uint8_t reg)
{
outb(reg, 0x70);
return inb(0x71);
}
static uint8_t bcd_to_bin(uint8_t bcd)
{
return (bcd >> 4) * 10 + (bcd & 0x0F);
}
#endif
//------------------------------------------------------------------------------
// Public Functions
//------------------------------------------------------------------------------
bool rtc_get_time(rtc_time_t *dt)
{
#if defined(__i386__) || defined(__x86_64__)
// Wait for any update in progress to complete (UIP bit, status register A).
for (int i = 0; i < 20000 && (rtc_read(0x0A) & 0x80); i++) {}
uint8_t rtc_stb = rtc_read(0x0B);
uint8_t rtc_sec = rtc_read(0x00);
uint8_t rtc_min = rtc_read(0x02);
uint8_t rtc_hour = rtc_read(0x04);
uint8_t rtc_day = rtc_read(0x07);
uint8_t rtc_mon = rtc_read(0x08);
uint8_t rtc_year = rtc_read(0x09);
uint8_t rtc_cent = rtc_read(0x32);
bool rtc_pm = rtc_hour & 0x80;
rtc_hour &= 0x7F;
// Registers are BCD unless the RTC is in binary mode (status register B, DM bit).
if (!(rtc_stb & 0x04)) {
rtc_sec = bcd_to_bin(rtc_sec);
rtc_min = bcd_to_bin(rtc_min);
rtc_hour = bcd_to_bin(rtc_hour);
rtc_day = bcd_to_bin(rtc_day);
rtc_mon = bcd_to_bin(rtc_mon);
rtc_year = bcd_to_bin(rtc_year);
rtc_cent = bcd_to_bin(rtc_cent);
}
// Convert 12-hour mode (hour bit 7 = PM) to 24-hour.
if (!(rtc_stb & 0x02)) {
rtc_hour = rtc_hour % 12 + (rtc_pm ? 12 : 0);
}
dt->year = (rtc_cent ? rtc_cent * 100 : 2000) + rtc_year;
dt->month = rtc_mon;
dt->day = rtc_day;
dt->hour = rtc_hour;
dt->min = rtc_min;
dt->sec = rtc_sec;
// Reject out-of-range values (ie: dead CMOS battery) and use the build date instead.
if (dt->month >= 1 && dt->month <= 12 && dt->day >= 1 && dt->day <= 31
&& dt->year >= 1980 && dt->year <= 2107
&& dt->hour <= 23 && dt->min <= 59 && dt->sec <= 59) {
return true;
}
#endif
// Build date & time of the binary: __DATE__ is "Mmm dd yyyy", __TIME__ is "hh:mm:ss".
static const char bdate[] = __DATE__;
static const char btime[] = __TIME__;
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
dt->month = 1;
for (int i = 0; i < 12; i++) {
if (memcmp(bdate, &months[i * 3], 3) == 0) {
dt->month = i + 1;
break;
}
}
dt->day = (bdate[4] == ' ' ? 0 : (bdate[4] - '0') * 10) + bdate[5] - '0';
dt->year = (bdate[7] - '0') * 1000 + (bdate[8] - '0') * 100 + (bdate[9] - '0') * 10 + bdate[10] - '0';
dt->hour = (btime[0] - '0') * 10 + btime[1] - '0';
dt->min = (btime[3] - '0') * 10 + btime[4] - '0';
dt->sec = (btime[6] - '0') * 10 + btime[7] - '0';
return false;
}
+35
View File
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef RTC_H
#define RTC_H
/**
* \file
*
* Provides the current date & time, from the CMOS RTC where available,
* falling back to the build date & time of the binary.
*
*//*
* Copyright (C) 2026 Sam Demeulemeester.
*/
#include <stdbool.h>
/**
* A calendar date & time.
*/
typedef struct {
int year; // 4-digit year
int month; // 1 - 12
int day; // 1 - 31
int hour; // 0 - 23
int min; // 0 - 59
int sec; // 0 - 59
} rtc_time_t;
/**
* Fills dt with the current RTC date & time on x86, or with the binary's
* build date & time elsewhere (and when the RTC returns out-of-range
* values, e.g. dead CMOS battery). Returns true only for live RTC data.
*/
bool rtc_get_time(rtc_time_t *dt);
#endif // RTC_H