mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2026-08-26 13:17:12 -05:00
Add FAT file timestamps to USB report saving (RTC on x86, build date elsewhere)
This commit is contained in:
+11
-70
@@ -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);
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user