Implement RTC support
Some checks failed
Builds / ExectOS (amd64, release) (push) Failing after -59m35s
Builds / ExectOS (amd64, debug) (push) Failing after -59m33s
Builds / ExectOS (i686, debug) (push) Failing after -59m33s
Builds / ExectOS (i686, release) (push) Failing after -59m35s

This commit is contained in:
2026-04-24 09:52:07 +02:00
parent b1e849a251
commit 2dd1fdf869
10 changed files with 466 additions and 8 deletions

View File

@@ -54,10 +54,6 @@
/* Maximum number of I/O APICs */
#define APIC_MAX_IOAPICS 64
/* CMOS controller I/O ports */
#define CMOS_ADDRESS_PORT 0x70
#define CMOS_DATA_PORT 0x71
/* I/O APIC base address */
#define IOAPIC_DEFAULT_BASE 0xFEC00000
@@ -94,6 +90,40 @@
#define PIT_DATA_PORT1 0x41
#define PIT_DATA_PORT2 0x42
/* CMOS controller access ports */
#define CMOS_SELECT_PORT 0x70
#define CMOS_DATA_PORT 0x71
/* CMOD Select port definitions */
#define CMOS_NMI_SELECT 0x80
#define CMOS_REGISTER_SECOND 0x00
#define CMOS_REGISTER_MINUTE 0x02
#define CMOS_REGISTER_HOUR 0x04
#define CMOS_REGISTER_WEEKDAY 0x06
#define CMOS_REGISTER_DAY 0x07
#define CMOS_REGISTER_MONTH 0x08
#define CMOS_REGISTER_YEAR 0x09
#define CMOS_REGISTER_A 0x0A
#define CMOS_REGISTER_B 0x0B
#define CMOS_REGISTER_C 0x0C
/* CMOS Register A definitions */
#define CMOS_REGISTER_A_RATE_MASK 0x0F
#define CMOS_REGISTER_A_UPDATE_IN_PROGRESS 0x80
/* CMOS Register B definitions */
#define CMOS_REGISTER_B_24_HOUR 0x02
#define CMOS_REGISTER_B_BINARY 0x04
#define CMOS_REGISTER_B_PERIODIC 0x40
#define CMOS_REGISTER_B_SET_CLOCK 0x80
/* CMOS Register C definitions */
#define CMOS_REGISTER_C_PERIODIC 0x40
#define CMOS_REGISTER_C_INTERRUPT 0x80
/* CMOS RTC 24-hour mode */
#define CMOS_RTC_POST_MERIDIEM 0x80
/* Serial ports information */
#define COMPORT_ADDRESS {0x3F8, 0x2F8, 0x3E8, 0x2E8, 0x5F8, 0x4F8, 0x5E8, 0x4E8}
#define COMPORT_COUNT 8

View File

@@ -60,10 +60,6 @@
/* Maximum number of I/O APICs */
#define APIC_MAX_IOAPICS 64
/* CMOS controller I/O ports */
#define CMOS_ADDRESS_PORT 0x70
#define CMOS_DATA_PORT 0x71
/* I/O APIC base address */
#define IOAPIC_DEFAULT_BASE 0xFEC00000
@@ -99,6 +95,42 @@
/* PIT ports definitions */
#define PIT_COMMAND_PORT 0x43
#define PIT_DATA_PORT0 0x40
#define PIT_DATA_PORT1 0x41
#define PIT_DATA_PORT2 0x42
/* CMOS controller access ports */
#define CMOS_SELECT_PORT 0x70
#define CMOS_DATA_PORT 0x71
/* CMOD Select port definitions */
#define CMOS_NMI_SELECT 0x80
#define CMOS_REGISTER_SECOND 0x00
#define CMOS_REGISTER_MINUTE 0x02
#define CMOS_REGISTER_HOUR 0x04
#define CMOS_REGISTER_WEEKDAY 0x06
#define CMOS_REGISTER_DAY 0x07
#define CMOS_REGISTER_MONTH 0x08
#define CMOS_REGISTER_YEAR 0x09
#define CMOS_REGISTER_A 0x0A
#define CMOS_REGISTER_B 0x0B
#define CMOS_REGISTER_C 0x0C
/* CMOS Register A definitions */
#define CMOS_REGISTER_A_RATE_MASK 0x0F
#define CMOS_REGISTER_A_UPDATE_IN_PROGRESS 0x80
/* CMOS Register B definitions */
#define CMOS_REGISTER_B_24_HOUR 0x02
#define CMOS_REGISTER_B_BINARY 0x04
#define CMOS_REGISTER_B_PERIODIC 0x40
#define CMOS_REGISTER_B_SET_CLOCK 0x80
/* CMOS Register C definitions */
#define CMOS_REGISTER_C_PERIODIC 0x40
#define CMOS_REGISTER_C_INTERRUPT 0x80
/* CMOS RTC 24-hour mode */
#define CMOS_RTC_POST_MERIDIEM 0x80
/* Serial ports information */
#define COMPORT_ADDRESS {0x3F8, 0x2F8, 0x3E8, 0x2E8, 0x5F8, 0x4F8, 0x5E8, 0x4E8}

View File

@@ -111,5 +111,18 @@ typedef struct _RTL_SHA1_CONTEXT
UCHAR Buffer[SHA1_BLOCK_SIZE];
} RTL_SHA1_CONTEXT, *PRTL_SHA1_CONTEXT;
/* Runtime time fields structure definition */
typedef struct _TIME_FIELDS
{
SHORT Year;
SHORT Month;
SHORT Day;
SHORT Hour;
SHORT Minute;
SHORT Second;
SHORT Milliseconds;
SHORT Weekday;
} TIME_FIELDS, *PTIME_FIELDS;
#endif /* __XTOS_ASSEMBLER__ */
#endif /* __XTDK_RTLTYPES_H */

View File

@@ -335,6 +335,7 @@ typedef struct _STRING STRING, *PSTRING;
typedef struct _STRING32 STRING32, *PSTRING32;
typedef struct _STRING64 STRING64, *PSTRING64;
typedef struct _THREAD_INFORMATION_BLOCK THREAD_INFORMATION_BLOCK, *PTHREAD_INFORMATION_BLOCK;
typedef struct _TIME_FIELDS TIME_FIELDS, *PTIME_FIELDS;
typedef struct _UEFI_FIRMWARE_INFORMATION UEFI_FIRMWARE_INFORMATION, *PUEFI_FIRMWARE_INFORMATION;
typedef struct _UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
typedef struct _UNICODE_STRING32 UNICODE_STRING32, *PUNICODE_STRING32;

View File

@@ -21,6 +21,7 @@ list(APPEND XTOSKRNL_SOURCE
${XTOSKRNL_SOURCE_DIR}/hl/${ARCH}/ioport.cc
${XTOSKRNL_SOURCE_DIR}/hl/${ARCH}/irq.cc
${XTOSKRNL_SOURCE_DIR}/hl/${ARCH}/pic.cc
${XTOSKRNL_SOURCE_DIR}/hl/${ARCH}/rtc.cc
${XTOSKRNL_SOURCE_DIR}/hl/${ARCH}/runlevel.cc
${XTOSKRNL_SOURCE_DIR}/hl/${ARCH}/timer.cc
${XTOSKRNL_SOURCE_DIR}/hl/acpi.cc

13
xtoskrnl/hl/amd64/rtc.cc Normal file
View File

@@ -0,0 +1,13 @@
/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/hl/amd64/rtc.cc
* DESCRIPTION: Hardware Real-Time Clock (RTC) support
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/* Include common RTC interface */
#include ARCH_COMMON(rtc.cc)

13
xtoskrnl/hl/i686/rtc.cc Normal file
View File

@@ -0,0 +1,13 @@
/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/hl/i686/rtc.cc
* DESCRIPTION: Hardware Real-Time Clock (RTC) support
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/* Include common RTC interface */
#include ARCH_COMMON(rtc.cc)

328
xtoskrnl/hl/x86/rtc.cc Normal file
View File

@@ -0,0 +1,328 @@
/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/hl/x86/rtc.cc
* DESCRIPTION: Hardware Real-Time Clock (RTC) support
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#include <xtos.hh>
/**
* Queries the hardware Real-Time Clock (RTC) for the current date and time.
*
* @param Time
* Supplies a pointer to a structure to receive the system time.
*
* @return This routine returns the status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
HL::Rtc::GetRealTimeClock(OUT PTIME_FIELDS Time)
{
UCHAR Century1, Century2, CenturyRegister, RegisterB;
TIME_FIELDS TimeProbe1, TimeProbe2;
PACPI_FADT FadtTable;
BOOLEAN PostMeridiem;
XTSTATUS Status;
ULONG Index;
/* Locate the ACPI FADT table */
Status = HL::Acpi::GetAcpiTable(ACPI_FADT_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&FadtTable);
if(Status == STATUS_SUCCESS && FadtTable && FadtTable->CenturyAlarmIndex)
{
/* Cache the dynamically provided Century register index */
CenturyRegister = FadtTable->CenturyAlarmIndex;
}
else
{
/* Century register is unavailable */
CenturyRegister = 0;
Century1 = 0;
Century2 = 0;
}
/* Read the RTC Status Register B to determine hardware data formats */
RegisterB = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_B);
/* Assume failure */
Status = STATUS_UNSUCCESSFUL;
/* Execute a maximum of 100 retries to obtain a stable RTC snapshot */
for(Index = 0; Index < 100; Index++)
{
/* Wait until the RTC hardware finishes any ongoing background updates */
while(HL::Firmware::ReadCmosRegister(CMOS_REGISTER_A) & CMOS_REGISTER_A_UPDATE_IN_PROGRESS)
{
/* Yield the processor */
AR::CpuFunc::YieldProcessor();
}
/* Latch the first sequential hardware time snapshot */
TimeProbe1.Hour = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_HOUR);
TimeProbe1.Minute = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_MINUTE);
TimeProbe1.Second = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_SECOND);
TimeProbe1.Day = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_DAY);
TimeProbe1.Month = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_MONTH);
TimeProbe1.Year = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_YEAR);
TimeProbe1.Weekday = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_WEEKDAY);
/* Check if Century register is available */
if(CenturyRegister)
{
/* Read the corresponding Century register */
Century1 = HL::Firmware::ReadCmosRegister(CenturyRegister);
}
/* Wait until the RTC hardware finishes any ongoing background updates */
while(HL::Firmware::ReadCmosRegister(CMOS_REGISTER_A) & CMOS_REGISTER_A_UPDATE_IN_PROGRESS)
{
/* Yield the processor */
AR::CpuFunc::YieldProcessor();
}
/* Latch the second sequential hardware time snapshot for verification */
TimeProbe2.Hour = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_HOUR);
TimeProbe2.Minute = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_MINUTE);
TimeProbe2.Second = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_SECOND);
TimeProbe2.Day = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_DAY);
TimeProbe2.Month = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_MONTH);
TimeProbe2.Year = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_YEAR);
TimeProbe2.Weekday = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_WEEKDAY);
/* Check if Century register is available */
if(CenturyRegister)
{
/* Read the corresponding Century register */
Century2 = HL::Firmware::ReadCmosRegister(CenturyRegister);
}
/* Compare both snapshots to guarantee data consistency */
if((TimeProbe1.Hour == TimeProbe2.Hour) &&
(TimeProbe1.Minute == TimeProbe2.Minute) &&
(TimeProbe1.Second == TimeProbe2.Second) &&
(TimeProbe1.Day == TimeProbe2.Day) &&
(TimeProbe1.Month == TimeProbe2.Month) &&
(TimeProbe1.Year == TimeProbe2.Year) &&
(TimeProbe1.Weekday == TimeProbe2.Weekday) &&
(Century1 == Century2))
{
/* A stable time sample was acquired, break the loop */
Status = STATUS_SUCCESS;
break;
}
}
/* Copy the validated data into the output buffer */
Time->Hour = TimeProbe1.Hour;
Time->Minute = TimeProbe1.Minute;
Time->Second = TimeProbe1.Second;
Time->Milliseconds = 0;
Time->Day = TimeProbe1.Day;
Time->Month = TimeProbe1.Month;
Time->Year = TimeProbe1.Year;
Time->Weekday = TimeProbe1.Weekday;
/* Check if RTC is operating in 12-hour mode */
if(!(RegisterB & CMOS_REGISTER_B_24_HOUR))
{
/* Cache the PM status and strip the hardware flag */
PostMeridiem = (Time->Hour & CMOS_RTC_POST_MERIDIEM) != 0;
Time->Hour &= ~CMOS_RTC_POST_MERIDIEM;
}
/* Convert Binary-Coded Decimal (BCD) values to standard integers if necessary */
if(!(RegisterB & CMOS_REGISTER_B_BINARY))
{
/* Decode all standard time fields */
Time->Hour = BCD_TO_DECIMAL(Time->Hour);
Time->Minute = BCD_TO_DECIMAL(Time->Minute);
Time->Second = BCD_TO_DECIMAL(Time->Second);
Time->Day = BCD_TO_DECIMAL(Time->Day);
Time->Month = BCD_TO_DECIMAL(Time->Month);
Time->Year = BCD_TO_DECIMAL(Time->Year);
Time->Weekday = BCD_TO_DECIMAL(Time->Weekday);
/* Check if Century byte is available */
if(CenturyRegister)
{
/* Convert Century byte */
Century1 = BCD_TO_DECIMAL(Century1);
}
}
/* Standardize hours into a strict 24-hour format */
if(!(RegisterB & CMOS_REGISTER_B_24_HOUR))
{
/* Adjust for midnight and noon boundary cases */
if(Time->Hour == 12)
{
/* 12 AM evaluates to 00:00, 12 PM evaluates to 12:00 */
Time->Hour = PostMeridiem ? 12 : 0;
}
else
{
/* Add 12 hours for PM times */
Time->Hour += PostMeridiem ? 12 : 0;
}
}
/* Merge the century offset with the 2-digit hardware year */
if(Century1 >= 19 && Century1 <= 30)
{
/* Utilize the hardware-provided century base */
Time->Year += (Century1 * 100);
}
else
{
/* Century byte is invalid; apply the sliding window */
Time->Year += (Time->Year > 80) ? 1900 : 2000;
}
/* Return status code */
return Status;
}
/**
* Updates the hardware Real-Time Clock (RTC) with the provided date and time.
*
* @param Time
* Supplies a pointer to a structure with populated data and time.
*
* @return This routine returns the status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
HL::Rtc::SetRealTimeClock(IN PTIME_FIELDS Time)
{
UCHAR Century, CenturyRegister, RegisterB;
TIME_FIELDS SystemTime;
BOOLEAN PostMeridiem;
PACPI_FADT FadtTable;
XTSTATUS Status;
/* Validate the input time boundaries against calendar limits */
if(Time->Hour > 23 || Time->Minute > 59 || Time->Second > 59 ||
Time->Day == 0 || Time->Day > 31 || Time->Month == 0 ||
Time->Month > 12 || Time->Weekday == 0 || Time->Weekday > 7)
{
/* Invalid time parameters, return error code */
return STATUS_INVALID_PARAMETER;
}
/* Assume Ante Meridiem */
PostMeridiem = FALSE;
/* Extract local copy */
SystemTime.Hour = Time->Hour;
SystemTime.Minute = Time->Minute;
SystemTime.Second = Time->Second;
SystemTime.Day = Time->Day;
SystemTime.Month = Time->Month;
SystemTime.Year = (Time->Year % 100);
SystemTime.Weekday = Time->Weekday;
Century = (UCHAR)(Time->Year / 100);
/* Locate the ACPI FADT table */
Status = HL::Acpi::GetAcpiTable(ACPI_FADT_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&FadtTable);
if(Status == STATUS_SUCCESS && FadtTable && FadtTable->CenturyAlarmIndex)
{
/* Cache the dynamically provided Century register index */
CenturyRegister = FadtTable->CenturyAlarmIndex;
}
else
{
/* Century register is unavailable */
CenturyRegister = 0;
}
/* Read the RTC Status Register B to determine hardware data formats */
RegisterB = HL::Firmware::ReadCmosRegister(CMOS_REGISTER_B);
/* Format hours if the hardware is running in 12-hour mode */
if(!(RegisterB & CMOS_REGISTER_B_24_HOUR))
{
/* Determine if the time is PM */
PostMeridiem = (SystemTime.Hour >= 12);
/* Adjust for midnight and noon boundary cases */
if(SystemTime.Hour == 0)
{
/* Midnight evaluates to 12 AM */
SystemTime.Hour = 12;
}
else if(SystemTime.Hour > 12)
{
/* Post-noon hour */
SystemTime.Hour -= 12;
}
/* Convert to BCD first if needed and apply the hardware PM flag */
if(!(RegisterB & CMOS_REGISTER_B_BINARY))
{
/* Encode to BCD */
SystemTime.Hour = DECIMAL_TO_BCD(SystemTime.Hour);
}
/* Apply the hardware PM flag to the highest bit */
if(PostMeridiem)
{
/* Set PM flag */
SystemTime.Hour |= CMOS_RTC_POST_MERIDIEM;
}
}
else
{
/* 24-hour mode, simply encode to BCD if necessary */
if(!(RegisterB & CMOS_REGISTER_B_BINARY))
{
/* Encode to BCD */
SystemTime.Hour = DECIMAL_TO_BCD(SystemTime.Hour);
}
}
/* Convert remaining standard fields to BCD if necessary */
if(!(RegisterB & CMOS_REGISTER_B_BINARY))
{
/* Encode all standard time fields */
SystemTime.Minute = DECIMAL_TO_BCD(SystemTime.Minute);
SystemTime.Second = DECIMAL_TO_BCD(SystemTime.Second);
SystemTime.Day = DECIMAL_TO_BCD(SystemTime.Day);
SystemTime.Month = DECIMAL_TO_BCD(SystemTime.Month);
SystemTime.Year = DECIMAL_TO_BCD(SystemTime.Year);
SystemTime.Weekday = DECIMAL_TO_BCD((UCHAR)SystemTime.Weekday);
/* Encode the Century byte */
Century = DECIMAL_TO_BCD(Century);
}
/* Freeze the RTC to prevent data tearing */
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_B, RegisterB | CMOS_REGISTER_B_SET_CLOCK);
/* Push the formatted time values into the hardware registers */
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_HOUR, SystemTime.Hour);
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_MINUTE, SystemTime.Minute);
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_SECOND, SystemTime.Second);
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_DAY, SystemTime.Day);
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_MONTH, SystemTime.Month);
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_YEAR, SystemTime.Year);
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_WEEKDAY, SystemTime.Weekday);
/* Check if Century register is available */
if(CenturyRegister)
{
/* Write the corresponding Century register */
HL::Firmware::WriteCmosRegister(CenturyRegister, Century);
}
/* Unfreeze the RTC */
HL::Firmware::WriteCmosRegister(CMOS_REGISTER_B, RegisterB);
/* Return success status code */
return STATUS_SUCCESS;
}

View File

@@ -21,6 +21,7 @@
#include <hl/ioreg.hh>
#include <hl/irq.hh>
#include <hl/pic.hh>
#include <hl/rtc.hh>
#include <hl/runlevel.hh>
#include <hl/timer.hh>

View File

@@ -0,0 +1,26 @@
/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/includes/hl/rtc.hh
* DESCRIPTION: Hardware Real-Time Clock (RTC) support
* DEVELOPERS: Aiken Harris <harraiken91@gmail.com>
*/
#ifndef __XTOSKRNL_HL_RTC_HH
#define __XTOSKRNL_HL_RTC_HH
#include <xtos.hh>
/* Hardware Layer */
namespace HL
{
class Rtc
{
public:
STATIC XTAPI XTSTATUS GetRealTimeClock(OUT PTIME_FIELDS Time);
STATIC XTAPI XTSTATUS SetRealTimeClock(IN PTIME_FIELDS Time);
};
}
#endif /* __XTOSKRNL_HL_RTC_HH */