194 lines
7.8 KiB
C
194 lines
7.8 KiB
C
/*
|
|
Copyright 2025 Kamil Kowalczyk
|
|
|
|
Redistribution and use in source and binary forms, with or
|
|
without modification, are permitted provided that the following
|
|
conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
3. Neither the name of the copyright holder nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <libk/types.h>
|
|
#include <libk/util.h>
|
|
#include <libk/string.h>
|
|
#include <libk/stdatomic.h>
|
|
#include <sys/cpu.h>
|
|
#include <sys/gdt_flush.h>
|
|
#include <sys/tss_flush.h>
|
|
#include <sys/idt_flush.h>
|
|
#include <sys/isr.h>
|
|
#include <sys/pic.h>
|
|
|
|
#define KERNEL_INTR_STACK_SIZE 8192
|
|
|
|
static volatile struct gdt_entry gdt[6];
|
|
static volatile struct gdt_ptr gdtptr;
|
|
static volatile struct tss tss;
|
|
static volatile struct idt_entry idt[0x100];
|
|
static volatile struct idt_ptr idtptr;
|
|
static volatile byte_t kernel_intr_stack[KERNEL_INTR_STACK_SIZE];
|
|
|
|
static struct { volatile uint32_t eflags; atomic_int nesting; } intr_state =
|
|
{ .eflags = 0, .nesting = 0 };
|
|
|
|
static void gdt_make_entry(int index, uint32_t base, uint32_t limit,
|
|
uint8_t type, uint8_t privl, uint8_t system) {
|
|
gdt[index].limit_low = (uint16_t)limit;
|
|
gdt[index].limit_up = (uint16_t)(limit >> 16);
|
|
gdt[index].base_low = (uint16_t)base;
|
|
gdt[index].base_mid = (uint8_t)(base >> 16);
|
|
gdt[index].base_up = (uint8_t)(base >> 24);
|
|
gdt[index].access = type | (system << 4) | (privl << 5) | (1<<7);
|
|
gdt[index].limit_up |= (1<<7) | (1<<6);
|
|
}
|
|
|
|
static void gdt_init(void) {
|
|
gdtptr.limit = sizeof(gdt) - 1;
|
|
gdtptr.base = (uint32_t)&gdt;
|
|
gdt_make_entry(0, 0, 0, 0, 0, 0);
|
|
gdt_make_entry(GDT_KCODE, 0, 0xFFFFF, GDT_CODESEG, GDT_KERNEL_PRIVL, GDT_MEMORY);
|
|
gdt_make_entry(GDT_KDATA, 0, 0xFFFFF, GDT_DATASEG, GDT_KERNEL_PRIVL, GDT_MEMORY);
|
|
gdt_make_entry(GDT_UCODE, 0, 0xFFFFF, GDT_CODESEG, GDT_USER_PRIVL, GDT_MEMORY);
|
|
gdt_make_entry(GDT_UDATA, 0, 0xFFFFF, GDT_DATASEG, GDT_USER_PRIVL, GDT_MEMORY);
|
|
gdt_make_entry(GDT_TSS, (uint32_t)&tss, TSS_SIZE, GDT_TSS_SEG, GDT_KERNEL_PRIVL, GDT_SYSTEM);
|
|
|
|
gdt_flush((ptr_t)&gdtptr);
|
|
}
|
|
|
|
static void tss_init(void) {
|
|
memset((void *)&tss, 0, sizeof(tss));
|
|
|
|
tss.ldt_selector = 0;
|
|
tss.prev_tss = GDT_TSS;
|
|
tss.iomap_base = sizeof(struct tss);
|
|
tss.esp_0 = (uint32_t)&kernel_intr_stack[KERNEL_INTR_STACK_SIZE - 1];
|
|
tss.ss_0 = GDT_KERNEL_DS;
|
|
|
|
tss_flush();
|
|
}
|
|
|
|
static void idt_make_entry(int index, uint32_t base, uint32_t sel, uint8_t type, uint8_t access) {
|
|
volatile struct idt_entry *ent = &idt[index];
|
|
ent->base_low = (uint16_t)(base & 0xFFFF);
|
|
ent->base_up = (uint16_t)((base >> 16) & 0xFFFF);
|
|
ent->sel = (uint16_t)sel;
|
|
ent->always0 = 0;
|
|
ent->flags = type | (access << 5) | (1<<7);
|
|
}
|
|
|
|
void idt_init(void) {
|
|
memset((void *)idt, 0, sizeof(idt));
|
|
idtptr.base = (uint32_t)idt;
|
|
idtptr.limit = sizeof(idt) - 1;
|
|
|
|
idt_make_entry(0, (uint32_t)&except0, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(1, (uint32_t)&except1, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(2, (uint32_t)&except2, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(3, (uint32_t)&except3, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(4, (uint32_t)&except4, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(5, (uint32_t)&except5, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(6, (uint32_t)&except6, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(7, (uint32_t)&except7, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(8, (uint32_t)&except8, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(9, (uint32_t)&except9, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(10, (uint32_t)&except10, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(11, (uint32_t)&except11, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(12, (uint32_t)&except12, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(13, (uint32_t)&except13, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(14, (uint32_t)&except14, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(15, (uint32_t)&except15, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(16, (uint32_t)&except16, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(17, (uint32_t)&except17, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(18, (uint32_t)&except18, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(19, (uint32_t)&except19, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(20, (uint32_t)&except20, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(21, (uint32_t)&except21, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(22, (uint32_t)&except22, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(23, (uint32_t)&except23, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(24, (uint32_t)&except24, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(25, (uint32_t)&except25, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(26, (uint32_t)&except26, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(27, (uint32_t)&except27, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(28, (uint32_t)&except28, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(29, (uint32_t)&except29, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(30, (uint32_t)&except30, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(31, (uint32_t)&except31, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(32, (uint32_t)&irq0, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(33, (uint32_t)&irq1, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(34, (uint32_t)&irq2, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(35, (uint32_t)&irq3, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(36, (uint32_t)&irq4, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(37, (uint32_t)&irq5, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(38, (uint32_t)&irq6, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(39, (uint32_t)&irq7, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(40, (uint32_t)&irq8, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(41, (uint32_t)&irq9, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(42, (uint32_t)&irq10, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(43, (uint32_t)&irq11, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(44, (uint32_t)&irq12, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(45, (uint32_t)&irq13, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(46, (uint32_t)&irq14, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(47, (uint32_t)&irq15, GDT_KERNEL_CS, 0x0E, 0);
|
|
idt_make_entry(128, (uint32_t)&except128, GDT_KERNEL_CS, 0x0E, 3);
|
|
|
|
idt_flush((ptr_t)&idtptr);
|
|
}
|
|
|
|
void cpu_init(void) {
|
|
gdt_init();
|
|
tss_init();
|
|
idt_init();
|
|
pic_init();
|
|
}
|
|
|
|
static uint32_t intr_save1(void) {
|
|
uint32_t eflags;
|
|
__asm__ volatile ("pushfl; cli; popl %0;" : "=r"(eflags) :: "memory", "cc");
|
|
return eflags;
|
|
}
|
|
|
|
static void intr_restore1(uint32_t eflags) {
|
|
if (eflags & (1 << 9)) {
|
|
__asm__ volatile("sti" ::: "memory", "cc");
|
|
}
|
|
}
|
|
|
|
void intr_save(void) {
|
|
int prev = atomic_fetch_add_explicit(&intr_state.nesting, 1, memory_order_acq_rel);
|
|
if (prev == 0) {
|
|
intr_state.eflags = intr_save1();
|
|
}
|
|
}
|
|
|
|
void intr_restore(void) {
|
|
int prev = atomic_fetch_sub_explicit(&intr_state.nesting, 1, memory_order_acq_rel);
|
|
if (prev == 1) {
|
|
intr_restore1(intr_state.eflags);
|
|
}
|
|
}
|
|
|
|
void cpu_relax(void) {
|
|
__asm__ volatile("pause");
|
|
}
|