diff --git a/etc/make/amd64/platform.mk b/etc/make/amd64/platform.mk index 85b147a..5334b9e 100644 --- a/etc/make/amd64/platform.mk +++ b/etc/make/amd64/platform.mk @@ -49,7 +49,8 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \ $(O)/sys/amd64/acpi_osl_table.o \ $(O)/sys/amd64/acpi_osl_irq.o \ $(O)/sys/amd64/acpi_osl_hw.o \ - $(O)/sys/amd64/sys/binfmt_elf.o + $(O)/sys/amd64/sys/binfmt_elf.o \ + $(O)/sys/amd64/hw/rtc.o kernel_LINKER=sys/amd64/link.ld kernel_LDFLAGS=-nostdlib \ diff --git a/include/sys/amd64/hw/acpi.h b/include/sys/amd64/hw/acpi.h index fd8d7ec..32d675c 100644 --- a/include/sys/amd64/hw/acpi.h +++ b/include/sys/amd64/hw/acpi.h @@ -44,6 +44,49 @@ struct acpi_madt { char entry[]; } __attribute__((packed)); +struct acpi_fadt { + struct acpi_header hdr; + + uint32_t firmware_ctrl; + uint32_t dsdt; + + uint8_t __res0; + + uint8_t preferred_pm_profile; + uint16_t sci_int; + uint32_t sci_cmd_port; + uint8_t acpi_enable; + uint8_t acpi_disable; + uint8_t s4bios_req; + uint8_t pstate_control; + uint32_t pm1a_event_block; + uint32_t pm1b_event_block; + uint32_t pm1a_control_block; + uint32_t pm1b_control_block; + uint32_t pm2_control_block; + uint32_t pm_timer_block; + uint32_t gpe0_block; + uint32_t gpe1_block; + uint8_t pm1_event_length; + uint8_t pm1_control_length; + uint8_t pm2_control_length; + uint8_t pm_timer_length; + uint8_t gpe0_length; + uint8_t gpe1_length; + uint8_t gpe1_base; + uint8_t cstate_control; + uint16_t worst_c2_latency; + uint16_t worst_c3_latency; + uint16_t flush_size; + uint16_t flush_stride; + uint8_t duty_offset; + uint8_t duty_width; + uint8_t day_alarm; + uint8_t month_alarm; + uint8_t century; + //... +} __attribute__((packed)); + extern struct acpi_madt *acpi_madt; void amd64_acpi_init(void); diff --git a/include/sys/amd64/hw/rtc.h b/include/sys/amd64/hw/rtc.h new file mode 100644 index 0000000..1812228 --- /dev/null +++ b/include/sys/amd64/hw/rtc.h @@ -0,0 +1,5 @@ +#pragma once +#include "sys/types.h" + +void rtc_set_century(uint8_t c); +void rtc_init(void); diff --git a/sys/amd64/hw/acpi.c b/sys/amd64/hw/acpi.c index 599a92a..15e8001 100644 --- a/sys/amd64/hw/acpi.c +++ b/sys/amd64/hw/acpi.c @@ -1,11 +1,13 @@ #include "sys/amd64/hw/acpi.h" #include "sys/amd64/hw/apic.h" +#include "sys/amd64/hw/rtc.h" #include "sys/string.h" #include "sys/debug.h" #include "sys/panic.h" #include "acpi.h" struct acpi_madt *acpi_madt = NULL; +struct acpi_fadt *acpi_fadt = NULL; static uint8_t checksum(const void *ptr, size_t size) { uint8_t v = 0; @@ -112,9 +114,21 @@ void amd64_acpi_init(void) { } acpi_madt = (struct acpi_madt *) hdr; + } else if (!strncmp((const char *) hdr, "FACP", 4)) { + kdebug("Found FADT = %p\n", entry_addr); + if (checksum(hdr, hdr->length) != 0) { + kdebug("Entry is invalid\n"); + while (1); + } + + acpi_fadt = (struct acpi_fadt *) hdr; } } + if (acpi_fadt) { + rtc_set_century(acpi_fadt->century); + } + if (!acpi_madt) { panic("ACPI: No MADT present\n"); } diff --git a/sys/amd64/hw/rtc.c b/sys/amd64/hw/rtc.c new file mode 100644 index 0000000..e764f09 --- /dev/null +++ b/sys/amd64/hw/rtc.c @@ -0,0 +1,128 @@ +#include "sys/amd64/hw/rtc.h" +#include "sys/amd64/hw/irq.h" +#include "sys/amd64/hw/io.h" +#include "sys/debug.h" +#include "sys/string.h" + +#define RTC_CMOS_REG_PORT 0x70 +#define RTC_CMOS_DATA_PORT 0x71 + +#define RTC_DISABLE_NMI 0x80 +#define RTC_REGA_STATUS 0x0A +#define RTC_REGB_STATUS 0x0B +#define RTC_REGC_STATUS 0x0C + +#define RTC_REG_SECOND 0x00 +#define RTC_REG_MINUTE 0x02 +#define RTC_REG_HOUR 0x04 +#define RTC_REG_WEEKDAY 0x06 +#define RTC_REG_DAY 0x07 +#define RTC_REG_MONTH 0x08 +#define RTC_REG_YEAR 0x09 + +#define RTC_REGB_BCD 0x04 +#define RTC_REGB_AMPM 0x02 + +#define FROMBCD(bcd) ((((bcd) / 16) * 10) + \ + ((bcd & 0xF))) + +struct rtc_time { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t weekday; + uint8_t day; + uint8_t month; + uint16_t year; +}; + +// [s]Please change this in Y3K[/s]I hope x86 dies by 3000 +static uint8_t rtc_century = 20; + +static inline void cmos_outb(uint8_t reg, uint8_t val) { + outb(RTC_CMOS_REG_PORT, RTC_DISABLE_NMI | reg); + for (size_t i = 0; i < 1000000; ++i); + outb(RTC_CMOS_DATA_PORT, val); +} + +static inline uint8_t cmos_inb(uint8_t reg) { + outb(RTC_CMOS_REG_PORT, RTC_DISABLE_NMI | reg); + for (size_t i = 0; i < 1000000; ++i); + return inb(RTC_CMOS_DATA_PORT); + // TODO; maybe reenable NMI? +} + +static inline uint8_t cmos_read_time(uint8_t reg, uint8_t reg_b) { + if (!(reg_b & RTC_REGB_BCD)) { + return FROMBCD(cmos_inb(reg)); + } else { + return cmos_inb(reg); + } +} + +static void rtc_read(struct rtc_time *time) { + uint8_t reg_b = cmos_inb(RTC_REGB_STATUS); + + time->second = cmos_read_time(RTC_REG_SECOND, reg_b); + time->minute = cmos_read_time(RTC_REG_MINUTE, reg_b); + time->hour = cmos_read_time(RTC_REG_HOUR, reg_b); + time->weekday = cmos_read_time(RTC_REG_WEEKDAY, reg_b); + time->day = cmos_read_time(RTC_REG_DAY, reg_b); + time->month = cmos_read_time(RTC_REG_MONTH, reg_b); + time->year = cmos_read_time(RTC_REG_YEAR, reg_b) + 100 * rtc_century; +} + +static int rtc_irq(void) { + uint8_t reg_c = cmos_inb(RTC_REGC_STATUS); + if (reg_c == 0) { + return -1; + } + // Do something here? + return 0; +} + +static void rtc_enable_irq(void) { + uint8_t prev; + + // Gives IRQs approximately every 0.5s + prev = cmos_inb(RTC_REGA_STATUS); + prev &= 0xF0; + prev |= 0xF; + cmos_outb(RTC_REGA_STATUS, prev); + + // Enable IRQs + prev = cmos_inb(RTC_REGB_STATUS); + cmos_outb(RTC_REGB_STATUS, prev | 0x40); +} + +void rtc_set_century(uint8_t century) { + uint8_t reg_b = cmos_inb(RTC_REGB_STATUS); + uint8_t century_val = cmos_read_time(century, reg_b); + + kdebug("Setting century: %d\n", century_val * 100); + rtc_century = century_val; +} + +void rtc_init(void) { + static const char *day_names[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static const char *month_names[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + struct rtc_time time; + + cmos_inb(RTC_REGC_STATUS); + irq_add_leg_handler(8, rtc_irq); + rtc_enable_irq(); + + rtc_read(&time); + kdebug("%s %02u %s %04u %02u:%02u:%02u\n", + day_names[(time.weekday - 1) % 7], + time.day, + month_names[(time.month - 1) % 12], + time.year, + time.hour, time.minute, time.second); +} diff --git a/sys/amd64/kernel.c b/sys/amd64/kernel.c index 8b9e7f4..bc497db 100644 --- a/sys/amd64/kernel.c +++ b/sys/amd64/kernel.c @@ -11,6 +11,7 @@ #include "sys/amd64/hw/pci/pci.h" #include "sys/amd64/hw/rs232.h" #include "sys/amd64/hw/ps2.h" +#include "sys/amd64/hw/rtc.h" #include "sys/panic.h" #include "sys/assert.h" #include "sys/fs/vfs.h" @@ -39,6 +40,7 @@ void kernel_main(struct amd64_loader_data *data) { extern void sched_init(void); sched_init(); amd64_apic_init(); + rtc_init(); pci_init(); vfs_init();