Browse Source

apci & lapic

master
Mathieu Serandour 2 years ago
parent
commit
91ed6bc293
  1. 2
      .gitignore
  2. 11
      Makefile
  3. BIN
      disk_root/EFI/BOOT/BOOTX64.EFI
  4. 19
      disk_root/boot/limine.cfg
  5. BIN
      disk_root/boot/limine.sys
  6. 53
      kernel/Makefile
  7. 208
      kernel/acpi/acpi.c
  8. 14
      kernel/acpi/acpi.h
  9. 182
      kernel/acpi/acpitables.h
  10. 218
      kernel/cpuid.h
  11. 50
      kernel/cpuid.s
  12. 231
      kernel/debug/dump.c
  13. 46
      kernel/debug/dump.h
  14. 38
      kernel/entry.c
  15. 95
      kernel/int/apic.c
  16. 53
      kernel/int/apic.h
  17. 222
      kernel/int/idt.c
  18. 70
      kernel/int/idt.h
  19. 12
      kernel/int/idt.s
  20. 265
      kernel/int/isr.c
  21. 6
      kernel/klib/math.h
  22. 38
      kernel/klib/sprintf.c
  23. 62
      kernel/linker.ld
  24. 17
      kernel/memory/kalloc.c
  25. 38
      kernel/regs.s
  26. 19
      kernel/video/terminal.c
  27. 10
      kernel/video/terminal.h
  28. 139
      kernel/video/video.c
  29. 3
      kernel/video/video.h
  30. BIN
      resources/bmp/charmap.bmp
  31. 36
      resources/bmp/font_converter.py

2
.gitignore

@ -1,7 +1,7 @@
.vscode
disk.hdd
img_moint
limine-bootloader
limine-bootloader/
*.o
include_cp
*.elf

11
Makefile

@ -3,14 +3,17 @@
HDD_ROOT := disc_root
HDD_FILE := disk.hdd
USED_LOOPBACK := /dev/loop6
LIMINE_INSTALL := ./limine-bootloader/limine-install-linux-x86_64
QEMU_PATH := "/mnt/d/Program Files/qemu/qemu-system-x86_64.exe"
QEMU_ARGS := -monitor stdio -d cpu_reset -bios "d:/Program Files/qemu/bios/OVMF.fd"
QEMU_ARGS := -monitor stdio -bios "d:/Program Files/qemu/bios/OVMF.fd" -m 256 -vga std -no-reboot
# -bios "d:/Program Files/qemu/bios/OVMF.fd"
run: all
$(QEMU_PATH) $(QEMU_ARGS) $(HDD_FILE)
$(QEMU_PATH) $(QEMU_ARGS) -drive format=raw,file=$(HDD_FILE)
all: disk
@ -22,7 +25,7 @@ $(HDD_FILE): kernel/entry.c
sudo /sbin/parted -s $(HDD_FILE) mklabel gpt
sudo /sbin/parted -s $(HDD_FILE) mkpart ESP fat32 2048s 100%
sudo /sbin/parted -s $(HDD_FILE) set 1 esp on
./limine-bootloader/limine-install $(HDD_FILE)
$(LIMINE_INSTALL) $(HDD_FILE)
disk: kernel $(HDD_FILE)

BIN
disk_root/EFI/BOOT/BOOTX64.EFI

Binary file not shown.

19
disk_root/boot/limine.cfg

@ -2,20 +2,29 @@ DEFAULT_ENTRY=1
#TIMEOUT=10
#GRAPHICS=yes
#VERBOSE=yes
TIMEOUT=0
TIMEOUT=10
GRAPHICS=no
VERBOSE=no
THEME_MARGIN=64
#RESOLUTION=800x600
RESOLUTION=800x600
#BACKGROUND_PATH=boot:///boot/bg.bmp
#THEME_BACKGROUND=8f000000
BACKGROUND_STYLE=centered
:Stivale2 Test
:Bincows
PROTOCOL=stivale2
#RESOLUTION=800x600
#RESOLUTION=960x540
#BACKGROUND_PATH=boot:///boot/bg.bmp
KERNEL_PATH=boot:///boot/kernel.elf
BACKGROUND_STYLE=centered
BACKGROUND_STYLE=centered
:Lucarnel
PROTOCOL=stivale
#RESOLUTION=800x600
#BACKGROUND_PATH=boot:///boot/bg.bmp
KERNEL_PATH=boot:///boot/lucarnel.elf

BIN
disk_root/boot/limine.sys

Binary file not shown.

53
kernel/Makefile

@ -4,34 +4,31 @@ ASM := nasm
ASM_FLAGS := -felf64
CFLAGS = -Wall -Wextra -O3 -pipe
INTERNALLDFLAGS := \
-fno-pic \
-Wl,-static,--no-dynamic-linker,-ztext \
-znocombreloc \
-nostdlib \
-Tlinker.ld \
-z max-page-size=0x1000
INTERNALCFLAGS := \
-znocombreloc \
-I/opt/cross/include/ \
-std=gnu11 \
-ffreestanding \
-fno-stack-protector \
-fno-pic \
-mno-80387 \
-mno-mmx \
-mno-3dnow \
-mno-sse \
-mno-sse2 \
-mno-red-zone \
-m64
INTERNALLDFLAGS := -Tlinker.ld \
-nostdlib \
-Wl,--export-dynamic \
-zmax-page-size=0x1000 \
-static \
-pie \
-ztext
# ,--dynamic-linker
INTERNALCFLAGS := -mgeneral-regs-only \
-ffreestanding \
-mno-red-zone \
-mno-80387 \
-fno-omit-frame-pointer \
-std=gnu11 \
-Wall -Wextra \
-fcompare-debug-second \
-I/opt/cross/include/ \
-fpie
CFILES := $(shell find ./ -type f -name '*.c')
SFILES := $(shell find ./ -type f -name '*.s')
OBJ := $(SFILES:.s=.s.o) $(CFILES:.c=.c.o) charmap.bmp.o
OBJ := $(SFILES:.s=.s.o) $(CFILES:.c=.c.o)
#charmap.bmp.o
.PHONY: all clean
all: $(KERNEL)
@ -41,10 +38,10 @@ all: $(KERNEL)
$(KERNEL): $(OBJ)
$(CC) $(INTERNALLDFLAGS) $(OBJ) -o $@
%.bmp.o: ../resources/bmp/%.bmp
$(LD) -r -b binary -o $@ $<
%.txt.o: ../resources/ascii/%.txt
$(LD) -r -b binary -o $@ $<
#%.bmp.o: ../resources/bmp/%.bmp
# $(LD) -r -b binary -o $@ $<
#%.txt.o: ../resources/ascii/%.txt
# $(LD) -r -b binary -o $@ $<
%.c.o: %.c
$(CC) $(CFLAGS) $(INTERNALCFLAGS) -c $< -o $@

208
kernel/acpi/acpi.c

@ -1,55 +1,153 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "acpi.h"
#include "../common.h"
#include "../debug/assert.h"
#include "../debug/dump.h"
#include "../klib/sprintf.h"
#include "acpitables.h"
bool checksum(void* table, size_t size) {
uint8_t sum = 0;
uint8_t* raw = table;
for(size_t i;size > 0; --size) {
sum += raw[i++];
}
return sum == 0;
}
typedef uint64_t rsdp_entry_t;
void read_acpi_tables(void* rsdp_location) {
struct RSDPDescriptor20* rsdpd = rsdp_location;
dump(rsdpd, sizeof(struct RSDPDescriptor20), 8, DUMP_HEX8);
assert(rsdpd->firstPart.revision >= 2);
// checksum for xsdt
assert(rsdpd->length == sizeof(rsdpd));
assert(checksum(rsdpd, rsdpd->length));
// lets parse it!!
const struct ACPISDTHeader* xsdt = (void *)rsdpd->xsdtAddress;
size_t n_entries = (xsdt->length - sizeof(xsdt)) / sizeof(rsdp_entry_t);
rsdp_entry_t* table = (xsdt + 1);
for(int i = 0; i < n_entries; i++) {
kprintf("%3u: %s\n", table[i])
//table[i]
}
}
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "acpi.h"
#include "../common.h"
#include "../debug/assert.h"
#include "../debug/dump.h"
#include "../klib/sprintf.h"
#include "../klib/string.h"
#include "acpitables.h"
#include "../int/apic.h"
extern struct APICConfig* apic_config;
static bool __ATTR_PURE__ checksum(const void* table, size_t size) {
uint8_t sum = 0;
const uint8_t* raw = table;
for(size_t i;size > 0; --size) {
sum += raw[i++];
}
return sum == 0;
}
static void parse_madt(const struct MADT* table);
static void parse_fadt(const struct ACPISDTHeader* table);
#define MADT_SIGNATURE 0x43495041
#define FACP_SIGNATURE 0x50434146
void read_acpi_tables(const void* rsdp_location) {
const struct RSDPDescriptor20* rsdpd = rsdp_location;
assert(rsdpd->firstPart.revision >= 2);
// checksum for xsdt
assert(rsdpd->length == sizeof(struct RSDPDescriptor20));
assert(checksum(rsdpd, rsdpd->length));
// lets parse it!!
const struct XSDT* xsdt = (void *)rsdpd->xsdtAddress;
size_t n_entries = (xsdt->header.length - sizeof(xsdt->header)) / sizeof(void*);
bool madt_parsed = false,
fadt_parsed = false;
for(size_t i = 0; i < n_entries; i++) {
const struct ACPISDTHeader* table = xsdt->entries[i];
assert(checksum(&table, table->length));
switch(table->signature.raw) {
case MADT_SIGNATURE:
parse_madt((const struct MADT *)table);
madt_parsed = true;
break;
case FACP_SIGNATURE:
parse_fadt(table);
fadt_parsed = true;
break;
default:
break;
}
kprintf("%3u: %4s\n", i, table->signature.arg);
}
asm volatile("hlt");
assert(madt_parsed);
assert(fadt_parsed);
}
static void parse_madt(const struct MADT* table) {
// checksum is already done
apic_config = (void*)(uint64_t)table->lAPIC_address;
const void* ptr = table->entries;
const void* end = (const void*)table + table->header.length;
/// dont override the override lapic or io apic entry
bool override_lapic_passed = false;
bool override_ioapic_passed = false;
(void)override_lapic_passed;
(void)override_ioapic_passed;
kprintf("table size: %u\n", end-ptr);
while(ptr < end) {
//for(int i = 10; i>0; i--) {
const struct MADTEntryHeader* entry_header = ptr;
switch(entry_header->type) {
case APIC_TYPE_LAPIC:
{
// const struct MADT_lapic_entry* entry = ptr;
}
break;
case APIC_TYPE_IO_APIC:
{
// const struct MADT_ioapic_entry* entry = ptr;
}
break;
case APIC_TYPE_IO_INTERRUPT_SOURCE_OVERRIDE:
{
// const struct MADT_ioapic_interrupt_source_override_entry* entry = ptr;
}
break;
case APIC_TYPE_IO_NMI:
{
// const struct MADT_IO_NMI_entry* entry = ptr;
}
break;
case APIC_TYPE_LOCAL_NMI:
{
// const struct MADT_LOCAL_NMI_entry* entry = ptr;
}
break;
case APIC_TYPE_LAPIC_ADDRESS_OVERRIDE:
{
// const struct MADT_LAPIC_address_override_entry* entry = ptr;
}
break;
case APIC_TYPE_LAPICX2:
{
// const struct MADT_lapicx2_entry* entry = ptr;
}
break;
default:
kprintf("WARNING: invalid APIC MADT entry %u\n", entry_header->type);
}
kprintf("entry: type %u ; size %u\n", entry_header->type,entry_header->length);
ptr += entry_header->length;
}
}
static void parse_fadt(const struct ACPISDTHeader* table) {
(void) table;
}

14
kernel/acpi/acpi.h

@ -1,7 +1,7 @@
#pragma once
/*
read RSDP, XSDP, MADT, FADT
*/
void read_acpi_tables(void* rsdp_location);
#pragma once
/*
read RSDP, XSDP, MADT, FADT
*/
void read_acpi_tables(const void* rsdp_location);

182
kernel/acpi/acpitables.h

@ -1,41 +1,141 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "../common.h"
struct RSDPDescriptor {
uint8_t signature[8];
uint8_t checksum;
uint8_t OEMID[6];
uint8_t revision;
uint32_t rsdtAddress;
} __packed;
struct RSDPDescriptor20 {
struct RSDPDescriptor firstPart;
uint32_t length;
uint64_t xsdtAddress;
uint8_t extendedChecksum;
uint8_t reserved[3];
} __packed;
// plagia from
// https://github.com/DorianXGH/Lucarnel/blob/master/src/includes/acpi.h
//
struct ACPISDTHeader
{
uint8_t signature[4]; // signature of the table
uint32_t length; // length of the table
uint8_t revision;
uint8_t checksum;
uint8_t OEMID[6];
uint8_t OEMtableID[8];
uint32_t OEMrevision;
uint32_t creatorID;
uint32_t creator_revision;
} __packed;
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "../common.h"
struct RSDPDescriptor {
uint8_t signature[8];
uint8_t checksum;
uint8_t OEMID[6];
uint8_t revision;
uint32_t rsdtAddress;
} __packed;
struct RSDPDescriptor20 {
struct RSDPDescriptor firstPart;
uint32_t length;
uint64_t xsdtAddress;
uint8_t extendedChecksum;
uint8_t reserved[3];
} __packed;
union acpi_signature {
char arg[4];
uint32_t raw;
} __packed;
// plagia from
// https://github.com/DorianXGH/Lucarnel/blob/master/src/includes/acpi.h
//
struct ACPISDTHeader {
union acpi_signature signature;
uint32_t length; // length of the table
uint8_t revision;
uint8_t checksum;
uint8_t OEMID[6];
uint8_t OEMtableID[8];
uint32_t OEMrevision;
uint32_t creatorID;
uint32_t creator_revision;
} __packed;
struct XSDT {
struct ACPISDTHeader header;
struct ACPISDTHeader* entries[];
} __packed;
struct RSDT {
struct ACPISDTHeader header;
uint32_t entries[];
} __packed;
struct MADTEntryHeader {
uint8_t type;
uint8_t length;
} __packed;
struct MADT_lapic_entry {
struct MADTEntryHeader header;
uint8_t proc_apic_ID;
uint8_t procID;
uint32_t flags; // bit0: enabled
} __packed;
struct MADT_ioapic_entry {
struct MADTEntryHeader header;
uint8_t id;
uint8_t reserved;
uint32_t address;
uint32_t global_system_interrupt_base;
} __packed;
struct MADT_ioapic_interrupt_source_override_entry {
struct MADTEntryHeader header;
uint8_t bus_source;
uint8_t irq_source;
uint32_t global_system_interrupt;
uint16_t flags;
} __packed;
struct MADT_IO_NMI_entry {
struct MADTEntryHeader header;
uint8_t source;
uint8_t reserved;
uint32_t global_system_interrupt;
} __packed;
struct MADT_LOCAL_NMI_entry {
struct MADTEntryHeader header;
uint8_t procID;
uint16_t flags;
uint8_t lint; // 0 or 1
} __packed;
struct MADT_LAPIC_address_override_entry {
struct MADTEntryHeader header;
uint8_t procID;
uint8_t flags;
uint32_t lint; // 0 or 1
} __packed;
struct MADT_lapicx2_entry {
struct MADTEntryHeader header;
uint8_t proc_lapic_ID;
uint64_t flags; // bit0: enabled
uint32_t acpi_id;
} __packed;
struct MADT {
struct ACPISDTHeader header;
uint32_t lAPIC_address;
uint32_t flags;
struct MADTEntryHeader* entries[];
} __packed;
// MADT entry types
#define APIC_TYPE_LAPIC 0
#define APIC_TYPE_IO_APIC 1
#define APIC_TYPE_IO_INTERRUPT_SOURCE_OVERRIDE 2
#define APIC_TYPE_IO_NMI 3
#define APIC_TYPE_LOCAL_NMI 4
#define APIC_TYPE_LAPIC_ADDRESS_OVERRIDE 5
#define APIC_TYPE_LAPICX2 9

218
kernel/cpuid.h

@ -1,109 +1,109 @@
#pragma once
#include <stdint.h>
struct cpuid_regs {
uint32_t eax, ebx, ecx, edx;
};
void cpuid(uint32_t eax, struct cpuid_regs* out_regs);
// doc from
/**
*
Basic CPUID Information
Initial EAX Value Register Information Provided about the Processor
0H EAX Maximum Input Value for Basic CPUID Information (see second table)
- EBX "Genu"
- ECX "ntel"
- EDX "ineI"
01H EAX Version Information: Type, Family, Model, and Stepping ID
- EBX Bits 7-0: Brand Index
- - Bits 15-8: CLFLUSH line size (Value . 8 = cache line size in bytes)
- - Bits 23-16: Number of logical processors per physical processor; two for the Pentium 4
processor supporting Hyper-Threading Technology
- - Bits 31-24: Local APIC ID
- ECX Extended Feature Information (see fourth table)
- EDX Feature Information (see fifth table)
02H EAX Cache and TLB Information (see sixth table)
- EBX Cache and TLB Information
- ECX Cache and TLB Information
- EDX Cache and TLB Information
03H ECX Bits 00-31 of 96 bit processor serial number. (Available in Pentium III processor only;
otherwise, the value in this register is reserved.)
- EDX Bits 32-63 of 96 bit processor serial number. (Available in Pentium III processor only;
otherwise, the value in this register is reserved.)
- - NOTE: Processor serial number (PSN) is not supported in the Pentium 4 processor or later.
On all models, use the PSN flag (returned using CPUID) to check for PSN support before
accessing the feature. See AP-485, Intel Processor Identification and the CPUID Instruction
(Order Number 241618) for more information on PSN.
04H EAX Bits 4-0: Cache Type**
- - Bits 7-5: Cache Level (starts at 1)
- - Bits 8: Self Initializing cache level (does not need SW initialization)
- - Bits 9: Fully Associative cache
- - Bits 13-10: Reserved
- - Bits 25-14: Number of threads sharing this cache*
- - Bits 31-26: Number of processor cores on this die (Multicore)*
- EBX Bits 11-00: L = System Coherency Line Size*
- - Bits 21-12: P = Physical Line partitions*
- - Bits 31-22: W = Ways of associativity*
- ECX Bits 31-00: S = Number of Sets*
- EDX Reserved = 0
- - 0 = Null - No more caches
- - 1 = Data Cache
- - 2 = Instruction Cache
- - 3 = Unified Cache
- - 4-31 = Reserved
- - NOTE: CPUID leaves > 3 < 80000000 are only visible when IA32_CR_MISC_ENABLES.BOOT_NT4 (bit
22) is clear (Default)
5H EAX Bits 15-00: Smallest monitor-line size in bytes (default is processor's monitor granularity)
- EBX Bits 15-00: Largest monitor-line size in bytes (default is processor's monitor granularity)
*Add one to the value in the register file to get the number. For example, the number of processor cores is EAX[31:26]+1.
** Cache Types fields
Extended Function CPUID Information
Initial EAX Value Register Information Provided about the Processor
80000000H EAX Maximum Input Value for Extended Function CPUID Information (see second table).
- EBX Reserved
- ECX Reserved
- EDX Reserved
80000001H EAX Extended Processor Signature and Extended Feature Bits. (Currently reserved)
- EBX Reserved
- ECX Reserved
- EDX Reserved
80000002H EAX Processor Brand String
- EBX Processor Brand String Continued
- ECX Processor Brand String Continued
- EDX Processor Brand String Continued
80000003H EAX Processor Brand String Continued
- EBX Processor Brand String Continued
- ECX Processor Brand String Continued
- EDX Processor Brand String Continued
80000004H EAX Processor Brand String Continued
- EBX Processor Brand String Continued
- ECX Processor Brand String Continued
- EDX Processor Brand String Continued
80000005H EAX Reserved = 0
- EBX Reserved = 0
- ECX Reserved = 0
- EDX Reserved = 0
80000006H EAX Reserved = 0
- EBX Reserved = 0
- ECX Bits 0-7: Cache Line Size
- - Bits 15-12: L2 Associativity
- - Bits 31-16: Cache size in 1K units
- EDX Reserved = 0
- 80000007H EAX Reserved = 0
- EBX Reserved = 0
- ECX Reserved = 0
- EDX Reserved = 0
80000008H EAX Reserved = 0
- EBX Reserved = 0
- ECX Reserved = 0
- EDX Reserved = 0
*/
#pragma once
#include <stdint.h>
struct cpuid_regs {
uint32_t eax, ebx, ecx, edx;
};
void cpuid(uint32_t eax, struct cpuid_regs* out_regs);
// doc from
/**
*
Basic CPUID Information
Initial EAX Value Register Information Provided about the Processor
0H EAX Maximum Input Value for Basic CPUID Information (see second table)
- EBX "Genu"
- ECX "ntel"
- EDX "ineI"
01H EAX Version Information: Type, Family, Model, and Stepping ID
- EBX Bits 7-0: Brand Index
- - Bits 15-8: CLFLUSH line size (Value . 8 = cache line size in bytes)
- - Bits 23-16: Number of logical processors per physical processor; two for the Pentium 4
processor supporting Hyper-Threading Technology
- - Bits 31-24: Local APIC ID
- ECX Extended Feature Information (see fourth table)
- EDX Feature Information (see fifth table)
02H EAX Cache and TLB Information (see sixth table)
- EBX Cache and TLB Information
- ECX Cache and TLB Information
- EDX Cache and TLB Information
03H ECX Bits 00-31 of 96 bit processor serial number. (Available in Pentium III processor only;
otherwise, the value in this register is reserved.)
- EDX Bits 32-63 of 96 bit processor serial number. (Available in Pentium III processor only;
otherwise, the value in this register is reserved.)
- - NOTE: Processor serial number (PSN) is not supported in the Pentium 4 processor or later.
On all models, use the PSN flag (returned using CPUID) to check for PSN support before
accessing the feature. See AP-485, Intel Processor Identification and the CPUID Instruction
(Order Number 241618) for more information on PSN.
04H EAX Bits 4-0: Cache Type**
- - Bits 7-5: Cache Level (starts at 1)
- - Bits 8: Self Initializing cache level (does not need SW initialization)
- - Bits 9: Fully Associative cache
- - Bits 13-10: Reserved
- - Bits 25-14: Number of threads sharing this cache*
- - Bits 31-26: Number of processor cores on this die (Multicore)*
- EBX Bits 11-00: L = System Coherency Line Size*
- - Bits 21-12: P = Physical Line partitions*
- - Bits 31-22: W = Ways of associativity*
- ECX Bits 31-00: S = Number of Sets*
- EDX Reserved = 0
- - 0 = Null - No more caches
- - 1 = Data Cache
- - 2 = Instruction Cache
- - 3 = Unified Cache
- - 4-31 = Reserved
- - NOTE: CPUID leaves > 3 < 80000000 are only visible when IA32_CR_MISC_ENABLES.BOOT_NT4 (bit
22) is clear (Default)
5H EAX Bits 15-00: Smallest monitor-line size in bytes (default is processor's monitor granularity)
- EBX Bits 15-00: Largest monitor-line size in bytes (default is processor's monitor granularity)
*Add one to the value in the register file to get the number. For example, the number of processor cores is EAX[31:26]+1.
** Cache Types fields
Extended Function CPUID Information
Initial EAX Value Register Information Provided about the Processor
80000000H EAX Maximum Input Value for Extended Function CPUID Information (see second table).
- EBX Reserved
- ECX Reserved
- EDX Reserved
80000001H EAX Extended Processor Signature and Extended Feature Bits. (Currently reserved)
- EBX Reserved
- ECX Reserved
- EDX Reserved
80000002H EAX Processor Brand String
- EBX Processor Brand String Continued
- ECX Processor Brand String Continued
- EDX Processor Brand String Continued
80000003H EAX Processor Brand String Continued
- EBX Processor Brand String Continued
- ECX Processor Brand String Continued
- EDX Processor Brand String Continued
80000004H EAX Processor Brand String Continued
- EBX Processor Brand String Continued
- ECX Processor Brand String Continued
- EDX Processor Brand String Continued
80000005H EAX Reserved = 0
- EBX Reserved = 0
- ECX Reserved = 0
- EDX Reserved = 0
80000006H EAX Reserved = 0
- EBX Reserved = 0
- ECX Bits 0-7: Cache Line Size
- - Bits 15-12: L2 Associativity
- - Bits 31-16: Cache size in 1K units
- EDX Reserved = 0
- 80000007H EAX Reserved = 0
- EBX Reserved = 0
- ECX Reserved = 0
- EDX Reserved = 0
80000008H EAX Reserved = 0
- EBX Reserved = 0
- ECX Reserved = 0
- EDX Reserved = 0
*/

50
kernel/cpuid.s

@ -1,26 +1,26 @@
[section .text]
[global cpuid]
;struct cpuid_regs {
; uint32_t eax, ebx, ecx, edx;
;}
; void cpuid(uint32_t eax, struct cpuid_regs* out_regs);
cpuid:
push ebp
mov ebp, esp
push rbx
mov eax, edi
cpuid
mov [rdi + 0], eax
mov [rdi + 4], ebx
mov [rdi + 8], ecx
mov [rdi + 12], edx
pop rbx
leave
[section .text]
[global cpuid]
;struct cpuid_regs {
; uint32_t eax, ebx, ecx, edx;
;}
; void cpuid(uint32_t eax, struct cpuid_regs* out_regs);
cpuid:
push ebp
mov ebp, esp
push rbx
mov eax, edi
cpuid
mov [rdi + 0], eax
mov [rdi + 4], ebx
mov [rdi + 8], ecx
mov [rdi + 12], edx
pop rbx
leave
ret

231
kernel/debug/dump.c

@ -1,116 +1,115 @@
#include "dump.h"
#include "../klib/sprintf.h"
/**
* dump a memory chunk in kprintf
* addr: address of the beginning of the chunk
* size: number of bytes to dump
* line_size: number of words per line
*
* mode: either
* DUMP_HEX8 : hexadecimal integers of size 8 bits
* DUMP_HEX32 : hexadecimal integers of size 32 bits
* DUMP_HEX64 : hexadecimal integers of size 64 bits
* DUMP_DEC8 : decimal integers of size 8 bits
* DUMP_DEC32 : decimal integers of size 32 bits
* DUMP_DEC64 : decimal integers of size 64 bits
*
**/
void dump(const void* addr, size_t size, size_t line_size, uint8_t mode) {
char row_fmt[8], last_row_fmt[8];
size_t pitch; // sizeof word
int i = 0;
row_fmt [i] = '%';
last_row_fmt[i] = '%';
i++;
// create fmts for printf
// word width
if((mode & DUMP_8) != 0) {
// byte mode
row_fmt [i] = '2';
last_row_fmt[i] = '2';
i++;
pitch = 1;
}
else if((mode & DUMP_64) == 0) {
// normal 32bit mode
row_fmt [i] = '8';
last_row_fmt[i] = '8';
i++;
pitch = 4;
}
else {
// long mode
row_fmt [i] = 'l';
last_row_fmt[i] = 'l';
i++;
row_fmt [i] = '1';
last_row_fmt[i] = '1';
i++;
row_fmt [i] = '6';
last_row_fmt[i] = '6';
i++;
pitch = 8;
}
// base
char base_id;
if((mode & DUMP_HEX) == 0) // hex
base_id = 'x';
else // dec
base_id = 'u';
row_fmt [i] = base_id;
last_row_fmt[i] = base_id;
i++;
row_fmt [i] = ' ';
last_row_fmt[i] = '\n';
i++;
row_fmt [i] = '\0';
last_row_fmt[i] = '\0';
i++;
size /= pitch;
size_t lines = (size + line_size - 1) / line_size;
// iterator ptr
const uint8_t* ptr = addr;
// create mask : create the complementary with 0xff...ff << n
// then complement it
const uint64_t mask = ~(~0llu << 8*pitch);
for(size_t i = 0; i < lines; i++) {
// the last line might not be full
// therefore we have to check each one
for(size_t j = 0; j < line_size-1; j++) {
if(size-- <= 1)
break;
else {
kprintf(row_fmt, *(uint64_t *)ptr & mask);
ptr+=pitch;
}
}
kprintf(last_row_fmt, *(uint64_t *)ptr & mask);
ptr+=pitch;
}
}
#include "dump.h"
#include "../klib/sprintf.h"
/**
* dump a memory chunk in kprintf
* addr: address of the beginning of the chunk
* size: number of bytes to dump
* line_size: number of words per line
*
* mode: either
* DUMP_HEX8 : hexadecimal integers of size 8 bits
* DUMP_HEX32 : hexadecimal integers of size 32 bits
* DUMP_HEX64 : hexadecimal integers of size 64 bits
* DUMP_DEC8 : decimal integers of size 8 bits
* DUMP_DEC32 : decimal integers of size 32 bits
* DUMP_DEC64 : decimal integers of size 64 bits
*
**/
void dump(const void* addr, size_t size, size_t line_size, uint8_t mode) {
char row_fmt[16], last_row_fmt[16];
size_t pitch; // sizeof word
int i = 0;
row_fmt [i] = '%';
last_row_fmt[i] = '%';
i++;
// create fmts for printf
// word width
if((mode & DUMP_8) != 0) {
// byte mode
row_fmt [i] = '2';
last_row_fmt[i] = '2';
i++;
pitch = 1;
}
else if((mode & DUMP_64) == 0) {
// normal 32bit mode
row_fmt [i] = '8';
last_row_fmt[i] = '8';
i++;
pitch = 4;
}
else {
// long mode
row_fmt [i] = 'l';
last_row_fmt[i] = 'l';
i++;
row_fmt [i] = '1';
last_row_fmt[i] = '1';
i++;
row_fmt [i] = '6';
last_row_fmt[i] = '6';
i++;
pitch = 8;
}
// base
char base_id;
if((mode & DUMP_HEX) == 0) // hex
base_id = 'x';
else // dec
base_id = 'u';
row_fmt [i] = base_id;
last_row_fmt[i] = base_id;
i++;
row_fmt [i] = ' ';
last_row_fmt[i] = '\n';
i++;
row_fmt [i] = '\0';
last_row_fmt[i] = '\0';
i++;
size /= pitch;
size_t lines = (size + line_size - 1) / line_size;
// iterator ptr
const uint8_t* ptr = addr;
// create mask : create the complementary with 0xff...ff << n
// then complement it
const uint64_t mask = ~(~0llu << 8*pitch);
for(size_t i = 0; i < lines; i++) {
// the last line might not be full
// therefore we have to check each one
for(size_t j = 0; j < line_size-1; j++) {
if(size-- <= 1)
break;
else {
kprintf(row_fmt, *(uint64_t *)ptr & mask);
ptr+=pitch;
}
}
kprintf(last_row_fmt, *(uint64_t *)ptr & mask);
ptr+=pitch;
}
}

46
kernel/debug/dump.h

@ -1,23 +1,23 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#define DUMP_HEX 0
#define DUMP_DEC 16
#define DUMP_8 32
#define DUMP_32 0
#define DUMP_64 1
#define DUMP_HEX8 (DUMP_HEX | DUMP_8)
#define DUMP_HEX32 (DUMP_HEX | DUMP_32)
#define DUMP_HEX64 (DUMP_HEX | DUMP_64)
#define DUMP_DEC8 (DUMP_DEC | DUMP_8)
#define DUMP_DEC32 (DUMP_DEC | DUMP_32)
#define DUMP_DEC64 (DUMP_DEC | DUMP_64)
void dump(const void* addr, size_t size, size_t line_size, uint8_t mode);
#pragma once
#include <stddef.h>
#include <stdint.h>
#define DUMP_HEX 0
#define DUMP_DEC 16
#define DUMP_8 32
#define DUMP_32 0
#define DUMP_64 1
#define DUMP_HEX8 (DUMP_HEX | DUMP_8)
#define DUMP_HEX32 (DUMP_HEX | DUMP_32)
#define DUMP_HEX64 (DUMP_HEX | DUMP_64)
#define DUMP_DEC8 (DUMP_DEC | DUMP_8)
#define DUMP_DEC32 (DUMP_DEC | DUMP_32)
#define DUMP_DEC64 (DUMP_DEC | DUMP_64)
void dump(const void* addr, size_t size, size_t line_size, uint8_t mode);

38
kernel/entry.c

@ -10,6 +10,7 @@
#include "acpi/acpi.h"
#include "common.h"
#include "regs.h"
#include "int/apic.h"
#include "int/idt.h"
@ -44,9 +45,9 @@ static struct stivale2_header_tag_framebuffer framebuffer_hdr_tag = {
},
// We set all the framebuffer specifics to 0 as we want the bootloader
// to pick the best it can.
.framebuffer_width = 0,
.framebuffer_height = 0,
.framebuffer_bpp = 0
.framebuffer_width = 1152,
.framebuffer_height = 864,
.framebuffer_bpp = 32
};
// The stivale2 specification says we need to define a "header structure".
@ -94,8 +95,8 @@ void *stivale2_get_tag(struct stivale2_struct *stivale2_struct, uint64_t id) {
}
#define PRINT_VAL(v) kprintf(#v "=%ld\n", v);
#define PRINT_HEX(v) kprintf(#v "=%lx\n", v);
#define PRINT_VAL(v) kprintf(#v "=%ld\n", (uint64_t)v);
#define PRINT_HEX(v) kprintf(#v "=%lx\n", (uint64_t)v);
// const char but represents a big string
extern const char _binary_bootmessage_txt;
@ -112,6 +113,14 @@ static void debug_terminal() {
kputs(buff);
}
static void print_fb_infos(struct stivale2_struct_tag_framebuffer* fbtag) {
PRINT_VAL(fbtag->framebuffer_width);
PRINT_VAL(fbtag->framebuffer_height);
PRINT_VAL(fbtag->framebuffer_pitch);
PRINT_VAL(fbtag->framebuffer_bpp);
PRINT_HEX(fbtag->framebuffer_addr);
}
#pragma GCC diagnostic pop
@ -148,7 +157,7 @@ void _start(struct stivale2_struct *stivale2_struct) {
uint64_t rsdp_location = rsdp_tag_ptr->rsdp;
Image sc = {.w = fbtag.framebuffer_width,
@ -165,13 +174,24 @@ void _start(struct stivale2_struct *stivale2_struct) {
setup_terminal();
setup_isr();
terminal_set_colors(0xf0f0f0, 0x007000);
terminal_clear();
kputs(&_binary_bootmessage_txt);
read_acpi_tables((void*)rsdp_location);
asm volatile("sti");
kputs("DONE\n");
kputs(&_binary_bootmessage_txt);
apic_setup_clock();
for(;;) {
asm volatile("hlt");
kprintf("%lu\t", clock());
}
for(;;) {

95
kernel/int/apic.c

@ -0,0 +1,95 @@
#include <stdint.h>
#include "../debug/assert.h"
#include "idt.h"
#include "apic.h"
#include "../klib/sprintf.h"
#define LAPIC_TIMER_IRQ 0xff
struct APICConfig* apic_config = NULL;
void outb(uint16_t dx, uint16_t al);
uint8_t inb(uint16_t dx);
static void pit_wait(size_t ms) {
outb(0x43, 0x30);
uint16_t val = 0x4a9 * ms;
outb(0x40, (uint8_t)val);
outb(0x40, (uint8_t)(val >> 8));
for(;;) {
outb(0x43, 0xe2);
uint8_t status = inb(0x40);
if ((status & (1 << 7)) != 0) {
break;
}
}
}
static unsigned read_pit_count(void) {
unsigned count = 0;
// Disable interrupts
uint64_t rf = get_rflags();
_cli();
// al = channel in bits 6 and 7, remaining bits clear
outb(0x43,0b0000000);
count = inb(0x40); // Low byte
count |= inb(0x40)<<8; // High byte
set_rflags(rf);
return count;
}
uint64_t apic_timer_clock_count = 0;
__attribute__((interrupt)) void lapic_timer_handler(struct IFrame* frame) {
(void) frame;
++apic_timer_clock_count;
kprintf("%x\n", apic_timer_clock_count);
apic_config->end_of_interrupt.reg = 0;
}
uint64_t clock(void) {
return apic_timer_clock_count;
}
void apic_setup_clock(void) {
assert(apic_config != NULL);
set_irq_handler(LAPIC_TIMER_IRQ, lapic_timer_handler);
// disable apic and set spurious int to 32
apic_config->spurious_interrupt_vector.reg = 0 | LAPIC_TIMER_IRQ;
apic_config->timer_divide_configuration.reg = 2; // divide by 8
apic_config->timer_initial_count.reg = UINT32_MAX;
pit_wait(1); /// wait for 1 ms
//apic_config->timer_initial_count.reg = UINT32_MAX - apic_config->timer_current_count.reg;
for(int i = 0; i < 1000000000; i++) {
kprintf("%x\t%x\n", apic_config->timer_current_count.reg, read_pit_count());
// asm volatile("hlt");
}
// count
// enable the clock irqs
apic_config->spurious_interrupt_vector.reg = 0x100 | LAPIC_TIMER_IRQ;
}

53
kernel/int/apic.h

@ -0,0 +1,53 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "../debug/assert.h"
struct APICRegister
{
uint32_t reg;
uint32_t reserved[3];
};
static_assert(sizeof(struct APICRegister) == 16);
struct APICConfig
{
struct APICRegister reserved1[2]; //
struct APICRegister LAPIC_ID; // RW
struct APICRegister LAPIC_version; // R
struct APICRegister reserved2[4]; //
struct APICRegister task_priority; // RW
struct APICRegister arbitration_priority; // R
struct APICRegister processor_priority; // R
struct APICRegister end_of_interrupt; // W
struct APICRegister remote_read; // R
struct APICRegister logical_destination; // RW
struct APICRegister destination_format; // RW
struct APICRegister spurious_interrupt_vector; // RW
struct APICRegister in_service[8]; // R
struct APICRegister trigger_mode[8]; // R
struct APICRegister interrupt_request[8]; // R
struct APICRegister error_status; // R
struct APICRegister reserved3[6]; //
struct APICRegister LVT_corrected_machine_check_interrupt;// RW
struct APICRegister interrupt_command[2]; // RW
struct APICRegister LVT_timer; // RW
struct APICRegister LVT_thermal_sensor; // RW
struct APICRegister LVT_performance_monitoring_counters; // RW
struct APICRegister LVT_LINT0; // RW
struct APICRegister LVT_LINT1; // RW
struct APICRegister LVT_error; // RW
struct APICRegister timer_initial_count; // RW
struct APICRegister timer_current_count; // R
struct APICRegister reserved4[4]; //
struct APICRegister timer_divide_configuration; // RW
struct APICRegister reserved5; //
};
static_assert(sizeof(struct APICConfig) == 0x400);
void apic_setup_clock(void);
uint64_t clock(void);

222
kernel/int/idt.c

@ -1,98 +1,124 @@
#include <stddef.h>
#include <stdint.h>
#include "../common.h"
#include "idt.h"
typedef struct {
uint16_t size;
const void* offset;
} __packed IDTD;
typedef struct {
uint8_t gate_type: 4;
uint8_t z : 1;
uint8_t dpl : 2;
uint8_t p : 1;
} __packed type_attr_t;
static_assert(sizeof(type_attr_t) == 1);
typedef struct {
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t ist; // bits 0..2 holds Interrupt
// Stack Table offset, rest of bits zero.
type_attr_t type_attr; // type and attributes
uint16_t offset_2; // offset bits 16..31
uint32_t offset_3; // offset bits 32..63
uint32_t zero; // reserved
} __packed IDTE;
IDTE idt[256] = {0};
void _lidt(IDTD* idtd);
void setup_idt(void) {
IDTD idt_descriptor = {
.size = 255 * sizeof(IDTE),
.offset = idt,
};
_lidt(&idt_descriptor);
}
static type_attr_t make_type_attr_t(uint8_t gate_type) {
assert((gate_type & 0xf0) == 0);
return (type_attr_t) {
.gate_type = gate_type,
.z = 0,
.dpl = 0,
.p = 1,
};
}
static IDTE make_idte(void* handler, type_attr_t type_attr) {
uint64_t h = (uint64_t) handler;
return (IDTE) {
.offset_1 = h & 0xffff,
.selector = 0x08, // kernel code segment
.ist = 0,
.type_attr = type_attr,
.offset_2 = (h >> 16) & 0xffff,
.offset_3 = (h >> 32) & 0xffffffff,
.zero = 0,
};
}
static IDTE make_isr(void* handler) {
return make_idte(handler, make_type_attr_t(ATTR_64_GATE));
}
void set_interrupt_handler(uint16_t number, void* handler) {
idt[number] = make_isr(handler);
}
void _cli(void) {
asm volatile("cli");
}
void _sti(void) {
asm volatile("sti");
}
#include <stddef.h>
#include <stdint.h>
#include "../common.h"
#include "idt.h"
typedef struct {
uint16_t size;
const void* offset;
} __packed IDTD;
typedef struct {
uint8_t gate_type: 4;
uint8_t z : 1;
uint8_t dpl : 2;
uint8_t p : 1;
} __packed type_attr_t;
static_assert(sizeof(type_attr_t) == 1);
typedef struct {
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t ist; // bits 0..2 holds Interrupt
// Stack Table offset, rest of bits zero.
type_attr_t type_attr; // type and attributes
uint16_t offset_2; // offset bits 16..31
uint32_t offset_3; // offset bits 32..63
uint32_t zero; // reserved
} __packed IDTE;
IDTE idt[256] = {0};
void _lidt(IDTD* idtd);
void setup_idt(void) {
IDTD idt_descriptor = {
.size = 255 * sizeof(IDTE),
.offset = idt,
};
_lidt(&idt_descriptor);
}
static type_attr_t make_type_attr_t(uint8_t gate_type) {
assert((gate_type & 0xf0) == 0);
return (type_attr_t) {
.gate_type = gate_type,
.z = 0,
.dpl = 0,
.p = 1,
};
}
static IDTE make_idte(void* handler, type_attr_t type_attr) {
uint64_t h = (uint64_t) handler;
return (IDTE) {
.offset_1 = h & 0xffff,
.selector = 0x08, // kernel code segment
.ist = 0,
.type_attr = type_attr,
.offset_2 = (h >> 16) & 0xffff,
.offset_3 = (h >> 32) & 0xffffffff,
.zero = 0,
};
}
static IDTE make_isr(void* handler) {
return make_idte(handler, make_type_attr_t(ATTR_64_GATE));
}
static IDTE make_irq(void* handler) {
return make_idte(handler, make_type_attr_t(ATTR_64_TRAP));
}
void set_rflags(uint64_t RFLAGS);
uint64_t get_rflags(void);
void set_irs_handler(uint16_t number, void* handler) {
// make sure to disable interrupts
// while updating the idt
// then save IF to its old state
uint64_t rflags = get_rflags();
_cli();
idt[number] = make_isr(handler);
set_rflags(rflags);
}
void set_irq_handler(uint16_t number, void* handler) {
// same as above
uint64_t rflags = get_rflags();
_cli();
idt[number] = make_irq(handler);
set_rflags(rflags);
}
void _cli(void) {
asm volatile("cli");
}
void _sti(void) {
asm volatile("sti");
}

70
kernel/int/idt.h

@ -1,35 +1,37 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
#include <stddef.h>
#include "../debug/assert.h"
#define ATTR_64_GATE 0b1110
#define ATTR_64_TRAP 0b1110
struct IFrame {
uint64_t RIP;
uint64_t CS;
uint64_t RFLAGS;
uint64_t RSP;
uint64_t SS;
} __packed;
static_assert(sizeof(struct IFrame) == 40);
void setup_idt(void);
void set_interrupt_handler(uint16_t number, void* handler);
void _cli(void);
void _sti(void);
void setup_isr(void);
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
#include <stddef.h>
#include "../debug/assert.h"
#include