playground/
.vscode/settings.json
.vscode/*.log
-.VSCodeCounter/
\ No newline at end of file
+.VSCodeCounter/
+.idea
{
"type": "gdb",
"request": "attach",
- "name": "Attach to QEMU",
+ "name": "LunaixOS",
"executable": "${workspaceRoot}/build/bin/lunaix.bin",
"target": ":1234",
"remote": true,
/* 根据System V ABI,栈地址必须16字节对齐 */
/* 这里只是一个临时栈,在_hhk_init里面我们会初始化内核专用栈 */
stack_bottom:
- .skip 16318, 0
+ .skip 16318 * 2, 0
stack_top:
memory: guest=1024, host=1024
+clock: sync=realtime, time0=utc, rtc_sync=1
+
boot: cdrom
\ No newline at end of file
-CC := i686-elf-gcc
-AS := i686-elf-as
+TOOLCHAIN := ${HOME}/opt/cross-compiler/bin/
+CC := ${TOOLCHAIN}i686-elf-gcc
+AS := ${TOOLCHAIN}i686-elf-as
ARCH_OPT := -D__ARCH_IA32
--- /dev/null
+#include <hal/acpi/acpi.h>
+
+#include <lunaix/mm/kalloc.h>
+#include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+
+#include <klibc/string.h>
+
+#include "parser/madt_parser.h"
+
+acpi_context* toc = NULL;
+
+LOG_MODULE("ACPI")
+
+int
+acpi_rsdp_validate(acpi_rsdp_t* rsdp);
+
+acpi_rsdp_t*
+acpi_locate_rsdp(multiboot_info_t* mb_info);
+
+int
+acpi_init(multiboot_info_t* mb_info)
+{
+ acpi_rsdp_t* rsdp = acpi_locate_rsdp(mb_info);
+
+ assert_msg(rsdp, "Fail to locate ACPI_RSDP");
+ assert_msg(acpi_rsdp_validate(rsdp), "Invalid ACPI_RSDP (checksum failed)");
+
+ kprintf(KINFO "RSDP found at %p, RSDT: %p\n", rsdp, rsdp->rsdt);
+
+ acpi_rsdt_t* rsdt = rsdp->rsdt;
+
+ toc = lxcalloc(1, sizeof(acpi_context));
+ assert_msg(toc, "Fail to create ACPI context");
+
+ strncpy(toc->oem_id, rsdt->header.oem_id, 6);
+ toc->oem_id[6] = '\0';
+
+ size_t entry_n = (rsdt->header.length - sizeof(acpi_sdthdr_t)) >> 2;
+ for (size_t i = 0; i < entry_n; i++) {
+ acpi_sdthdr_t* sdthdr = ((acpi_apic_t**)&(rsdt->entry))[i];
+ switch (sdthdr->signature) {
+ case ACPI_MADT_SIG:
+ madt_parse((acpi_madt_t*)sdthdr, toc);
+ break;
+ default:
+ break;
+ }
+ }
+
+ kprintf(KINFO "OEM: %s\n", toc->oem_id);
+ kprintf(KINFO "IOAPIC address: %p\n", toc->madt.ioapic->ioapic_addr);
+ kprintf(KINFO "APIC address: %p\n", toc->madt.apic_addr);
+
+ for (size_t i = 0; i < 24; i++) {
+ acpi_intso_t* intso = toc->madt.irq_exception[i];
+ if (!intso)
+ continue;
+
+ kprintf(KINFO "IRQ #%u -> GSI #%u\n", intso->source, intso->gsi);
+ }
+}
+
+acpi_context*
+acpi_get_context()
+{
+ assert_msg(toc, "ACPI is not initialized");
+ return toc;
+}
+
+int
+acpi_rsdp_validate(acpi_rsdp_t* rsdp)
+{
+ uint8_t sum = 0;
+ uint8_t* rsdp_ptr = (uint8_t*)rsdp;
+ for (size_t i = 0; i < 20; i++) {
+ sum += *(rsdp_ptr + i);
+ }
+
+ return sum == 0;
+}
+
+#define VIRTUAL_BOX_PROBLEM
+
+acpi_rsdp_t*
+acpi_locate_rsdp(multiboot_info_t* mb_info)
+{
+ acpi_rsdp_t* rsdp = NULL;
+
+ // You can't trust memory map from multiboot in virtual box!
+ // They put ACPI RSDP in the FUCKING 0xe0000 !!!
+ // Which is reported to be free area bt multiboot!
+ // SWEET CELESTIA!!!
+#ifndef VIRTUAL_BOX_PROBLEM
+ multiboot_memory_map_t* mmap = (multiboot_memory_map_t*)mb_info->mmap_addr;
+ for (size_t i = 0, j = 0; j < mb_info->mmap_length && !rsdp;
+ i++, j += MB_MMAP_ENTRY_SIZE) {
+ multiboot_memory_map_t entry = mmap[i];
+ if (entry.type != MULTIBOOT_MEMORY_RESERVED ||
+ entry.addr_low > 0x100000) {
+ continue;
+ }
+
+ uint8_t* mem_start = entry.addr_low & ~0xf;
+ size_t len = entry.len_low;
+ for (size_t j = 0; j < len; j += 16) {
+ uint32_t sig_low = *((uint32_t*)(mem_start + j));
+ // uint32_t sig_high = *((uint32_t*)(mem_start+j) + 1);
+ if (sig_low == ACPI_RSDP_SIG_L) {
+ rsdp = (acpi_rsdp_t*)(mem_start + j);
+ break;
+ }
+ }
+ }
+#else
+ // You know what, I just search the entire 1MiB for Celestia's sake.
+ uint8_t* mem_start = 0x4000;
+ for (size_t j = 0; j < 0x100000; j += 16) {
+ uint32_t sig_low = *((uint32_t*)(mem_start + j));
+ // uint32_t sig_high = *((uint32_t*)(mem_start+j) + 1);
+ if (sig_low == ACPI_RSDP_SIG_L) {
+ rsdp = (acpi_rsdp_t*)(mem_start + j);
+ break;
+ }
+ }
+#endif
+
+ return rsdp;
+}
\ No newline at end of file
--- /dev/null
+#include "madt_parser.h"
+#include <lunaix/mm/kalloc.h>
+
+void
+madt_parse(acpi_madt_t* madt, acpi_context* toc)
+{
+ toc->madt.apic_addr = madt->apic_addr;
+
+ // FUTURE: make madt.{apic,ioapic} as array or linked list.
+ uint8_t* ics_start = (uint8_t*)((uintptr_t)madt + sizeof(acpi_madt_t));
+ uintptr_t ics_end = (uintptr_t)madt + madt->header.length;
+
+ // Cosidering only one IOAPIC present (max 24 pins)
+ // FIXME: use hash table instead
+ toc->madt.irq_exception =
+ (acpi_intso_t*)lxcalloc(24, sizeof(acpi_intso_t*));
+
+ size_t so_idx = 0;
+ while (ics_start < ics_end) {
+ acpi_ics_hdr_t* entry = (acpi_ics_hdr_t*)ics_start;
+ switch (entry->type) {
+ case ACPI_MADT_LAPIC:
+ toc->madt.apic = (acpi_apic_t*)entry;
+ break;
+ case ACPI_MADT_IOAPIC:
+ toc->madt.ioapic = (acpi_ioapic_t*)entry;
+ break;
+ case ACPI_MADT_INTSO:
+ {
+ acpi_intso_t* intso_tbl = (acpi_intso_t*)entry;
+ toc->madt.irq_exception[intso_tbl->source] = intso_tbl;
+ break;
+ }
+ default:
+ break;
+ }
+
+ ics_start += entry->length;
+ }
+}
\ No newline at end of file
--- /dev/null
+#ifndef __LUNAIX_PARSER_MADT_PARSER_H
+#define __LUNAIX_PARSER_MADT_PARSER_H
+
+#include <hal/acpi/acpi.h>
+
+/**
+ * @brief Parse the MADT and populated into main TOC
+ *
+ * @param rsdt RSDT
+ * @param toc The main TOC
+ */
+void madt_parse(acpi_madt_t* madt, acpi_context* toc);
+
+#endif /* __LUNAIX_PARSER_MADT_PARSER_H */
--- /dev/null
+/**
+ * @file apic.c
+ * @author Lunaixsky
+ * @brief Abstraction for Advanced Programmable Interrupts Controller (APIC)
+ * @version 0.1
+ * @date 2022-03-06
+ *
+ * @copyright Copyright (c) 2022
+ *
+ */
+#include <hal/apic.h>
+#include <hal/cpu.h>
+#include <hal/pic.h>
+#include <hal/rtc.h>
+
+#include <arch/x86/interrupts.h>
+
+#include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+
+LOG_MODULE("APIC")
+
+void
+apic_setup_timer();
+
+void
+apic_setup_lvts();
+
+void
+init_apic()
+{
+ // ensure that external interrupt is disabled
+ cpu_disable_interrupt();
+
+ // Make sure the APIC is there
+ // FUTURE: Use 8259 as fallback
+ assert_msg(cpu_has_apic(), "No APIC detected!");
+
+ // As we are going to use APIC, disable the old 8259 PIC
+ pic_disable();
+
+ // Hardware enable the APIC
+ // By setting bit 11 of IA32_APIC_BASE register
+ // Note: After this point, you can't disable then re-enable it until a reset (i.e., reboot)
+ asm volatile (
+ "movl %0, %%ecx\n"
+ "rdmsr\n"
+ "orl %1, %%eax\n"
+ "wrmsr\n"
+ ::"i"(IA32_APIC_BASE_MSR), "i"(IA32_APIC_ENABLE)
+ : "eax", "ecx", "edx"
+ );
+
+ // Print the basic information of our current local APIC
+ uint32_t apic_id = apic_read_reg(APIC_IDR) >> 24;
+ uint32_t apic_ver = apic_read_reg(APIC_VER);
+
+ kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u\n",
+ apic_id,
+ apic_ver & 0xff,
+ (apic_ver >> 16) & 0xff);
+
+ // initialize the local vector table (LVT)
+ apic_setup_lvts();
+
+ // initialize priority registers
+
+ // set the task priority to the lowest possible, so all external interrupts are acceptable
+ // Note, the lowest possible priority class is 2, not 0, 1, as they are reserved for
+ // internal interrupts (vector 0-31, and each p-class resposible for 16 vectors).
+ // See Intel Manual Vol. 3A, 10-29
+ apic_write_reg(APIC_TPR, APIC_PRIORITY(2, 0));
+
+ // enable APIC
+ uint32_t spiv = apic_read_reg(APIC_SPIVR);
+
+ // install our handler for spurious interrupt.
+ spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV;
+ apic_write_reg(APIC_SPIVR, spiv);
+
+ // setup timer and performing calibrations
+ apic_setup_timer();
+}
+
+#define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
+
+// Pin LINT#1 is configured for relaying NMI, but we masked it here as I think
+// it is too early for that
+// LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
+#define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
+#define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector)
+#define LVT_ENTRY_TIMER(vector, mode) (LVT_DELIVERY_FIXED | mode | vector)
+
+void
+apic_setup_lvts()
+{
+ apic_write_reg(APIC_LVT_LINT0, LVT_ENTRY_LINT0(APIC_LINT0_IV));
+ apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
+ apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
+}
+
+void
+temp_intr_routine_rtc_tick(const isr_param* param);
+
+void
+temp_intr_routine_apic_timer(const isr_param* param);
+
+void
+test_timer(const isr_param* param);
+
+uint32_t apic_timer_base_freq = 0;
+
+// Don't optimize them! Took me an half hour to figure that out...
+
+volatile uint32_t rtc_counter = 0;
+volatile uint8_t apic_timer_done = 0;
+
+#define APIC_CALIBRATION_CONST 0x100000
+
+void
+apic_setup_timer()
+{
+ cpu_disable_interrupt();
+
+ // Setup APIC timer
+
+ // Setup a one-shot timer, we will use this to measure the bus speed. So we can
+ // then calibrate apic timer to work at *nearly* accurate hz
+ apic_write_reg(APIC_TIMER_LVT, LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_ONESHOT));
+
+ // Set divider to 64
+ apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
+
+ /*
+ Timer calibration process - measure the APIC timer base frequency
+
+ step 1: setup a temporary isr for RTC timer which trigger at each tick (1024Hz)
+ step 2: setup a temporary isr for #APIC_TIMER_IV
+ step 3: setup the divider, APIC_TIMER_DCR
+ step 4: Startup RTC timer
+ step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer
+ (this must be followed immediately after step 4)
+ step 6: issue a write to EOI and clean up.
+
+ When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the rtc timer's
+ counter, k, and disable RTC timer immediately (although the RTC interrupts should be
+ blocked by local APIC as we are currently busy on handling #APIC_TIMER_IV)
+
+ So the apic timer frequency F_apic in Hz can be calculate as
+ v / F_apic = k / 1024
+ => F_apic = v / k * 1024
+
+ */
+
+ apic_timer_base_freq = 0;
+ rtc_counter = 0;
+ apic_timer_done = 0;
+
+ intr_subscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer);
+ intr_subscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick);
+
+
+ rtc_enable_timer(); // start RTC timer
+ apic_write_reg(APIC_TIMER_ICR, APIC_CALIBRATION_CONST); // start APIC timer
+
+ // enable interrupt, just for our RTC start ticking!
+ cpu_enable_interrupt();
+
+ wait_until(apic_timer_done);
+
+ // cpu_disable_interrupt();
+
+ assert_msg(apic_timer_base_freq, "Fail to initialize timer");
+
+ kprintf(KINFO "Timer base frequency: %u Hz\n", apic_timer_base_freq);
+
+ // cleanup
+ intr_unsubscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer);
+ intr_unsubscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick);
+
+ // TODO: now setup timer with our custom frequency which we can derived from the base frequency
+ // we measured
+
+ apic_write_reg(APIC_TIMER_LVT, LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_PERIODIC));
+ intr_subscribe(APIC_TIMER_IV, test_timer);
+
+ apic_write_reg(APIC_TIMER_ICR, apic_timer_base_freq);
+}
+
+volatile rtc_datetime datetime;
+
+void
+test_timer(const isr_param* param) {
+
+ rtc_get_datetime(&datetime);
+
+ kprintf(KWARN "%u/%02u/%02u %02u:%02u:%02u\r",
+ datetime.year,
+ datetime.month,
+ datetime.day,
+ datetime.hour,
+ datetime.minute,
+ datetime.second);
+}
+
+void
+temp_intr_routine_rtc_tick(const isr_param* param) {
+ rtc_counter++;
+
+ // dummy read on register C so RTC can send anther interrupt
+ // This strange behaviour observed in virtual box & bochs
+ (void) rtc_read_reg(RTC_REG_C);
+}
+
+void
+temp_intr_routine_apic_timer(const isr_param* param) {
+ apic_timer_base_freq = APIC_CALIBRATION_CONST / rtc_counter * RTC_TIMER_BASE_FREQUENCY;
+ apic_timer_done = 1;
+
+ rtc_disable_timer();
+}
\ No newline at end of file
j+=4;
}
brand_out[48] = '\0';
+}
+
+
+int
+cpu_has_apic() {
+ // reference: Intel manual, section 10.4.2
+ reg32 eax = 0, ebx = 0, edx = 0, ecx = 0;
+ __get_cpuid(1, &eax, &ebx, &ecx, &edx);
+
+ return (edx & 0x100);
+}
+
+void
+cpu_rdmsr(uint32_t msr_idx, uint32_t* reg_high, uint32_t* reg_low)
+{
+ uint32_t h = 0, l = 0;
+ asm volatile("rdmsr" : "=d"(h), "=a"(l) : "c"(msr_idx));
+
+ *reg_high = h;
+ *reg_low = l;
+}
+
+void
+cpu_wrmsr(uint32_t msr_idx, uint32_t reg_high, uint32_t reg_low)
+{
+ asm volatile("wrmsr" : : "d"(reg_high), "a"(reg_low), "c"(msr_idx));
}
\ No newline at end of file
--- /dev/null
+#include <arch/x86/interrupts.h>
+#include <hal/ioapic.h>
+#include <hal/acpi/acpi.h>
+
+
+#define IOAPIC_REG_SEL *((volatile uint32_t*)(IOAPIC_BASE_VADDR + IOAPIC_IOREGSEL))
+#define IOAPIC_REG_WIN *((volatile uint32_t*)(IOAPIC_BASE_VADDR + IOAPIC_IOWIN))
+
+uint8_t
+ioapic_get_irq(acpi_context* acpi_ctx, uint8_t old_irq);
+
+void
+ioapic_init() {
+ // Remapping the IRQs
+
+ acpi_context* acpi_ctx = acpi_get_context();
+
+ // Remap the IRQ 8 (rtc timer's vector) to RTC_TIMER_IV in ioapic
+ // (Remarks IRQ 8 is pin INTIN8)
+ // See IBM PC/AT Technical Reference 1-10 for old RTC IRQ
+ // See Intel's Multiprocessor Specification for IRQ - IOAPIC INTIN mapping config.
+
+ // The ioapic_get_irq is to make sure we capture those overriden IRQs
+
+ // PC_AT_IRQ_RTC -> RTC_TIMER_IV, fixed, edge trigged, polarity=high, physical, APIC ID 0
+ ioapic_redirect(ioapic_get_irq(acpi_ctx, PC_AT_IRQ_RTC), RTC_TIMER_IV, 0, IOAPIC_DELMOD_FIXED);
+}
+
+uint8_t
+ioapic_get_irq(acpi_context* acpi_ctx, uint8_t old_irq) {
+ if (old_irq >= 24) {
+ return old_irq;
+ }
+ acpi_intso_t* int_override = acpi_ctx->madt.irq_exception[old_irq];
+ return int_override ? (uint8_t)int_override->gsi : old_irq;
+}
+
+void
+ioapic_write(uint8_t sel, uint32_t val) {
+ IOAPIC_REG_SEL = sel;
+ IOAPIC_REG_WIN = val;
+}
+
+uint32_t
+ioapic_read(uint8_t sel) {
+ IOAPIC_REG_SEL = sel;
+ return IOAPIC_REG_WIN;
+}
+
+void
+ioapic_redirect(uint8_t irq, uint8_t vector, uint8_t dest, uint32_t flags) {
+ uint8_t reg_sel = IOAPIC_IOREDTBL_BASE + irq * 2;
+
+ // Write low 32 bits
+ ioapic_write(reg_sel, (vector | flags) & 0x1FFFF);
+
+ // Write high 32 bits
+ ioapic_write(reg_sel + 1, (dest << 24));
+}
\ No newline at end of file
+/**
+ * @file rtc.c
+ * @author Lunaixsky
+ * @brief RTC & CMOS abstraction. Reference: MC146818A & Intel Series 500 PCH datasheet
+ * @version 0.1
+ * @date 2022-03-07
+ *
+ * @copyright Copyright (c) 2022
+ *
+ */
#include <hal/rtc.h>
+#include <klibc/string.h>
+
+void
+rtc_init() {
+ uint8_t regA = rtc_read_reg(RTC_REG_A | WITH_NMI_DISABLED);
+ regA = (regA & ~0x7f) | RTC_FREQUENCY_1024HZ | RTC_DIVIDER_33KHZ;
+ rtc_write_reg(RTC_REG_A | WITH_NMI_DISABLED, regA);
+
+ // Make sure the rtc timer is disabled by default
+ rtc_disable_timer();
+}
uint8_t
rtc_read_reg(uint8_t reg_selector)
return io_inb(RTC_TARGET_PORT);
}
+void
+rtc_write_reg(uint8_t reg_selector, uint8_t val)
+{
+ io_outb(RTC_INDEX_PORT, reg_selector);
+ io_outb(RTC_TARGET_PORT, val);
+}
+
uint8_t
bcd2dec(uint8_t bcd)
{
- return (bcd >> 4) * 10 + (bcd & 0x0f);
+ return ((bcd & 0xF0) >> 1) + ((bcd & 0xF0) >> 3) + (bcd & 0xf);
+}
+
+int
+rtc_date_same(rtc_datetime* a, rtc_datetime* b) {
+ return a->year == b->year &&
+ a->month == b->month &&
+ a->day == b->day &&
+ a->weekday == b->weekday &&
+ a->minute == b->minute &&
+ a->second == b->second;
}
void
rtc_get_datetime(rtc_datetime* datetime)
{
- datetime->year = rtc_read_reg(RTC_REG_YRS);
- datetime->month = rtc_read_reg(RTC_REG_MTH);
- datetime->day = rtc_read_reg(RTC_REG_DAY);
- datetime->weekday = rtc_read_reg(RTC_REG_WDY);
- datetime->hour = rtc_read_reg(RTC_REG_HRS);
- datetime->minute = rtc_read_reg(RTC_REG_MIN);
- datetime->second = rtc_read_reg(RTC_REG_SEC);
+ rtc_datetime current;
+
+ do
+ {
+ while (rtc_read_reg(RTC_REG_A) & 0x80);
+ memcpy(¤t, datetime, sizeof(rtc_datetime));
+
+ datetime->year = rtc_read_reg(RTC_REG_YRS);
+ datetime->month = rtc_read_reg(RTC_REG_MTH);
+ datetime->day = rtc_read_reg(RTC_REG_DAY);
+ datetime->weekday = rtc_read_reg(RTC_REG_WDY);
+ datetime->hour = rtc_read_reg(RTC_REG_HRS);
+ datetime->minute = rtc_read_reg(RTC_REG_MIN);
+ datetime->second = rtc_read_reg(RTC_REG_SEC);
+ } while (!rtc_date_same(datetime, ¤t));
uint8_t regbv = rtc_read_reg(RTC_REG_B);
// To 24 hour format
- if (!RTC_24HRS_ENCODED(regbv)) {
- datetime->hour = (datetime->hour >> 7) ? (12 + datetime->hour & 0x80)
- : (datetime->hour & 0x80);
+ if (!RTC_24HRS_ENCODED(regbv) && (datetime->hour >> 7)) {
+ datetime->hour = (12 + datetime->hour & 0x80);
}
datetime->year += RTC_CURRENT_CENTRY * 100;
+}
+
+void
+rtc_enable_timer() {
+ uint8_t regB = rtc_read_reg(RTC_REG_B | WITH_NMI_DISABLED);
+ rtc_write_reg(RTC_REG_B | WITH_NMI_DISABLED, regB | RTC_TIMER_ON);
+}
+
+void
+rtc_disable_timer() {
+ uint8_t regB = rtc_read_reg(RTC_REG_B | WITH_NMI_DISABLED);
+ rtc_write_reg(RTC_REG_B | WITH_NMI_DISABLED, regB & ~RTC_TIMER_ON);
}
\ No newline at end of file
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
+
+#define MB_MMAP_ENTRY_SIZE sizeof(multiboot_memory_map_t)
+
#endif
#endif
\ No newline at end of file
#ifndef __LUNAIX_IDT_H
-#define __LUNAIX_IDT_H 1
+#define __LUNAIX_IDT_H
#define IDT_ATTR(dpl) ((0x70 << 5) | (dpl & 3) << 13 | 1 << 15)
void
_init_idt();
-#endif
\ No newline at end of file
+
+#endif /* __LUNAIX_IDT_H */
\ No newline at end of file
#define FAULT_VIRTUALIZATION_EXCEPTION 20
#define FAULT_CONTROL_PROTECTION 21
+// LunaixOS related
#define LUNAIX_SYS_PANIC 32
+#define EX_INTERRUPT_BEGIN 200
+// APIC related
+#define APIC_ERROR_IV 200
+#define APIC_LINT0_IV 201
+#define APIC_TIMER_IV 202
+#define APIC_SPIV_IV 203
+
+#define RTC_TIMER_IV 210
+
+
+#define PC_AT_IRQ_RTC 8
+#define PC_AT_IRQ_KBD_BUF_FULL 1
+
#ifndef __ASM__
#include <hal/cpu.h>
typedef struct {
unsigned int ss;
} __attribute__((packed)) isr_param;
+typedef void (*int_subscriber)(isr_param*);
+
+#pragma region ISR_DECLARATION
void
_asm_isr0();
void
_asm_isr21();
-
void
_asm_isr32();
void
-interrupt_handler(isr_param* param);
+_asm_isr200();
+
+void
+_asm_isr201();
+
+void
+_asm_isr202();
+
+void
+_asm_isr203();
+
+void
+_asm_isr210();
+
+#pragma endregion
+
+void
+intr_subscribe(const uint8_t vector, int_subscriber);
+
+void
+intr_unsubscribe(const uint8_t vector, int_subscriber);
+
+void
+intr_set_fallback_handler(int_subscriber);
+
+void
+intr_handler(isr_param* param);
+
+void
+intr_routine_init();
#endif
--- /dev/null
+#ifndef __LUNAIX_ACPI_ACPI_H
+#define __LUNAIX_ACPI_ACPI_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <arch/x86/boot/multiboot.h>
+
+#include "sdt.h"
+#include "madt.h"
+
+#define ACPI_RSDP_SIG_L 0x20445352 // 'RSD '
+#define ACPI_RSDP_SIG_H 0x20525450 // 'PTR '
+
+#define ACPI_MADT_SIG 0x43495041 // 'APIC'
+
+typedef struct {
+ uint32_t signature_l;
+ uint32_t signature_h;
+ uint8_t chksum;
+ uint8_t oem_id[6];
+ // Revision
+ uint8_t rev;
+ acpi_rsdt_t* rsdt;
+ uint32_t length;
+ acpi_sdthdr_t* xsdt;
+ uint8_t x_chksum;
+ char reserved[3]; // Reserved field
+} __attribute__((packed)) acpi_rsdp_t;
+
+/**
+ * @brief Main TOC of ACPI tables, provide hassle-free access of ACPI info.
+ *
+ */
+typedef struct
+{
+ // Make it as null terminated
+ char oem_id[7];
+ acpi_madt_toc_t madt;
+} acpi_context;
+
+int
+acpi_init(multiboot_info_t* mb_info);
+
+acpi_context*
+acpi_get_context();
+
+#endif /* __LUNAIX_ACPI_ACPI_H */
--- /dev/null
+#ifndef __LUNAIX_ACPI_MADT_H
+#define __LUNAIX_ACPI_MADT_H
+
+#include "sdt.h"
+
+#define ACPI_MADT_LAPIC 0x0 // Local APIC
+#define ACPI_MADT_IOAPIC 0x1 // I/O APIC
+#define ACPI_MADT_INTSO 0x2 // Interrupt Source Override
+
+/**
+ * @brief ACPI Interrupt Controller Structure (ICS) Header
+ *
+ */
+typedef struct
+{
+ uint8_t type;
+ uint8_t length;
+} __attribute__((packed)) acpi_ics_hdr_t;
+
+/**
+ * @brief ACPI Processor Local APIC Structure (PLAS)
+ * This structure tell information about our Local APIC per processor. Including
+ * the MMIO addr.
+ *
+ */
+typedef struct
+{
+ acpi_ics_hdr_t header;
+ uint8_t processor_id;
+ uint8_t apic_id;
+ uint32_t flags;
+} __attribute__((packed)) acpi_apic_t;
+
+/**
+ * @brief ACPI IO APIC Structure (IOAS)
+ *
+ * This structure tell information about our I/O APIC on motherboard. Including
+ * the MMIO addr.
+ *
+ */
+typedef struct
+{
+ acpi_ics_hdr_t header;
+ uint8_t ioapic_id;
+ uint8_t reserved;
+ uint32_t ioapic_addr;
+ // The global system interrupt offset for this IOAPIC. (Kind of IRQ offset for a slave IOAPIC)
+ uint32_t gis_offset;
+} __attribute__((packed)) acpi_ioapic_t;
+
+/**
+ * @brief ACPI Interrupt Source Override (INTSO)
+ *
+ * According to the ACPI Spec, the IRQ config between APIC and 8259 PIC can be
+ * assumed to be identically mapped. However, some manufactures may have their
+ * own preference and hence expections may be introduced. This structure provide
+ * information on such exception.
+ *
+ */
+typedef struct
+{
+ acpi_ics_hdr_t header;
+ uint8_t bus;
+ // source, which is the original IRQ back in the era of IBM PC/AT, the 8259
+ // PIC
+ uint8_t source;
+ // global system interrupt. The override of source in APIC mode
+ uint32_t gsi;
+ uint16_t flags;
+} __attribute__((packed)) acpi_intso_t;
+
+typedef struct
+{
+ acpi_sdthdr_t header;
+ void* apic_addr;
+ uint32_t flags;
+ // Here is a bunch of packed ICS reside here back-to-back.
+} __attribute__((packed)) acpi_madt_t;
+
+typedef struct
+{
+ void* apic_addr;
+ acpi_apic_t* apic;
+ acpi_ioapic_t* ioapic;
+ acpi_intso_t** irq_exception;
+} acpi_madt_toc_t;
+
+#endif /* __LUNAIX_ACPI_MADT_H */
--- /dev/null
+#ifndef __LUNAIX_ACPI_SDT_H
+#define __LUNAIX_ACPI_SDT_H
+
+#include <stdint.h>
+
+typedef struct
+{
+ uint32_t signature;
+ uint32_t length;
+ // Revision
+ uint8_t rev;
+ uint8_t chksum;
+ char oem_id[6];
+ char oem_table_id[8];
+ uint32_t oem_rev;
+ uint32_t vendor_id;
+ uint32_t vendor_rev;
+} __attribute__((packed)) acpi_sdthdr_t;
+
+typedef struct
+{
+ acpi_sdthdr_t header;
+ acpi_sdthdr_t* entry;
+} __attribute__((packed)) acpi_rsdt_t;
+
+#endif /* __LUNAIX_ACPI_SDT_H */
--- /dev/null
+#ifndef __LUNAIX_APIC_H
+#define __LUNAIX_APIC_H
+
+#include <stdint.h>
+
+#define APIC_BASE_VADDR 0x1000
+#define __APIC_BASE_PADDR 0xFEE00000
+
+#define IA32_APIC_BASE_MSR 0x1B
+#define IA32_APIC_ENABLE 0x800
+
+/*
+ * Common APIC memory-mapped registers
+ * Ref: Intel Manual, Vol. 3A, Table 10-1
+ */
+
+#define APIC_IDR 0x20 // ID Reg
+#define APIC_VER 0x30 // Version Reg
+#define APIC_TPR 0x80 // Task Priority
+#define APIC_APR 0x90 // Arbitration Priority
+#define APIC_PPR 0xA0 // Processor Priority
+#define APIC_EOI 0xB0 // End-Of-Interrupt
+#define APIC_RRD 0xC0 // Remote Read
+#define APIC_LDR 0xD0 // Local Destination Reg
+#define APIC_DFR 0xE0 // Destination Format Reg
+#define APIC_SPIVR 0xF0 // Spurious Interrupt Vector Reg
+#define APIC_ISR_BASE 0x100 // Base address for In-Service-Interrupt bitmap register (256bits)
+#define APIC_TMR_BASE 0x180 // Base address for Trigger-Mode bitmap register (256bits)
+#define APIC_IRR_BASE 0x200 // Base address for Interrupt-Request bitmap register (256bits)
+#define APIC_ESR 0x280 // Error Status Reg
+#define APIC_ICR_BASE 0x300 // Interrupt Command
+#define APIC_LVT_LINT0 0x350
+#define APIC_LVT_LINT1 0x360
+#define APIC_LVT_ERROR 0x370
+
+// APIC Timer specific
+#define APIC_TIMER_LVT 0x320
+#define APIC_TIMER_ICR 0x380 // Initial Count
+#define APIC_TIMER_CCR 0x390 // Current Count
+#define APIC_TIMER_DCR 0x3E0 // Divide Configuration
+
+#define APIC_SPIV_FOCUS_DISABLE 0x200
+#define APIC_SPIV_APIC_ENABLE 0x100
+#define APIC_SPIV_EOI_BROADCAST 0x1000
+
+#define LVT_DELIVERY_FIXED 0
+#define LVT_DELIVERY_NMI (0x4 << 8)
+#define LVT_TRIGGER_EDGE (0 << 15)
+#define LVT_TRIGGER_LEVEL (1 << 15)
+#define LVT_MASKED (1 << 16)
+#define LVT_TIMER_ONESHOT (0 << 17)
+#define LVT_TIMER_PERIODIC (1 << 17)
+
+// Dividers for timer. See Intel Manual Vol3A. 10-17 (pp. 3207), Figure 10-10
+#define APIC_TIMER_DIV1 0b1011
+#define APIC_TIMER_DIV2 0b0000
+#define APIC_TIMER_DIV4 0b0001
+#define APIC_TIMER_DIV8 0b0010
+#define APIC_TIMER_DIV16 0b0011
+#define APIC_TIMER_DIV32 0b1000
+#define APIC_TIMER_DIV64 0b1001
+#define APIC_TIMER_DIV128 0b1010
+
+#define APIC_PRIORITY(cls, subcls) (((cls) << 4) | (subcls))
+
+#define apic_read_reg(reg) (*(uint32_t*)(APIC_BASE_VADDR + (reg)))
+#define apic_write_reg(reg, val) (*(uint32_t*)(APIC_BASE_VADDR + (reg)) = (val))
+
+void
+init_apic();
+
+/**
+ * @brief Tell the APIC that the handler for current interrupt is finished.
+ * This will issue a write action to EOI register.
+ *
+ */
+inline static void
+apic_done_servicing() {
+ apic_write_reg(APIC_EOI, 0);
+}
+
+#endif /* __LUNAIX_APIC_H */
void
cpu_get_brand(char* brand_out);
+int
+cpu_has_apic();
+
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-type"
static inline reg32
static inline void
cpu_invplg(void* va)
{
- asm("invlpg (%0)" ::"r"((uintptr_t)va) : "memory");
+ asm volatile("invlpg (%0)" ::"r"((uintptr_t)va) : "memory");
}
static inline void
cpu_enable_interrupt()
{
- asm("sti");
+ asm volatile("sti");
}
static inline void
cpu_disable_interrupt()
{
- asm("cli");
+ asm volatile("cli");
}
static inline void
: "r"(interm));
}
+void
+cpu_rdmsr(uint32_t msr_idx, uint32_t* reg_high, uint32_t* reg_low);
+
+void
+cpu_wrmsr(uint32_t msr_idx, uint32_t reg_high, uint32_t reg_low);
+
#endif
\ No newline at end of file
--- /dev/null
+#ifndef __LUNAIX_IOAPIC_H
+#define __LUNAIX_IOAPIC_H
+
+#include <stdint.h>
+
+#define IOAPIC_IOREGSEL 0x00
+#define IOAPIC_IOWIN 0x10
+#define IOAPIC_IOREDTBL_BASE 0x10
+
+#define IOAPIC_REG_ID 0x00
+#define IOAPIC_REG_VER 0x01
+#define IOAPIC_REG_ARB 0x02
+
+#define IOAPIC_DELMOD_FIXED 0b000
+#define IOAPIC_DELMOD_LPRIO 0b001
+#define IOAPIC_DELMOD_NMI 0b100
+
+#define IOAPIC_MASKED (1 << 16)
+#define IOAPIC_TRIG_LEVEL (1 << 15)
+#define IOAPIC_INTPOL_L (1 << 13)
+#define IOAPIC_DESTMOD_LOGIC (1 << 11)
+
+#define IOAPIC_BASE_VADDR 0x2000
+
+void
+ioapic_init();
+
+void
+ioapic_write(uint8_t sel, uint32_t val);
+
+uint32_t
+ioapic_read(uint8_t sel);
+
+void
+ioapic_redirect(uint8_t irq, uint8_t vector, uint8_t dest, uint32_t flags);
+
+#endif /* __LUNAIX_IOAPIC_H */
#define __LUNAIX_PIC_H
// TODO: PIC
+static inline void
+pic_disable()
+{
+ // ref: https://wiki.osdev.org/8259_PIC
+ asm volatile ("movb $0xff, %al\n"
+ "outb %al, $0xa1\n"
+ "outb %al, $0x21\n");
+}
+
#endif /* __LUNAIX_PIC_H */
#define RTC_BIN_ENCODED(reg) (reg & 0x04)
#define RTC_24HRS_ENCODED(reg) (reg & 0x02)
+#define RTC_TIMER_BASE_FREQUENCY 1024
+#define RTC_TIMER_ON 0x40
+
+#define RTC_FREQUENCY_1024HZ 0b110
+#define RTC_DIVIDER_33KHZ (0b010 << 4)
+
typedef struct
{
uint32_t year; // use int32 as we need to store the 4-digit year
uint8_t second;
} rtc_datetime;
+void
+rtc_init();
uint8_t
rtc_read_reg(uint8_t reg_selector);
+void
+rtc_write_reg(uint8_t reg_selector, uint8_t val);
+
void
rtc_get_datetime(rtc_datetime* datetime);
+void
+rtc_enable_timer();
+
+void
+rtc_disable_timer();
+
#endif /* __LUNAIX_RTC_H */
--- /dev/null
+#ifndef __LUNAIX_STDIO_H
+#define __LUNAIX_STDIO_H
+#include <stdarg.h>
+#include <stddef.h>
+
+void
+__sprintf_internal(char* buffer, char* fmt, size_t max_len, va_list vargs);
+
+void sprintf(char* buffer, char* fmt, ...);
+void snprintf(char* buffer, size_t n, char* fmt, ...);
+#endif /* __LUNAIX_STDIO_H */
size_t
strnlen(const char* str, size_t max_len);
+char*
+strncpy(char* dest, const char* src, size_t n);
+
const char*
strchr(const char* str, int character);
+++ /dev/null
-#ifndef __LUNAIX_STDIO_H
-#define __LUNAIX_STDIO_H
-#include <stdarg.h>
-
-#ifdef __LUNAIX_LIBC
-void __sprintf_internal(char* buffer, char* fmt, va_list args);
-#endif
-
-void sprintf(char* buffer, char* fmt, ...);
-void printf(char* fmt, ...);
-
-#endif /* __LUNAIX_STDIO_H */
* @return void*
*/
void*
-lxcalloc(size_t size);
+lxcalloc(size_t n, size_t elem);
/**
* @brief Free the memory region allocated by kmalloc
int
vmm_alloc_pages(void* va, size_t sz, pt_attr tattr);
+/**
+ * @brief 设置一个映射,如果映射已存在,则忽略。
+ *
+ * @param va
+ * @param pa
+ * @param attr
+ */
+void
+vmm_set_mapping(void* va, void* pa, pt_attr attr);
+
/**
* @brief 删除一个映射
*
}
void __assert_fail(const char* expr, const char* file, unsigned int line) __attribute__((noinline, noreturn));
#else
-#define assert(cond) //assert nothing
-#define assert_msg(cond, msg) //assert nothing
+#define assert(cond) (void)(cond); //assert nothing
+#define assert_msg(cond, msg) (void)(cond); //assert nothing
#endif
+void panick(const char* msg);
+
+
+#define wait_until(cond) while(!(cond));
+#define loop_until(cond) while(!(cond));
#endif /* __LUNAIX_SPIKE_H */
--- /dev/null
+#ifndef __LUNAIX_SYSLOG_H
+#define __LUNAIX_SYSLOG_H
+
+#include <stdarg.h>
+
+#define _LEVEL_INFO "0"
+#define _LEVEL_WARN "1"
+#define _LEVEL_ERROR "2"
+
+#define KINFO "\x1b" _LEVEL_INFO
+#define KWARN "\x1b" _LEVEL_WARN
+#define KERROR "\x1b" _LEVEL_ERROR
+
+#define LOG_MODULE(module) \
+ static void kprintf(const char* fmt, ...) { \
+ va_list args; \
+ va_start(args, fmt); \
+ __kprintf(module, fmt, args); \
+ va_end(args); \
+ }
+
+void
+__kprintf(const char* component, const char* fmt, va_list args);
+
+
+void
+kprint_panic(const char* fmt, ...);
+
+#endif /* __LUNAIX_SYSLOG_H */
void
tty_get_cpos(unsigned int* x, unsigned int* y);
+vga_attribute
+tty_get_theme();
+
#endif /* __LUNAIX_TTY_H */
_set_idt_entry(FAULT_GENERAL_PROTECTION, 0x08, _asm_isr13, 0);
_set_idt_entry(FAULT_PAGE_FAULT, 0x08, _asm_isr14, 0);
+ _set_idt_entry(APIC_ERROR_IV, 0x08, _asm_isr200, 0);
+ _set_idt_entry(APIC_LINT0_IV, 0x08, _asm_isr201, 0);
+ _set_idt_entry(APIC_TIMER_IV, 0x08, _asm_isr202, 0);
+ _set_idt_entry(APIC_SPIV_IV, 0x08, _asm_isr203, 0);
+
+ _set_idt_entry(RTC_TIMER_IV, 0x08, _asm_isr210, 0);
+
// system defined interrupts
_set_idt_entry(LUNAIX_SYS_PANIC, 0x08, _asm_isr32, 0);
}
\ No newline at end of file
isr_template LUNAIX_SYS_PANIC
+ isr_template APIC_ERROR_IV
+ isr_template APIC_LINT0_IV
+ isr_template APIC_TIMER_IV
+ isr_template APIC_SPIV_IV
+ isr_template RTC_TIMER_IV
+
interrupt_wrapper:
pushl %esp
pushl %esi
subl $16, %esp
movl %eax, (%esp)
- call interrupt_handler
- popl %eax
- movl %eax, %esp
+ call intr_handler
+ popl %esp
popl %eax
popl %ebx
#include <arch/x86/interrupts.h>
+#include <hal/apic.h>
#include <hal/cpu.h>
-#include <libc/stdio.h>
+#include <lunaix/syslog.h>
#include <lunaix/tty/tty.h>
+int_subscriber subscribers[256];
+
+static int_subscriber fallback = (int_subscriber) 0;
+
void
-panic_msg(const char* msg)
-{
- tty_set_theme(VGA_COLOR_WHITE, VGA_COLOR_RED);
- tty_clear_line(10);
- tty_clear_line(11);
- tty_clear_line(12);
- tty_set_cpos(0, 11);
- printf(" %s", msg);
+intr_subscribe(const uint8_t vector, int_subscriber subscriber) {
+ subscribers[vector] = subscriber;
+}
+
+void
+intr_unsubscribe(const uint8_t vector, int_subscriber subscriber) {
+ if (subscribers[vector] == subscriber) {
+ subscribers[vector] = (int_subscriber) 0;
+ }
+}
+
+void
+intr_set_fallback_handler(int_subscriber subscribers) {
+ fallback = subscribers;
}
void
-panic(const char* msg, isr_param* param)
+intr_handler(isr_param* param)
{
- char buf[1024];
- sprintf(buf,
- "INT %u: (%x) [%p: %p] %s",
+ if (param->vector <= 255) {
+ int_subscriber subscriber = subscribers[param->vector];
+ if (subscriber) {
+ subscriber(param);
+ goto done;
+ }
+ }
+
+ if (fallback) {
+ fallback(param);
+ goto done;
+ }
+
+ kprint_panic("INT %u: (%x) [%p: %p] Unknown",
param->vector,
param->err_code,
param->cs,
- param->eip,
- msg);
- panic_msg(buf);
- while (1)
- ;
-}
+ param->eip);
-void
-interrupt_handler(isr_param* param)
-{
- switch (param->vector) {
- case 0:
- panic("Division by 0", param);
- break; // never reach
- case FAULT_GENERAL_PROTECTION:
- panic("General Protection", param);
- break; // never reach
- case FAULT_PAGE_FAULT:
- void* pg_fault_ptr = cpu_rcr2();
- if (pg_fault_ptr) {
- panic("Page Fault", param);
- } else {
- panic("Null pointer reference", param);
- }
- break; // never reach
- case LUNAIX_SYS_PANIC:
- panic_msg((char*)(param->registers.edi));
- while (1)
- ;
- break; // never reach
- default:
- panic("Unknown Interrupt", param);
- break; // never reach
+done:
+ // for all external interrupts except the spurious interrupt
+ // this is required by Intel Manual Vol.3A, section 10.8.1 & 10.8.5
+ if (param->vector >= EX_INTERRUPT_BEGIN && param->vector != APIC_SPIV_IV) {
+ apic_done_servicing();
}
+ return;
}
\ No newline at end of file
--- /dev/null
+#include <arch/x86/interrupts.h>
+#include <lunaix/tty/tty.h>
+#include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+
+#include <klibc/stdio.h>
+
+#include <hal/apic.h>
+
+
+static void
+__print_panic_msg(const char* msg, const isr_param* param)
+{
+ kprint_panic(" INT %u: (%x) [%p: %p] %s",
+ param->vector,
+ param->err_code,
+ param->cs,
+ param->eip,
+ msg);
+}
+
+void
+intr_routine_divide_zero (const isr_param* param)
+{
+ __print_panic_msg("Divide by zero!", param);
+ spin();
+}
+
+void
+intr_routine_general_protection (const isr_param* param)
+{
+ __print_panic_msg("General Protection", param);
+ spin();
+}
+
+void
+intr_routine_page_fault (const isr_param* param)
+{
+ void* pg_fault_ptr = cpu_rcr2();
+ if (!pg_fault_ptr) {
+ __print_panic_msg("Null pointer reference", param);
+ } else {
+ char buf[32];
+ sprintf(buf, "Page fault on %p", pg_fault_ptr);
+ __print_panic_msg(buf, param);
+ }
+ spin();
+}
+
+void
+intr_routine_sys_panic (const isr_param* param)
+{
+ __print_panic_msg((char*)(param->registers.edi), param);
+ spin();
+}
+
+void
+intr_routine_fallback (const isr_param* param)
+{
+ __print_panic_msg("Unknown Interrupt", param);
+ spin();
+}
+
+/**
+ * @brief ISR for Spurious interrupt
+ *
+ * @param isr_param passed by CPU
+ */
+void
+intr_routine_apic_spi (const isr_param* param)
+{
+ // FUTURE: do nothing for now
+}
+
+void
+intr_routine_apic_error (const isr_param* param)
+{
+ uint32_t error_reg = apic_read_reg(APIC_ESR);
+ char buf[32];
+ sprintf(buf, "APIC error, ESR=0x%x", error_reg);
+ __print_panic_msg(buf, param);
+ spin();
+}
+
+void
+intr_routine_init()
+{
+ intr_subscribe(FAULT_DIVISION_ERROR, intr_routine_divide_zero);
+ intr_subscribe(FAULT_GENERAL_PROTECTION, intr_routine_general_protection);
+ intr_subscribe(FAULT_PAGE_FAULT, intr_routine_page_fault);
+ intr_subscribe(LUNAIX_SYS_PANIC, intr_routine_sys_panic);
+ intr_subscribe(APIC_SPIV_IV, intr_routine_apic_spi);
+ intr_subscribe(APIC_ERROR_IV, intr_routine_apic_error);
+
+ intr_set_fallback_handler(intr_set_fallback_handler);
+}
\ No newline at end of file
_after_gdt:
- movl $mb_info, (%esp)
+ movl $mb_info, _k_init_mb_info
call _kernel_pre_init
#include <lunaix/mm/vmm.h>
#include <lunaix/mm/kalloc.h>
#include <lunaix/spike.h>
+#include <lunaix/syslog.h>
+
+#include <hal/rtc.h>
+#include <hal/apic.h>
+#include <hal/ioapic.h>
+#include <hal/acpi/acpi.h>
#include <arch/x86/boot/multiboot.h>
#include <arch/x86/idt.h>
+#include <arch/x86/interrupts.h>
-#include <libc/stdio.h>
+#include <klibc/stdio.h>
#include <stdint.h>
#include <stddef.h>
extern uint8_t __kernel_end;
extern uint8_t __init_hhk_end;
+multiboot_info_t* _k_init_mb_info;
+
+LOG_MODULE("INIT");
+
void
setup_memory(multiboot_memory_map_t* map, size_t map_size);
setup_kernel_runtime();
void
-_kernel_pre_init(multiboot_info_t* mb_info) {
+lock_reserved_memory();
+
+void
+unlock_reserved_memory();
+
+void
+_kernel_pre_init() {
_init_idt();
+ intr_routine_init();
- pmm_init(MEM_1MB + (mb_info->mem_upper << 10));
+ pmm_init(MEM_1MB + (_k_init_mb_info->mem_upper << 10));
vmm_init();
+ rtc_init();
tty_init((void*)VGA_BUFFER_PADDR);
- tty_set_theme(VGA_COLOR_GREEN, VGA_COLOR_BLACK);
+ tty_set_theme(VGA_COLOR_WHITE, VGA_COLOR_BLACK);
}
void
-_kernel_init(multiboot_info_t* mb_info) {
- printf("[KERNEL] === Initialization === \n");
+_kernel_init() {
+ kprintf("[MM] Mem: %d KiB, Extended Mem: %d KiB\n",
+ _k_init_mb_info->mem_lower,
+ _k_init_mb_info->mem_upper);
- printf("[MM] Mem: %d KiB, Extended Mem: %d KiB\n",
- mb_info->mem_lower,
- mb_info->mem_upper);
+ unsigned int map_size = _k_init_mb_info->mmap_length / sizeof(multiboot_memory_map_t);
+
+ setup_memory((multiboot_memory_map_t*)_k_init_mb_info->mmap_addr, map_size);
- unsigned int map_size = mb_info->mmap_length / sizeof(multiboot_memory_map_t);
- setup_memory((multiboot_memory_map_t*)mb_info->mmap_addr, map_size);
setup_kernel_runtime();
}
void
_kernel_post_init() {
- printf("[KERNEL] === Post Initialization === \n");
size_t hhk_init_pg_count = ((uintptr_t)(&__init_hhk_end)) >> PG_SIZE_BITS;
- printf("[MM] Releaseing %d pages from 0x0.\n", hhk_init_pg_count);
+ kprintf(KINFO "[MM] Releaseing %d pages from 0x0.\n", hhk_init_pg_count);
- // 清除 hhk_init 与前1MiB的映射
- for (size_t i = 0; i < hhk_init_pg_count; i++) {
+ // Fuck it, I will no longer bother this little 1MiB
+ // I just release 4 pages for my APIC & IOAPIC remappings
+ for (size_t i = 0; i < 3; i++) {
vmm_unmap_page((void*)(i << PG_SIZE_BITS));
}
+
+ // 锁定所有系统预留页(内存映射IO,ACPI之类的),并且进行1:1映射
+ lock_reserved_memory();
- assert_msg(kalloc_init(), "Fail to initialize heap");
+ acpi_init(_k_init_mb_info);
+ uintptr_t ioapic_addr = acpi_get_context()->madt.ioapic->ioapic_addr;
+
+ pmm_mark_page_occupied(FLOOR(__APIC_BASE_PADDR, PG_SIZE_BITS));
+ pmm_mark_page_occupied(FLOOR(ioapic_addr, PG_SIZE_BITS));
+
+ vmm_set_mapping(APIC_BASE_VADDR, __APIC_BASE_PADDR, PG_PREM_RW);
+ vmm_set_mapping(IOAPIC_BASE_VADDR, ioapic_addr, PG_PREM_RW);
+
+ ioapic_init();
+ init_apic();
+
+ for (size_t i = 256; i < hhk_init_pg_count; i++) {
+ vmm_unmap_page((void*)(i << PG_SIZE_BITS));
+ }
+}
+
+void
+lock_reserved_memory() {
+ multiboot_memory_map_t* mmaps = _k_init_mb_info->mmap_addr;
+ size_t map_size = _k_init_mb_info->mmap_length / sizeof(multiboot_memory_map_t);
+ for (unsigned int i = 0; i < map_size; i++) {
+ multiboot_memory_map_t mmap = mmaps[i];
+ if (mmap.type == MULTIBOOT_MEMORY_AVAILABLE) {
+ continue;
+ }
+ uint8_t* pa = PG_ALIGN(mmap.addr_low);
+ size_t pg_num = CEIL(mmap.len_low, PG_SIZE_BITS);
+ for (size_t j = 0; j < pg_num; j++)
+ {
+ vmm_set_mapping((pa + (j << PG_SIZE_BITS)), (pa + (j << PG_SIZE_BITS)), PG_PREM_R);
+ }
+ }
+}
+
+void
+unlock_reserved_memory() {
+ multiboot_memory_map_t* mmaps = _k_init_mb_info->mmap_addr;
+ size_t map_size = _k_init_mb_info->mmap_length / sizeof(multiboot_memory_map_t);
+ for (unsigned int i = 0; i < map_size; i++) {
+ multiboot_memory_map_t mmap = mmaps[i];
+ if (mmap.type == MULTIBOOT_MEMORY_AVAILABLE) {
+ continue;
+ }
+ uint8_t* pa = PG_ALIGN(mmap.addr_low);
+ size_t pg_num = CEIL(mmap.len_low, PG_SIZE_BITS);
+ for (size_t j = 0; j < pg_num; j++)
+ {
+ vmm_unmap_page((pa + (j << PG_SIZE_BITS)));
+ }
+ }
}
// 按照 Memory map 标识可用的物理页
void
setup_memory(multiboot_memory_map_t* map, size_t map_size) {
+
+ // First pass, to mark the physical pages
for (unsigned int i = 0; i < map_size; i++) {
multiboot_memory_map_t mmap = map[i];
- printf("[MM] Base: 0x%x, len: %u KiB, type: %u\n",
+ kprintf("[MM] Base: 0x%x, len: %u KiB, type: %u\n",
map[i].addr_low,
map[i].len_low >> 10,
map[i].type);
// 整数向上取整除法
uintptr_t pg = map[i].addr_low + 0x0fffU;
pmm_mark_chunk_free(pg >> PG_SIZE_BITS, map[i].len_low >> PG_SIZE_BITS);
- printf("[MM] Freed %u pages start from 0x%x\n",
+ kprintf(KINFO "[MM] Freed %u pages start from 0x%x\n",
map[i].len_low >> PG_SIZE_BITS,
pg & ~0x0fffU);
}
}
- // 将内核占据的页设为已占用
- size_t pg_count = (uintptr_t)(&__kernel_end - &__kernel_start) >> PG_SIZE_BITS;
- pmm_mark_chunk_occupied(V2P(&__kernel_start) >> PG_SIZE_BITS, pg_count);
- printf("[MM] Allocated %d pages for kernel.\n", pg_count);
+ // 将内核占据的页,包括前1MB,hhk_init 设为已占用
+ size_t pg_count = V2P(&__kernel_end) >> PG_SIZE_BITS;
+ pmm_mark_chunk_occupied(0, pg_count);
+ kprintf(KINFO "[MM] Allocated %d pages for kernel.\n", pg_count);
size_t vga_buf_pgs = VGA_BUFFER_SIZE >> PG_SIZE_BITS;
// 更新VGA缓冲区位置至虚拟地址
tty_set_buffer((void*)VGA_BUFFER_VADDR);
- printf("[MM] Mapped VGA to %p.\n", VGA_BUFFER_VADDR);
+ kprintf(KINFO "[MM] Mapped VGA to %p.\n", VGA_BUFFER_VADDR);
+
}
void
for (size_t i = 0; i < (K_STACK_SIZE >> PG_SIZE_BITS); i++) {
vmm_alloc_page((void*)(K_STACK_START + (i << PG_SIZE_BITS)), PG_PREM_RW);
}
- printf("[MM] Allocated %d pages for stack start at %p\n", K_STACK_SIZE>>PG_SIZE_BITS, K_STACK_START);
+ kprintf(KINFO "[MM] Allocated %d pages for stack start at %p\n", K_STACK_SIZE>>PG_SIZE_BITS, K_STACK_START);
+ assert_msg(kalloc_init(), "Fail to initialize heap");
}
\ No newline at end of file
#include <hal/cpu.h>
#include <hal/rtc.h>
-#include <libc/stdio.h>
+#include <lunaix/syslog.h>
#include <lunaix/mm/kalloc.h>
#include <lunaix/mm/vmm.h>
#include <lunaix/spike.h>
extern uint8_t __kernel_start;
+LOG_MODULE("LX")
+
void
_kernel_main()
{
char buf[64];
- printf("Hello higher half kernel world!\nWe are now running in virtual "
+ kprintf(KINFO "Hello higher half kernel world!\nWe are now running in virtual "
"address space!\n\n");
cpu_get_brand(buf);
- printf("CPU: %s\n\n", buf);
+ kprintf("CPU: %s\n\n", buf);
void* k_start = vmm_v2p(&__kernel_start);
- printf(
- "The kernel's base address mapping: %p->%p\n", &__kernel_start, k_start);
+ kprintf(KINFO "The kernel's base address mapping: %p->%p\n", &__kernel_start, k_start);
// test malloc & free
big_[1] = 23;
big_[2] = 3;
- printf("%u, %u, %u\n", big_[0], big_[1], big_[2]);
+ kprintf(KINFO "%u, %u, %u\n", big_[0], big_[1], big_[2]);
// good free
lxfree(arr);
lxfree(big_);
- rtc_datetime datetime;
-
- rtc_get_datetime(&datetime);
-
- printf("%u/%u/%u %u:%u:%u",
- datetime.year,
- datetime.month,
- datetime.day,
- datetime.hour,
- datetime.minute,
- datetime.second);
+ spin();
}
\ No newline at end of file
--- /dev/null
+#include <lunaix/syslog.h>
+#include <lunaix/tty/tty.h>
+#include <klibc/stdio.h>
+
+#define MAX_KPRINTF_BUF_SIZE 1024
+#define MAX_XFMT_SIZE 1024
+
+char buf[MAX_KPRINTF_BUF_SIZE];
+
+void
+__kprintf(const char* component, const char* fmt, va_list args) {
+ if (!fmt) return;
+ char log_level = '0';
+ char expanded_fmt[MAX_XFMT_SIZE];
+ vga_attribute current_theme = tty_get_theme();
+
+ if (*fmt == '\x1b') {
+ log_level = *(++fmt);
+ fmt++;
+ }
+
+ switch (log_level)
+ {
+ case '0':
+ snprintf(expanded_fmt, MAX_XFMT_SIZE, "[%s] (%s) %s", "INFO", component, fmt);
+ break;
+ case '1':
+ tty_set_theme(VGA_COLOR_BROWN, current_theme >> 12);
+ snprintf(expanded_fmt, MAX_XFMT_SIZE, "[%s] (%s) %s", "INFO", component, fmt);
+ break;
+ case '2':
+ tty_set_theme(VGA_COLOR_LIGHT_RED, current_theme >> 12);
+ snprintf(expanded_fmt, MAX_XFMT_SIZE, "[%s] (%s) %s", "EROR", component, fmt);
+ break;
+ default:
+ snprintf(expanded_fmt, MAX_XFMT_SIZE, "[%s] (%s) %s", "LOG", component, fmt);
+ break;
+ }
+
+ __sprintf_internal(buf, expanded_fmt, MAX_KPRINTF_BUF_SIZE, args);
+ tty_put_str(buf);
+ tty_set_theme(current_theme >> 8, current_theme >> 12);
+}
+
+void
+kprint_panic(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ tty_set_theme(VGA_COLOR_WHITE, VGA_COLOR_RED);
+ tty_clear_line(10);
+ tty_clear_line(11);
+ tty_clear_line(12);
+ tty_set_cpos(0, 11);
+
+ __sprintf_internal(buf, fmt, MAX_KPRINTF_BUF_SIZE, args);
+ tty_put_str(buf);
+
+ va_end(args);
+}
\ No newline at end of file
#include <lunaix/constants.h>
#include <lunaix/spike.h>
-#include <libc/string.h>
+#include <klibc/string.h>
#include <stdint.h>
}
void*
-lxcalloc(size_t size) {
- void* ptr = lxmalloc(size);
+lxcalloc(size_t n, size_t elem) {
+ size_t pd = n * elem;
+
+ // overflow detection
+ if (pd < elem || pd < n) {
+ return NULL;
+ }
+
+ void* ptr = lxmalloc(pd);
if (!ptr) {
return NULL;
}
- return memset(ptr, 0, size);
+ return memset(ptr, 0, pd);
}
void
#include <hal/cpu.h>
-#include <libc/string.h>
+#include <klibc/string.h>
#include <lunaix/mm/page.h>
#include <lunaix/mm/pmm.h>
#include <lunaix/mm/vmm.h>
return true;
}
+void
+vmm_set_mapping(void* va, void* pa, pt_attr attr) {
+ assert(((uintptr_t)va & 0xFFFU) == 0);
+
+ uint32_t l1_index = L1_INDEX(va);
+ uint32_t l2_index = L2_INDEX(va);
+
+ // prevent map of recursive mapping region
+ if (l1_index == 1023) {
+ return;
+ }
+
+ __vmm_map_internal(l1_index, l2_index, (uintptr_t)pa, attr, false);
+}
+
void
vmm_unmap_page(void* va)
{
uint32_t l1_index = L1_INDEX(va);
uint32_t l2_index = L2_INDEX(va);
+
+ // prevent unmap of recursive mapping region
+ if (l1_index == 1023) {
+ return;
+ }
+
x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
x86_pte_t l1pte = l1pt->entry[l1_index];
#include <lunaix/spike.h>
#include <arch/x86/interrupts.h>
-#include <libc/stdio.h>
+#include <klibc/stdio.h>
static char buffer[1024];
void __assert_fail(const char* expr, const char* file, unsigned int line) {
- sprintf(buffer, "[ASSERT] %s (%s:%u)", expr, file, line);
+ sprintf(buffer, "%s (%s:%u)", expr, file, line);
// Here we load the buffer's address into %edi ("D" constraint)
// This is a convention we made that the LUNAIX_SYS_PANIC syscall will
);
spin(); // never reach
+}
+
+void panick(const char* msg) {
+ asm(
+ "int %0"
+ ::"i"(LUNAIX_SYS_PANIC), "D"(msg)
+ );
+ spin();
}
\ No newline at end of file
-#include <libc/string.h>
+#include <klibc/string.h>
#include <lunaix/tty/tty.h>
#include <lunaix/constants.h>
#include <stdint.h>
tty_get_cpos(unsigned int* x, unsigned int* y) {
*x = tty_x;
*y = tty_y;
+}
+
+vga_attribute
+tty_get_theme() {
+ return tty_theme_color;
}
\ No newline at end of file
#define __LUNAIX_LIBC
-#include <libc/stdio.h>
-#include <libc/stdlib.h>
-#include <libc/string.h>
+#include <klibc/stdio.h>
+#include <klibc/stdlib.h>
+#include <klibc/string.h>
#include <stdint.h>
#define NUMBUFSIZ 24
// FIXME: use something like IO_FILE to abstract this into a more flexible, stream based, vprintf
void
-__sprintf_internal(char* buffer, char* fmt, va_list vargs)
+__sprintf_internal(char* buffer, char* fmt, size_t max_len, va_list vargs)
{
// This sprintf just a random implementation I found it on Internet . lol.
// Of course, with some modifications for porting to LunaixOS :)
char numbuf[NUMBUFSIZ];
uint32_t ptr = 0;
for (; *fmt; ++fmt) {
+ if (max_len && ptr >= max_len - 1) {
+ break;
+ }
+
if (*fmt != '%') {
buffer[ptr++] = *fmt;
continue;
{
va_list args;
va_start(args, fmt);
- __sprintf_internal(buffer, fmt, args);
+ __sprintf_internal(buffer, fmt, 0, args);
+ va_end(args);
+}
+
+void
+snprintf(char* buffer, size_t n, char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ __sprintf_internal(buffer, fmt, n, args);
va_end(args);
}
\ No newline at end of file
#define __LUNAIX_LIBC
#include <stddef.h>
-#include <libc/stdlib.h>
+#include <klibc/stdlib.h>
char base_char[] = "0123456789abcdefghijklmnopqrstuvwxyz";
#include <stdint.h>
-#include <libc/string.h>
+#include <klibc/string.h>
void*
memcpy(void* dest, const void* src, size_t num)
-#include <libc/string.h>
+#include <klibc/string.h>
const char*
strchr(const char* str, int character)
--- /dev/null
+#include <klibc/string.h>
+
+char*
+strcpy(char* dest, const char* src) {
+ char c;
+ unsigned int i = 0;
+ while ((c = src[i]))
+ {
+ dest[i] = c;
+ i++;
+ }
+ dest[i] = '\0';
+ return dest;
+}
+
+char*
+strncpy(char* dest, const char* src, size_t n) {
+ char c;
+ unsigned int i = 0;
+ while ((c = src[i]) && i < n) dest[i++] = c;
+ while (i < n) dest[i++] = 0;
+ return dest;
+}
\ No newline at end of file
-#include <libc/string.h>
+#include <klibc/string.h>
size_t
strlen(const char* str)
+++ /dev/null
-#define __LUNAIX_LIBC
-#include <libc/stdio.h>
-#include <stdarg.h>
-
-#include <lunaix/tty/tty.h>
-
-void
-printf(char* fmt, ...)
-{
- char buffer[1024];
- va_list args;
- va_start(args, fmt);
- __sprintf_internal(buffer, fmt, args);
- va_end(args);
-
- // 啊哈,直接操纵framebuffer。日后须改进(FILE* ?)
- tty_put_str(buffer);
-}
\ No newline at end of file
+++ /dev/null
-#include <libc/string.h>
-
-char*
-strcpy(char* dest, const char* src) {
- char c;
- unsigned int i = 0;
- while ((c = src[i]))
- {
- dest[i] = c;
- i++;
- }
- dest[i] = '\0';
- return dest;
-}
\ No newline at end of file
all-debug: LDFLAGS := -g -ffreestanding $(O) -nostdlib -lgcc
all-debug: clean $(BUILD_DIR)/$(OS_ISO)
@echo "Dumping the disassembled kernel code to $(BUILD_DIR)/kdump.txt"
- @i686-elf-objdump -S $(BIN_DIR)/$(OS_BIN) > $(BUILD_DIR)/kdump.txt
+ @${TOOLCHAIN}/i686-elf-objdump -S $(BIN_DIR)/$(OS_BIN) > $(BUILD_DIR)/kdump.txt
clean:
@rm -rf $(BUILD_DIR)
@telnet 127.0.0.1 $(QEMU_MON_PORT)
debug-qemu: all-debug
- @i686-elf-objcopy --only-keep-debug $(BIN_DIR)/$(OS_BIN) $(BUILD_DIR)/kernel.dbg
- @qemu-system-i386 -s -S -cdrom $(BUILD_DIR)/$(OS_ISO) -monitor telnet::$(QEMU_MON_PORT),server,nowait &
+ @${TOOLCHAIN}/i686-elf-objcopy --only-keep-debug $(BIN_DIR)/$(OS_BIN) $(BUILD_DIR)/kernel.dbg
+ @qemu-system-i386 -m 1G -rtc base=utc -s -S -cdrom $(BUILD_DIR)/$(OS_ISO) -monitor telnet::$(QEMU_MON_PORT),server,nowait &
@sleep 1
- @$(QEMU_MON_TERM) -e "telnet 127.0.0.1 $(QEMU_MON_PORT)"
+ @$(QEMU_MON_TERM) -- telnet 127.0.0.1 $(QEMU_MON_PORT)
@gdb -s $(BUILD_DIR)/kernel.dbg -ex "target remote localhost:1234"
debug-qemu-vscode: all-debug
- @i686-elf-objcopy --only-keep-debug $(BIN_DIR)/$(OS_BIN) $(BUILD_DIR)/kernel.dbg
- @qemu-system-i386 -s -S -cdrom $(BUILD_DIR)/$(OS_ISO) -monitor telnet::$(QEMU_MON_PORT),server,nowait &
+ @${TOOLCHAIN}/i686-elf-objcopy --only-keep-debug $(BIN_DIR)/$(OS_BIN) $(BUILD_DIR)/kernel.dbg
+ @qemu-system-i386 -s -S -m 1G -cdrom $(BUILD_DIR)/$(OS_ISO) -monitor telnet::$(QEMU_MON_PORT),server,nowait &
@sleep 0.5
@telnet 127.0.0.1 $(QEMU_MON_PORT)