Merge remote-tracking branch 'Kernel/master'

This commit is contained in:
EnderIce2
2024-11-20 05:00:33 +02:00
468 changed files with 112800 additions and 1 deletions

View File

@ -0,0 +1,36 @@
/* Based on this tutorial:
https://github.com/s-matyukevich/raspberry-pi-os */
.section ".text.boot", "a"
.extern _bss_start
.extern _bss_end
.extern arm64Entry
memzero:
str xzr, [x0], #8
subs x1, x1, #8
b.gt memzero
ret
.global _start
_start:
mrs x0, mpidr_el1
and x0, x0, #0xFF
cbz x0, _start2
b CPU_Loop
_start2:
adr x0, _bss_start
adr x1, _bss_end
sub x1, x1, x0
bl memzero
mov sp, #0x200000
bl arm64Entry
Halt:
wfe
b Halt
CPU_Loop:
b CPU_Loop

View File

@ -0,0 +1,59 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <smp.hpp>
#include <ints.hpp>
#include <memory.hpp>
#include <cpu.hpp>
#include "../../../kernel.h"
volatile bool CPUEnabled = false;
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static __aligned(0x1000) CPUData CPUs[MAX_CPU] = {0};
CPUData *GetCPU(uint64_t id) { return &CPUs[id]; }
CPUData *GetCurrentCPU()
{
uint64_t ret = 0;
if (!CPUs[ret].IsActive)
{
error("CPU %d is not active!", ret);
return &CPUs[0];
}
if (CPUs[ret].Checksum != CPU_DATA_CHECKSUM)
{
error("CPU %d data is corrupted!", ret);
return &CPUs[0];
}
return &CPUs[ret];
}
namespace SMP
{
int CPUCores = 0;
void Initialize(void *madt)
{
fixme("SMP::Initialize() is not implemented!");
}
}

View File

@ -0,0 +1,27 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <debug.h>
#include <cpu.hpp>
EXTERNC void arm64Entry(uint64_t dtb_ptr32, uint64_t x1, uint64_t x2, uint64_t x3)
{
trace("Hello, World!");
CPU::Halt(true);
}

View File

View File

@ -0,0 +1,90 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
ENTRY(_start)
SECTIONS
{
_bootstrap_start = .;
.text.boot :
{
*(.text.boot)
. += CONSTANT(MAXPAGESIZE);
_bss_start = .;
*(.text.bss)
_bss_end = .;
}
_bootstrap_end = .;
_kernel_start = .;
_kernel_text_start = .;
.text :
{
KEEP(*(.text.boot))
*(.text .text.*)
}
. = ALIGN(4096);
_kernel_text_end = .;
_kernel_data_start = .;
.data :
{
*(.data .data.*)
}
. = ALIGN(4096);
_kernel_data_end = .;
_kernel_rodata_start = .;
.rodata :
{
*(.rodata .rodata.*)
}
. = ALIGN(4096);
.init_array :
{
PROVIDE_HIDDEN(__init_array_start = .);
KEEP(*(.init_array .ctors))
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN(__fini_array_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
_kernel_rodata_end = .;
_kernel_bss_start = .;
.bss :
{
*(.bss .bss.*)
}
. = ALIGN(4096);
_kernel_bss_end = .;
_kernel_end = .;
_bss_size = _kernel_end - _kernel_rodata_end;
/DISCARD/ :
{
*(.comment*)
*(.note*)
}
}

View File

@ -0,0 +1,40 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <convert.h>
#include <debug.h>
namespace Memory
{
bool Virtual::Check(void *VirtualAddress, PTFlag Flag, MapType Type)
{
}
void *Virtual::GetPhysical(void *VirtualAddress)
{
}
void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
{
}
void Virtual::Unmap(void *VirtualAddress, MapType Type)
{
}
}

View File

@ -0,0 +1,31 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <syscalls.hpp>
#include <cpu.hpp>
extern "C" __naked __used __no_stack_protector void SystemCallHandlerStub()
{
}
extern "C" uint64_t SystemCallsHandler(SyscallsFrame *regs);
void InitializeSystemCalls()
{
}

View File

@ -0,0 +1,38 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
.extern Multiboot_start
.section .bootstrap.text, "a"
.global _start
_start:
/* Check for multiboot */
cmp $0x2BADB002, %eax
je .Multiboot
/* Unkown bootloader */
.Hang:
cli
hlt
jmp .Hang
/* Multiboot */
.Multiboot:
call Multiboot_start
jmp .Hang

View File

@ -0,0 +1,317 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <boot/binfo.h>
#include <types.h>
#include <debug.h>
#include <convert.h>
#include "../../../../../tools/limine/limine.h"
#include "../../../../kernel.h"
void InitLimine();
static volatile struct limine_entry_point_request EntryPointRequest = {
.id = LIMINE_ENTRY_POINT_REQUEST,
.revision = 0,
.response = NULL,
.entry = InitLimine};
static volatile struct limine_bootloader_info_request BootloaderInfoRequest = {
.id = LIMINE_BOOTLOADER_INFO_REQUEST,
.revision = 0};
static volatile struct limine_framebuffer_request FramebufferRequest = {
.id = LIMINE_FRAMEBUFFER_REQUEST,
.revision = 0};
static volatile struct limine_memmap_request MemmapRequest = {
.id = LIMINE_MEMMAP_REQUEST,
.revision = 0};
static volatile struct limine_kernel_address_request KernelAddressRequest = {
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
.revision = 0};
static volatile struct limine_rsdp_request RsdpRequest = {
.id = LIMINE_RSDP_REQUEST,
.revision = 0};
static volatile struct limine_kernel_file_request KernelFileRequest = {
.id = LIMINE_KERNEL_FILE_REQUEST,
.revision = 0};
static volatile struct limine_module_request ModuleRequest = {
.id = LIMINE_MODULE_REQUEST,
.revision = 0};
static volatile struct limine_smbios_request SmbiosRequest = {
.id = LIMINE_SMBIOS_REQUEST,
.revision = 0};
void *TempStackPtr = NULL;
__naked __used __no_stack_protector void InitLimine()
{
asmv("mov %%rsp, %0"
: "=r"(TempStackPtr));
asmv("mov %0, %%rsp"
:
: "r"((uintptr_t)TempStackPtr - 0xFFFF800000000000));
asmv("mov $0, %rax\n"
"mov $0, %rbx\n"
"mov $0, %rcx\n"
"mov $0, %rdx\n"
"mov $0, %rsi\n"
"mov $0, %rdi\n"
"mov $0, %rbp\n"
"mov $0, %r8\n"
"mov $0, %r9\n"
"mov $0, %r10\n"
"mov $0, %r11\n"
"mov $0, %r12\n"
"mov $0, %r13\n"
"mov $0, %r14\n"
"mov $0, %r15");
asmv("jmp InitLimineAfterStack");
}
nsa NIF void InitLimineAfterStack()
{
struct BootInfo binfo = {};
struct limine_bootloader_info_response *BootloaderInfoResponse = BootloaderInfoRequest.response;
info("Bootloader: %s %s", BootloaderInfoResponse->name, BootloaderInfoResponse->version);
struct limine_framebuffer_response *FrameBufferResponse = FramebufferRequest.response;
struct limine_memmap_response *MemmapResponse = MemmapRequest.response;
struct limine_kernel_address_response *KernelAddressResponse = KernelAddressRequest.response;
struct limine_rsdp_response *RsdpResponse = RsdpRequest.response;
struct limine_kernel_file_response *KernelFileResponse = KernelFileRequest.response;
struct limine_module_response *ModuleResponse = ModuleRequest.response;
struct limine_smbios_response *SmbiosResponse = SmbiosRequest.response;
if (FrameBufferResponse == NULL || FrameBufferResponse->framebuffer_count < 1)
{
error("No framebuffer available [%#lx;%ld]", FrameBufferResponse,
(FrameBufferResponse == NULL) ? 0 : FrameBufferResponse->framebuffer_count);
inf_loop asmv("hlt");
}
if (MemmapResponse == NULL || MemmapResponse->entry_count < 1)
{
error("No memory map available [%#lx;%ld]", MemmapResponse,
(MemmapResponse == NULL) ? 0 : MemmapResponse->entry_count);
inf_loop asmv("hlt");
}
if (KernelAddressResponse == NULL)
{
error("No kernel address available [%#lx]", KernelAddressResponse);
inf_loop asmv("hlt");
}
if (RsdpResponse == NULL || RsdpResponse->address == 0)
{
error("No RSDP address available [%#lx;%#lx]", RsdpResponse,
(RsdpResponse == NULL) ? 0 : RsdpResponse->address);
inf_loop asmv("hlt");
}
if (KernelFileResponse == NULL || KernelFileResponse->kernel_file == NULL)
{
error("No kernel file available [%#lx;%#lx]", KernelFileResponse,
(KernelFileResponse == NULL) ? 0 : KernelFileResponse->kernel_file);
inf_loop asmv("hlt");
}
/* Actual parsing starts here */
for (uint64_t i = 0; i < FrameBufferResponse->framebuffer_count; i++)
{
struct limine_framebuffer *framebuffer = FrameBufferResponse->framebuffers[i];
switch (framebuffer->memory_model)
{
case LIMINE_FRAMEBUFFER_RGB:
binfo.Framebuffer[i].Type = RGB;
break;
default:
{
error("Unsupported framebuffer memory model %d", framebuffer->memory_model);
inf_loop asmv("hlt");
}
}
binfo.Framebuffer[i].BaseAddress = (void *)((uintptr_t)framebuffer->address - 0xFFFF800000000000);
binfo.Framebuffer[i].Width = (uint32_t)framebuffer->width;
binfo.Framebuffer[i].Height = (uint32_t)framebuffer->height;
binfo.Framebuffer[i].Pitch = (uint32_t)framebuffer->pitch;
binfo.Framebuffer[i].BitsPerPixel = framebuffer->bpp;
binfo.Framebuffer[i].RedMaskSize = framebuffer->red_mask_size;
binfo.Framebuffer[i].RedMaskShift = framebuffer->red_mask_shift;
binfo.Framebuffer[i].GreenMaskSize = framebuffer->green_mask_size;
binfo.Framebuffer[i].GreenMaskShift = framebuffer->green_mask_shift;
binfo.Framebuffer[i].BlueMaskSize = framebuffer->blue_mask_size;
binfo.Framebuffer[i].BlueMaskShift = framebuffer->blue_mask_shift;
binfo.Framebuffer[i].ExtendedDisplayIdentificationData = framebuffer->edid;
binfo.Framebuffer[i].EDIDSize = framebuffer->edid_size;
debug("Framebuffer %d: %dx%d %d bpp", i,
binfo.Framebuffer[i].Width,
binfo.Framebuffer[i].Height,
binfo.Framebuffer[i].BitsPerPixel);
debug("More info:\nAddress: %#lx\nPitch: %ld\nType: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d\nEDID: %#lx\nEDIDSize: %d",
binfo.Framebuffer[i].BaseAddress,
binfo.Framebuffer[i].Pitch,
binfo.Framebuffer[i].Type,
binfo.Framebuffer[i].RedMaskSize,
binfo.Framebuffer[i].RedMaskShift,
binfo.Framebuffer[i].GreenMaskSize,
binfo.Framebuffer[i].GreenMaskShift,
binfo.Framebuffer[i].BlueMaskSize,
binfo.Framebuffer[i].BlueMaskShift,
binfo.Framebuffer[i].ExtendedDisplayIdentificationData,
binfo.Framebuffer[i].EDIDSize);
}
binfo.Memory.Entries = MemmapResponse->entry_count;
for (uint64_t i = 0; i < MemmapResponse->entry_count; i++)
{
if (MemmapResponse->entry_count > MAX_MEMORY_ENTRIES)
{
warn("Too many memory entries, skipping the rest...");
break;
}
struct limine_memmap_entry *entry = MemmapResponse->entries[i];
if (!entry)
{
warn("Null memory entry %ld (%#lx), skipping...", i, entry);
continue;
}
binfo.Memory.Size += entry->length;
switch (entry->type)
{
case LIMINE_MEMMAP_USABLE:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = Usable;
break;
case LIMINE_MEMMAP_RESERVED:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = Reserved;
break;
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = ACPIReclaimable;
break;
case LIMINE_MEMMAP_ACPI_NVS:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = ACPINVS;
break;
case LIMINE_MEMMAP_BAD_MEMORY:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = BadMemory;
break;
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = BootloaderReclaimable;
break;
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = KernelAndModules;
break;
case LIMINE_MEMMAP_FRAMEBUFFER:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = Framebuffer;
break;
default:
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
binfo.Memory.Entry[i].Length = entry->length;
binfo.Memory.Entry[i].Type = Unknown;
break;
}
}
if (ModuleResponse != NULL && ModuleResponse->module_count > 0)
{
for (uint64_t i = 0; i < ModuleResponse->module_count; i++)
{
if (i > MAX_MODULES)
{
warn("Too many modules, skipping the rest...");
break;
}
binfo.Modules[i].Address = (void *)((uint64_t)ModuleResponse->modules[i]->address - 0xFFFF800000000000);
binfo.Modules[i].Size = ModuleResponse->modules[i]->size;
strncpy(binfo.Modules[i].Path,
ModuleResponse->modules[i]->path,
strlen(ModuleResponse->modules[i]->path) + 1);
strncpy(binfo.Modules[i].CommandLine,
ModuleResponse->modules[i]->cmdline,
strlen(ModuleResponse->modules[i]->cmdline) + 1);
debug("Module %d:\nAddress: %#lx\nPath: \"%s\"\nCommand Line: \"%s\"\nSize: %ld",
i,
binfo.Modules[i].Address,
binfo.Modules[i].Path,
binfo.Modules[i].CommandLine,
binfo.Modules[i].Size);
}
}
binfo.RSDP = (struct RSDPInfo *)((uintptr_t)RsdpResponse->address - 0xFFFF800000000000);
debug("RSDP: %#lx [Signature: \"%.8s\"] [OEM: \"%.6s\"]",
binfo.RSDP, binfo.RSDP->Signature, binfo.RSDP->OEMID);
if (SmbiosResponse->entry_64 != NULL)
binfo.SMBIOSPtr = (void *)((uintptr_t)SmbiosResponse->entry_64 - 0xFFFF800000000000);
else if (SmbiosResponse->entry_32 != NULL)
binfo.SMBIOSPtr = (void *)((uintptr_t)SmbiosResponse->entry_32 - 0xFFFF800000000000);
else
binfo.SMBIOSPtr = NULL;
debug("SMBIOS: %#lx %#lx (binfo: %#lx)",
SmbiosResponse->entry_32,
SmbiosResponse->entry_64,
binfo.SMBIOSPtr);
binfo.Kernel.PhysicalBase = (void *)KernelAddressResponse->physical_base;
binfo.Kernel.VirtualBase = (void *)KernelAddressResponse->virtual_base;
binfo.Kernel.FileBase = (void *)((uintptr_t)KernelFileResponse->kernel_file->address - 0xFFFF800000000000);
binfo.Kernel.Size = KernelFileResponse->kernel_file->size;
strncpy(binfo.Kernel.CommandLine,
KernelFileResponse->kernel_file->cmdline,
strlen(KernelFileResponse->kernel_file->cmdline) + 1);
debug("Kernel physical address: %#lx", binfo.Kernel.PhysicalBase);
debug("Kernel virtual address: %#lx", binfo.Kernel.VirtualBase);
strncpy(binfo.Bootloader.Name,
BootloaderInfoResponse->name,
strlen(BootloaderInfoResponse->name) + 1);
strncpy(binfo.Bootloader.Version,
BootloaderInfoResponse->version,
strlen(BootloaderInfoResponse->version) + 1);
Entry(&binfo);
}

View File

@ -0,0 +1,81 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.intel_syntax noprefix
.code32
.section .bootstrap.text, "a"
.global DetectCPUID
DetectCPUID:
pushfd
pop eax
mov ecx, eax
xor eax, 0x200000
push eax
popfd
pushfd
pop eax
push ecx
popfd
xor eax, ecx
jz .NoCPUID
mov eax, 0x1
ret
.NoCPUID:
xor eax, eax
ret
.global Detect64Bit
Detect64Bit:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .NoLongMode
mov eax, 0x80000001
cpuid
test edx, 0x20000000
jz .NoLongMode
mov eax, 0x1
ret
.NoLongMode:
xor eax, eax
ret
.global DetectPSE
DetectPSE:
mov eax, 0x00000001
cpuid
test edx, 0x00000008
jz .NoPSE
mov eax, 0x1
ret
.NoPSE:
xor eax, eax
ret
.global DetectPAE
DetectPAE:
mov eax, 0x00000001
cpuid
test edx, 0x00000040
jz .NoPAE
mov eax, 0x1
ret
.NoPAE:
xor eax, eax
ret

View File

@ -0,0 +1,64 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
.section .bootstrap.text, "a"
.align 32
.global gdtr
gdtr:
.word GDT32_END - GDT32 - 1
.long GDT32
.align 32
GDT32:
.quad 0x0
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF9A
.byte 0x00
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF92
.byte 0x00
.word 0x0100
.word 0x1000
.byte 0x00
.word 0x4092
.byte 0x00
GDT32_END:
nop
.global LoadGDT32
LoadGDT32:
lgdt [gdtr]
ljmp $0x8, $ActivateGDT
ActivateGDT:
mov $0x10, %cx
mov %cx, %ss
mov %cx, %ds
mov %cx, %es
mov %cx, %fs
mov $0x18, %cx
mov %cx, %gs
ret

View File

@ -0,0 +1,62 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code64
.section .bootstrap.data, "a"
/* Access bits */
A = 0x1
RW = 0x2
DC = 0x4
E = 0x8
S = 0x10
DPL0 = 0x0 /* 0 << 5 ???? */
DPL1 = 0x20
P = 0x80
/* Flags bits */
LONG_MODE = 0x20
SZ_32 = 0x40
GRAN_4K = 0x80
.global GDT64.Null
.global GDT64.Code
.global GDT64.Data
.global GDT64.Tss
.global GDT64.Ptr
GDT64:
GDT64.Null = . - GDT64
.quad 0
GDT64.Code = . - GDT64
.long 0xFFFF
.byte 0
.byte P | S | E | RW
.byte GRAN_4K | LONG_MODE | 0xF
.byte 0
GDT64.Data = . - GDT64
.long 0xFFFF
.byte 0
.byte P | S | RW
.byte GRAN_4K | SZ_32 | 0xF
.byte 0
GDT64.Tss = . - GDT64
.long 0x00000068
.long 0x00CF8900
GDT64.Ptr:
.word . - GDT64 - 1
.quad GDT64

View File

@ -0,0 +1,301 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
union __attribute__((packed)) PageTableEntry
{
struct
{
bool Present : 1; // 0
bool ReadWrite : 1; // 1
bool UserSupervisor : 1; // 2
bool WriteThrough : 1; // 3
bool CacheDisable : 1; // 4
bool Accessed : 1; // 5
bool Dirty : 1; // 6
bool PageAttributeTable : 1; // 7
bool Global : 1; // 8
uint8_t Available0 : 3; // 9-11
uint64_t Address : 40; // 12-51
uint32_t Available1 : 7; // 52-58
uint8_t ProtectionKey : 4; // 59-62
bool ExecuteDisable : 1; // 63
};
uint64_t raw;
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
{
_Address &= 0x000000FFFFFFFFFF;
this->raw &= 0xFFF0000000000FFF;
this->raw |= (_Address << 12);
}
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
};
struct __attribute__((packed)) PageTableEntryPtr
{
PageTableEntry Entries[512];
};
union __attribute__((packed)) PageDirectoryEntry
{
struct
{
bool Present : 1; // 0
bool ReadWrite : 1; // 1
bool UserSupervisor : 1; // 2
bool WriteThrough : 1; // 3
bool CacheDisable : 1; // 4
bool Accessed : 1; // 5
bool Available0 : 1; // 6
bool PageSize : 1; // 7
uint8_t Available1 : 4; // 8-11
uint64_t Address : 40; // 12-51
uint32_t Available2 : 11; // 52-62
bool ExecuteDisable : 1; // 63
};
uint64_t raw;
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
{
_Address &= 0x000000FFFFFFFFFF;
this->raw &= 0xFFF0000000000FFF;
this->raw |= (_Address << 12);
}
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
};
struct __attribute__((packed)) PageDirectoryEntryPtr
{
PageDirectoryEntry Entries[512];
};
union __attribute__((packed)) PageDirectoryPointerTableEntry
{
struct
{
bool Present : 1; // 0
bool ReadWrite : 1; // 1
bool UserSupervisor : 1; // 2
bool WriteThrough : 1; // 3
bool CacheDisable : 1; // 4
bool Accessed : 1; // 5
bool Available0 : 1; // 6
bool PageSize : 1; // 7
uint8_t Available1 : 4; // 8-11
uint64_t Address : 40; // 12-51
uint32_t Available2 : 11; // 52-62
bool ExecuteDisable : 1; // 63
};
uint64_t raw;
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
{
_Address &= 0x000000FFFFFFFFFF;
this->raw &= 0xFFF0000000000FFF;
this->raw |= (_Address << 12);
}
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
};
struct __attribute__((packed)) PageDirectoryPointerTableEntryPtr
{
PageDirectoryPointerTableEntry Entries[512];
};
union __attribute__((packed)) PageMapLevel4
{
struct
{
bool Present : 1; // 0
bool ReadWrite : 1; // 1
bool UserSupervisor : 1; // 2
bool WriteThrough : 1; // 3
bool CacheDisable : 1; // 4
bool Accessed : 1; // 5
bool Available0 : 1; // 6
bool Reserved0 : 1; // 7
uint8_t Available1 : 4; // 8-11
uint64_t Address : 40; // 12-51
uint32_t Available2 : 11; // 52-62
bool ExecuteDisable : 1; // 63
};
uint64_t raw;
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
{
_Address &= 0x000000FFFFFFFFFF;
this->raw &= 0xFFF0000000000FFF;
this->raw |= (_Address << 12);
}
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
};
struct PageTable4
{
PageMapLevel4 Entries[512];
} __attribute__((aligned(0x1000)));
extern "C" char BootPageTable[];
extern uintptr_t _kernel_start, _kernel_end;
__attribute__((section(".bootstrap.data"))) static PageTable4 *BPTable = (PageTable4 *)BootPageTable;
__attribute__((section(".bootstrap.data"))) static size_t BPT_Allocated = 0x4000;
__always_inline inline nsa NIF void *RequestPage()
{
void *Page = (void *)(BootPageTable + BPT_Allocated);
BPT_Allocated += 0x1000;
if (BPT_Allocated >= 0x10000) /* The length of BootPageTable */
{
while (true)
;
}
return Page;
}
class PageMapIndexer
{
public:
uintptr_t PMLIndex = 0;
uintptr_t PDPTEIndex = 0;
uintptr_t PDEIndex = 0;
uintptr_t PTEIndex = 0;
__always_inline inline nsa NIF PageMapIndexer(uintptr_t VirtualAddress)
{
uintptr_t Address = VirtualAddress;
Address >>= 12;
this->PTEIndex = Address & 0x1FF;
Address >>= 9;
this->PDEIndex = Address & 0x1FF;
Address >>= 9;
this->PDPTEIndex = Address & 0x1FF;
Address >>= 9;
this->PMLIndex = Address & 0x1FF;
}
};
__attribute__((section(".bootstrap.text"))) nsa NIF void MB2_64_Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags)
{
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
uint64_t DirectoryFlags = Flags & 0x3F;
PageMapLevel4 PML4 = BPTable->Entries[Index.PMLIndex];
PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr;
if (!PML4.Present)
{
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)RequestPage();
if (PDPTEPtr == nullptr)
return;
{
void *ptr = PDPTEPtr;
uint8_t value = 0;
size_t num = 0x1000;
uint8_t *p = (uint8_t *)ptr;
for (size_t i = 0; i < num; i++)
p[i] = value;
}
PML4.Present = true;
PML4.SetAddress((uintptr_t)PDPTEPtr >> 12);
}
else
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12);
PML4.raw |= DirectoryFlags;
BPTable->Entries[Index.PMLIndex] = PML4;
PageDirectoryPointerTableEntry PDPTE = PDPTEPtr->Entries[Index.PDPTEIndex];
PageDirectoryEntryPtr *PDEPtr = nullptr;
if (!PDPTE.Present)
{
PDEPtr = (PageDirectoryEntryPtr *)RequestPage();
if (PDEPtr == nullptr)
return;
{
void *ptr = PDEPtr;
uint8_t value = 0;
size_t num = 0x1000;
uint8_t *p = (uint8_t *)ptr;
for (size_t i = 0; i < num; i++)
p[i] = value;
}
PDPTE.Present = true;
PDPTE.SetAddress((uintptr_t)PDEPtr >> 12);
}
else
PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE.GetAddress() << 12);
PDPTE.raw |= DirectoryFlags;
PDPTEPtr->Entries[Index.PDPTEIndex] = PDPTE;
PageDirectoryEntry PDE = PDEPtr->Entries[Index.PDEIndex];
PageTableEntryPtr *PTEPtr = nullptr;
if (!PDE.Present)
{
PTEPtr = (PageTableEntryPtr *)RequestPage();
if (PTEPtr == nullptr)
return;
{
void *ptr = PTEPtr;
uint8_t value = 0;
size_t num = 0x1000;
uint8_t *p = (uint8_t *)ptr;
for (size_t i = 0; i < num; i++)
p[i] = value;
}
PDE.Present = true;
PDE.SetAddress((uintptr_t)PTEPtr >> 12);
}
else
PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE.GetAddress() << 12);
PDE.raw |= DirectoryFlags;
PDEPtr->Entries[Index.PDEIndex] = PDE;
PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex];
PTE.Present = true;
PTE.raw |= Flags;
PTE.SetAddress((uintptr_t)PhysicalAddress >> 12);
PTEPtr->Entries[Index.PTEIndex] = PTE;
asmv("invlpg (%0)"
:
: "r"(VirtualAddress)
: "memory");
}
EXTERNC __attribute__((section(".bootstrap.text"))) nsa NIF __attribute__((section(".bootstrap.text"))) void UpdatePageTable64()
{
BPTable = (PageTable4 *)BootPageTable;
uintptr_t KernelStart = (uintptr_t)&_kernel_start;
uintptr_t KernelEnd = (uintptr_t)&_kernel_end;
uintptr_t PhysicalStart = KernelStart - 0xFFFFFFFF80000000;
for (uintptr_t i = KernelStart; i < KernelEnd; i += 0x1000)
{
MB2_64_Map((void *)i, (void *)PhysicalStart, 0x3);
PhysicalStart += 0x1000;
}
asmv("mov %%cr3, %%rax\n"
"mov %%rax, %%cr3\n"
:
:
: "rax");
}

View File

@ -0,0 +1,62 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
PAGE_TABLE_SIZE = 0x4
.code32
.section .bootstrap.data, "a"
.align 0x1000
.global BootPageTable
BootPageTable:
.space 0x10000 /* 0x4000 bytes will be used in UpdatePageTable */
.section .bootstrap.text, "a"
.global UpdatePageTable
UpdatePageTable:
mov $(BootPageTable + 0x0000), %edi /* First PML4E */
mov $(BootPageTable + 0x1000), %eax /* First PDPTE */
or $0x3, %eax /* Bitwise OR on eax (PDPTE) with 11b (Present, Write) */
mov %eax, (%edi) /* Write 11b to PML4E */
mov $(BootPageTable + 0x1000), %edi /* First PDPTE */
mov $(BootPageTable + 0x2000), %eax /* First PDE */
or $0x3, %eax /* Bitwise OR on eax (PDE) with 11b (Present, Write) */
mov $PAGE_TABLE_SIZE, %ecx /* For loop instruction */
mov $0x0, %ebx /* Value to store in the next 4 bytes */
.FillPageTableLevel3:
mov %eax, (%edi) /* Store modified PDE in PDPTE */
mov %ebx, 0x4(%edi) /* Store the ebx value in the next 4 bytes */
add $0x1000, %eax /* Increment (page size) */
adc $0x0, %ebx /* Add 0 to carry flag */
add $0x8, %edi /* Add 8 to edi (next PDE) */
loop .FillPageTableLevel3 /* Loop until ecx is 0 */
mov $(BootPageTable + 0x2000), %edi /* First PDE */
mov $0x83, %eax /* Present, Write, Large Page */
mov $(512 * PAGE_TABLE_SIZE), %ecx /* For loop instruction */
mov $0x0, %ebx /* Value to store in the next 4 bytes */
.FillPageTableLevel2:
mov %eax, (%edi) /* Store modified PDE in PDPTE */
mov %ebx, 0x4(%edi) /* Store the ebx value in the next 4 bytes */
add $0x200000, %eax /* Increment (page size) */
adc $0x0, %ebx /* Add 0 (carry flag) to ebx to increment if there was a carry */
add $0x8, %edi /* Add 8 to edi (next PDE) */
loop .FillPageTableLevel2 /* Loop until ecx is 0 */
ret

View File

@ -0,0 +1,38 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
.extern Multiboot_start
.section .multiboot, "a"
.align 4
MULTIBOOT_HEADER:
.long 0x1BADB002
.long 0x1 | 0x2 | 0x4
.long -(0x1BADB002 + (0x1 | 0x2 | 0x4))
/* KLUDGE */
.long 0
.long 0
.long 0
.long 0
.long 0
/* VIDEO MODE */
.long 0
.long 0
.long 0
.long 0

View File

@ -0,0 +1,92 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
.extern Multiboot_start
/* https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html */
.section .multiboot2, "a"
.align 0x1000
MULTIBOOT2_HEADER_START:
.long 0xE85250D6
.long 0
.long (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START)
.long 0x100000000 - (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START) - 0 - 0xE85250D6
.align 8
InfoRequestTag_Start:
.word 1
.word 0
.long InfoRequestTag_End - InfoRequestTag_Start
.long 1 /* Command Line */
.long 2 /* Boot Loader Name */
.long 3 /* Module */
.long 4 /* Basic Memory Information */
.long 5 /* BIOS Boot Device */
.long 6 /* Memory Map */
.long 7 /* VBE */
.long 8 /* Framebuffer */
.long 9 /* ELF Sections */
.long 10 /* APM Table */
.long 11 /* EFI 32-bit System Table Pointer */
.long 12 /* EFI 64-bit System Table Pointer */
/* .long 13 */ /* SMBIOS */
.long 14 /* ACPI Old */
.long 15 /* ACPI New */
.long 16 /* Network */
.long 17 /* EFI Memory Map */
.long 18 /* EFI Boot Services Notifier */
.long 19 /* EFI 32-bit Image Handle Pointer */
.long 20 /* EFI 64-bit Image Handle Pointer */
.long 21 /* Load Base Address */
InfoRequestTag_End:
.align 8
FramebufferTag_Start:
.word 5
.word 1
.long FramebufferTag_End - FramebufferTag_Start
.long 0
.long 0
.long 32
FramebufferTag_End:
.align 8
EGATextSupportTag_Start:
.word 4
.word 0
.long EGATextSupportTag_End - EGATextSupportTag_Start
.long 0 /* https://www.gnu.org/software/grub/manual/multiboot2/html_node/Console-header-tags.html */
EGATextSupportTag_End:
.align 8
AlignedModulesTag_Start:
.word 6
.word 0
.long AlignedModulesTag_End - AlignedModulesTag_Start
AlignedModulesTag_End:
.align 8
EntryAddressTag_Start:
.word 3
.word 0
.long EntryAddressTag_End - EntryAddressTag_Start
.long Multiboot_start
EntryAddressTag_End:
.align 8
EndTag_Start:
.word 0
.word 0
.long EndTag_End - EndTag_Start
EndTag_End:
MULTIBOOT2_HEADER_END:
nop

View File

@ -0,0 +1,210 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <boot/protocol/multiboot.h>
#include <memory.hpp>
#include "../../../../kernel.h"
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
{
multiboot_info *InfoAddress = r_cst(multiboot_info *, Info);
if (InfoAddress->flags & MULTIBOOT_INFO_MEMORY)
{
fixme("mem_lower: %#x, mem_upper: %#x",
InfoAddress->mem_lower, InfoAddress->mem_upper);
}
if (InfoAddress->flags & MULTIBOOT_INFO_BOOTDEV)
{
fixme("boot_device: %#x",
InfoAddress->boot_device);
}
if (InfoAddress->flags & MULTIBOOT_INFO_CMDLINE)
{
strncpy(mb2binfo.Kernel.CommandLine,
r_cst(const char *, InfoAddress->cmdline),
strlen(r_cst(const char *, InfoAddress->cmdline)));
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
}
if (InfoAddress->flags & MULTIBOOT_INFO_MODS)
{
multiboot_mod_list *module = r_cst(multiboot_mod_list *, InfoAddress->mods_addr);
for (size_t i = 0; i < InfoAddress->mods_count; i++)
{
if (i > MAX_MODULES)
{
warn("Too many modules, skipping the rest...");
break;
}
mb2binfo.Modules[i].Address = (void *)(uint64_t)module[i].mod_start;
mb2binfo.Modules[i].Size = module[i].mod_end - module[i].mod_start;
strncpy(mb2binfo.Modules[i].Path, "(null)", 6);
strncpy(mb2binfo.Modules[i].CommandLine, r_cst(const char *, module[i].cmdline),
strlen(r_cst(const char *, module[i].cmdline)));
debug("Module: %s", mb2binfo.Modules[i].Path);
}
}
if (InfoAddress->flags & MULTIBOOT_INFO_AOUT_SYMS)
{
fixme("aout_sym: [tabsize: %#x, strsize: %#x, addr: %#x, reserved: %#x]",
InfoAddress->u.aout_sym.tabsize, InfoAddress->u.aout_sym.strsize,
InfoAddress->u.aout_sym.addr, InfoAddress->u.aout_sym.reserved);
}
if (InfoAddress->flags & MULTIBOOT_INFO_ELF_SHDR)
{
mb2binfo.Kernel.Symbols.Num = InfoAddress->u.elf_sec.num;
mb2binfo.Kernel.Symbols.EntSize = InfoAddress->u.elf_sec.size;
mb2binfo.Kernel.Symbols.Shndx = InfoAddress->u.elf_sec.shndx;
mb2binfo.Kernel.Symbols.Sections = s_cst(uintptr_t, InfoAddress->u.elf_sec.addr);
}
if (InfoAddress->flags & MULTIBOOT_INFO_MEM_MAP)
{
mb2binfo.Memory.Entries = InfoAddress->mmap_length / sizeof(multiboot_mmap_entry);
for (uint32_t i = 0; i < mb2binfo.Memory.Entries; i++)
{
if (i > MAX_MEMORY_ENTRIES)
{
warn("Too many memory entries, skipping the rest...");
break;
}
multiboot_mmap_entry entry = r_cst(multiboot_mmap_entry *, InfoAddress->mmap_addr)[i];
mb2binfo.Memory.Size += entry.len;
switch (entry.type)
{
case MULTIBOOT_MEMORY_AVAILABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = Usable;
break;
case MULTIBOOT_MEMORY_RESERVED:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = Reserved;
break;
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
break;
case MULTIBOOT_MEMORY_NVS:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = ACPINVS;
break;
case MULTIBOOT_MEMORY_BADRAM:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = BadMemory;
break;
default:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = Unknown;
break;
}
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
mb2binfo.Memory.Entry[i].BaseAddress,
mb2binfo.Memory.Entry[i].Length,
mb2binfo.Memory.Entry[i].Type);
}
}
if (InfoAddress->flags & MULTIBOOT_INFO_DRIVE_INFO)
{
fixme("drives_length: %d, drives_addr: %#x",
InfoAddress->drives_length, InfoAddress->drives_addr);
}
if (InfoAddress->flags & MULTIBOOT_INFO_CONFIG_TABLE)
{
fixme("config_table: %#x",
InfoAddress->config_table);
}
if (InfoAddress->flags & MULTIBOOT_INFO_BOOT_LOADER_NAME)
{
strncpy(mb2binfo.Bootloader.Name,
r_cst(const char *, InfoAddress->boot_loader_name),
strlen(r_cst(const char *, InfoAddress->boot_loader_name)));
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
}
if (InfoAddress->flags & MULTIBOOT_INFO_APM_TABLE)
{
fixme("apm_table: %#x",
InfoAddress->apm_table);
}
if (InfoAddress->flags & MULTIBOOT_INFO_VBE_INFO)
{
fixme("vbe_control_info: %#x, vbe_mode_info: %#x, vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x",
InfoAddress->vbe_control_info, InfoAddress->vbe_mode_info,
InfoAddress->vbe_mode, InfoAddress->vbe_interface_seg,
InfoAddress->vbe_interface_off, InfoAddress->vbe_interface_len);
}
if (InfoAddress->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)
{
static int fb_count = 0;
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)InfoAddress->framebuffer_addr;
mb2binfo.Framebuffer[fb_count].Width = InfoAddress->framebuffer_width;
mb2binfo.Framebuffer[fb_count].Height = InfoAddress->framebuffer_height;
mb2binfo.Framebuffer[fb_count].Pitch = InfoAddress->framebuffer_pitch;
mb2binfo.Framebuffer[fb_count].BitsPerPixel = InfoAddress->framebuffer_bpp;
switch (InfoAddress->framebuffer_type)
{
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
{
mb2binfo.Framebuffer[fb_count].Type = Indexed;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
{
mb2binfo.Framebuffer[fb_count].Type = RGB;
mb2binfo.Framebuffer[fb_count].RedMaskSize = InfoAddress->framebuffer_red_mask_size;
mb2binfo.Framebuffer[fb_count].RedMaskShift = InfoAddress->framebuffer_red_field_position;
mb2binfo.Framebuffer[fb_count].GreenMaskSize = InfoAddress->framebuffer_green_mask_size;
mb2binfo.Framebuffer[fb_count].GreenMaskShift = InfoAddress->framebuffer_green_field_position;
mb2binfo.Framebuffer[fb_count].BlueMaskSize = InfoAddress->framebuffer_blue_mask_size;
mb2binfo.Framebuffer[fb_count].BlueMaskShift = InfoAddress->framebuffer_blue_field_position;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
{
mb2binfo.Framebuffer[fb_count].Type = EGA;
break;
}
default:
{
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
break;
}
}
debug("Framebuffer %d: %dx%d %d bpp",
fb_count, InfoAddress->framebuffer_width,
InfoAddress->framebuffer_height,
InfoAddress->framebuffer_bpp);
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
InfoAddress->framebuffer_addr, InfoAddress->framebuffer_pitch, InfoAddress->framebuffer_type,
InfoAddress->framebuffer_red_mask_size, InfoAddress->framebuffer_red_field_position, InfoAddress->framebuffer_green_mask_size,
InfoAddress->framebuffer_green_field_position, InfoAddress->framebuffer_blue_mask_size, InfoAddress->framebuffer_blue_field_position);
}
mb2binfo.Kernel.PhysicalBase = (void *)&_bootstrap_start;
mb2binfo.Kernel.VirtualBase = (void *)(uint64_t)((uint64_t)&_bootstrap_start + 0xFFFFFFFF80000000);
mb2binfo.Kernel.Size = ((uint64_t)&_kernel_end - (uint64_t)&_kernel_start) + ((uint64_t)&_bootstrap_end - (uint64_t)&_bootstrap_start);
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
Entry(&mb2binfo);
}

View File

@ -0,0 +1,284 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <boot/protocol/multiboot2.h>
#include <memory.hpp>
#include "../../../../kernel.h"
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
{
auto InfoAddress = Info;
for (auto Tag = (struct multiboot_tag *)((uint8_t *)InfoAddress + 8);
;
Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7)))
{
if (Tag->type == MULTIBOOT_TAG_TYPE_END)
{
debug("End of multiboot2 tags");
break;
}
switch (Tag->type)
{
case MULTIBOOT_TAG_TYPE_CMDLINE:
{
strncpy(mb2binfo.Kernel.CommandLine,
((multiboot_tag_string *)Tag)->string,
strlen(((multiboot_tag_string *)Tag)->string));
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
break;
}
case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
{
strncpy(mb2binfo.Bootloader.Name,
((multiboot_tag_string *)Tag)->string,
strlen(((multiboot_tag_string *)Tag)->string));
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
break;
}
case MULTIBOOT_TAG_TYPE_MODULE:
{
multiboot_tag_module *module = (multiboot_tag_module *)Tag;
static int module_count = 0;
mb2binfo.Modules[module_count].Address = (void *)(uint64_t)module->mod_start;
mb2binfo.Modules[module_count].Size = module->mod_end - module->mod_start;
strncpy(mb2binfo.Modules[module_count].Path, "(null)", 6);
strncpy(mb2binfo.Modules[module_count].CommandLine, module->cmdline,
strlen(module->cmdline));
debug("Module: %s", mb2binfo.Modules[module_count].Path);
module_count++;
break;
}
case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
{
multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag;
fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]",
meminfo->mem_lower, meminfo->mem_upper);
break;
}
case MULTIBOOT_TAG_TYPE_BOOTDEV:
{
multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag;
fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]",
bootdev->biosdev, bootdev->slice, bootdev->part);
break;
}
case MULTIBOOT_TAG_TYPE_MMAP:
{
multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag;
size_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry);
mb2binfo.Memory.Entries = EntryCount;
for (uint32_t i = 0; i < EntryCount; i++)
{
if (i > MAX_MEMORY_ENTRIES)
{
warn("Too many memory entries, skipping the rest...");
break;
}
multiboot_mmap_entry entry = mmap->entries[i];
mb2binfo.Memory.Size += entry.len;
switch (entry.type)
{
case MULTIBOOT_MEMORY_AVAILABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = Usable;
break;
case MULTIBOOT_MEMORY_RESERVED:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = Reserved;
break;
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
break;
case MULTIBOOT_MEMORY_NVS:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = ACPINVS;
break;
case MULTIBOOT_MEMORY_BADRAM:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = BadMemory;
break;
default:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = entry.len;
mb2binfo.Memory.Entry[i].Type = Unknown;
break;
}
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
mb2binfo.Memory.Entry[i].BaseAddress,
mb2binfo.Memory.Entry[i].Length,
mb2binfo.Memory.Entry[i].Type);
}
break;
}
case MULTIBOOT_TAG_TYPE_VBE:
{
multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag;
fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]",
vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len);
break;
}
case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
{
multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag;
static int fb_count = 0;
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr;
mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width;
mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height;
mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch;
mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp;
switch (fb->common.framebuffer_type)
{
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
{
mb2binfo.Framebuffer[fb_count].Type = Indexed;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
{
mb2binfo.Framebuffer[fb_count].Type = RGB;
mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size;
mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position;
mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size;
mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position;
mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size;
mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
{
mb2binfo.Framebuffer[fb_count].Type = EGA;
break;
}
default:
{
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
break;
}
}
debug("Framebuffer %d: %dx%d %d bpp", fb_count, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp);
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type,
fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size,
fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position);
fb_count++;
break;
}
case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
{
multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag;
mb2binfo.Kernel.Symbols.Num = elf->num;
mb2binfo.Kernel.Symbols.EntSize = elf->entsize;
mb2binfo.Kernel.Symbols.Shndx = elf->shndx;
mb2binfo.Kernel.Symbols.Sections = r_cst(uintptr_t, elf->sections);
break;
}
case MULTIBOOT_TAG_TYPE_APM:
{
multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag;
fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]",
apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len);
break;
}
case MULTIBOOT_TAG_TYPE_EFI32:
{
multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag;
fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size);
break;
}
case MULTIBOOT_TAG_TYPE_EFI64:
{
multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag;
fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size);
break;
}
case MULTIBOOT_TAG_TYPE_SMBIOS:
{
multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag;
fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor);
break;
}
case MULTIBOOT_TAG_TYPE_ACPI_OLD:
{
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp;
debug("OLD ACPI RSDP: %p", mb2binfo.RSDP);
break;
}
case MULTIBOOT_TAG_TYPE_ACPI_NEW:
{
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp;
debug("NEW ACPI RSDP: %p", mb2binfo.RSDP);
break;
}
case MULTIBOOT_TAG_TYPE_NETWORK:
{
multiboot_tag_network *net = (multiboot_tag_network *)Tag;
fixme("network->[dhcpack: %p]", net->dhcpack);
break;
}
case MULTIBOOT_TAG_TYPE_EFI_MMAP:
{
multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag;
fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]",
efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap);
break;
}
case MULTIBOOT_TAG_TYPE_EFI_BS:
{
fixme("efi_bs->[%p] (unknown structure)", Tag);
break;
}
case MULTIBOOT_TAG_TYPE_EFI32_IH:
{
multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag;
fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer);
break;
}
case MULTIBOOT_TAG_TYPE_EFI64_IH:
{
multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag;
fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer);
break;
}
case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
{
multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag;
mb2binfo.Kernel.PhysicalBase = (void *)(uint64_t)load_base_addr->load_base_addr;
mb2binfo.Kernel.VirtualBase = (void *)(uint64_t)(load_base_addr->load_base_addr + 0xFFFFFFFF80000000);
mb2binfo.Kernel.Size = ((uint64_t)&_kernel_end - (uint64_t)&_kernel_start) + ((uint64_t)&_bootstrap_end - (uint64_t)&_bootstrap_start);
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
break;
}
default:
{
error("Unknown multiboot2 tag type: %d", Tag->type);
break;
}
}
}
Entry(&mb2binfo);
}

View File

@ -0,0 +1,53 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <memory.hpp>
#include "../../../../kernel.h"
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
EXTERNC void multiboot_main(uintptr_t Magic, uintptr_t Info)
{
BootInfo mb2binfo{};
if (Info == NULL || Magic == NULL)
{
if (Magic == NULL)
error("Multiboot magic is NULL");
if (Info == NULL)
error("Multiboot info is NULL");
CPU::Stop();
}
else if (Magic == MULTIBOOT_BOOTLOADER_MAGIC)
multiboot_parse(mb2binfo, Magic, Info);
else if (Magic == MULTIBOOT2_BOOTLOADER_MAGIC)
multiboot2_parse(mb2binfo, Magic, Info);
else
{
error("Unknown multiboot magic %#x", Magic);
CPU::Stop();
}
}

View File

@ -0,0 +1,124 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
KERNEL_STACK_SIZE = 0x4000 /* 16KB */
.extern DetectCPUID
.extern Detect64Bit
.extern DetectPSE
.extern DetectPAE
.extern multiboot_main
.extern LoadGDT32
.extern BootPageTable
.extern UpdatePageTable
.extern GDT64.Ptr
.extern GDT64.Code
.extern GDT64.Data
.section .bootstrap.data, "a"
MB_HeaderMagic:
.quad 0
MB_HeaderInfo:
.quad 0
.section .bootstrap.text, "a"
x32Hang:
cli
hlt
jmp x32Hang
.global Multiboot_start
Multiboot_start:
cli
mov %eax, [MB_HeaderMagic]
mov %ebx, [MB_HeaderInfo]
call DetectCPUID
cmp $0, %eax
je x32Hang
call Detect64Bit
cmp $0, %eax
je x32Hang
call DetectPSE
cmp $0, %eax
je x32Hang
call DetectPAE
cmp $0, %eax
je x32Hang
mov %cr4, %ecx
or $0x00000010, %ecx /* PSE */
or $0x00000020, %ecx /* PAE */
mov %ecx, %cr4
call LoadGDT32
call UpdatePageTable
mov $BootPageTable, %ecx
mov %ecx, %cr3
mov $0xC0000080, %ecx /* EFER */
rdmsr
or $0x800, %eax /* LME */
or $0x100, %eax /* LMA */
or $0x1, %eax /* SCE */
wrmsr
mov %cr0, %ecx
or $0x80000000, %ecx /* PG */
or $0x1, %ecx /* PE */
mov %ecx, %cr0
lgdt [GDT64.Ptr]
ljmp $GDT64.Code, $HigherHalfStart
.extern UpdatePageTable64
.code64
HigherHalfStart:
mov $GDT64.Data, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
call UpdatePageTable64
mov $(KernelStack + KERNEL_STACK_SIZE), %rsp
mov $0x0, %rbp
mov [MB_HeaderMagic], %rdi
mov [MB_HeaderInfo], %rsi
push %rsi
push %rdi
call multiboot_main
.Hang:
hlt
jmp .Hang
.section .bootstrap.bss, "a"
.align 16
KernelStack:
.space KERNEL_STACK_SIZE

View File

@ -0,0 +1,487 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "apic.hpp"
#include <memory.hpp>
#include <acpi.hpp>
#include <uart.hpp>
#include <lock.hpp>
#include <cpu.hpp>
#include <smp.hpp>
#include <io.h>
#include "../../../kernel.h"
NewLock(APICLock);
using namespace CPU::x64;
using namespace CPU::x86;
/*
In constructor 'APIC::APIC::APIC(int)':
warning: left shift count >= width of type
| APICBaseAddress = BaseStruct.ApicBaseLo << 12u | BaseStruct.ApicBaseHi << 32u;
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
*/
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
namespace APIC
{
// headache
// https://www.amd.com/system/files/TechDocs/24593.pdf
// https://www.naic.edu/~phil/software/intel/318148.pdf
uint32_t APIC::Read(uint32_t Register)
{
#ifdef DEBUG
if (Register != APIC_ICRLO &&
Register != APIC_ICRHI &&
Register != APIC_ID)
debug("APIC::Read(%#lx) [x2=%d]",
Register, x2APICSupported ? 1 : 0);
#endif
if (unlikely(x2APICSupported))
assert(!"x2APIC is not supported");
CPU::MemBar::Barrier();
uint32_t ret = *((volatile uint32_t *)((uintptr_t)APICBaseAddress + Register));
CPU::MemBar::Barrier();
return ret;
}
void APIC::Write(uint32_t Register, uint32_t Value)
{
#ifdef DEBUG
if (Register != APIC_EOI &&
Register != APIC_TDCR &&
Register != APIC_TIMER &&
Register != APIC_TICR &&
Register != APIC_ICRLO &&
Register != APIC_ICRHI)
debug("APIC::Write(%#lx, %#lx) [x2=%d]",
Register, Value, x2APICSupported ? 1 : 0);
#endif
if (unlikely(x2APICSupported))
assert(!"x2APIC is not supported");
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)APICBaseAddress) + Register)) = Value;
CPU::MemBar::Barrier();
}
void APIC::IOWrite(uint64_t Base, uint32_t Register, uint32_t Value)
{
debug("APIC::IOWrite(%#lx, %#lx, %#lx)", Base, Register, Value);
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)Base + 16))) = Value;
CPU::MemBar::Barrier();
}
uint32_t APIC::IORead(uint64_t Base, uint32_t Register)
{
debug("APIC::IORead(%#lx, %#lx)", Base, Register);
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
CPU::MemBar::Barrier();
uint32_t ret = *((volatile uint32_t *)(((uintptr_t)Base + 16)));
CPU::MemBar::Barrier();
return ret;
}
void APIC::EOI()
{
Memory::SwapPT swap =
Memory::SwapPT(KernelPageTable, thisPageTable);
if (this->x2APICSupported)
wrmsr(MSR_X2APIC_EOI, 0);
else
this->Write(APIC_EOI, 0);
}
void APIC::WaitForIPI()
{
if (this->x2APICSupported)
{
ErrorStatusRegister esr{};
esr.raw = uint32_t(rdmsr(MSR_X2APIC_ESR));
UNUSED(esr);
/* FIXME: Not sure if this is required or
how to implement it. */
}
else
{
InterruptCommandRegister icr{};
do
{
icr.split.Low = this->Read(APIC_ICRLO);
CPU::Pause();
} while (icr.DS != Idle);
}
}
void APIC::ICR(InterruptCommandRegister icr)
{
SmartCriticalSection(APICLock);
if (x2APICSupported)
{
assert(icr.MT != LowestPriority);
assert(icr.MT != DeliveryMode);
assert(icr.MT != ExtINT);
wrmsr(MSR_X2APIC_ICR, icr.raw);
this->WaitForIPI();
}
else
{
this->Write(APIC_ICRHI, icr.split.High);
this->Write(APIC_ICRLO, icr.split.Low);
this->WaitForIPI();
}
}
void APIC::SendInitIPI(int CPU)
{
SmartCriticalSection(APICLock);
InterruptCommandRegister icr{};
if (x2APICSupported)
{
icr.x2.MT = INIT;
icr.x2.L = Assert;
icr.x2.DES = uint8_t(CPU);
wrmsr(MSR_X2APIC_ICR, icr.raw);
this->WaitForIPI();
}
else
{
icr.MT = INIT;
icr.L = Assert;
icr.DES = uint8_t(CPU);
this->Write(APIC_ICRHI, icr.split.High);
this->Write(APIC_ICRLO, icr.split.Low);
this->WaitForIPI();
}
}
void APIC::SendStartupIPI(int CPU, uint64_t StartupAddress)
{
SmartCriticalSection(APICLock);
InterruptCommandRegister icr{};
if (x2APICSupported)
{
icr.x2.VEC = s_cst(uint8_t, StartupAddress >> 12);
icr.x2.MT = Startup;
icr.x2.L = Assert;
icr.x2.DES = uint8_t(CPU);
wrmsr(MSR_X2APIC_ICR, icr.raw);
this->WaitForIPI();
}
else
{
icr.VEC = s_cst(uint8_t, StartupAddress >> 12);
icr.MT = Startup;
icr.L = Assert;
icr.DES = uint8_t(CPU);
this->Write(APIC_ICRHI, icr.split.High);
this->Write(APIC_ICRLO, icr.split.Low);
this->WaitForIPI();
}
}
uint32_t APIC::IOGetMaxRedirect(uint32_t APICID)
{
ACPI::MADT::MADTIOApic *ioapic = ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[APICID];
uint32_t TableAddress = (this->IORead(ioapic->Address, GetIOAPICVersion));
IOAPICVersion ver = {.raw = TableAddress};
return ver.MLE + 1;
}
void APIC::RawRedirectIRQ(uint8_t Vector, uint32_t GSI, uint16_t Flags, uint8_t CPU, int Status)
{
int64_t IOAPICTarget = -1;
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
for (size_t i = 0; i < madt->ioapic.size(); i++)
{
if (madt->ioapic[i]->GSIBase <= GSI)
{
if (madt->ioapic[i]->GSIBase + IOGetMaxRedirect(uint32_t(i)) > GSI)
{
IOAPICTarget = i;
break;
}
}
}
if (IOAPICTarget == -1)
{
error("No ISO table found for I/O APIC");
return;
}
IOAPICRedirectEntry Entry{};
Entry.VEC = Vector;
Entry.DES = CPU;
if (Flags & ActiveHighLow)
Entry.IPP = 1;
if (Flags & EdgeLevel)
Entry.TGM = 1;
if (!Status)
Entry.M = 1;
uint32_t IORegister = (GSI - madt->ioapic[IOAPICTarget]->GSIBase) * 2 + 16;
this->IOWrite(madt->ioapic[IOAPICTarget]->Address,
IORegister, Entry.split.Low);
this->IOWrite(madt->ioapic[IOAPICTarget]->Address,
IORegister + 1, Entry.split.High);
}
void APIC::RedirectIRQ(uint8_t CPU, uint8_t IRQ, int Status)
{
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
for (uint64_t i = 0; i < madt->iso.size(); i++)
if (madt->iso[i]->IRQSource == IRQ)
{
debug("[ISO %d] Mapping to source IRQ%#d GSI:%#lx on CPU %d",
i, madt->iso[i]->IRQSource, madt->iso[i]->GSI, CPU);
this->RawRedirectIRQ(madt->iso[i]->IRQSource + 0x20,
madt->iso[i]->GSI,
madt->iso[i]->Flags,
CPU, Status);
return;
}
debug("Mapping IRQ%d on CPU %d", IRQ, CPU);
this->RawRedirectIRQ(IRQ + 0x20, IRQ, 0, CPU, Status);
}
void APIC::RedirectIRQs(uint8_t CPU)
{
SmartCriticalSection(APICLock);
debug("Redirecting IRQs...");
for (uint8_t i = 0; i < 16; i++)
this->RedirectIRQ(CPU, i, 1);
debug("Redirecting IRQs completed.");
}
APIC::APIC(int Core)
{
SmartCriticalSection(APICLock);
APIC_BASE BaseStruct = {.raw = rdmsr(MSR_APIC_BASE)};
uint64_t BaseLow = BaseStruct.ABALow;
uint64_t BaseHigh = BaseStruct.ABAHigh;
this->APICBaseAddress = BaseLow << 12u | BaseHigh << 32u;
trace("APIC Address: %#lx", this->APICBaseAddress);
Memory::Virtual().Map((void *)this->APICBaseAddress,
(void *)this->APICBaseAddress,
Memory::RW | Memory::PCD);
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
if (cpuid.ECX.x2APIC)
{
this->x2APICSupported = cpuid.ECX.x2APIC;
debug("x2APIC is supported");
}
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
if (cpuid.ECX.x2APIC)
{
this->x2APICSupported = cpuid.ECX.x2APIC;
debug("x2APIC is supported");
}
}
BaseStruct.AE = 1;
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
if (this->x2APICSupported)
{
BaseStruct.EXTD = 1;
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
}
if (!this->x2APICSupported)
{
this->Write(APIC_TPR, 0x0);
this->Write(APIC_DFR, 0xF0000000);
this->Write(APIC_LDR, this->Read(APIC_ID));
}
else
{
wrmsr(MSR_X2APIC_TPR, 0x0);
}
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
for (size_t i = 0; i < madt->nmi.size(); i++)
{
if (madt->nmi[i]->processor != 0xFF &&
Core != madt->nmi[i]->processor)
break;
uint32_t nmi = 0x402;
if (madt->nmi[i]->flags & 2)
nmi |= 1 << 13;
if (madt->nmi[i]->flags & 8)
nmi |= 1 << 15;
if (madt->nmi[i]->lint == 0)
{
if (this->x2APICSupported)
wrmsr(MSR_X2APIC_LVT_LINT0, nmi);
else
this->Write(APIC_LINT0, nmi);
}
else if (madt->nmi[i]->lint == 1)
{
if (this->x2APICSupported)
wrmsr(MSR_X2APIC_LVT_LINT1, nmi);
else
this->Write(APIC_LINT1, nmi);
}
}
/* Setup the spurious interrupt vector */
Spurious svr{};
if (this->x2APICSupported)
svr.raw = uint32_t(rdmsr(MSR_X2APIC_SIVR));
else
svr.raw = this->Read(APIC_SVR);
svr.VEC = IRQ223;
svr.ASE = 1;
if (this->x2APICSupported)
wrmsr(MSR_X2APIC_SIVR, svr.raw);
else
this->Write(APIC_SVR, svr.raw);
static int once = 0;
if (!once++)
{
// Disable PIT
outb(0x43, 0x28);
outb(0x40, 0x0);
// Disable PIC
outb(0x21, 0xFF);
outb(0xA1, 0xFF);
}
}
APIC::~APIC() {}
void Timer::OnInterruptReceived(CPU::TrapFrame *) {}
void Timer::OneShot(uint32_t Vector, uint64_t Miliseconds)
{
/* FIXME: Sometimes APIC stops firing when debugging, why? */
LVTTimer timer{};
timer.VEC = uint8_t(Vector);
timer.TMM = LVTTimerMode::OneShot;
LVTTimerDivide Divider = DivideBy8;
SmartCriticalSection(APICLock);
if (this->lapic->x2APIC)
{
// wrmsr(MSR_X2APIC_DIV_CONF, Divider); <- gpf on real hardware
wrmsr(MSR_X2APIC_INIT_COUNT, uint32_t(Ticks * Miliseconds));
wrmsr(MSR_X2APIC_LVT_TIMER, uint32_t(timer.raw));
}
else
{
this->lapic->Write(APIC_TDCR, Divider);
this->lapic->Write(APIC_TICR, uint32_t(Ticks * Miliseconds));
this->lapic->Write(APIC_TIMER, uint32_t(timer.raw));
}
}
Timer::Timer(APIC *apic) : Interrupts::Handler(0) /* IRQ0 */
{
SmartCriticalSection(APICLock);
this->lapic = apic;
LVTTimerDivide Divider = DivideBy8;
trace("Initializing APIC timer on CPU %d",
GetCurrentCPU()->ID);
if (this->lapic->x2APIC)
{
wrmsr(MSR_X2APIC_DIV_CONF, Divider);
wrmsr(MSR_X2APIC_INIT_COUNT, 0xFFFFFFFF);
}
else
{
this->lapic->Write(APIC_TDCR, Divider);
this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
}
TimeManager->Sleep(1, Time::Units::Milliseconds);
// Mask the timer
if (this->lapic->x2APIC)
{
wrmsr(MSR_X2APIC_LVT_TIMER, 0x10000 /* LVTTimer.Mask flag */);
Ticks = 0xFFFFFFFF - rdmsr(MSR_X2APIC_CUR_COUNT);
}
else
{
this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */);
Ticks = 0xFFFFFFFF - this->lapic->Read(APIC_TCCR);
}
// Config for IRQ0 timer
LVTTimer timer{};
timer.VEC = IRQ0;
timer.M = Unmasked;
timer.TMM = LVTTimerMode::OneShot;
// Initialize APIC timer
if (this->lapic->x2APIC)
{
wrmsr(MSR_X2APIC_DIV_CONF, Divider);
wrmsr(MSR_X2APIC_INIT_COUNT, Ticks);
wrmsr(MSR_X2APIC_LVT_TIMER, timer.raw);
}
else
{
this->lapic->Write(APIC_TDCR, Divider);
this->lapic->Write(APIC_TICR, uint32_t(Ticks));
this->lapic->Write(APIC_TIMER, uint32_t(timer.raw));
}
trace("%d APIC Timer %d ticks in.",
GetCurrentCPU()->ID, Ticks);
KPrint("APIC Timer: \x1b[1;32m%ld\x1b[0m ticks.", Ticks);
}
Timer::~Timer()
{
}
}

View File

@ -0,0 +1,386 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_APIC_H__
#define __FENNIX_KERNEL_APIC_H__
#include <types.h>
#include <ints.hpp>
#include <cpu.hpp>
namespace APIC
{
enum APICRegisters
{
/* APIC ID Register */
APIC_ID = 0x20,
/* APIC Version Register */
APIC_VER = 0x30,
/* Task Priority Register (TPR) */
APIC_TPR = 0x80,
/* Arbitration Priority Register (APR) */
APIC_APR = 0x90,
/* Processor Priority Register (PPR) */
APIC_PPR = 0xA0,
/* End of Interrupt Register (EOI) */
APIC_EOI = 0xB0,
/* Remote Read Register */
APIC_RRD = 0xC0,
/* Logical Destination Register (LDR) */
APIC_LDR = 0xD0,
/* Destination Format Register (DFR) */
APIC_DFR = 0xE0,
/* Spurious Interrupt Vector Register */
APIC_SVR = 0xF0,
/* In-Service Register (ISR) */
APIC_ISR = 0x100,
/* Trigger Mode Register (TMR) */
APIC_TMR = 0x180,
/* Interrupt Request Register (IRR) */
APIC_IRR = 0x200,
/* Error Status Register (ESR) */
APIC_ESR = 0x280,
/* Interrupt Command Register Low (bits 31:0) */
APIC_ICRLO = 0x300,
/* Interrupt Command Register High (bits 63:32) */
APIC_ICRHI = 0x310,
/* Timer Local Vector Table Entry */
APIC_TIMER = 0x320,
/* Thermal Local Vector Table Entry */
APIC_THERMAL = 0x330,
/* Performance Counter Local Vector Table Entry */
APIC_PERF = 0x340,
/* Local Interrupt 0 Vector Table Entry */
APIC_LINT0 = 0x350,
/* Local Interrupt 1 Vector Table Entry */
APIC_LINT1 = 0x360,
/* Error Vector Table Entry */
APIC_ERROR = 0x370,
/* Timer Initial Count Register */
APIC_TICR = 0x380,
/* Timer Current Count Register */
APIC_TCCR = 0x390,
/* Timer Divide Configuration Register */
APIC_TDCR = 0x3E0,
/* Extended APIC Feature Register */
APIC_EFR = 0x400,
/* Extended APIC Control Register */
APIC_ECR = 0x410,
/* Specific End of Interrupt Register (SEOI) */
APIC_SEOI = 0x420,
/* Interrupt Enable Registers (IER) */
APIC_IER0 = 0x480,
/* Extended Interrupt [3:0] Local Vector Table Registers */
APIC_EILVT0 = 0x500,
};
enum IOAPICRegisters
{
GetIOAPICVersion = 0x1
};
enum IOAPICFlags
{
ActiveHighLow = 2,
EdgeLevel = 8
};
enum APICMessageType
{
Fixed = 0b000,
LowestPriority = 0b001, /* Reserved */
SMI = 0b010,
DeliveryMode = 0b011, /* Reserved */
NMI = 0b100,
INIT = 0b101,
Startup = 0b110,
ExtINT = 0b111 /* Reserved */
};
enum APICDestinationMode
{
Physical = 0b0,
Logical = 0b1
};
enum APICDeliveryStatus
{
Idle = 0b0,
SendPending = 0b1
};
enum APICLevel
{
DeAssert = 0b0,
Assert = 0b1
};
enum APICTriggerMode
{
Edge = 0b0,
Level = 0b1
};
enum APICDestinationShorthand
{
NoShorthand = 0b00,
Self = 0b01,
AllIncludingSelf = 0b10,
AllExcludingSelf = 0b11
};
enum LVTTimerDivide
{
DivideBy2 = 0b000,
DivideBy4 = 0b001,
DivideBy8 = 0b010,
DivideBy16 = 0b011,
DivideBy32 = 0b100,
DivideBy64 = 0b101,
DivideBy128 = 0b110,
DivideBy1 = 0b111
};
enum LVTTimerMask
{
Unmasked = 0b0,
Masked = 0b1
};
enum LVTTimerMode
{
OneShot = 0b00,
Periodic = 0b01,
TSCDeadline = 0b10
};
typedef union
{
struct
{
/** Vector */
uint64_t VEC : 8;
/** Reserved */
uint64_t Reserved0 : 4;
/** Delivery Status */
uint64_t DS : 1;
/** Reserved */
uint64_t Reserved1 : 3;
/** Mask */
uint64_t M : 1;
/** Timer Mode */
uint64_t TMM : 1;
/** Reserved */
uint64_t Reserved2 : 14;
};
uint32_t raw;
} __packed LVTTimer;
typedef union
{
struct
{
/** Vector */
uint64_t VEC : 8;
/** APIC Software Enable */
uint64_t ASE : 1;
/** Focus CPU Core Checking */
uint64_t FCC : 1;
/** Reserved */
uint64_t Reserved0 : 22;
};
uint32_t raw;
} __packed Spurious;
typedef union
{
struct
{
/** Vector */
uint64_t VEC : 8;
/** Message Type */
uint64_t MT : 3;
/** Destination Mode */
uint64_t DM : 1;
/** Delivery Status */
uint64_t DS : 1;
/** Reserved */
uint64_t Reserved0 : 1;
/** Level */
uint64_t L : 1;
/** Trigger Mode */
uint64_t TGM : 1;
/** Remote Read Status */
uint64_t RSS : 2;
/** Destination Shorthand */
uint64_t DSH : 2;
/** Reserved */
uint64_t Reserved2 : 36;
/** Destination */
uint64_t DES : 8;
};
struct
{
/** Vector */
uint64_t VEC : 8;
/** Message Type */
uint64_t MT : 3;
/** Destination Mode */
uint64_t DM : 1;
/** Reserved */
uint64_t Reserved0 : 2;
/** Level */
uint64_t L : 1;
/** Trigger Mode */
uint64_t TGM : 1;
/** Reserved */
uint64_t Reserved1 : 2;
/** Destination Shorthand */
uint64_t DSH : 2;
/** Reserved */
uint64_t Reserved2 : 12;
/** Destination */
uint64_t DES : 32;
} x2;
struct
{
uint32_t Low;
uint32_t High;
} split;
uint64_t raw;
} __packed InterruptCommandRegister;
typedef union
{
struct
{
/** Reserved */
uint64_t Reserved0 : 2;
/** Sent Accept Error */
uint64_t SAE : 1;
/** Receive Accept Error */
uint64_t RAE : 1;
/** Reserved */
uint64_t Reserved1 : 1;
/** Sent Illegal Vector */
uint64_t SIV : 1;
/** Received Illegal Vector */
uint64_t RIV : 1;
/** Illegal Register Address */
uint64_t IRA : 1;
/** Reserved */
uint64_t Reserved2 : 24;
};
uint32_t raw;
} ErrorStatusRegister;
typedef union
{
struct
{
/** Interrupt Vector */
uint64_t VEC : 8;
/** Delivery Mode */
uint64_t MT : 3;
/** Destination Mode */
uint64_t DM : 1;
/** Delivery Status */
uint64_t DS : 1;
/** Interrupt Input Pin Polarity */
uint64_t IPP : 1;
/** Remote IRR */
uint64_t RIR : 1;
/** Trigger Mode */
uint64_t TGM : 1;
/** Mask */
uint64_t M : 1;
/** Reserved */
uint64_t Reserved0 : 15;
/** Reserved */
uint64_t Reserved1 : 24;
/** Destination */
uint64_t DES : 8;
};
struct
{
uint32_t Low;
uint32_t High;
} split;
uint64_t raw;
} __packed IOAPICRedirectEntry;
typedef union
{
struct
{
/** Version */
uint64_t VER : 8;
/** Reserved */
uint64_t Reserved0 : 8;
/** Max LVT Entries */
uint64_t MLE : 8;
/** Reserved */
uint64_t Reserved1 : 7;
/** Extended APIC Register Space Present */
uint64_t EAS : 1;
};
uint32_t raw;
} __packed IOAPICVersion;
class APIC
{
private:
bool x2APICSupported = false;
uint64_t APICBaseAddress = 0;
public:
decltype(x2APICSupported) &x2APIC = x2APICSupported;
uint32_t Read(uint32_t Register);
void Write(uint32_t Register, uint32_t Value);
void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value);
uint32_t IORead(uint64_t Base, uint32_t Register);
void EOI();
void RedirectIRQs(uint8_t CPU = 0);
void WaitForIPI();
void ICR(InterruptCommandRegister icr);
void SendInitIPI(int CPU);
void SendStartupIPI(int CPU, uint64_t StartupAddress);
uint32_t IOGetMaxRedirect(uint32_t APICID);
void RawRedirectIRQ(uint8_t Vector, uint32_t GSI, uint16_t Flags, uint8_t CPU, int Status);
void RedirectIRQ(uint8_t CPU, uint8_t IRQ, int Status);
APIC(int Core);
~APIC();
};
class Timer : public Interrupts::Handler
{
private:
APIC *lapic;
uint64_t Ticks = 0;
void OnInterruptReceived(CPU::TrapFrame *Frame);
public:
uint64_t GetTicks() { return Ticks; }
void OneShot(uint32_t Vector, uint64_t Miliseconds);
Timer(APIC *apic);
~Timer();
};
}
#endif // !__FENNIX_KERNEL_APIC_H__

View File

@ -0,0 +1,215 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "gdt.hpp"
#include <memory.hpp>
#include <smp.hpp>
#include <cpu.hpp>
#include <debug.h>
namespace GlobalDescriptorTable
{
static GlobalDescriptorTableEntries GDTEntriesTemplate = {
.Null = 0,
.Code = {
.SegmentLimitLow = 0xFFFF,
.BaseAddressLow = 0x0,
.BaseAddressHigh = 0x0,
.Accessed = 0,
.Readable = 1,
.Conforming = 0,
.Executable = 1,
.Type = 1,
.DescriptorPrivilegeLevel = 0,
.Present = 1,
.SegmentLimitHigh = 0xF,
.Available = 0,
.Long = 1,
.Default = 0,
.Granularity = 1,
.BaseAddressHigher = 0x0,
},
.Data = {
.SegmentLimitLow = 0xFFFF,
.BaseAddressLow = 0x0,
.BaseAddressHigh = 0x0,
.Accessed = 0,
.Writable = 1,
.ExpandDown = 0,
.Executable = 0,
.Type = 1,
.DescriptorPrivilegeLevel = 0,
.Present = 1,
.SegmentLimitHigh = 0xF,
.Available = 0,
.Reserved = 0,
.Default = 0,
.Granularity = 1,
.BaseAddressHigher = 0x0,
},
.UserData = {
.SegmentLimitLow = 0xFFFF,
.BaseAddressLow = 0x0,
.BaseAddressHigh = 0x0,
.Accessed = 0,
.Writable = 1,
.ExpandDown = 1,
.Executable = 0,
.Type = 1,
.DescriptorPrivilegeLevel = 3,
.Present = 1,
.SegmentLimitHigh = 0xF,
.Available = 0,
.Reserved = 0,
.Default = 0,
.Granularity = 1,
.BaseAddressHigher = 0x0,
},
.UserCode = {
.SegmentLimitLow = 0xFFFF,
.BaseAddressLow = 0x0,
.BaseAddressHigh = 0x0,
.Accessed = 0,
.Readable = 1,
.Conforming = 0,
.Executable = 1,
.Type = 1,
.DescriptorPrivilegeLevel = 3,
.Present = 1,
.SegmentLimitHigh = 0xF,
.Available = 0,
.Long = 1,
.Default = 0,
.Granularity = 1,
.BaseAddressHigher = 0x0,
},
.TaskStateSegment{},
};
GlobalDescriptorTableEntries GDTEntries[MAX_CPU] __aligned(16);
GlobalDescriptorTableDescriptor gdt[MAX_CPU] __aligned(16);
TaskStateSegment tss[MAX_CPU] = {
0,
{0, 0, 0},
0,
{0, 0, 0, 0, 0, 0, 0},
0,
0,
0,
};
void *CPUStackPointer[MAX_CPU];
nsa void Init(int Core)
{
GDTEntries[Core] = GDTEntriesTemplate;
gdt[Core] =
{
.Limit = sizeof(GlobalDescriptorTableEntries) - 1,
.BaseAddress = &GDTEntries[Core],
};
debug("GDT: %#lx", &gdt[Core]);
debug("GDT KERNEL CODE %#lx", GDT_KERNEL_CODE);
debug("GDT KERNEL DATA %#lx", GDT_KERNEL_DATA);
debug("GDT USER CODE %#lx", GDT_USER_CODE);
debug("GDT USER DATA %#lx", GDT_USER_DATA);
debug("GDT TSS %#lx", GDT_TSS);
CPU::x64::lgdt(&gdt[Core]);
asmv("movq %%rsp, %%rax\n"
"pushq $16\n"
"pushq %%rax\n"
"pushfq\n"
"pushq $8\n"
"pushq $1f\n"
"iretq\n"
"1:\n"
"movw $16, %%ax\n"
"movw %%ax, %%ds\n"
"movw %%ax, %%es\n" ::
: "memory", "rax");
CPUStackPointer[Core] = StackManager.Allocate(STACK_SIZE);
memset(CPUStackPointer[Core], 0, STACK_SIZE);
debug("CPU %d Stack Pointer: %#lx-%#lx (%d pages)", Core,
CPUStackPointer[Core], (uintptr_t)CPUStackPointer[Core] + STACK_SIZE,
TO_PAGES(STACK_SIZE + 1));
uintptr_t Base = (uintptr_t)&tss[Core];
size_t Limit = Base + sizeof(TaskStateSegment);
SystemSegmentDescriptor *tssDesc = &gdt[Core].BaseAddress->TaskStateSegment;
tssDesc->SegmentLimitLow = Limit & 0xFFFF;
tssDesc->BaseAddressLow = Base & 0xFFFF;
tssDesc->BaseAddressMiddle = (Base >> 16) & 0xFF;
tssDesc->Type = AVAILABLE_64BIT_TSS;
tssDesc->Zero0 = 0;
tssDesc->DescriptorPrivilegeLevel = 0;
tssDesc->Present = 1;
tssDesc->Available = 0;
tssDesc->Reserved0 = 0;
tssDesc->Granularity = 0;
tssDesc->BaseAddressHigh = (Base >> 24) & 0xFF;
tssDesc->BaseAddressHigher = s_cst(uint32_t, (Base >> 32) & 0xFFFFFFFF);
tssDesc->Reserved1 = 0;
tssDesc->Zero1 = 0;
tssDesc->Reserved2 = 0;
tss[Core].IOMapBaseAddressOffset = sizeof(TaskStateSegment);
tss[Core].StackPointer[0] = (uint64_t)CPUStackPointer[Core] + STACK_SIZE;
tss[Core].StackPointer[1] = 0x0;
tss[Core].StackPointer[2] = 0x0;
for (size_t i = 0; i < sizeof(tss[Core].InterruptStackTable) / sizeof(tss[Core].InterruptStackTable[7]); i++)
{
void *NewStack = StackManager.Allocate(STACK_SIZE);
tss[Core].InterruptStackTable[i] = (uint64_t)NewStack + STACK_SIZE;
memset((void *)(tss[Core].InterruptStackTable[i] - STACK_SIZE), 0, STACK_SIZE);
debug("IST-%d: %#lx-%#lx", i, NewStack, (uintptr_t)NewStack + STACK_SIZE);
}
CPU::x64::ltr(GDT_TSS);
debug("Global Descriptor Table initialized");
}
nsa void SetKernelStack(void *Stack)
{
long CPUID = GetCurrentCPU()->ID;
if (Stack != nullptr)
tss[CPUID].StackPointer[0] = (uint64_t)Stack;
else
tss[CPUID].StackPointer[0] = (uint64_t)CPUStackPointer[CPUID] + STACK_SIZE;
/*
FIXME: There's a bug in kernel which if
we won't update "tss[CPUID].StackPointer[0]"
with the current stack pointer, the kernel
will crash.
*/
asmv("mov %%rsp, %0"
: "=r"(tss[CPUID].StackPointer[0]));
}
void *GetKernelStack() { return (void *)tss[GetCurrentCPU()->ID].StackPointer[0]; }
}

View File

@ -0,0 +1,67 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_GDT_H__
#define __FENNIX_KERNEL_GDT_H__
#include <types.h>
#include <cpu/x86/x64/SegmentDescriptors.hpp>
namespace GlobalDescriptorTable
{
struct TaskStateSegment
{
uint32_t Reserved0 __aligned(16);
uint64_t StackPointer[3];
uint64_t Reserved1;
uint64_t InterruptStackTable[7];
uint64_t Reserved2;
uint16_t Reserved3;
uint16_t IOMapBaseAddressOffset;
} __packed;
struct GlobalDescriptorTableEntries
{
uint64_t Null;
CodeSegmentDescriptor Code;
DataSegmentDescriptor Data;
DataSegmentDescriptor UserData;
CodeSegmentDescriptor UserCode;
SystemSegmentDescriptor TaskStateSegment;
} __packed;
struct GlobalDescriptorTableDescriptor
{
uint16_t Limit;
GlobalDescriptorTableEntries *BaseAddress;
} __packed;
extern void *CPUStackPointer[];
extern TaskStateSegment tss[];
void Init(int Core);
void SetKernelStack(void *Stack);
void *GetKernelStack();
}
#define GDT_KERNEL_CODE offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Code)
#define GDT_KERNEL_DATA offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Data)
#define GDT_USER_CODE (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserCode) | 3)
#define GDT_USER_DATA (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserData) | 3)
#define GDT_TSS (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, TaskStateSegment) | 3)
#endif // !__FENNIX_KERNEL_GDT_H__

View File

@ -0,0 +1,916 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "idt.hpp"
#include <memory.hpp>
#include <cpu.hpp>
#include <debug.h>
#include <io.h>
#include "gdt.hpp"
#include "../../../kernel.h"
/* conversion from 'uint64_t' {aka 'long unsigned int'} to 'unsigned char:2' may change value */
#pragma GCC diagnostic ignored "-Wconversion"
extern "C" void MainInterruptHandler(void *Data);
extern "C" void SchedulerInterruptHandler(void *Data);
extern "C" void ExceptionHandler(void *Data);
#define __stub_handler \
__naked __used __no_stack_protector __aligned(16)
namespace InterruptDescriptorTable
{
__aligned(8) static IDTGateDescriptor Entries[0x100];
__aligned(8) IDTRegister IDTr = {
.Limit = sizeof(Entries) - 1,
.BaseAddress = Entries,
};
void SetEntry(uint8_t Index, void (*Base)(),
InterruptStackTableType InterruptStackTable,
GateType Gate, PrivilegeLevelType Ring,
bool Present, uint16_t SegmentSelector)
{
switch (Gate)
{
case CALL_GATE_64BIT:
{
CallGate gate{
.TargetOffsetLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)),
.TargetSelector = SegmentSelector,
.Reserved0 = 0,
.Type = Gate,
.Zero0 = 0,
.DescriptorPrivilegeLevel = Ring,
.Present = Present,
.TargetOffsetMiddle = s_cst(uint16_t, ((uint64_t)Base >> 16)),
.TargetOffsetHigh = s_cst(uint32_t, ((uint64_t)Base >> 32)),
.Reserved1 = 0,
.Zero1 = 0,
.Reserved2 = 0,
};
Entries[Index].Call = gate;
break;
}
case INTERRUPT_GATE_64BIT:
{
InterruptGate gate{
.TargetOffsetLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)),
.TargetSelector = SegmentSelector,
.InterruptStackTable = InterruptStackTable,
.Reserved0 = 0,
.Type = Gate,
.Zero = 0,
.DescriptorPrivilegeLevel = Ring,
.Present = Present,
.TargetOffsetMiddle = s_cst(uint16_t, ((uint64_t)Base >> 16)),
.TargetOffsetHigh = s_cst(uint32_t, ((uint64_t)Base >> 32)),
.Reserved1 = 0,
};
Entries[Index].Interrupt = gate;
break;
}
case TRAP_GATE_64BIT:
{
TrapGate gate{
.TargetOffsetLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)),
.TargetSelector = SegmentSelector,
.InterruptStackTable = InterruptStackTable,
.Reserved0 = 0,
.Type = Gate,
.Zero = 0,
.DescriptorPrivilegeLevel = Ring,
.Present = Present,
.TargetOffsetMiddle = s_cst(uint16_t, ((uint64_t)Base >> 16)),
.TargetOffsetHigh = s_cst(uint32_t, ((uint64_t)Base >> 32)),
.Reserved1 = 0,
};
Entries[Index].Trap = gate;
break;
}
case LDT_64BIT:
case AVAILABLE_64BIT_TSS:
case BUSY_64BIT_TSS:
default:
{
assert(!"Unsupported gate type");
break;
}
}
}
extern "C" __stub_handler void ExceptionHandlerStub()
{
asm("cld\n"
"cli\n"
"pushq %rax\n"
"pushq %rbx\n"
"pushq %rcx\n"
"pushq %rdx\n"
"pushq %rsi\n"
"pushq %rdi\n"
"pushq %rbp\n"
"pushq %r8\n"
"pushq %r9\n"
"pushq %r10\n"
"pushq %r11\n"
"pushq %r12\n"
"pushq %r13\n"
"pushq %r14\n"
"pushq %r15\n"
"movq %ds, %rax\n pushq %rax\n"
"movq %es, %rax\n pushq %rax\n"
"movq %fs, %rax\n pushq %rax\n"
"movq %gs, %rax\n pushq %rax\n"
"movq %dr7, %rax\n pushq %rax\n"
"movq %dr6, %rax\n pushq %rax\n"
"movq %dr3, %rax\n pushq %rax\n"
"movq %dr2, %rax\n pushq %rax\n"
"movq %dr1, %rax\n pushq %rax\n"
"movq %dr0, %rax\n pushq %rax\n"
"movq %cr8, %rax\n pushq %rax\n"
"movq %cr4, %rax\n pushq %rax\n"
"movq %cr3, %rax\n pushq %rax\n"
"movq %cr2, %rax\n pushq %rax\n"
"movq %cr0, %rax\n pushq %rax\n"
"movq %rsp, %rdi\n"
"call ExceptionHandler\n"
"popq %rax\n movq %rax, %cr0\n"
"popq %rax\n movq %rax, %cr2\n"
"popq %rax\n movq %rax, %cr3\n"
"popq %rax\n movq %rax, %cr4\n"
"popq %rax\n movq %rax, %cr8\n"
"popq %rax\n movq %rax, %dr0\n"
"popq %rax\n movq %rax, %dr1\n"
"popq %rax\n movq %rax, %dr2\n"
"popq %rax\n movq %rax, %dr3\n"
"popq %rax\n movq %rax, %dr6\n"
"popq %rax\n movq %rax, %dr7\n"
"popq %rax\n movq %rax, %gs\n"
"popq %rax\n movq %rax, %fs\n"
"popq %rax\n movq %rax, %es\n"
"popq %rax\n movq %rax, %ds\n"
"popq %r15\n"
"popq %r14\n"
"popq %r13\n"
"popq %r12\n"
"popq %r11\n"
"popq %r10\n"
"popq %r9\n"
"popq %r8\n"
"popq %rbp\n"
"popq %rdi\n"
"popq %rsi\n"
"popq %rdx\n"
"popq %rcx\n"
"popq %rbx\n"
"popq %rax\n"
"addq $16, %rsp\n"
"iretq"); // pop CS RIP RFLAGS SS RSP
}
extern "C" __stub_handler void InterruptHandlerStub()
{
asm("cld\n"
"cli\n"
"pushq %rax\n"
"pushq %rbx\n"
"pushq %rcx\n"
"pushq %rdx\n"
"pushq %rsi\n"
"pushq %rdi\n"
"pushq %rbp\n"
"pushq %r8\n"
"pushq %r9\n"
"pushq %r10\n"
"pushq %r11\n"
"pushq %r12\n"
"pushq %r13\n"
"pushq %r14\n"
"pushq %r15\n"
"movq %rsp, %rdi\n"
"call MainInterruptHandler\n"
"popq %r15\n"
"popq %r14\n"
"popq %r13\n"
"popq %r12\n"
"popq %r11\n"
"popq %r10\n"
"popq %r9\n"
"popq %r8\n"
"popq %rbp\n"
"popq %rdi\n"
"popq %rsi\n"
"popq %rdx\n"
"popq %rcx\n"
"popq %rbx\n"
"popq %rax\n"
"addq $16, %rsp\n"
"sti\n"
"iretq"); // pop CS RIP RFLAGS SS RSP
}
extern "C" __stub_handler void SchedulerHandlerStub()
{
asm("cld\n"
"cli\n"
"pushq %rax\n"
"pushq %rbx\n"
"pushq %rcx\n"
"pushq %rdx\n"
"pushq %rsi\n"
"pushq %rdi\n"
"pushq %rbp\n"
"pushq %r8\n"
"pushq %r9\n"
"pushq %r10\n"
"pushq %r11\n"
"pushq %r12\n"
"pushq %r13\n"
"pushq %r14\n"
"pushq %r15\n"
/* TODO: Add advanced check so we won't update the cr3 when not needed */
"movq %cr3, %rax\n pushq %rax\n" /* Push opt */
"pushq %rax\n" /* Push ppt */
"movq %rsp, %rdi\n"
"call SchedulerInterruptHandler\n"
"popq %rax\n movq %rax, %cr3\n" /* Restore to ppt */
"popq %rax\n" /* Pop opt */
"popq %r15\n"
"popq %r14\n"
"popq %r13\n"
"popq %r12\n"
"popq %r11\n"
"popq %r10\n"
"popq %r9\n"
"popq %r8\n"
"popq %rbp\n"
"popq %rdi\n"
"popq %rsi\n"
"popq %rdx\n"
"popq %rcx\n"
"popq %rbx\n"
"popq %rax\n"
"addq $16, %rsp\n"
"sti\n"
"iretq"); // pop CS RIP RFLAGS SS RSP
}
#pragma region Interrupt Macros
#define EXCEPTION_HANDLER(num) \
__stub_handler static void InterruptHandler_##num() \
{ \
asm("pushq $0\n" \
"pushq $" #num "\n" \
"jmp ExceptionHandlerStub"); \
}
#define EXCEPTION_ERROR_HANDLER(num) \
__stub_handler static void InterruptHandler_##num() \
{ \
asm("pushq $" #num "\n" \
"jmp ExceptionHandlerStub"); \
}
#define INTERRUPT_HANDLER(num) \
__stub_handler void InterruptHandler_##num() \
{ \
asm("pushq $0\n" \
"pushq $" #num "\n" \
"jmp InterruptHandlerStub\n"); \
}
#define SCHEDULER_HANDLER(num) \
__stub_handler void InterruptHandler_##num() \
{ \
asm("pushq $0\n" \
"pushq $" #num "\n" \
"jmp SchedulerHandlerStub\n"); \
}
/* ISR */
EXCEPTION_HANDLER(0x0);
EXCEPTION_HANDLER(0x1);
EXCEPTION_HANDLER(0x2);
EXCEPTION_HANDLER(0x3);
EXCEPTION_HANDLER(0x4);
EXCEPTION_HANDLER(0x5);
EXCEPTION_HANDLER(0x6);
EXCEPTION_HANDLER(0x7);
EXCEPTION_ERROR_HANDLER(0x8);
EXCEPTION_HANDLER(0x9);
EXCEPTION_ERROR_HANDLER(0xa);
EXCEPTION_ERROR_HANDLER(0xb);
EXCEPTION_ERROR_HANDLER(0xc);
EXCEPTION_ERROR_HANDLER(0xd);
EXCEPTION_ERROR_HANDLER(0xe);
EXCEPTION_HANDLER(0xf);
EXCEPTION_ERROR_HANDLER(0x10);
EXCEPTION_HANDLER(0x11);
EXCEPTION_HANDLER(0x12);
EXCEPTION_HANDLER(0x13);
EXCEPTION_HANDLER(0x14);
EXCEPTION_HANDLER(0x15);
EXCEPTION_HANDLER(0x16);
EXCEPTION_HANDLER(0x17);
EXCEPTION_HANDLER(0x18);
EXCEPTION_HANDLER(0x19);
EXCEPTION_HANDLER(0x1a);
EXCEPTION_HANDLER(0x1b);
EXCEPTION_HANDLER(0x1c);
EXCEPTION_HANDLER(0x1d);
EXCEPTION_HANDLER(0x1e);
EXCEPTION_HANDLER(0x1f);
/* IRQ */
INTERRUPT_HANDLER(0x20)
INTERRUPT_HANDLER(0x21)
INTERRUPT_HANDLER(0x22)
INTERRUPT_HANDLER(0x23)
INTERRUPT_HANDLER(0x24)
INTERRUPT_HANDLER(0x25)
INTERRUPT_HANDLER(0x26)
INTERRUPT_HANDLER(0x27)
INTERRUPT_HANDLER(0x28)
INTERRUPT_HANDLER(0x29)
INTERRUPT_HANDLER(0x2a)
INTERRUPT_HANDLER(0x2b)
INTERRUPT_HANDLER(0x2c)
INTERRUPT_HANDLER(0x2d)
INTERRUPT_HANDLER(0x2e)
INTERRUPT_HANDLER(0x2f)
/* Reserved by OS */
SCHEDULER_HANDLER(0x30)
INTERRUPT_HANDLER(0x31)
INTERRUPT_HANDLER(0x32)
INTERRUPT_HANDLER(0x33)
INTERRUPT_HANDLER(0x34)
INTERRUPT_HANDLER(0x35)
INTERRUPT_HANDLER(0x36)
INTERRUPT_HANDLER(0x37)
INTERRUPT_HANDLER(0x38)
INTERRUPT_HANDLER(0x39)
INTERRUPT_HANDLER(0x3a)
INTERRUPT_HANDLER(0x3b)
INTERRUPT_HANDLER(0x3c)
INTERRUPT_HANDLER(0x3d)
/* Free */
INTERRUPT_HANDLER(0x3e)
INTERRUPT_HANDLER(0x3f)
INTERRUPT_HANDLER(0x40)
INTERRUPT_HANDLER(0x41)
INTERRUPT_HANDLER(0x42)
INTERRUPT_HANDLER(0x43)
INTERRUPT_HANDLER(0x44)
INTERRUPT_HANDLER(0x45)
INTERRUPT_HANDLER(0x46)
INTERRUPT_HANDLER(0x47)
INTERRUPT_HANDLER(0x48)
INTERRUPT_HANDLER(0x49)
INTERRUPT_HANDLER(0x4a)
INTERRUPT_HANDLER(0x4b)
INTERRUPT_HANDLER(0x4c)
INTERRUPT_HANDLER(0x4d)
INTERRUPT_HANDLER(0x4e)
INTERRUPT_HANDLER(0x4f)
INTERRUPT_HANDLER(0x50)
INTERRUPT_HANDLER(0x51)
INTERRUPT_HANDLER(0x52)
INTERRUPT_HANDLER(0x53)
INTERRUPT_HANDLER(0x54)
INTERRUPT_HANDLER(0x55)
INTERRUPT_HANDLER(0x56)
INTERRUPT_HANDLER(0x57)
INTERRUPT_HANDLER(0x58)
INTERRUPT_HANDLER(0x59)
INTERRUPT_HANDLER(0x5a)
INTERRUPT_HANDLER(0x5b)
INTERRUPT_HANDLER(0x5c)
INTERRUPT_HANDLER(0x5d)
INTERRUPT_HANDLER(0x5e)
INTERRUPT_HANDLER(0x5f)
INTERRUPT_HANDLER(0x60)
INTERRUPT_HANDLER(0x61)
INTERRUPT_HANDLER(0x62)
INTERRUPT_HANDLER(0x63)
INTERRUPT_HANDLER(0x64)
INTERRUPT_HANDLER(0x65)
INTERRUPT_HANDLER(0x66)
INTERRUPT_HANDLER(0x67)
INTERRUPT_HANDLER(0x68)
INTERRUPT_HANDLER(0x69)
INTERRUPT_HANDLER(0x6a)
INTERRUPT_HANDLER(0x6b)
INTERRUPT_HANDLER(0x6c)
INTERRUPT_HANDLER(0x6d)
INTERRUPT_HANDLER(0x6e)
INTERRUPT_HANDLER(0x6f)
INTERRUPT_HANDLER(0x70)
INTERRUPT_HANDLER(0x71)
INTERRUPT_HANDLER(0x72)
INTERRUPT_HANDLER(0x73)
INTERRUPT_HANDLER(0x74)
INTERRUPT_HANDLER(0x75)
INTERRUPT_HANDLER(0x76)
INTERRUPT_HANDLER(0x77)
INTERRUPT_HANDLER(0x78)
INTERRUPT_HANDLER(0x79)
INTERRUPT_HANDLER(0x7a)
INTERRUPT_HANDLER(0x7b)
INTERRUPT_HANDLER(0x7c)
INTERRUPT_HANDLER(0x7d)
INTERRUPT_HANDLER(0x7e)
INTERRUPT_HANDLER(0x7f)
INTERRUPT_HANDLER(0x80)
INTERRUPT_HANDLER(0x81)
INTERRUPT_HANDLER(0x82)
INTERRUPT_HANDLER(0x83)
INTERRUPT_HANDLER(0x84)
INTERRUPT_HANDLER(0x85)
INTERRUPT_HANDLER(0x86)
INTERRUPT_HANDLER(0x87)
INTERRUPT_HANDLER(0x88)
INTERRUPT_HANDLER(0x89)
INTERRUPT_HANDLER(0x8a)
INTERRUPT_HANDLER(0x8b)
INTERRUPT_HANDLER(0x8c)
INTERRUPT_HANDLER(0x8d)
INTERRUPT_HANDLER(0x8e)
INTERRUPT_HANDLER(0x8f)
INTERRUPT_HANDLER(0x90)
INTERRUPT_HANDLER(0x91)
INTERRUPT_HANDLER(0x92)
INTERRUPT_HANDLER(0x93)
INTERRUPT_HANDLER(0x94)
INTERRUPT_HANDLER(0x95)
INTERRUPT_HANDLER(0x96)
INTERRUPT_HANDLER(0x97)
INTERRUPT_HANDLER(0x98)
INTERRUPT_HANDLER(0x99)
INTERRUPT_HANDLER(0x9a)
INTERRUPT_HANDLER(0x9b)
INTERRUPT_HANDLER(0x9c)
INTERRUPT_HANDLER(0x9d)
INTERRUPT_HANDLER(0x9e)
INTERRUPT_HANDLER(0x9f)
INTERRUPT_HANDLER(0xa0)
INTERRUPT_HANDLER(0xa1)
INTERRUPT_HANDLER(0xa2)
INTERRUPT_HANDLER(0xa3)
INTERRUPT_HANDLER(0xa4)
INTERRUPT_HANDLER(0xa5)
INTERRUPT_HANDLER(0xa6)
INTERRUPT_HANDLER(0xa7)
INTERRUPT_HANDLER(0xa8)
INTERRUPT_HANDLER(0xa9)
INTERRUPT_HANDLER(0xaa)
INTERRUPT_HANDLER(0xab)
INTERRUPT_HANDLER(0xac)
INTERRUPT_HANDLER(0xad)
INTERRUPT_HANDLER(0xae)
INTERRUPT_HANDLER(0xaf)
INTERRUPT_HANDLER(0xb0)
INTERRUPT_HANDLER(0xb1)
INTERRUPT_HANDLER(0xb2)
INTERRUPT_HANDLER(0xb3)
INTERRUPT_HANDLER(0xb4)
INTERRUPT_HANDLER(0xb5)
INTERRUPT_HANDLER(0xb6)
INTERRUPT_HANDLER(0xb7)
INTERRUPT_HANDLER(0xb8)
INTERRUPT_HANDLER(0xb9)
INTERRUPT_HANDLER(0xba)
INTERRUPT_HANDLER(0xbb)
INTERRUPT_HANDLER(0xbc)
INTERRUPT_HANDLER(0xbd)
INTERRUPT_HANDLER(0xbe)
INTERRUPT_HANDLER(0xbf)
INTERRUPT_HANDLER(0xc0)
INTERRUPT_HANDLER(0xc1)
INTERRUPT_HANDLER(0xc2)
INTERRUPT_HANDLER(0xc3)
INTERRUPT_HANDLER(0xc4)
INTERRUPT_HANDLER(0xc5)
INTERRUPT_HANDLER(0xc6)
INTERRUPT_HANDLER(0xc7)
INTERRUPT_HANDLER(0xc8)
INTERRUPT_HANDLER(0xc9)
INTERRUPT_HANDLER(0xca)
INTERRUPT_HANDLER(0xcb)
INTERRUPT_HANDLER(0xcc)
INTERRUPT_HANDLER(0xcd)
INTERRUPT_HANDLER(0xce)
INTERRUPT_HANDLER(0xcf)
INTERRUPT_HANDLER(0xd0)
INTERRUPT_HANDLER(0xd1)
INTERRUPT_HANDLER(0xd2)
INTERRUPT_HANDLER(0xd3)
INTERRUPT_HANDLER(0xd4)
INTERRUPT_HANDLER(0xd5)
INTERRUPT_HANDLER(0xd6)
INTERRUPT_HANDLER(0xd7)
INTERRUPT_HANDLER(0xd8)
INTERRUPT_HANDLER(0xd9)
INTERRUPT_HANDLER(0xda)
INTERRUPT_HANDLER(0xdb)
INTERRUPT_HANDLER(0xdc)
INTERRUPT_HANDLER(0xdd)
INTERRUPT_HANDLER(0xde)
INTERRUPT_HANDLER(0xdf)
INTERRUPT_HANDLER(0xe0)
INTERRUPT_HANDLER(0xe1)
INTERRUPT_HANDLER(0xe2)
INTERRUPT_HANDLER(0xe3)
INTERRUPT_HANDLER(0xe4)
INTERRUPT_HANDLER(0xe5)
INTERRUPT_HANDLER(0xe6)
INTERRUPT_HANDLER(0xe7)
INTERRUPT_HANDLER(0xe8)
INTERRUPT_HANDLER(0xe9)
INTERRUPT_HANDLER(0xea)
INTERRUPT_HANDLER(0xeb)
INTERRUPT_HANDLER(0xec)
INTERRUPT_HANDLER(0xed)
INTERRUPT_HANDLER(0xee)
INTERRUPT_HANDLER(0xef)
INTERRUPT_HANDLER(0xf0)
INTERRUPT_HANDLER(0xf1)
INTERRUPT_HANDLER(0xf2)
INTERRUPT_HANDLER(0xf3)
INTERRUPT_HANDLER(0xf4)
INTERRUPT_HANDLER(0xf5)
INTERRUPT_HANDLER(0xf6)
INTERRUPT_HANDLER(0xf7)
INTERRUPT_HANDLER(0xf8)
INTERRUPT_HANDLER(0xf9)
INTERRUPT_HANDLER(0xfa)
INTERRUPT_HANDLER(0xfb)
INTERRUPT_HANDLER(0xfc)
INTERRUPT_HANDLER(0xfd)
INTERRUPT_HANDLER(0xfe)
INTERRUPT_HANDLER(0xff)
#pragma endregion Interrupt Macros
void Init(int Core)
{
if (Core == 0) /* Disable PIC using BSP */
{
// PIC
outb(0x20, 0x10 | 0x1);
outb(0x80, 0);
outb(0xA0, 0x10 | 0x10);
outb(0x80, 0);
outb(0x21, 0x20);
outb(0x80, 0);
outb(0xA1, 0x28);
outb(0x80, 0);
outb(0x21, 0x04);
outb(0x80, 0);
outb(0xA1, 0x02);
outb(0x80, 0);
outb(0x21, 1);
outb(0x80, 0);
outb(0xA1, 1);
outb(0x80, 0);
// Masking and disabling PIC
outb(0x21, 0xff);
outb(0x80, 0);
outb(0xA1, 0xff);
}
bool EnableISRs = true;
#ifdef DEBUG
EnableISRs = !DebuggerIsAttached;
if (!EnableISRs)
KPrint("\x1b[34mThe debugger is attached, disabling all ISRs.");
#endif
/* ISR */
SetEntry(0x0, InterruptHandler_0x0, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1, InterruptHandler_0x1, IST1, INTERRUPT_GATE_64BIT, RING3, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x2, InterruptHandler_0x2, IST2, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x3, InterruptHandler_0x3, IST1, INTERRUPT_GATE_64BIT, RING3, (!DebuggerIsAttached), GDT_KERNEL_CODE); /* Do not handle breakpoints if we are debugging the kernel. */
SetEntry(0x4, InterruptHandler_0x4, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x5, InterruptHandler_0x5, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x6, InterruptHandler_0x6, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x7, InterruptHandler_0x7, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x8, InterruptHandler_0x8, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x9, InterruptHandler_0x9, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xa, InterruptHandler_0xa, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xb, InterruptHandler_0xb, IST1, INTERRUPT_GATE_64BIT, RING0, (!DebuggerIsAttached), GDT_KERNEL_CODE);
SetEntry(0xc, InterruptHandler_0xc, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xd, InterruptHandler_0xd, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xe, InterruptHandler_0xe, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs /* FIXME: CoW? */, GDT_KERNEL_CODE);
SetEntry(0xf, InterruptHandler_0xf, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x10, InterruptHandler_0x10, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x11, InterruptHandler_0x11, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x12, InterruptHandler_0x12, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x13, InterruptHandler_0x13, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x14, InterruptHandler_0x14, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x15, InterruptHandler_0x15, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x16, InterruptHandler_0x16, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x17, InterruptHandler_0x17, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x18, InterruptHandler_0x18, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x19, InterruptHandler_0x19, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1a, InterruptHandler_0x1a, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1b, InterruptHandler_0x1b, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1c, InterruptHandler_0x1c, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1d, InterruptHandler_0x1d, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1e, InterruptHandler_0x1e, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1f, InterruptHandler_0x1f, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
/* IRQ */
SetEntry(0x20, InterruptHandler_0x20, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x21, InterruptHandler_0x21, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x22, InterruptHandler_0x22, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x23, InterruptHandler_0x23, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x24, InterruptHandler_0x24, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x25, InterruptHandler_0x25, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x26, InterruptHandler_0x26, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x27, InterruptHandler_0x27, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x28, InterruptHandler_0x28, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x29, InterruptHandler_0x29, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2a, InterruptHandler_0x2a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2b, InterruptHandler_0x2b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2c, InterruptHandler_0x2c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2d, InterruptHandler_0x2d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2e, InterruptHandler_0x2e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2f, InterruptHandler_0x2f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
/* Reserved by OS */
SetEntry(0x30, InterruptHandler_0x30, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x31, InterruptHandler_0x31, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x32, InterruptHandler_0x32, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x33, InterruptHandler_0x33, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x34, InterruptHandler_0x34, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x35, InterruptHandler_0x35, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x36, InterruptHandler_0x36, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x37, InterruptHandler_0x37, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x38, InterruptHandler_0x38, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x39, InterruptHandler_0x39, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3a, InterruptHandler_0x3a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3b, InterruptHandler_0x3b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3c, InterruptHandler_0x3c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3d, InterruptHandler_0x3d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
/* Free */
SetEntry(0x3e, InterruptHandler_0x3e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3f, InterruptHandler_0x3f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x40, InterruptHandler_0x40, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x41, InterruptHandler_0x41, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x42, InterruptHandler_0x42, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x43, InterruptHandler_0x43, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x44, InterruptHandler_0x44, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x45, InterruptHandler_0x45, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x46, InterruptHandler_0x46, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x47, InterruptHandler_0x47, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x48, InterruptHandler_0x48, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x49, InterruptHandler_0x49, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4a, InterruptHandler_0x4a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4b, InterruptHandler_0x4b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4c, InterruptHandler_0x4c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4d, InterruptHandler_0x4d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4e, InterruptHandler_0x4e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4f, InterruptHandler_0x4f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x50, InterruptHandler_0x50, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x51, InterruptHandler_0x51, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x52, InterruptHandler_0x52, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x53, InterruptHandler_0x53, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x54, InterruptHandler_0x54, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x55, InterruptHandler_0x55, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x56, InterruptHandler_0x56, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x57, InterruptHandler_0x57, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x58, InterruptHandler_0x58, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x59, InterruptHandler_0x59, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5a, InterruptHandler_0x5a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5b, InterruptHandler_0x5b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5c, InterruptHandler_0x5c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5d, InterruptHandler_0x5d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5e, InterruptHandler_0x5e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5f, InterruptHandler_0x5f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x60, InterruptHandler_0x60, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x61, InterruptHandler_0x61, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x62, InterruptHandler_0x62, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x63, InterruptHandler_0x63, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x64, InterruptHandler_0x64, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x65, InterruptHandler_0x65, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x66, InterruptHandler_0x66, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x67, InterruptHandler_0x67, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x68, InterruptHandler_0x68, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x69, InterruptHandler_0x69, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6a, InterruptHandler_0x6a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6b, InterruptHandler_0x6b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6c, InterruptHandler_0x6c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6d, InterruptHandler_0x6d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6e, InterruptHandler_0x6e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6f, InterruptHandler_0x6f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x70, InterruptHandler_0x70, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x71, InterruptHandler_0x71, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x72, InterruptHandler_0x72, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x73, InterruptHandler_0x73, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x74, InterruptHandler_0x74, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x75, InterruptHandler_0x75, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x76, InterruptHandler_0x76, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x77, InterruptHandler_0x77, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x78, InterruptHandler_0x78, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x79, InterruptHandler_0x79, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7a, InterruptHandler_0x7a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7b, InterruptHandler_0x7b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7c, InterruptHandler_0x7c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7d, InterruptHandler_0x7d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7e, InterruptHandler_0x7e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7f, InterruptHandler_0x7f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x80, InterruptHandler_0x80, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x81, InterruptHandler_0x81, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x82, InterruptHandler_0x82, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x83, InterruptHandler_0x83, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x84, InterruptHandler_0x84, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x85, InterruptHandler_0x85, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x86, InterruptHandler_0x86, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x87, InterruptHandler_0x87, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x88, InterruptHandler_0x88, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x89, InterruptHandler_0x89, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8a, InterruptHandler_0x8a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8b, InterruptHandler_0x8b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8c, InterruptHandler_0x8c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8d, InterruptHandler_0x8d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8e, InterruptHandler_0x8e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8f, InterruptHandler_0x8f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x90, InterruptHandler_0x90, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x91, InterruptHandler_0x91, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x92, InterruptHandler_0x92, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x93, InterruptHandler_0x93, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x94, InterruptHandler_0x94, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x95, InterruptHandler_0x95, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x96, InterruptHandler_0x96, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x97, InterruptHandler_0x97, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x98, InterruptHandler_0x98, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x99, InterruptHandler_0x99, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9a, InterruptHandler_0x9a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9b, InterruptHandler_0x9b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9c, InterruptHandler_0x9c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9d, InterruptHandler_0x9d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9e, InterruptHandler_0x9e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9f, InterruptHandler_0x9f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa0, InterruptHandler_0xa0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa1, InterruptHandler_0xa1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa2, InterruptHandler_0xa2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa3, InterruptHandler_0xa3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa4, InterruptHandler_0xa4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa5, InterruptHandler_0xa5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa6, InterruptHandler_0xa6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa7, InterruptHandler_0xa7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa8, InterruptHandler_0xa8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa9, InterruptHandler_0xa9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xaa, InterruptHandler_0xaa, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xab, InterruptHandler_0xab, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xac, InterruptHandler_0xac, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xad, InterruptHandler_0xad, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xae, InterruptHandler_0xae, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xaf, InterruptHandler_0xaf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb0, InterruptHandler_0xb0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb1, InterruptHandler_0xb1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb2, InterruptHandler_0xb2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb3, InterruptHandler_0xb3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb4, InterruptHandler_0xb4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb5, InterruptHandler_0xb5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb6, InterruptHandler_0xb6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb7, InterruptHandler_0xb7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb8, InterruptHandler_0xb8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb9, InterruptHandler_0xb9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xba, InterruptHandler_0xba, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbb, InterruptHandler_0xbb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbc, InterruptHandler_0xbc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbd, InterruptHandler_0xbd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbe, InterruptHandler_0xbe, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbf, InterruptHandler_0xbf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc0, InterruptHandler_0xc0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc1, InterruptHandler_0xc1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc2, InterruptHandler_0xc2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc3, InterruptHandler_0xc3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc4, InterruptHandler_0xc4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc5, InterruptHandler_0xc5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc6, InterruptHandler_0xc6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc7, InterruptHandler_0xc7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc8, InterruptHandler_0xc8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc9, InterruptHandler_0xc9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xca, InterruptHandler_0xca, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcb, InterruptHandler_0xcb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcc, InterruptHandler_0xcc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcd, InterruptHandler_0xcd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xce, InterruptHandler_0xce, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcf, InterruptHandler_0xcf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd0, InterruptHandler_0xd0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd1, InterruptHandler_0xd1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd2, InterruptHandler_0xd2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd3, InterruptHandler_0xd3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd4, InterruptHandler_0xd4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd5, InterruptHandler_0xd5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd6, InterruptHandler_0xd6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd7, InterruptHandler_0xd7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd8, InterruptHandler_0xd8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd9, InterruptHandler_0xd9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xda, InterruptHandler_0xda, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdb, InterruptHandler_0xdb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdc, InterruptHandler_0xdc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdd, InterruptHandler_0xdd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xde, InterruptHandler_0xde, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdf, InterruptHandler_0xdf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe0, InterruptHandler_0xe0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe1, InterruptHandler_0xe1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe2, InterruptHandler_0xe2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe3, InterruptHandler_0xe3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe4, InterruptHandler_0xe4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe5, InterruptHandler_0xe5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe6, InterruptHandler_0xe6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe7, InterruptHandler_0xe7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe8, InterruptHandler_0xe8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe9, InterruptHandler_0xe9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xea, InterruptHandler_0xea, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xeb, InterruptHandler_0xeb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xec, InterruptHandler_0xec, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xed, InterruptHandler_0xed, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xee, InterruptHandler_0xee, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xef, InterruptHandler_0xef, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf0, InterruptHandler_0xf0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf1, InterruptHandler_0xf1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf2, InterruptHandler_0xf2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf3, InterruptHandler_0xf3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf4, InterruptHandler_0xf4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf5, InterruptHandler_0xf5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf6, InterruptHandler_0xf6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf7, InterruptHandler_0xf7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf8, InterruptHandler_0xf8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf9, InterruptHandler_0xf9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfa, InterruptHandler_0xfa, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfb, InterruptHandler_0xfb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfc, InterruptHandler_0xfc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfd, InterruptHandler_0xfd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfe, InterruptHandler_0xfe, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xff, InterruptHandler_0xff, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
CPU::x64::lidt(&IDTr);
}
}

View File

@ -0,0 +1,51 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_IDT_H__
#define __FENNIX_KERNEL_IDT_H__
#include <types.h>
#include <cpu/x86/x64/SegmentDescriptors.hpp>
namespace InterruptDescriptorTable
{
union IDTGateDescriptor
{
InterruptGate Interrupt;
TrapGate Trap;
CallGate Call;
};
struct IDTRegister
{
uint16_t Limit;
IDTGateDescriptor *BaseAddress;
} __packed;
void SetEntry(uint8_t Index,
void (*Base)(),
InterruptStackTableType InterruptStackTable,
GateType Gate,
PrivilegeLevelType Ring,
bool Present,
uint16_t SegmentSelector);
void Init(int Core);
}
#endif // !__FENNIX_KERNEL_IDT_H__

View File

@ -0,0 +1,198 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <smp.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <ints.hpp>
#include <assert.h>
#include <cpu.hpp>
#include <atomic>
#include "../../../kernel.h"
#include "apic.hpp"
extern "C" uint64_t _trampoline_start, _trampoline_end;
/* https://wiki.osdev.org/Memory_Map_(x86) */
enum SMPTrampolineAddress
{
PAGE_TABLE = 0x500,
START_ADDR = 0x520,
STACK = 0x570,
GDT = 0x580,
IDT = 0x590,
CORE = 0x600,
TRAMPOLINE_START = 0x2000
};
std::atomic_bool CPUEnabled = false;
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static __aligned(PAGE_SIZE) CPUData CPUs[MAX_CPU] = {0};
nsa CPUData *GetCPU(long id) { return &CPUs[id]; }
nsa CPUData *GetCurrentCPU()
{
if (unlikely(!Interrupts::apic[0]))
return &CPUs[0]; /* No APIC means we are on the BSP. */
APIC::APIC *apic = (APIC::APIC *)Interrupts::apic[0];
int CoreID = 0;
if (CPUEnabled.load(std::memory_order_acquire) == true)
{
Memory::SwapPT swap =
Memory::SwapPT(KernelPageTable, thisPageTable);
if (apic->x2APIC)
CoreID = int(CPU::x64::rdmsr(CPU::x64::MSR_X2APIC_APICID));
else
CoreID = apic->Read(APIC::APIC_ID) >> 24;
}
if (unlikely((&CPUs[CoreID])->IsActive != true))
{
error("CPU %d is not active!", CoreID);
assert((&CPUs[0])->IsActive == true); /* We can't continue without the BSP. */
return &CPUs[0];
}
assert((&CPUs[CoreID])->Checksum == CPU_DATA_CHECKSUM); /* This should never happen. */
return &CPUs[CoreID];
}
extern "C" void StartCPU()
{
CPU::Interrupts(CPU::Disable);
int CoreID = (int)*reinterpret_cast<int *>(CORE);
CPU::InitializeFeatures(CoreID);
// Initialize GDT and IDT
Interrupts::Initialize(CoreID);
Interrupts::Enable(CoreID);
Interrupts::InitializeTimer(CoreID);
asmv("mov %0, %%rsp" ::"r"((&CPUs[CoreID])->Stack));
CPU::Interrupts(CPU::Enable);
KPrint("CPU %d is online", CoreID);
CPUEnabled.store(true, std::memory_order_release);
CPU::Halt(true);
}
namespace SMP
{
int CPUCores = 0;
void Initialize(void *_madt)
{
if (!_madt)
{
error("MADT is NULL");
return;
}
ACPI::MADT *madt = (ACPI::MADT *)_madt;
if (madt->lapic.size() < 1)
{
error("No CPUs found!");
return;
}
int Cores = madt->CPUCores + 1;
if (Config.Cores > madt->CPUCores + 1)
KPrint("More cores requested than available. Using %d cores",
madt->CPUCores + 1);
else if (Config.Cores != 0)
Cores = Config.Cores;
CPUCores = Cores;
uint64_t TrampolineLength = (uintptr_t)&_trampoline_end -
(uintptr_t)&_trampoline_start;
Memory::Virtual().Map(0x0, 0x0, Memory::PTFlag::RW);
/* We reserved the TRAMPOLINE_START address inside Physical class. */
Memory::Virtual().Map((void *)TRAMPOLINE_START,
(void *)TRAMPOLINE_START,
TrampolineLength, Memory::PTFlag::RW);
memcpy((void *)TRAMPOLINE_START, &_trampoline_start, TrampolineLength);
debug("Trampoline address: %#lx-%#lx",
TRAMPOLINE_START,
TRAMPOLINE_START + TrampolineLength);
void *CPUTmpStack = KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1));
asmv("sgdt [0x580]");
asmv("sidt [0x590]");
VPOKE(uintptr_t, STACK) = (uintptr_t)CPUTmpStack + STACK_SIZE;
VPOKE(uintptr_t, PAGE_TABLE) = (uintptr_t)KernelPageTable;
VPOKE(uintptr_t, START_ADDR) = (uintptr_t)&StartCPU;
for (int i = 0; i < Cores; i++)
{
ACPI::MADT::LocalAPIC *lapic = madt->lapic[i];
APIC::APIC *apic = (APIC::APIC *)Interrupts::apic[0];
debug("Initializing CPU %d", lapic->APICId);
uint8_t APIC_ID = 0;
if (apic->x2APIC)
APIC_ID = uint8_t(CPU::x64::rdmsr(CPU::x64::MSR_X2APIC_APICID));
else
APIC_ID = uint8_t(apic->Read(APIC::APIC_ID) >> 24);
if (APIC_ID != lapic->APICId)
{
VPOKE(int, CORE) = i;
if (!apic->x2APIC)
{
APIC::InterruptCommandRegister icr{};
icr.MT = APIC::INIT;
icr.DES = lapic->APICId;
apic->ICR(icr);
}
apic->SendInitIPI(lapic->APICId);
TimeManager->Sleep(20, Time::Units::Milliseconds);
apic->SendStartupIPI(lapic->APICId, TRAMPOLINE_START);
debug("Waiting for CPU %d to load...", lapic->APICId);
uint64_t Timeout = TimeManager->CalculateTarget(2, Time::Units::Seconds);
while (CPUEnabled.load(std::memory_order_acquire) == false)
{
if (TimeManager->GetCounter() > Timeout)
{
error("CPU %d failed to load!", lapic->APICId);
KPrint("\x1b[1;37;41mCPU %d failed to load!",
lapic->APICId);
break;
}
CPU::Pause();
}
trace("CPU %d loaded.", lapic->APICId);
CPUEnabled.store(false, std::memory_order_release);
}
else
KPrint("CPU %d is the BSP", lapic->APICId);
}
KernelAllocator.FreePages(CPUTmpStack, TO_PAGES(STACK_SIZE + 1));
/* We are going to unmap the page after we are done with it. */
Memory::Virtual().Unmap(0x0);
CPUEnabled.store(true, std::memory_order_release);
}
}

View File

@ -0,0 +1,179 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
/* This has to be the same as enum SMPTrampolineAddress. */
TRAMPOLINE_PAGE_TABLE = 0x500
TRAMPOLINE_START_ADDR = 0x520
TRAMPOLINE_STACK = 0x570
TRAMPOLINE_GDT = 0x580
TRAMPOLINE_IDT = 0x590
TRAMPOLINE_CORE = 0x600
TRAMPOLINE_START = 0x2000
.section .rodata
/* ========== 16-bit ========== */
.code16
.global _trampoline_start
_trampoline_start:
cli
cld
call Trampoline16
Trampoline16:
mov $0x0, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
/* Load Protected Mode GDT */
lgdt [ProtectedMode_gdtr - _trampoline_start + TRAMPOLINE_START]
/* Enable Protected Mode */
mov %cr0, %eax
or $0x1, %al
mov %eax, %cr0
/* Jump to Protected Mode */
ljmp $0x8, $(Trampoline32 - _trampoline_start + TRAMPOLINE_START)
/* ========== 32-bit ========== */
.code32
Trampoline32:
mov $0x10, %bx
mov %bx, %ds
mov %bx, %es
mov %bx, %ss
/* Set a page table */
mov [TRAMPOLINE_PAGE_TABLE], %eax
mov %eax, %cr3
/* Enable PAE and PSE */
mov %cr4, %eax
or $0x20, %eax /* PAE */
or $0x80, %eax /* PSE */
mov %eax, %cr4
/* Enable Long Mode */
mov $0xC0000080, %ecx
rdmsr
or $0x100, %eax /* LME */
wrmsr
/* Enable paging */
mov %cr0, %eax
or $0x80000000, %eax /* PG */
mov %eax, %cr0
/* Load Long Mode GDT */
lgdt [LongMode_gdtr - _trampoline_start + TRAMPOLINE_START]
/* Jump to Long Mode */
ljmp $0x8, $(Trampoline64 - _trampoline_start + TRAMPOLINE_START)
/* ========== 64-bit ========== */
.code64
Trampoline64:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $0x0, %ax
mov %ax, %fs
mov %ax, %gs
/* Set custom GDT & IDT */
lgdt [TRAMPOLINE_GDT]
lidt [TRAMPOLINE_IDT]
/* Set up stack */
mov [TRAMPOLINE_STACK], %rsp
mov $0x0, %rbp
/* Reset RFLAGS */
push $0x0
popf
/* Jump to TrampolinePrepareExit */
call TrampolineExit
.extern StartCPU
TrampolineExit:
mov $StartCPU, %rax
call *%rax
.align 16
ProtectedMode_gdtr:
.word ProtectedModeGDTEnd - ProtectedModeGDTStart - 1
.long ProtectedModeGDTStart - _trampoline_start + TRAMPOLINE_START
.align 16
ProtectedModeGDTStart:
/* NULL segment */
.quad 0x0
/* Code segment */
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF9A
.byte 0x00
/* Data segment */
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF92
.byte 0x00
ProtectedModeGDTEnd:
nop
.align 16
LongMode_gdtr:
.word LongModeGDTEnd - LongModeGDTStart - 1
.quad LongModeGDTStart - _trampoline_start + TRAMPOLINE_START
.align 16
LongModeGDTStart:
/* NULL segment */
.quad 0x0
/* Code segment */
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xAF98
.byte 0x00
/* Data segment */
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF92
.byte 0x00
LongModeGDTEnd:
nop
.global _trampoline_end
_trampoline_end:

View File

129
Kernel/arch/amd64/linker.ld Normal file
View File

@ -0,0 +1,129 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
OUTPUT_FORMAT(elf64-x86-64)
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
PF_R = 0x4;
PF_W = 0x2;
PF_X = 0x1;
PHDRS
{
bootstrap PT_LOAD FLAGS( PF_R | PF_W /*| PF_X*/ );
text PT_LOAD FLAGS( PF_R | PF_X );
data PT_LOAD FLAGS( PF_R | PF_W );
rodata PT_LOAD FLAGS( PF_R );
bss PT_LOAD FLAGS( PF_R | PF_W );
}
KERNEL_VMA = 0xFFFFFFFF80000000;
SECTIONS
{
. = 0x100000;
_bootstrap_start = .;
.bootstrap ALIGN(CONSTANT(MAXPAGESIZE)) :
{
*(.multiboot)
*(.multiboot2)
*(.bootstrap .bootstrap.*)
} :bootstrap
_bootstrap_end = ALIGN(CONSTANT(MAXPAGESIZE));
. += KERNEL_VMA;
_kernel_start = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_text_start = ALIGN(CONSTANT(MAXPAGESIZE));
.text ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.text) - KERNEL_VMA)
{
*(.text .text.*)
} :text
_kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_data_start = ALIGN(CONSTANT(MAXPAGESIZE));
.data ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.data) - KERNEL_VMA)
{
*(.data .data.*)
} :data
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RW
{
KEEP (*(.eh_frame .eh_frame.*))
} :data
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RW
{
KEEP (*(.gcc_except_table .gcc_except_table.*))
} :data
_kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_rodata_start = ALIGN(CONSTANT(MAXPAGESIZE));
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.rodata) - KERNEL_VMA)
{
*(.rodata .rodata.*)
} :rodata
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__init_array_start = .);
KEEP(*(.init_array .ctors))
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
PROVIDE_HIDDEN (__init_array_end = .);
} :rodata
.fini_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.fini_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__fini_array_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
} :rodata
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA)
{
*(.eh_frame_hdr .eh_frame_hdr.*)
} :rodata
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RO
{
KEEP (*(.eh_frame .eh_frame.*))
} :rodata
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RO
{
KEEP (*(.gcc_except_table .gcc_except_table.*))
} :rodata
_kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_bss_start = ALIGN(CONSTANT(MAXPAGESIZE));
.bss ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.bss) - KERNEL_VMA)
{
*(COMMON)
*(.bss .bss.*)
} :bss
_kernel_bss_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_end = ALIGN(CONSTANT(MAXPAGESIZE));
/DISCARD/ :
{
*(.comment*)
*(.note*)
}
}

View File

@ -0,0 +1,97 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "acpi.hpp"
#include <memory.hpp>
#include <debug.h>
#include "../../kernel.h"
namespace ACPI
{
MADT::MADT(ACPI::MADTHeader *madt)
{
trace("Initializing MADT");
if (!madt)
{
error("MADT is NULL");
return;
}
CPUCores = 0;
LAPICAddress = (LAPIC *)(uintptr_t)madt->LocalControllerAddress;
for (uint8_t *ptr = (uint8_t *)(madt->Entries);
(uintptr_t)(ptr) < (uintptr_t)(madt) + madt->Header.Length;
ptr += *(ptr + 1))
{
switch (*(ptr))
{
case 0:
{
if (ptr[4] & 1)
{
lapic.push_back((LocalAPIC *)ptr);
KPrint("Local APIC %d (APIC %d) found.", lapic.back()->ACPIProcessorId, lapic.back()->APICId);
CPUCores++;
}
break;
}
case 1:
{
ioapic.push_back((MADTIOApic *)ptr);
KPrint("I/O APIC %d (Address %#lx) found.", ioapic.back()->APICID, ioapic.back()->Address);
Memory::Virtual(KernelPageTable).Map((void *)(uintptr_t)ioapic.back()->Address, (void *)(uintptr_t)ioapic.back()->Address, Memory::PTFlag::RW | Memory::PTFlag::PCD); // Make sure that the address is mapped.
break;
}
case 2:
{
iso.push_back((MADTIso *)ptr);
KPrint("ISO (IRQ:%#lx, BUS:%#lx, GSI:%#lx, %s/%s) found.",
iso.back()->IRQSource, iso.back()->BuSSource, iso.back()->GSI,
iso.back()->Flags & 0x00000004 ? "Active High" : "Active Low",
iso.back()->Flags & 0x00000100 ? "Edge Triggered" : "Level Triggered");
break;
}
case 4:
{
nmi.push_back((MADTNmi *)ptr);
KPrint("NMI %#lx (lint:%#lx) found.", nmi.back()->processor, nmi.back()->lint);
break;
}
case 5:
{
LAPICAddress = (LAPIC *)ptr;
KPrint("APIC found at %#lx", LAPICAddress);
break;
}
default:
{
KPrint("Unknown MADT entry %#lx", *(ptr));
break;
}
}
Memory::Virtual(KernelPageTable).Map((void *)LAPICAddress, (void *)LAPICAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD); // I should map more than one page?
}
CPUCores--; // We start at 0 (BSP) and end at 11 (APs), so we have 12 cores.
KPrint("Total CPU cores: %d", CPUCores + 1);
}
MADT::~MADT()
{
}
}

View File

@ -0,0 +1,518 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <convert.h>
#include <debug.h>
namespace Memory
{
bool Virtual::Check(void *VirtualAddress, PTFlag Flag)
{
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryPointerTableEntryPtr *PDPTE = nullptr;
PageDirectoryEntryPtr *PDE = nullptr;
PageTableEntryPtr *PTE = nullptr;
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (!PML4->Present)
{
debug("PML4 not present for %#lx", VirtualAddress);
return false;
}
PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->GetAddress() << 12);
if (!PDPTE)
{
debug("Failed to get PDPTE for %#lx", VirtualAddress);
return false;
}
if (PDPTE->Entries[Index.PDPTEIndex].PageSize)
{
bool result = PDPTE->Entries[Index.PDPTEIndex].raw & Flag;
if (!result)
{
debug("Failed to check %#lx for %#lx (raw: %#lx)", VirtualAddress, Flag,
PDPTE->Entries[Index.PDPTEIndex].raw);
}
return result;
}
PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
if (!PDE)
{
debug("Failed to get PDE for %#lx", VirtualAddress);
return false;
}
if (PDE->Entries[Index.PDEIndex].PageSize)
{
bool result = PDE->Entries[Index.PDEIndex].raw & Flag;
if (!result)
{
debug("Failed to check %#lx for %#lx (raw: %#lx)", VirtualAddress, Flag,
PDE->Entries[Index.PDEIndex].raw);
}
return result;
}
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
if (!PTE)
{
debug("Failed to get PTE for %#lx", VirtualAddress);
return false;
}
bool result = PTE->Entries[Index.PTEIndex].raw & Flag;
if (!result)
{
debug("Failed to check %#lx for %#lx (raw: %#lx)", VirtualAddress, Flag,
PTE->Entries[Index.PTEIndex].raw);
}
return result;
}
void *Virtual::GetPhysical(void *VirtualAddress)
{
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
PageDirectoryPointerTableEntryPtr *PDPTE = nullptr;
PageDirectoryEntryPtr *PDE = nullptr;
PageTableEntryPtr *PTE = nullptr;
if (PML4->Present)
{
PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->GetAddress() << 12);
if (PDPTE)
{
if (PDPTE->Entries[Index.PDPTEIndex].Present)
{
if (PDPTE->Entries[Index.PDPTEIndex].PageSize)
return (void *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
if (PDE)
{
if (PDE->Entries[Index.PDEIndex].Present)
{
if (PDE->Entries[Index.PDEIndex].PageSize)
return (void *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
if (PTE)
{
if (PTE->Entries[Index.PTEIndex].Present)
return (void *)((uintptr_t)PTE->Entries[Index.PTEIndex].GetAddress() << 12);
}
}
}
}
}
}
return nullptr;
}
Virtual::MapType Virtual::GetMapType(void *VirtualAddress)
{
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryPointerTableEntryPtr *PDPTE = nullptr;
PageDirectoryEntryPtr *PDE = nullptr;
PageTableEntryPtr *PTE = nullptr;
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (!PML4->Present)
goto ReturnLogError;
PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->GetAddress() << 12);
if (!PDPTE || !PDPTE->Entries[Index.PDPTEIndex].Present)
goto ReturnLogError;
if (PDPTE->Entries[Index.PDPTEIndex].PageSize)
return MapType::OneGiB;
PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
if (!PDE || !PDE->Entries[Index.PDEIndex].Present)
goto ReturnLogError;
if (PDE->Entries[Index.PDEIndex].PageSize)
return MapType::TwoMiB;
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
if (!PTE)
goto ReturnLogError;
if (PTE->Entries[Index.PTEIndex].Present)
return MapType::FourKiB;
ReturnLogError:
return MapType::NoMapType;
}
PageMapLevel5 *Virtual::GetPML5(void *VirtualAddress, MapType Type)
{
UNUSED(VirtualAddress);
UNUSED(Type);
stub; /* TODO */
return nullptr;
}
PageMapLevel4 *Virtual::GetPML4(void *VirtualAddress, MapType Type)
{
UNUSED(Type);
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (PML4->Present)
return PML4;
debug("PML4 not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryPointerTableEntry *Virtual::GetPDPTE(void *VirtualAddress, MapType Type)
{
UNUSED(Type);
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (!PML4->Present)
{
debug("PML4 not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
if (PDPTE->Present)
return PDPTE;
debug("PDPTE not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryEntry *Virtual::GetPDE(void *VirtualAddress, MapType Type)
{
UNUSED(Type);
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (!PML4->Present)
{
debug("PML4 not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
if (!PDPTE->Present)
{
debug("PDPTE not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
if (PDE->Present)
return PDE;
debug("PDE not present for %#lx", VirtualAddress);
return nullptr;
}
PageTableEntry *Virtual::GetPTE(void *VirtualAddress, MapType Type)
{
UNUSED(Type);
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFFFFFFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (!PML4->Present)
{
debug("PML4 not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
if (!PDPTE->Present)
{
debug("PDPTE not present for %#lx", VirtualAddress);
return nullptr;
}
PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
if (!PDE->Present)
{
debug("PDE not present for %#lx", VirtualAddress);
return nullptr;
}
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
if (PTE->Present)
return PTE;
debug("PTE not present for %#lx", VirtualAddress);
return nullptr;
}
void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
{
SmartLock(this->MemoryLock);
if (unlikely(!this->pTable))
{
error("No page table");
return;
}
Flags |= PTFlag::P;
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
uint64_t DirectoryFlags = Flags & 0x3F;
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr;
if (!PML4->Present)
{
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryPointerTableEntryPtr) + 1));
memset(PDPTEPtr, 0, sizeof(PageDirectoryPointerTableEntryPtr));
PML4->Present = true;
PML4->SetAddress((uintptr_t)PDPTEPtr >> 12);
}
else
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12);
PML4->raw |= DirectoryFlags;
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
if (Type == MapType::OneGiB)
{
PDPTE->raw |= Flags;
PDPTE->PageSize = true;
PDPTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
debug("Mapped 1GB page at %p to %p", VirtualAddress, PhysicalAddress);
return;
}
PageDirectoryEntryPtr *PDEPtr = nullptr;
if (!PDPTE->Present)
{
PDEPtr = (PageDirectoryEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryEntryPtr) + 1));
memset(PDEPtr, 0, sizeof(PageDirectoryEntryPtr));
PDPTE->Present = true;
PDPTE->SetAddress((uintptr_t)PDEPtr >> 12);
}
else
PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
PDPTE->raw |= DirectoryFlags;
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
if (Type == MapType::TwoMiB)
{
PDE->raw |= Flags;
PDE->PageSize = true;
PDE->SetAddress((uintptr_t)PhysicalAddress >> 12);
debug("Mapped 2MB page at %p to %p", VirtualAddress, PhysicalAddress);
return;
}
PageTableEntryPtr *PTEPtr = nullptr;
if (!PDE->Present)
{
PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1));
memset(PTEPtr, 0, sizeof(PageTableEntryPtr));
PDE->Present = true;
PDE->SetAddress((uintptr_t)PTEPtr >> 12);
}
else
PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
PDE->raw |= DirectoryFlags;
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
PTE->Present = true;
PTE->raw |= Flags;
PTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
CPU::x64::invlpg(VirtualAddress);
#ifdef DEBUG
/* https://stackoverflow.com/a/3208376/9352057 */
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
(byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \
(byte & 0x08 ? '1' : '0'), \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
if (!this->Check(VirtualAddress, (PTFlag)Flags)) // quick workaround just to see where it fails
warn("Failed to map v:%#lx p:%#lx with flags: " BYTE_TO_BINARY_PATTERN, VirtualAddress, PhysicalAddress, BYTE_TO_BINARY(Flags));
#endif
}
void Virtual::Unmap(void *VirtualAddress, MapType Type)
{
SmartLock(this->MemoryLock);
if (!this->pTable)
{
error("No page table");
return;
}
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
if (!PML4->Present)
return;
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
if (!PDPTE->Present)
return;
if (Type == MapType::OneGiB && PDPTE->PageSize)
{
PDPTE->Present = false;
return;
}
PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Address << 12);
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
if (!PDE->Present)
return;
if (Type == MapType::TwoMiB && PDE->PageSize)
{
PDE->Present = false;
return;
}
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE->Address << 12);
PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex];
if (!PTE.Present)
return;
PTE.Present = false;
PTEPtr->Entries[Index.PTEIndex] = PTE;
CPU::x64::invlpg(VirtualAddress);
}
void Virtual::Remap(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
{
SmartLock(this->MemoryLock);
if (unlikely(!this->pTable))
{
error("No page table");
return;
}
Flags |= PTFlag::P;
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
uint64_t DirectoryFlags = Flags & 0x3F;
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr;
if (!PML4->Present)
{
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryPointerTableEntryPtr) + 1));
memset(PDPTEPtr, 0, sizeof(PageDirectoryPointerTableEntryPtr));
PML4->Present = true;
PML4->SetAddress((uintptr_t)PDPTEPtr >> 12);
}
else
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12);
PML4->raw |= DirectoryFlags;
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
if (Type == MapType::OneGiB)
{
PDPTE->raw &= 0xFFF;
PDPTE->raw |= Flags;
PDPTE->PageSize = true;
PDPTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
debug("Mapped 1GB page at %p to %p", VirtualAddress, PhysicalAddress);
return;
}
PageDirectoryEntryPtr *PDEPtr = nullptr;
if (!PDPTE->Present)
{
PDEPtr = (PageDirectoryEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryEntryPtr) + 1));
memset(PDEPtr, 0, sizeof(PageDirectoryEntryPtr));
PDPTE->Present = true;
PDPTE->SetAddress((uintptr_t)PDEPtr >> 12);
}
else
PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
PDPTE->raw |= DirectoryFlags;
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
if (Type == MapType::TwoMiB)
{
PDE->raw &= 0xFFF;
PDE->raw |= Flags;
PDE->PageSize = true;
PDE->SetAddress((uintptr_t)PhysicalAddress >> 12);
debug("Mapped 2MB page at %p to %p", VirtualAddress, PhysicalAddress);
return;
}
PageTableEntryPtr *PTEPtr = nullptr;
if (!PDE->Present)
{
PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1));
memset(PTEPtr, 0, sizeof(PageTableEntryPtr));
PDE->Present = true;
PDE->SetAddress((uintptr_t)PTEPtr >> 12);
}
else
PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
PDE->raw |= DirectoryFlags;
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
PTE->raw &= 0xFFF;
PTE->raw |= Flags;
PTE->Present = true;
PTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
CPU::x64::invlpg(VirtualAddress);
}
}

View File

@ -0,0 +1,99 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <syscalls.hpp>
#include <cpu.hpp>
#include "cpu/gdt.hpp"
// https://supercip971.github.io/02-wingos-syscalls.html
using namespace CPU::x64;
// "core/SystemCalls.cpp"
extern "C" uint64_t SystemCallsHandler(SyscallsFrame *regs);
extern "C" void SystemCallHandlerStub();
extern "C" __naked __used __no_stack_protector __aligned(16) void SystemCallHandlerStub()
{
asmv("swapgs\n"); /* Swap GS to get the gsTCB */
asmv("mov %rsp, %gs:0x8\n"); /* We save the current rsp to gsTCB->TempStack */
asmv("mov %gs:0x0, %rsp\n"); /* Get gsTCB->SystemCallStack and set it as rsp */
asmv("push $0x1b\n"); /* Push user data segment for SyscallsFrame */
asmv("push %gs:0x8\n"); /* Push gsTCB->TempStack (old rsp) for SyscallsFrame */
asmv("push %r11\n"); /* Push the flags for SyscallsFrame */
asmv("push $0x23\n"); /* Push user code segment for SyscallsFrame */
asmv("push %rcx\n"); /* Push the return address for SyscallsFrame + sysretq (https://www.felixcloutier.com/x86/sysret) */
/* Push registers */
asmv("push %rax\n"
"push %rbx\n"
"push %rcx\n"
"push %rdx\n"
"push %rsi\n"
"push %rdi\n"
"push %rbp\n"
"push %r8\n"
"push %r9\n"
"push %r10\n"
"push %r11\n"
"push %r12\n"
"push %r13\n"
"push %r14\n"
"push %r15\n");
/* Set the first argument to the SyscallsFrame pointer */
asmv("mov %rsp, %rdi\n");
asmv("mov $0, %rbp\n");
asmv("call SystemCallsHandler\n");
/* Pop registers except rax */
asmv("pop %r15\n"
"pop %r14\n"
"pop %r13\n"
"pop %r12\n"
"pop %r11\n"
"pop %r10\n"
"pop %r9\n"
"pop %r8\n"
"pop %rbp\n"
"pop %rdi\n"
"pop %rsi\n"
"pop %rdx\n"
"pop %rcx\n"
"pop %rbx\n");
/* Restore rsp from gsTCB->TempStack */
asmv("mov %gs:0x8, %rsp\n");
#ifdef DEBUG
/* Easier to debug stacks */
asmv("movq $0, %gs:0x8\n");
#endif
asmv("swapgs\n"); /* Swap GS back to the user GS */
asmv("sti\n"); /* Enable interrupts */
asmv("sysretq\n"); /* Return to rcx address in user mode */
}
void InitializeSystemCalls()
{
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | 1);
wrmsr(MSR_STAR, ((uint64_t)(GDT_KERNEL_CODE) << 32) | ((uint64_t)(GDT_KERNEL_DATA | 3) << 48));
wrmsr(MSR_LSTAR, (uint64_t)SystemCallHandlerStub);
wrmsr(MSR_SYSCALL_MASK, (uint64_t)(1 << 9));
}

View File

@ -0,0 +1,38 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code64
.global _sig_native_trampoline_start
_sig_native_trampoline_start:
int $0x3
.global _sig_native_trampoline_end
_sig_native_trampoline_end:
.global _sig_linux_trampoline_start
_sig_linux_trampoline_start:
movq %rsp, %rbp
movq (%rbp), %rax
call *%rax
mov %rbp, %rsp
/* rt_sigreturn = 15 */
movq $15, %rax
syscall
.global _sig_linux_trampoline_end
_sig_linux_trampoline_end:

View File

@ -0,0 +1,379 @@
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wfloat-equal"
/* Source: https://github.com/glitchub/arith64 */
#define arith64_u64 unsigned long long int
#define arith64_s64 signed long long int
#define arith64_u32 unsigned int
#define arith64_s32 int
typedef union
{
arith64_u64 u64;
arith64_s64 s64;
struct
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
arith64_u32 hi;
arith64_u32 lo;
#else
arith64_u32 lo;
arith64_u32 hi;
#endif
} u32;
struct
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
arith64_s32 hi;
arith64_s32 lo;
#else
arith64_s32 lo;
arith64_s32 hi;
#endif
} s32;
} arith64_word;
#define arith64_hi(n) (arith64_word){.u64 = n}.u32.hi
#define arith64_lo(n) (arith64_word){.u64 = n}.u32.lo
#define arith64_neg(a, b) (((a) ^ ((((arith64_s64)(b)) >= 0) - 1)) + (((arith64_s64)(b)) < 0))
#define arith64_abs(a) arith64_neg(a, a)
arith64_s64 __absvdi2(arith64_s64 a)
{
return arith64_abs(a);
}
arith64_s64 __ashldi3(arith64_s64 a, int b)
{
arith64_word w = {.s64 = a};
b &= 63;
if (b >= 32)
{
w.u32.hi = w.u32.lo << (b - 32);
w.u32.lo = 0;
}
else if (b)
{
w.u32.hi = (w.u32.lo >> (32 - b)) | (w.u32.hi << b);
w.u32.lo <<= b;
}
return w.s64;
}
arith64_s64 __ashrdi3(arith64_s64 a, int b)
{
arith64_word w = {.s64 = a};
b &= 63;
if (b >= 32)
{
w.s32.lo = w.s32.hi >> (b - 32);
w.s32.hi >>= 31; // 0xFFFFFFFF or 0
}
else if (b)
{
w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b);
w.s32.hi >>= b;
}
return w.s64;
}
int __clzsi2(arith64_u32 a)
{
int b, n = 0;
b = !(a & 0xffff0000) << 4;
n += b;
a <<= b;
b = !(a & 0xff000000) << 3;
n += b;
a <<= b;
b = !(a & 0xf0000000) << 2;
n += b;
a <<= b;
b = !(a & 0xc0000000) << 1;
n += b;
a <<= b;
return n + !(a & 0x80000000);
}
int __clzdi2(arith64_u64 a)
{
int b, n = 0;
b = !(a & 0xffffffff00000000ULL) << 5;
n += b;
a <<= b;
b = !(a & 0xffff000000000000ULL) << 4;
n += b;
a <<= b;
b = !(a & 0xff00000000000000ULL) << 3;
n += b;
a <<= b;
b = !(a & 0xf000000000000000ULL) << 2;
n += b;
a <<= b;
b = !(a & 0xc000000000000000ULL) << 1;
n += b;
a <<= b;
return n + !(a & 0x8000000000000000ULL);
}
int __ctzsi2(arith64_u32 a)
{
int b, n = 0;
b = !(a & 0x0000ffff) << 4;
n += b;
a >>= b;
b = !(a & 0x000000ff) << 3;
n += b;
a >>= b;
b = !(a & 0x0000000f) << 2;
n += b;
a >>= b;
b = !(a & 0x00000003) << 1;
n += b;
a >>= b;
return n + !(a & 0x00000001);
}
int __ctzdi2(arith64_u64 a)
{
int b, n = 0;
b = !(a & 0x00000000ffffffffULL) << 5;
n += b;
a >>= b;
b = !(a & 0x000000000000ffffULL) << 4;
n += b;
a >>= b;
b = !(a & 0x00000000000000ffULL) << 3;
n += b;
a >>= b;
b = !(a & 0x000000000000000fULL) << 2;
n += b;
a >>= b;
b = !(a & 0x0000000000000003ULL) << 1;
n += b;
a >>= b;
return n + !(a & 0x0000000000000001ULL);
}
arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c)
{
if (b > a)
{
if (c)
*c = a;
return 0;
}
if (!arith64_hi(b))
{
if (b == 0)
{
volatile char x = 0;
x = 1 / x;
}
if (b == 1)
{
if (c)
*c = 0;
return a;
}
if (!arith64_hi(a))
{
if (c)
*c = arith64_lo(a) % arith64_lo(b);
return arith64_lo(a) / arith64_lo(b);
}
}
char bits = __clzdi2(b) - __clzdi2(a) + 1;
arith64_u64 rem = a >> bits;
a <<= 64 - bits;
arith64_u64 wrap = 0;
while (bits-- > 0)
{
rem = (rem << 1) | (a >> 63);
a = (a << 1) | (wrap & 1);
wrap = ((arith64_s64)(b - rem - 1) >> 63);
rem -= b & wrap;
}
if (c)
*c = rem;
return (a << 1) | (wrap & 1);
}
arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b)
{
arith64_u64 q = __divmoddi4(arith64_abs(a), arith64_abs(b), (void *)0);
return arith64_neg(q, a ^ b);
}
int __ffsdi2(arith64_u64 a) { return a ? __ctzdi2(a) + 1 : 0; }
arith64_u64 __lshrdi3(arith64_u64 a, int b)
{
arith64_word w = {.u64 = a};
b &= 63;
if (b >= 32)
{
w.u32.lo = w.u32.hi >> (b - 32);
w.u32.hi = 0;
}
else if (b)
{
w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b);
w.u32.hi >>= b;
}
return w.u64;
}
arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b)
{
arith64_u64 r;
__divmoddi4(arith64_abs(a), arith64_abs(b), &r);
return arith64_neg(r, a);
}
int __popcountsi2(arith64_u32 a)
{
a = a - ((a >> 1) & 0x55555555);
a = ((a >> 2) & 0x33333333) + (a & 0x33333333);
a = (a + (a >> 4)) & 0x0F0F0F0F;
a = (a + (a >> 16));
return (a + (a >> 8)) & 63;
}
int __popcountdi2(arith64_u64 a)
{
a = a - ((a >> 1) & 0x5555555555555555ULL);
a = ((a >> 2) & 0x3333333333333333ULL) + (a & 0x3333333333333333ULL);
a = (a + (a >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
a = (a + (a >> 32));
a = (a + (a >> 16));
return (a + (a >> 8)) & 127;
}
arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b) { return __divmoddi4(a, b, (void *)0); }
arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b)
{
arith64_u64 r;
__divmoddi4(a, b, &r);
return r;
}
/* Good documentation: https://splichal.eu/scripts/sphinx/gccint/_build/html/the-gcc-low-level-runtime-library/routines-for-floating-point-emulation.html */
double __adddf3(double a, double b) { return a + b; }
double __muldf3(double a, double b) { return a * b; }
double __floatsidf(int i) { return (double)i; }
int __ltdf2(double a, double b) { return a < b; }
int __gtdf2(double a, double b) { return a > b; }
int __nedf2(double a, double b) { return a != b; }
int __eqdf2(double a, double b) { return a == b; }
double __floatdidf(long i) { return (double)i; }
double __divdf3(double a, double b) { return a / b; }
double __subdf3(double a, double b) { return a - b; }
int __gedf2(double a, double b) { return a >= b; }
int __fixdfsi(double a) { return (int)a; }
long __fixdfdi(double a) { return (long)a; }
int __ledf2(double a, double b) { return a <= b; }
/* FIXME: Check if these functions are implemented correctly */
typedef long long int64_t;
typedef unsigned long long uint64_t;
typedef unsigned int uint32_t;
typedef struct
{
uint64_t value;
} atomic_uint64_t;
/* No longer needed? */
// uint64_t __atomic_load_8(const atomic_uint64_t *p)
// {
// uint64_t value;
// __asm__ volatile("lock cmpxchg8b %1"
// : "=A"(value)
// : "m"(*p)
// : "memory");
// return value;
// }
// void __atomic_store_8(atomic_uint64_t *p, uint64_t value)
// {
// __asm__ volatile("lock cmpxchg8b %0"
// : "=m"(p->value)
// : "a"((uint32_t)value), "d"((uint32_t)(value >> 32)), "m"(*p)
// : "memory");
// }
int __fixsfsi(float a)
{
return (int)a;
}
int __ltsf2(float a, float b)
{
return -(a < b);
}
int __nesf2(float a, float b)
{
return a == b;
}
int __eqsf2(float a, float b)
{
return !(a == b);
}
float __divsf3(float a, float b)
{
return (a / b);
}
double __extendsfdf2(float a)
{
return (double)a;
}
float __truncdfsf2(double a)
{
return (float)a;
}
float __subsf3(float a, float b)
{
return (a - b);
}
float __floatsisf(int a)
{
return (float)a;
}
int __fixunssfsi(float a)
{
return (int)a;
}
float __mulsf3(float a, float b)
{
return (a * b);
}
float __addsf3(float a, float b)
{
return (a + b);
}

View File

@ -0,0 +1,51 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
KERNEL_VIRTUAL_BASE = 0xC0000000 /* 3GB */
KERNEL_PAGE_NUMBER = 768 /* KERNEL_VIRTUAL_BASE >> 22 */
.section .bootstrap.data, "a"
.align 0x1000
.global BootPageTable
BootPageTable:
.long 0x00000083
.long 0x00400083
.long 0x00800083
.long 0x00C00083
.long 0x01000083
.long 0x01400083
.long 0x01800083
.long 0x01C00083
.long 0x02000083
.long 0x02400083
.rept (KERNEL_PAGE_NUMBER - 10)
.long 0
.endr
.long 0x00000083
.long 0x00400083
.long 0x00800083
.long 0x00C00083
.long 0x01000083
.long 0x01400083
.long 0x01800083
.long 0x01C00083
.long 0x02000083
.long 0x02400083
.rept (1024 - KERNEL_PAGE_NUMBER - 10)
.long 0
.endr

View File

@ -0,0 +1,38 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
.extern Multiboot_start
.section .bootstrap.text, "a"
.global _start
_start:
/* Check for multiboot */
cmp $0x2BADB002, %eax
je .Multiboot
/* Unkown bootloader */
.Hang:
cli
hlt
jmp .Hang
/* Multiboot */
.Multiboot:
call Multiboot_start
jmp .Hang

View File

@ -0,0 +1,27 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.intel_syntax noprefix
.code32
.section .multiboot, "a"
.align 4
MULTIBOOT_HEADER:
.long 0x1BADB002
.long 1 << 0 | 1 << 1
.long -(0x1BADB002 + (1 << 0 | 1 << 1))

View File

@ -0,0 +1,93 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.intel_syntax noprefix
.code32
.extern Multiboot_start
/* https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html */
.section .multiboot2, "a"
.align 0x1000
MULTIBOOT2_HEADER_START:
.long 0xE85250D6
.long 0
.long (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START)
.long 0x100000000 - (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START) - 0 - 0xE85250D6
.align 8
InfoRequestTag_Start:
.word 1
.word 0
.long InfoRequestTag_End - InfoRequestTag_Start
.long 1 /* Command Line */
.long 2 /* Boot Loader Name */
.long 3 /* Module */
.long 4 /* Basic Memory Information */
.long 5 /* BIOS Boot Device */
.long 6 /* Memory Map */
.long 7 /* VBE */
.long 8 /* Framebuffer */
.long 9 /* ELF Sections */
.long 10 /* APM Table */
.long 11 /* EFI 32-bit System Table Pointer */
.long 12 /* EFI 64-bit System Table Pointer */
/* .long 13 */ /* SMBIOS */
.long 14 /* ACPI Old */
.long 15 /* ACPI New */
.long 16 /* Network */
.long 17 /* EFI Memory Map */
.long 18 /* EFI Boot Services Notifier */
.long 19 /* EFI 32-bit Image Handle Pointer */
.long 20 /* EFI 64-bit Image Handle Pointer */
.long 21 /* Load Base Address */
InfoRequestTag_End:
.align 8
FramebufferTag_Start:
.word 5
.word 1
.long FramebufferTag_End - FramebufferTag_Start
.long 0
.long 0
.long 32
FramebufferTag_End:
.align 8
EGATextSupportTag_Start:
.word 4
.word 0
.long EGATextSupportTag_End - EGATextSupportTag_Start
.long 0 /* https://www.gnu.org/software/grub/manual/multiboot2/html_node/Console-header-tags.html */
EGATextSupportTag_End:
.align 8
AlignedModulesTag_Start:
.word 6
.word 0
.long AlignedModulesTag_End - AlignedModulesTag_Start
AlignedModulesTag_End:
.align 8
EntryAddressTag_Start:
.word 3
.word 0
.long EntryAddressTag_End - EntryAddressTag_Start
.long Multiboot_start
EntryAddressTag_End:
.align 8
EndTag_Start:
.word 0
.word 0
.long EndTag_End - EndTag_Start
EndTag_End:
MULTIBOOT2_HEADER_END:

View File

@ -0,0 +1,81 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.intel_syntax noprefix
.code32
.section .bootstrap.text, "a"
.global DetectCPUID
DetectCPUID:
pushfd
pop eax
mov ecx, eax
xor eax, 1 << 21
push eax
popfd
pushfd
pop eax
push ecx
popfd
xor eax, ecx
jz .NoCPUID
mov eax, 1
ret
.NoCPUID:
xor eax, eax
ret
.global Detect64Bit
Detect64Bit:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .NoLongMode
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz .NoLongMode
mov eax, 1
ret
.NoLongMode:
xor eax, eax
ret
.global DetectPSE
DetectPSE:
mov eax, 0x00000001
cpuid
test edx, 0x00000008
jz .NoPSE
mov eax, 1
ret
.NoPSE:
xor eax, eax
ret
.global DetectPAE
DetectPAE:
mov eax, 0x00000001
cpuid
test edx, 0x00000040
jz .NoPAE
mov eax, 1
ret
.NoPAE:
xor eax, eax
ret

View File

@ -0,0 +1,64 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
.section .bootstrap.text, "a"
.align 32
.global gdtr
gdtr:
.word GDT32_END - GDT32 - 1
.long GDT32
.align 32
GDT32:
.quad 0x0
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF9A
.byte 0x00
.word 0xFFFF
.word 0x0000
.byte 0x00
.word 0xCF92
.byte 0x00
.word 0x0100
.word 0x1000
.byte 0x00
.word 0x4092
.byte 0x00
GDT32_END:
nop
.global LoadGDT32
LoadGDT32:
lgdt [gdtr]
ljmp $0x8, $ActivateGDT
ActivateGDT:
mov $0x10, %cx
mov %cx, %ss
mov %cx, %ds
mov %cx, %es
mov %cx, %fs
mov $0x18, %cx
mov %cx, %gs
ret

View File

@ -0,0 +1,210 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <boot/protocol/multiboot.h>
#include <memory.hpp>
#include "../../../../kernel.h"
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
{
multiboot_info *InfoAddress = r_cst(multiboot_info *, Info);
if (InfoAddress->flags & MULTIBOOT_INFO_MEMORY)
{
fixme("mem_lower: %#x, mem_upper: %#x",
InfoAddress->mem_lower, InfoAddress->mem_upper);
}
if (InfoAddress->flags & MULTIBOOT_INFO_BOOTDEV)
{
fixme("boot_device: %#x",
InfoAddress->boot_device);
}
if (InfoAddress->flags & MULTIBOOT_INFO_CMDLINE)
{
strncpy(mb2binfo.Kernel.CommandLine,
r_cst(const char *, InfoAddress->cmdline),
strlen(r_cst(const char *, InfoAddress->cmdline)));
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
}
if (InfoAddress->flags & MULTIBOOT_INFO_MODS)
{
multiboot_mod_list *module = r_cst(multiboot_mod_list *, InfoAddress->mods_addr);
for (size_t i = 0; i < InfoAddress->mods_count; i++)
{
if (i > MAX_MODULES)
{
warn("Too many modules, skipping the rest...");
break;
}
mb2binfo.Modules[i].Address = (void *)(uint32_t)module[i].mod_start;
mb2binfo.Modules[i].Size = module[i].mod_end - module[i].mod_start;
strncpy(mb2binfo.Modules[i].Path, "(null)", 6);
strncpy(mb2binfo.Modules[i].CommandLine, r_cst(const char *, module[i].cmdline),
strlen(r_cst(const char *, module[i].cmdline)));
debug("Module: %s", mb2binfo.Modules[i].Path);
}
}
if (InfoAddress->flags & MULTIBOOT_INFO_AOUT_SYMS)
{
fixme("aout_sym: [tabsize: %#x, strsize: %#x, addr: %#x, reserved: %#x]",
InfoAddress->u.aout_sym.tabsize, InfoAddress->u.aout_sym.strsize,
InfoAddress->u.aout_sym.addr, InfoAddress->u.aout_sym.reserved);
}
if (InfoAddress->flags & MULTIBOOT_INFO_ELF_SHDR)
{
mb2binfo.Kernel.Symbols.Num = InfoAddress->u.elf_sec.num;
mb2binfo.Kernel.Symbols.EntSize = InfoAddress->u.elf_sec.size;
mb2binfo.Kernel.Symbols.Shndx = InfoAddress->u.elf_sec.shndx;
mb2binfo.Kernel.Symbols.Sections = s_cst(uintptr_t, InfoAddress->u.elf_sec.addr);
}
if (InfoAddress->flags & MULTIBOOT_INFO_MEM_MAP)
{
mb2binfo.Memory.Entries = InfoAddress->mmap_length / sizeof(multiboot_mmap_entry);
for (uint32_t i = 0; i < mb2binfo.Memory.Entries; i++)
{
if (i > MAX_MEMORY_ENTRIES)
{
warn("Too many memory entries, skipping the rest...");
break;
}
multiboot_mmap_entry entry = r_cst(multiboot_mmap_entry *, InfoAddress->mmap_addr)[i];
mb2binfo.Memory.Size += (__SIZE_TYPE__)entry.len;
switch (entry.type)
{
case MULTIBOOT_MEMORY_AVAILABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
mb2binfo.Memory.Entry[i].Type = Usable;
break;
case MULTIBOOT_MEMORY_RESERVED:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
mb2binfo.Memory.Entry[i].Type = Reserved;
break;
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
break;
case MULTIBOOT_MEMORY_NVS:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
mb2binfo.Memory.Entry[i].Type = ACPINVS;
break;
case MULTIBOOT_MEMORY_BADRAM:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
mb2binfo.Memory.Entry[i].Type = BadMemory;
break;
default:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
mb2binfo.Memory.Entry[i].Type = Unknown;
break;
}
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
mb2binfo.Memory.Entry[i].BaseAddress,
mb2binfo.Memory.Entry[i].Length,
mb2binfo.Memory.Entry[i].Type);
}
}
if (InfoAddress->flags & MULTIBOOT_INFO_DRIVE_INFO)
{
fixme("drives_length: %d, drives_addr: %#x",
InfoAddress->drives_length, InfoAddress->drives_addr);
}
if (InfoAddress->flags & MULTIBOOT_INFO_CONFIG_TABLE)
{
fixme("config_table: %#x",
InfoAddress->config_table);
}
if (InfoAddress->flags & MULTIBOOT_INFO_BOOT_LOADER_NAME)
{
strncpy(mb2binfo.Bootloader.Name,
r_cst(const char *, InfoAddress->boot_loader_name),
strlen(r_cst(const char *, InfoAddress->boot_loader_name)));
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
}
if (InfoAddress->flags & MULTIBOOT_INFO_APM_TABLE)
{
fixme("apm_table: %#x",
InfoAddress->apm_table);
}
if (InfoAddress->flags & MULTIBOOT_INFO_VBE_INFO)
{
fixme("vbe_control_info: %#x, vbe_mode_info: %#x, vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x",
InfoAddress->vbe_control_info, InfoAddress->vbe_mode_info,
InfoAddress->vbe_mode, InfoAddress->vbe_interface_seg,
InfoAddress->vbe_interface_off, InfoAddress->vbe_interface_len);
}
if (InfoAddress->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)
{
static int fb_count = 0;
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)InfoAddress->framebuffer_addr;
mb2binfo.Framebuffer[fb_count].Width = InfoAddress->framebuffer_width;
mb2binfo.Framebuffer[fb_count].Height = InfoAddress->framebuffer_height;
mb2binfo.Framebuffer[fb_count].Pitch = InfoAddress->framebuffer_pitch;
mb2binfo.Framebuffer[fb_count].BitsPerPixel = InfoAddress->framebuffer_bpp;
switch (InfoAddress->framebuffer_type)
{
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
{
mb2binfo.Framebuffer[fb_count].Type = Indexed;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
{
mb2binfo.Framebuffer[fb_count].Type = RGB;
mb2binfo.Framebuffer[fb_count].RedMaskSize = InfoAddress->framebuffer_red_mask_size;
mb2binfo.Framebuffer[fb_count].RedMaskShift = InfoAddress->framebuffer_red_field_position;
mb2binfo.Framebuffer[fb_count].GreenMaskSize = InfoAddress->framebuffer_green_mask_size;
mb2binfo.Framebuffer[fb_count].GreenMaskShift = InfoAddress->framebuffer_green_field_position;
mb2binfo.Framebuffer[fb_count].BlueMaskSize = InfoAddress->framebuffer_blue_mask_size;
mb2binfo.Framebuffer[fb_count].BlueMaskShift = InfoAddress->framebuffer_blue_field_position;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
{
mb2binfo.Framebuffer[fb_count].Type = EGA;
break;
}
default:
{
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
break;
}
}
debug("Framebuffer %d: %dx%d %d bpp",
fb_count, InfoAddress->framebuffer_width,
InfoAddress->framebuffer_height,
InfoAddress->framebuffer_bpp);
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
InfoAddress->framebuffer_addr, InfoAddress->framebuffer_pitch, InfoAddress->framebuffer_type,
InfoAddress->framebuffer_red_mask_size, InfoAddress->framebuffer_red_field_position, InfoAddress->framebuffer_green_mask_size,
InfoAddress->framebuffer_green_field_position, InfoAddress->framebuffer_blue_mask_size, InfoAddress->framebuffer_blue_field_position);
}
mb2binfo.Kernel.PhysicalBase = (void *)&_bootstrap_start;
mb2binfo.Kernel.VirtualBase = (void *)(uint32_t)((uint32_t)&_bootstrap_start + 0xC0000000);
mb2binfo.Kernel.Size = ((uint32_t)&_kernel_end - (uint32_t)&_kernel_start) + ((uint32_t)&_bootstrap_end - (uint32_t)&_bootstrap_start);
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
Entry(&mb2binfo);
}

View File

@ -0,0 +1,300 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <memory.hpp>
#include <boot/protocol/multiboot2.h>
#include "../../../../kernel.h"
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
{
if (Info == NULL || Magic == NULL)
{
if (Magic == NULL)
error("Multiboot magic is NULL");
if (Info == NULL)
error("Multiboot info is NULL");
CPU::Stop();
}
else if (Magic != MULTIBOOT2_BOOTLOADER_MAGIC)
{
error("Multiboot magic is invalid (%#x != %#x)", Magic, MULTIBOOT2_BOOTLOADER_MAGIC);
CPU::Stop();
}
{
auto InfoAddress = Info;
for (auto Tag = (struct multiboot_tag *)((uint8_t *)InfoAddress + 8);
;
Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7)))
{
if (Tag->type == MULTIBOOT_TAG_TYPE_END)
{
debug("End of multiboot2 tags");
break;
}
switch (Tag->type)
{
case MULTIBOOT_TAG_TYPE_CMDLINE:
{
strncpy(mb2binfo.Kernel.CommandLine,
((multiboot_tag_string *)Tag)->string,
strlen(((multiboot_tag_string *)Tag)->string));
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
break;
}
case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
{
strncpy(mb2binfo.Bootloader.Name,
((multiboot_tag_string *)Tag)->string,
strlen(((multiboot_tag_string *)Tag)->string));
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
break;
}
case MULTIBOOT_TAG_TYPE_MODULE:
{
multiboot_tag_module *module = (multiboot_tag_module *)Tag;
static int module_count = 0;
mb2binfo.Modules[module_count].Address = (void *)(uint32_t)module->mod_start;
mb2binfo.Modules[module_count].Size = module->mod_end - module->mod_start;
strncpy(mb2binfo.Modules[module_count].Path, "(null)", 6);
strncpy(mb2binfo.Modules[module_count].CommandLine, module->cmdline,
strlen(module->cmdline));
debug("Module: %s", mb2binfo.Modules[module_count].Path);
module_count++;
break;
}
case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
{
multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag;
fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]",
meminfo->mem_lower, meminfo->mem_upper);
break;
}
case MULTIBOOT_TAG_TYPE_BOOTDEV:
{
multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag;
fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]",
bootdev->biosdev, bootdev->slice, bootdev->part);
break;
}
case MULTIBOOT_TAG_TYPE_MMAP:
{
multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag;
size_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry);
mb2binfo.Memory.Entries = EntryCount;
for (uint32_t i = 0; i < EntryCount; i++)
{
if (i > MAX_MEMORY_ENTRIES)
{
warn("Too many memory entries, skipping the rest...");
break;
}
multiboot_mmap_entry entry = mmap->entries[i];
mb2binfo.Memory.Size += (size_t)entry.len;
switch (entry.type)
{
case MULTIBOOT_MEMORY_AVAILABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
mb2binfo.Memory.Entry[i].Type = Usable;
break;
case MULTIBOOT_MEMORY_RESERVED:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
mb2binfo.Memory.Entry[i].Type = Reserved;
break;
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
break;
case MULTIBOOT_MEMORY_NVS:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
mb2binfo.Memory.Entry[i].Type = ACPINVS;
break;
case MULTIBOOT_MEMORY_BADRAM:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
mb2binfo.Memory.Entry[i].Type = BadMemory;
break;
default:
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
mb2binfo.Memory.Entry[i].Type = Unknown;
break;
}
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
mb2binfo.Memory.Entry[i].BaseAddress,
mb2binfo.Memory.Entry[i].Length,
mb2binfo.Memory.Entry[i].Type);
}
break;
}
case MULTIBOOT_TAG_TYPE_VBE:
{
multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag;
fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]",
vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len);
break;
}
case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
{
multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag;
static int fb_count = 0;
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr;
mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width;
mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height;
mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch;
mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp;
switch (fb->common.framebuffer_type)
{
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
{
mb2binfo.Framebuffer[fb_count].Type = Indexed;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
{
mb2binfo.Framebuffer[fb_count].Type = RGB;
mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size;
mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position;
mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size;
mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position;
mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size;
mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position;
break;
}
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
{
mb2binfo.Framebuffer[fb_count].Type = EGA;
break;
}
default:
{
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
break;
}
}
debug("Framebuffer %d: %dx%d %d bpp", fb_count, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp);
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type,
fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size,
fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position);
fb_count++;
break;
}
case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
{
multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag;
mb2binfo.Kernel.Symbols.Num = elf->num;
mb2binfo.Kernel.Symbols.EntSize = elf->entsize;
mb2binfo.Kernel.Symbols.Shndx = elf->shndx;
mb2binfo.Kernel.Symbols.Sections = (uintptr_t)&elf->sections;
break;
}
case MULTIBOOT_TAG_TYPE_APM:
{
multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag;
fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]",
apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len);
break;
}
case MULTIBOOT_TAG_TYPE_EFI32:
{
multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag;
fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size);
break;
}
case MULTIBOOT_TAG_TYPE_EFI64:
{
multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag;
fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size);
break;
}
case MULTIBOOT_TAG_TYPE_SMBIOS:
{
multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag;
fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor);
break;
}
case MULTIBOOT_TAG_TYPE_ACPI_OLD:
{
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp;
debug("OLD ACPI RSDP: %p", mb2binfo.RSDP);
break;
}
case MULTIBOOT_TAG_TYPE_ACPI_NEW:
{
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp;
debug("NEW ACPI RSDP: %p", mb2binfo.RSDP);
break;
}
case MULTIBOOT_TAG_TYPE_NETWORK:
{
multiboot_tag_network *net = (multiboot_tag_network *)Tag;
fixme("network->[dhcpack: %p]", net->dhcpack);
break;
}
case MULTIBOOT_TAG_TYPE_EFI_MMAP:
{
multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag;
fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]",
efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap);
break;
}
case MULTIBOOT_TAG_TYPE_EFI_BS:
{
fixme("efi_bs->[%p] (unknown structure)", Tag);
break;
}
case MULTIBOOT_TAG_TYPE_EFI32_IH:
{
multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag;
fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer);
break;
}
case MULTIBOOT_TAG_TYPE_EFI64_IH:
{
multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag;
fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer);
break;
}
case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
{
multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag;
mb2binfo.Kernel.PhysicalBase = (void *)(uint32_t)load_base_addr->load_base_addr;
mb2binfo.Kernel.VirtualBase = (void *)(uint32_t)(load_base_addr->load_base_addr + 0xC0000000);
mb2binfo.Kernel.Size = (size_t)(((uint32_t)&_kernel_end - (uint32_t)&_kernel_start) + ((uint32_t)&_bootstrap_end - (uint32_t)&_bootstrap_start));
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
break;
}
default:
{
error("Unknown multiboot2 tag type: %d", Tag->type);
break;
}
}
}
}
Entry(&mb2binfo);
}

View File

@ -0,0 +1,53 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <memory.hpp>
#include "../../../../kernel.h"
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
EXTERNC void multiboot_main(uintptr_t Magic, uintptr_t Info)
{
BootInfo mb2binfo{};
if (Info == NULL || Magic == NULL)
{
if (Magic == NULL)
error("Multiboot magic is NULL");
if (Info == NULL)
error("Multiboot info is NULL");
CPU::Stop();
}
else if (Magic == MULTIBOOT_BOOTLOADER_MAGIC)
multiboot_parse(mb2binfo, Magic, Info);
else if (Magic == MULTIBOOT2_BOOTLOADER_MAGIC)
multiboot2_parse(mb2binfo, Magic, Info);
else
{
error("Unknown multiboot magic %#x", Magic);
CPU::Stop();
}
}

View File

@ -0,0 +1,79 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
.code32
KERNEL_STACK_SIZE = 0x4000 /* 16KB */
.extern DetectCPUID
.extern DetectPSE
.extern multiboot_main
.extern LoadGDT32
.extern BootPageTable
.section .bootstrap.data, "a"
MB_HeaderMagic:
.quad 0
MB_HeaderInfo:
.quad 0
.section .bootstrap.text, "a"
.global Multiboot_start
Multiboot_start:
cli
mov %eax, [MB_HeaderMagic]
mov %ebx, [MB_HeaderInfo]
call DetectCPUID
cmp $0, %eax
je .
call DetectPSE
cmp $0, %eax
je .
mov %cr4, %ecx
or $0x00000010, %ecx /* PSE */
mov %ecx, %cr4
call LoadGDT32
mov $BootPageTable, %ecx
mov %ecx, %cr3
mov %cr0, %ecx
or $0x80000000, %ecx /* PG */
mov %ecx, %cr0
mov $(KernelStack + KERNEL_STACK_SIZE), %esp
mov $0x0, %ebp
mov [MB_HeaderMagic], %eax
mov [MB_HeaderInfo], %ebx
push %ebx
push %eax
call multiboot_main
.Hang:
hlt
jmp .Hang
.section .bootstrap.bss, "a"
.align 16
KernelStack:
.space KERNEL_STACK_SIZE

View File

@ -0,0 +1,409 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "apic.hpp"
#include <memory.hpp>
#include <uart.hpp>
#include <lock.hpp>
#include <acpi.hpp>
#include <cpu.hpp>
#include <smp.hpp>
#include <io.h>
#include "../../../kernel.h"
NewLock(APICLock);
using namespace CPU::x32;
using namespace CPU::x86;
/*
In constructor 'APIC::APIC::APIC(int)':
warning: left shift count >= width of type
| APICBaseAddress = BaseStruct.ApicBaseLo << 12u | BaseStruct.ApicBaseHi << 32u;
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
*/
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
namespace APIC
{
// headache
// https://www.amd.com/system/files/TechDocs/24593.pdf
// https://www.naic.edu/~phil/software/intel/318148.pdf
uint32_t APIC::Read(uint32_t Register)
{
#ifdef DEBUG
if (Register != APIC_ICRLO &&
Register != APIC_ICRHI &&
Register != APIC_ID)
debug("APIC::Read(%#lx) [x2=%d]", Register, x2APICSupported ? 1 : 0);
#endif
if (x2APICSupported)
{
if (Register != APIC_ICRHI)
return s_cst(uint32_t, rdmsr((Register >> 4) + 0x800));
else
return s_cst(uint32_t, rdmsr(0x30 + 0x800));
}
else
{
CPU::MemBar::Barrier();
uint32_t ret = *((volatile uint32_t *)((uintptr_t)APICBaseAddress + Register));
CPU::MemBar::Barrier();
return ret;
}
}
void APIC::Write(uint32_t Register, uint32_t Value)
{
#ifdef DEBUG
if (Register != APIC_EOI &&
Register != APIC_TDCR &&
Register != APIC_TIMER &&
Register != APIC_TICR &&
Register != APIC_ICRLO &&
Register != APIC_ICRHI)
debug("APIC::Write(%#lx, %#lx) [x2=%d]", Register, Value, x2APICSupported ? 1 : 0);
#endif
if (x2APICSupported)
{
if (Register != APIC_ICRHI)
wrmsr((Register >> 4) + 0x800, Value);
else
wrmsr(MSR_X2APIC_ICR, Value);
}
else
{
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)APICBaseAddress) + Register)) = Value;
CPU::MemBar::Barrier();
}
}
void APIC::IOWrite(uint64_t Base, uint32_t Register, uint32_t Value)
{
debug("APIC::IOWrite(%#lx, %#lx, %#lx)", Base, Register, Value);
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)Base + 16))) = Value;
CPU::MemBar::Barrier();
}
uint32_t APIC::IORead(uint64_t Base, uint32_t Register)
{
debug("APIC::IORead(%#lx, %#lx)", Base, Register);
CPU::MemBar::Barrier();
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
CPU::MemBar::Barrier();
uint32_t ret = *((volatile uint32_t *)(((uintptr_t)Base + 16)));
CPU::MemBar::Barrier();
return ret;
}
void APIC::EOI() { this->Write(APIC_EOI, 0); }
void APIC::WaitForIPI()
{
InterruptCommandRegisterLow icr = {.raw = 0};
do
{
icr.raw = this->Read(APIC_ICRLO);
CPU::Pause();
} while (icr.DeliveryStatus != Idle);
}
void APIC::IPI(uint8_t CPU, InterruptCommandRegisterLow icr)
{
SmartCriticalSection(APICLock);
if (x2APICSupported)
{
wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw));
this->WaitForIPI();
}
else
{
this->Write(APIC_ICRHI, (CPU << 24));
this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw));
this->WaitForIPI();
}
}
void APIC::SendInitIPI(uint8_t CPU)
{
SmartCriticalSection(APICLock);
if (x2APICSupported)
{
InterruptCommandRegisterLow icr = {.raw = 0};
icr.DeliveryMode = INIT;
icr.Level = Assert;
wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw));
this->WaitForIPI();
}
else
{
InterruptCommandRegisterLow icr = {.raw = 0};
icr.DeliveryMode = INIT;
icr.Level = Assert;
this->Write(APIC_ICRHI, (CPU << 24));
this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw));
this->WaitForIPI();
}
}
void APIC::SendStartupIPI(uint8_t CPU, uint64_t StartupAddress)
{
SmartCriticalSection(APICLock);
if (x2APICSupported)
{
InterruptCommandRegisterLow icr = {.raw = 0};
icr.Vector = s_cst(uint8_t, StartupAddress >> 12);
icr.DeliveryMode = Startup;
icr.Level = Assert;
wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw));
this->WaitForIPI();
}
else
{
InterruptCommandRegisterLow icr = {.raw = 0};
icr.Vector = s_cst(uint8_t, StartupAddress >> 12);
icr.DeliveryMode = Startup;
icr.Level = Assert;
this->Write(APIC_ICRHI, (CPU << 24));
this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw));
this->WaitForIPI();
}
}
uint32_t APIC::IOGetMaxRedirect(uint32_t APICID)
{
uint32_t TableAddress = (this->IORead((((ACPI::MADT *)PowerManager->GetMADT())->ioapic[APICID]->Address), GetIOAPICVersion));
return ((IOAPICVersion *)&TableAddress)->MaximumRedirectionEntry;
}
void APIC::RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status)
{
uint64_t Value = Vector;
int64_t IOAPICTarget = -1;
for (uint64_t i = 0; ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(i)] != 0; i++)
if (((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(i)]->GSIBase <= GSI)
if (((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(i)]->GSIBase + IOGetMaxRedirect(s_cst(uint32_t, i)) > GSI)
{
IOAPICTarget = i;
break;
}
if (IOAPICTarget == -1)
{
error("No ISO table found for I/O APIC");
return;
}
// TODO: IOAPICRedirectEntry Entry = {.raw = 0};
if (Flags & ActiveHighLow)
Value |= (1 << 13);
if (Flags & EdgeLevel)
Value |= (1 << 15);
if (!Status)
Value |= (1 << 16);
Value |= (((uintptr_t)CPU) << 56);
uint32_t IORegister = (GSI - ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(IOAPICTarget)]->GSIBase) * 2 + 16;
this->IOWrite(((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(IOAPICTarget)]->Address,
IORegister, (uint32_t)Value);
this->IOWrite(((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(IOAPICTarget)]->Address,
IORegister + 1, (uint32_t)(Value >> 32));
}
void APIC::RedirectIRQ(int CPU, uint16_t IRQ, int Status)
{
for (uint64_t i = 0; i < ((ACPI::MADT *)PowerManager->GetMADT())->iso.size(); i++)
if (((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->IRQSource == IRQ)
{
debug("[ISO %d] Mapping to source IRQ%#d GSI:%#lx on CPU %d",
i, ((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->IRQSource,
((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->GSI,
CPU);
this->RawRedirectIRQ(((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->IRQSource + 0x20,
((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->GSI,
((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->Flags,
CPU, Status);
return;
}
debug("Mapping IRQ%d on CPU %d", IRQ, CPU);
this->RawRedirectIRQ(IRQ + 0x20, IRQ, 0, CPU, Status);
}
void APIC::RedirectIRQs(int CPU)
{
SmartCriticalSection(APICLock);
debug("Redirecting IRQs...");
for (uint8_t i = 0; i < 16; i++)
this->RedirectIRQ(CPU, i, 1);
debug("Redirecting IRQs completed.");
}
APIC::APIC(int Core)
{
SmartCriticalSection(APICLock);
APIC_BASE BaseStruct = {.raw = rdmsr(MSR_APIC_BASE)};
uint64_t BaseLow = BaseStruct.ApicBaseLo;
uint64_t BaseHigh = BaseStruct.ApicBaseHi;
this->APICBaseAddress = BaseLow << 12u | BaseHigh << 32u;
trace("APIC Address: %#lx", this->APICBaseAddress);
Memory::Virtual().Map((void *)this->APICBaseAddress, (void *)this->APICBaseAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD);
bool x2APICSupported = false;
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
if (cpuid.ECX.x2APIC)
{
// x2APICSupported = cpuid.ECX.x2APIC;
fixme("x2APIC is supported");
}
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
if (cpuid.ECX.x2APIC)
{
// x2APICSupported = cpuid.ECX.x2APIC;
fixme("x2APIC is supported");
}
}
if (x2APICSupported)
{
this->x2APICSupported = true;
wrmsr(MSR_APIC_BASE, (rdmsr(MSR_APIC_BASE) | (1 << 11)) & ~(1 << 10));
BaseStruct.EN = 1;
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
}
else
{
BaseStruct.EN = 1;
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
}
this->Write(APIC_TPR, 0x0);
// this->Write(APIC_SVR, this->Read(APIC_SVR) | 0x100); // 0x1FF or 0x100 ? on https://wiki.osdev.org/APIC is 0x100
if (!this->x2APICSupported)
{
this->Write(APIC_DFR, 0xF0000000);
this->Write(APIC_LDR, this->Read(APIC_ID));
}
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
for (size_t i = 0; i < madt->nmi.size(); i++)
{
if (madt->nmi[std::size_t(i)]->processor != 0xFF && Core != madt->nmi[std::size_t(i)]->processor)
return;
uint32_t nmi = 0x402;
if (madt->nmi[std::size_t(i)]->flags & 2)
nmi |= 1 << 13;
if (madt->nmi[std::size_t(i)]->flags & 8)
nmi |= 1 << 15;
if (madt->nmi[std::size_t(i)]->lint == 0)
this->Write(APIC_LINT0, nmi);
else if (madt->nmi[std::size_t(i)]->lint == 1)
this->Write(APIC_LINT1, nmi);
}
// Setup the spurrious interrupt vector
Spurious Spurious = {.raw = this->Read(APIC_SVR)};
Spurious.Vector = IRQ223; // TODO: Should I map the IRQ to something?
Spurious.Software = 1;
this->Write(APIC_SVR, s_cst(uint32_t, Spurious.raw));
static int once = 0;
if (!once++)
{
// Disable PIT
outb(0x43, 0x28);
outb(0x40, 0x0);
// Disable PIC
outb(0x21, 0xFF);
outb(0xA1, 0xFF);
}
}
APIC::~APIC() {}
void Timer::OnInterruptReceived(CPU::TrapFrame *Frame) { UNUSED(Frame); }
void Timer::OneShot(uint32_t Vector, uint64_t Miliseconds)
{
SmartCriticalSection(APICLock);
LVTTimer timer = {.raw = 0};
timer.Vector = s_cst(uint8_t, Vector);
timer.TimerMode = 0;
if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0)
this->lapic->Write(APIC_TDCR, DivideBy128);
else
this->lapic->Write(APIC_TDCR, DivideBy16);
this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks * Miliseconds));
this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw));
}
Timer::Timer(APIC *apic) : Interrupts::Handler(0) /* IRQ0 */
{
SmartCriticalSection(APICLock);
this->lapic = apic;
LVTTimerDivide Divider = DivideBy16;
trace("Initializing APIC timer on CPU %d", GetCurrentCPU()->ID);
this->lapic->Write(APIC_TDCR, Divider);
this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
TimeManager->Sleep(1, Time::Units::Milliseconds);
// Mask the timer
this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */);
Ticks = 0xFFFFFFFF - this->lapic->Read(APIC_TCCR);
// Config for IRQ0 timer
LVTTimer timer = {.raw = 0};
timer.Vector = IRQ0;
timer.Mask = Unmasked;
timer.TimerMode = LVTTimerMode::OneShot;
// Initialize APIC timer
this->lapic->Write(APIC_TDCR, Divider);
this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks));
this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw));
trace("%d APIC Timer %d ticks in.", GetCurrentCPU()->ID, Ticks);
KPrint("APIC Timer: \x1b[1;32m%ld\x1b[0m ticks.", Ticks);
}
Timer::~Timer()
{
}
}

View File

@ -0,0 +1,356 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_APIC_H__
#define __FENNIX_KERNEL_APIC_H__
#include <types.h>
#include <ints.hpp>
#include <cpu.hpp>
namespace APIC
{
enum APICRegisters
{
// source from: https://github.com/pdoane/osdev/blob/master/intr/local_apic.c
APIC_ID = 0x20, // Local APIC ID
APIC_VER = 0x30, // Local APIC Version
APIC_TPR = 0x80, // Task Priority
APIC_APR = 0x90, // Arbitration Priority
APIC_PPR = 0xA0, // Processor Priority
APIC_EOI = 0xB0, // EOI
APIC_RRD = 0xC0, // Remote Read
APIC_LDR = 0xD0, // Logical Destination
APIC_DFR = 0xE0, // Destination Format
APIC_SVR = 0xF0, // Spurious Interrupt Vector
APIC_ISR = 0x100, // In-Service (8 registers)
APIC_TMR = 0x180, // Trigger Mode (8 registers)
APIC_IRR = 0x200, // Interrupt Request (8 registers)
APIC_ESR = 0x280, // Error Status
APIC_ICRLO = 0x300, // Interrupt Command
APIC_ICRHI = 0x310, // Interrupt Command [63:32]
APIC_TIMER = 0x320, // LVT Timer
APIC_THERMAL = 0x330, // LVT Thermal Sensor
APIC_PERF = 0x340, // LVT Performance Counter
APIC_LINT0 = 0x350, // LVT LINT0
APIC_LINT1 = 0x360, // LVT LINT1
APIC_ERROR = 0x370, // LVT Error
APIC_TICR = 0x380, // Initial Count (for Timer)
APIC_TCCR = 0x390, // Current Count (for Timer)
APIC_TDCR = 0x3E0, // Divide Configuration (for Timer)
};
enum IOAPICRegisters
{
GetIOAPICVersion = 0x1
};
enum IOAPICFlags
{
ActiveHighLow = 2,
EdgeLevel = 8
};
enum APICDeliveryMode
{
Fixed = 0b000,
LowestPriority = 0b001, /* Reserved */
SMI = 0b010,
APIC_DELIVERY_MODE_RESERVED0 = 0b011, /* Reserved */
NMI = 0b100,
INIT = 0b101,
Startup = 0b110,
ExtINT = 0b111 /* Reserved */
};
enum APICDestinationMode
{
Physical = 0b0,
Logical = 0b1
};
enum APICDeliveryStatus
{
Idle = 0b0,
SendPending = 0b1
};
enum APICLevel
{
DeAssert = 0b0,
Assert = 0b1
};
enum APICTriggerMode
{
Edge = 0b0,
Level = 0b1
};
enum APICDestinationShorthand
{
NoShorthand = 0b00,
Self = 0b01,
AllIncludingSelf = 0b10,
AllExcludingSelf = 0b11
};
enum LVTTimerDivide
{
DivideBy2 = 0b000,
DivideBy4 = 0b001,
DivideBy8 = 0b010,
DivideBy16 = 0b011,
DivideBy32 = 0b100,
DivideBy64 = 0b101,
DivideBy128 = 0b110,
DivideBy1 = 0b111
};
enum LVTTimerMask
{
Unmasked = 0b0,
Masked = 0b1
};
enum LVTTimerMode
{
OneShot = 0b00,
Periodic = 0b01,
TSCDeadline = 0b10
};
typedef union
{
struct
{
/** @brief Interrupt Vector */
uint64_t Vector : 8;
/** @brief Reserved */
uint64_t Reserved0 : 4;
/**
* @brief Delivery Status
*
* 0: Idle
* 1: Send Pending
*/
uint64_t DeliveryStatus : 1;
/** @brief Reserved */
uint64_t Reserved1 : 3;
/**
* @brief Mask
*
* 0: Not masked
* 1: Masked
*/
uint64_t Mask : 1;
/** @brief Timer Mode
*
* 0: One-shot
* 1: Periodic
* 2: TSC-Deadline
*/
uint64_t TimerMode : 1;
/** @brief Reserved */
uint64_t Reserved2 : 14;
};
uint64_t raw;
} __packed LVTTimer;
typedef union
{
struct
{
/** @brief Spurious Vector */
uint64_t Vector : 8;
/** @brief Enable or disable APIC software */
uint64_t Software : 1;
/** @brief Focus Processor Checking */
uint64_t FocusProcessorChecking : 1;
/** @brief Reserved */
uint64_t Reserved : 2;
/** @brief Disable EOI Broadcast */
uint64_t DisableEOIBroadcast : 1;
/** @brief Reserved */
uint64_t Reserved1 : 19;
};
uint64_t raw;
} __packed Spurious;
typedef union
{
struct
{
/** @brief Interrupt Vector */
uint64_t Vector : 8;
/** @brief Delivery Mode */
uint64_t DeliveryMode : 3;
/** @brief Destination Mode
*
* 0: Physical
* 1: Logical
*/
uint64_t DestinationMode : 1;
/** @brief Delivery Status
*
* @note Reserved when in x2APIC mode
*/
uint64_t DeliveryStatus : 1;
/** @brief Reserved */
uint64_t Reserved0 : 1;
/** @brief Level
*
* 0: Deassert
* 1: Assert
*/
uint64_t Level : 1;
/** @brief Trigger Mode
*
* 0: Edge
* 1: Level
*/
uint64_t TriggerMode : 1;
/** @brief Reserved */
uint64_t Reserved1 : 2;
/** @brief Destination Shorthand
*
* 0: No shorthand
* 1: Self
* 2: All including self
* 3: All excluding self
*/
uint64_t DestinationShorthand : 2;
/** @brief Reserved */
uint64_t Reserved2 : 12;
};
uint64_t raw;
} __packed InterruptCommandRegisterLow;
typedef union
{
struct
{
/** @brief Reserved */
uint64_t Reserved0 : 24;
/** @brief Destination */
uint64_t Destination : 8;
};
uint64_t raw;
} __packed InterruptCommandRegisterHigh;
typedef union
{
struct
{
/** @brief Interrupt Vector */
uint64_t Vector : 8;
/** @brief Delivery Mode */
uint64_t DeliveryMode : 3;
/** @brief Destination Mode
*
* 0: Physical
* 1: Logical
*/
uint64_t DestinationMode : 1;
/** @brief Delivery Status */
uint64_t DeliveryStatus : 1;
/** @brief Interrupt Input Pin Polarity
*
* 0: Active High
* 1: Active Low
*/
uint64_t Polarity : 1;
/** @brief Remote IRR */
uint64_t RemoteIRR : 1;
/** @brief Trigger Mode
*
* 0: Edge
* 1: Level
*/
uint64_t TriggerMode : 1;
/** @brief Mask */
uint64_t Mask : 1;
/** @brief Reserved */
uint64_t Reserved0 : 15;
/** @brief Reserved */
uint64_t Reserved1 : 24;
/** @brief Destination */
uint64_t DestinationID : 8;
};
struct
{
uint64_t Low;
uint64_t High;
} split;
uint64_t raw;
} __packed IOAPICRedirectEntry;
typedef union
{
struct
{
uint64_t Version : 8;
uint64_t Reserved : 8;
uint64_t MaximumRedirectionEntry : 8;
uint64_t Reserved2 : 8;
};
uint64_t raw;
} __packed IOAPICVersion;
class APIC
{
private:
bool x2APICSupported = false;
uint64_t APICBaseAddress = 0;
public:
decltype(x2APICSupported) &x2APIC = x2APICSupported;
uint32_t Read(uint32_t Register);
void Write(uint32_t Register, uint32_t Value);
void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value);
uint32_t IORead(uint64_t Base, uint32_t Register);
void EOI();
void RedirectIRQs(int CPU = 0);
void WaitForIPI();
void IPI(uint8_t CPU, InterruptCommandRegisterLow icr);
void SendInitIPI(uint8_t CPU);
void SendStartupIPI(uint8_t CPU, uint64_t StartupAddress);
uint32_t IOGetMaxRedirect(uint32_t APICID);
void RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status);
void RedirectIRQ(int CPU, uint16_t IRQ, int Status);
APIC(int Core);
~APIC();
};
class Timer : public Interrupts::Handler
{
private:
APIC *lapic;
uint64_t Ticks = 0;
void OnInterruptReceived(CPU::TrapFrame *Frame);
public:
uint64_t GetTicks() { return Ticks; }
void OneShot(uint32_t Vector, uint64_t Miliseconds);
Timer(APIC *apic);
~Timer();
};
}
#endif // !__FENNIX_KERNEL_APIC_H__

View File

@ -0,0 +1,262 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "gdt.hpp"
#include <memory.hpp>
#include <smp.hpp>
#include <cpu.hpp>
#include <debug.h>
namespace GlobalDescriptorTable
{
static GlobalDescriptorTableEntries GDTEntriesTemplate = {
.Null =
{
.Limit0 = 0x0,
.BaseLow = 0x0,
.BaseMiddle = 0x0,
.Access = {.Raw = 0x0},
// .Limit1 = 0x0,
.Flags = {.Raw = 0x0},
.BaseHigh = 0x0,
},
.Code =
{
.Limit0 = 0xFFFF,
.BaseLow = 0x0,
.BaseMiddle = 0x0,
.Access = {
.A = 0,
.RW = 1,
.DC = 0,
.E = 1,
.S = 1,
.DPL = 0,
.P = 1,
},
// .Limit1 = 0xF,
.Flags = {
.Reserved = 0xF, /* Workaround for Limit1 */
.AVL = 0,
.L = 0,
.DB = 1,
.G = 1,
},
.BaseHigh = 0x0,
},
.Data = {
.Limit0 = 0xFFFF,
.BaseLow = 0x0,
.BaseMiddle = 0x0,
.Access = {
.A = 0,
.RW = 1,
.DC = 0,
.E = 0,
.S = 1,
.DPL = 0,
.P = 1,
},
// .Limit1 = 0xF,
.Flags = {
.Reserved = 0xF, /* Workaround for Limit1 */
.AVL = 0,
.L = 0,
.DB = 1,
.G = 1,
},
.BaseHigh = 0x0,
},
.UserData = {
.Limit0 = 0xFFFF,
.BaseLow = 0x0,
.BaseMiddle = 0x0,
.Access = {
.A = 0,
.RW = 1,
.DC = 0,
.E = 0,
.S = 1,
.DPL = 3,
.P = 1,
},
// .Limit1 = 0xF,
.Flags = {
.Reserved = 0xF, /* Workaround for Limit1 */
.AVL = 0,
.L = 0,
.DB = 1,
.G = 1,
},
.BaseHigh = 0x0,
},
.UserCode = {
.Limit0 = 0xFFFF,
.BaseLow = 0x0,
.BaseMiddle = 0x0,
.Access = {
.A = 0,
.RW = 1,
.DC = 0,
.E = 1,
.S = 1,
.DPL = 3,
.P = 1,
},
// .Limit1 = 0xF,
.Flags = {
.Reserved = 0xF, /* Workaround for Limit1 */
.AVL = 0,
.L = 0,
.DB = 1,
.G = 1,
},
.BaseHigh = 0x0,
},
.TaskStateSegment = {},
};
GlobalDescriptorTableEntries GDTEntries[MAX_CPU] __aligned(16);
GlobalDescriptorTableDescriptor gdt[MAX_CPU] __aligned(16);
TaskStateSegment tss[MAX_CPU] = {
0,
{0, 0, 0},
0,
{0, 0, 0, 0, 0, 0, 0},
0,
0,
0,
};
void *CPUStackPointer[MAX_CPU];
nsa void Init(int Core)
{
memcpy(&GDTEntries[Core], &GDTEntriesTemplate, sizeof(GlobalDescriptorTableEntries));
gdt[Core] = {.Length = sizeof(GlobalDescriptorTableEntries) - 1, .Entries = &GDTEntries[Core]};
debug("GDT: %#lx", &gdt[Core]);
debug("GDT KERNEL: CODE %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
GDT_KERNEL_CODE,
GDTEntries[Core].Code.Limit0,
GDTEntries[Core].Code.BaseLow,
GDTEntries[Core].Code.BaseMiddle,
GDTEntries[Core].Code.Access.Raw,
GDTEntries[Core].Code.Flags.Reserved,
GDTEntries[Core].Code.Flags.Raw & ~0xF,
GDTEntries[Core].Code.BaseHigh);
debug("GDT KERNEL: DATA %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
GDT_KERNEL_DATA,
GDTEntries[Core].Data.Limit0,
GDTEntries[Core].Data.BaseLow,
GDTEntries[Core].Data.BaseMiddle,
GDTEntries[Core].Data.Access.Raw,
GDTEntries[Core].Data.Flags.Reserved,
GDTEntries[Core].Data.Flags.Raw & ~0xF,
GDTEntries[Core].Data.BaseHigh);
debug("GDT USER: CODE %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
GDT_USER_CODE,
GDTEntries[Core].UserCode.Limit0,
GDTEntries[Core].UserCode.BaseLow,
GDTEntries[Core].UserCode.BaseMiddle,
GDTEntries[Core].UserCode.Access.Raw,
GDTEntries[Core].UserCode.Flags.Reserved,
GDTEntries[Core].UserCode.Flags.Raw & ~0xF,
GDTEntries[Core].UserCode.BaseHigh);
debug("GDT USER: DATA %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
GDT_USER_DATA,
GDTEntries[Core].UserData.Limit0,
GDTEntries[Core].UserData.BaseLow,
GDTEntries[Core].UserData.BaseMiddle,
GDTEntries[Core].UserData.Access.Raw,
GDTEntries[Core].UserData.Flags.Reserved,
GDTEntries[Core].UserData.Flags.Raw & ~0xF,
GDTEntries[Core].UserData.BaseHigh);
CPU::x32::lgdt(&gdt[Core]);
asmv("mov %%esp, %%eax\n"
"push $16\n"
"push %%eax\n"
"pushf\n"
"push $8\n"
"push $1f\n"
"iret\n"
"1:\n"
"movw $16, %%ax\n"
"movw %%ax, %%ds\n"
"movw %%ax, %%es\n" ::
: "memory", "eax");
CPUStackPointer[Core] = StackManager.Allocate(STACK_SIZE);
memset(CPUStackPointer[Core], 0, STACK_SIZE);
debug("CPU %d Stack Pointer: %#lx-%#lx (%d pages)", Core,
CPUStackPointer[Core], (uintptr_t)CPUStackPointer[Core] + STACK_SIZE,
TO_PAGES(STACK_SIZE + 1));
uintptr_t Base = (uintptr_t)&tss[Core];
size_t Limit = Base + sizeof(TaskStateSegment);
gdt[Core].Entries->TaskStateSegment.Limit = Limit & 0xFFFF;
gdt[Core].Entries->TaskStateSegment.BaseLow = Base & 0xFFFF;
gdt[Core].Entries->TaskStateSegment.BaseMiddle = uint8_t((Base >> 16) & 0xFF);
gdt[Core].Entries->TaskStateSegment.BaseHigh = uint8_t((Base >> 24) & 0xFF);
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
gdt[Core].Entries->TaskStateSegment.BaseUpper = s_cst(uint32_t, (Base >> 32) & 0xFFFFFFFF);
gdt[Core].Entries->TaskStateSegment.Access = {.A = 1, .RW = 0, .DC = 0, .E = 1, .S = 0, .DPL = 0, .P = 1};
gdt[Core].Entries->TaskStateSegment.Granularity = (0 << 4) | ((Limit >> 16) & 0xF);
tss[Core].IOMapBaseAddressOffset = sizeof(TaskStateSegment);
tss[Core].StackPointer[0] = (uint32_t)CPUStackPointer[Core] + STACK_SIZE;
tss[Core].StackPointer[1] = 0x0;
tss[Core].StackPointer[2] = 0x0;
for (size_t i = 0; i < sizeof(tss[Core].InterruptStackTable) / sizeof(tss[Core].InterruptStackTable[7]); i++)
{
void *NewStack = StackManager.Allocate(STACK_SIZE);
tss[Core].InterruptStackTable[i] = (uint32_t)NewStack + STACK_SIZE;
memset((void *)(tss[Core].InterruptStackTable[i] - STACK_SIZE), 0, STACK_SIZE);
debug("IST-%d: %#lx-%#lx", i, NewStack, (uintptr_t)NewStack + STACK_SIZE);
}
CPU::x32::ltr(GDT_TSS);
debug("Global Descriptor Table initialized");
}
nsa void SetKernelStack(void *Stack)
{
stub;
}
void *GetKernelStack() { return (void *)nullptr; }
}

View File

@ -0,0 +1,224 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_GDT_H__
#define __FENNIX_KERNEL_GDT_H__
#include <types.h>
namespace GlobalDescriptorTable
{
struct TaskStateSegmentEntry
{
/* LOW */
uint16_t Limit;
uint16_t BaseLow;
uint8_t BaseMiddle;
union GlobalDescriptorTableAccess
{
struct
{
/** @brief Access bit.
* @note The CPU sets this bit to 1 when the segment is accessed.
*/
uint8_t A : 1;
/** @brief Readable bit for code segments, writable bit for data segments.
* @details For code segments, this bit must be 1 for the segment to be readable.
* @details For data segments, this bit must be 1 for the segment to be writable.
*/
uint8_t RW : 1;
/** @brief Direction bit for data segments, conforming bit for code segments.
* @details For data segments, this bit must be 1 for the segment to grow up (higher addresses).
* @details For code segments, this bit must be 1 for code in the segment to be able to be executed from an equal or lower privilege level.
*/
uint8_t DC : 1;
/** @brief Executable bit.
* @details This bit must be 1 for code-segment descriptors.
* @details This bit must be 0 for data-segment and system descriptors.
*/
uint8_t E : 1;
/** @brief Descriptor type.
* @details This bit must be 0 for system descriptors.
* @details This bit must be 1 for code or data segment descriptor.
*/
uint8_t S : 1;
/** @brief Descriptor privilege level.
* @details This field determines the privilege level of the segment.
* @details 0 = kernel mode, 3 = user mode.
*/
uint8_t DPL : 2;
/** @brief Present bit.
* @details This bit must be 1 for all valid descriptors.
*/
uint8_t P : 1;
} __packed;
uint8_t Raw : 8;
} Access;
uint8_t Granularity;
uint8_t BaseHigh;
/* HIGH */
uint32_t BaseUpper;
uint32_t Reserved;
} __packed;
struct TaskStateSegment
{
uint32_t Reserved0 __aligned(16);
uint64_t StackPointer[3];
uint64_t Reserved1;
uint64_t InterruptStackTable[7];
uint64_t Reserved2;
uint16_t Reserved3;
uint16_t IOMapBaseAddressOffset;
} __packed;
struct GlobalDescriptorTableEntry
{
/** @brief Limit 0:15 */
uint16_t Limit0 : 16;
/** @brief Low Base 0:15 */
uint16_t BaseLow : 16;
/** @brief Middle Base 16:23 */
uint8_t BaseMiddle : 8;
/** @brief Access */
union GlobalDescriptorTableAccess
{
struct
{
/** @brief Access bit.
* @note The CPU sets this bit to 1 when the segment is accessed.
*/
uint8_t A : 1;
/** @brief Readable bit for code segments, writable bit for data segments.
* @details For code segments, this bit must be 1 for the segment to be readable.
* @details For data segments, this bit must be 1 for the segment to be writable.
*/
uint8_t RW : 1;
/** @brief Direction bit for data segments, conforming bit for code segments.
* @details For data segments, this bit must be 1 for the segment to grow up (higher addresses).
* @details For code segments, this bit must be 1 for code in the segment to be able to be executed from an equal or lower privilege level.
*/
uint8_t DC : 1;
/** @brief Executable bit.
* @details This bit must be 1 for code-segment descriptors.
* @details This bit must be 0 for data-segment and system descriptors.
*/
uint8_t E : 1;
/** @brief Descriptor type.
* @details This bit must be 0 for system descriptors.
* @details This bit must be 1 for code or data segment descriptor.
*/
uint8_t S : 1;
/** @brief Descriptor privilege level.
* @details This field determines the privilege level of the segment.
* @details 0 = kernel mode, 3 = user mode.
*/
uint8_t DPL : 2;
/** @brief Present bit.
* @details This bit must be 1 for all valid descriptors.
*/
uint8_t P : 1;
} __packed;
uint8_t Raw : 8;
} Access;
// /** @brief Limit 16:19 */
// uint16_t Limit1 : 4;
/** @brief Flags */
union GlobalDescriptorTableFlags
{
struct
{
uint8_t Reserved : 4; /* FIXME: Without this, the kernel crashes. */
/** @brief Available bit.
* @details This bit is available for use by system software.
*/
uint8_t AVL : 1;
/** @brief Long mode.
* @details If the long mode bit is clear, the segment is in 32-bit protected mode.
* @details If the long mode bit is set, the segment is in 64-bit long mode.
*/
uint8_t L : 1;
/** @brief Size flag.
* @details If the size bit is clear, the segment is in 16-bit protected mode.
* @details If the size bit is set, the segment is in 32-bit protected mode.
*/
uint8_t DB : 1;
/** @brief Granularity bit.
* @details If the granularity bit is clear, the segment limit is in 1 B blocks.
* @details If the granularity bit is set, the segment limit is in 4 KiB blocks.
*/
uint8_t G : 1;
} __packed;
uint8_t Raw : 8;
} Flags;
/** @brief High Base 24:31 */
uint8_t BaseHigh : 8;
} __packed;
struct GlobalDescriptorTableEntries
{
GlobalDescriptorTableEntry Null;
GlobalDescriptorTableEntry Code;
GlobalDescriptorTableEntry Data;
GlobalDescriptorTableEntry UserData;
GlobalDescriptorTableEntry UserCode;
TaskStateSegmentEntry TaskStateSegment;
} __packed;
struct GlobalDescriptorTableDescriptor
{
/** @brief GDT entries length */
uint16_t Length;
/** @brief GDT entries address */
GlobalDescriptorTableEntries *Entries;
} __packed;
extern void *CPUStackPointer[];
extern TaskStateSegment tss[];
void Init(int Core);
void SetKernelStack(void *Stack);
void *GetKernelStack();
}
#define GDT_KERNEL_CODE offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Code)
#define GDT_KERNEL_DATA offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Data)
#define GDT_USER_CODE (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserCode) | 3)
#define GDT_USER_DATA (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserData) | 3)
#define GDT_TSS (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, TaskStateSegment) | 3)
#endif // !__FENNIX_KERNEL_GDT_H__

View File

@ -0,0 +1,737 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "idt.hpp"
#include <memory.hpp>
#include <cpu.hpp>
#include <debug.h>
#include <io.h>
#include "gdt.hpp"
#include "../../../kernel.h"
/* conversion from 'uint64_t' {aka 'long unsigned int'} to 'unsigned char:2' may change value */
#pragma GCC diagnostic ignored "-Wconversion"
extern "C" void MainInterruptHandler(void *Data);
extern "C" void ExceptionHandler(void *Data);
namespace InterruptDescriptorTable
{
__aligned(8) static IDTGateDescriptor Entries[0x100];
__aligned(8) IDTRegister IDTr = {
.Limit = sizeof(Entries) - 1,
.BaseAddress = Entries,
};
void SetEntry(uint8_t Index,
void (*Base)(),
GateType Gate,
PrivilegeLevelType Ring,
bool Present,
uint16_t SegmentSelector)
{
switch (Gate)
{
case INTERRUPT_GATE_32BIT:
{
InterruptGate gate{
.TargetCodeSegmentOffsetLow = s_cst(uint16_t, ((uint32_t)Base & 0xFFFF)),
.TargetCodeSegmentSelector = SegmentSelector,
.Reserved0 = 0,
.Type = Gate,
.Zero = 0,
.DescriptorPrivilegeLevel = Ring,
.Present = Present,
.TargetCodeSegmentOffsetHigh = s_cst(uint16_t, ((uint32_t)Base >> 16)),
};
Entries[Index].Interrupt = gate;
break;
}
case TRAP_GATE_32BIT:
{
TrapGate gate{
.TargetCodeSegmentOffsetLow = s_cst(uint16_t, ((uint32_t)Base & 0xFFFF)),
.TargetCodeSegmentSelector = SegmentSelector,
.Reserved0 = 0,
.Type = Gate,
.Zero = 0,
.DescriptorPrivilegeLevel = Ring,
.Present = Present,
.TargetCodeSegmentOffsetHigh = s_cst(uint16_t, ((uint32_t)Base >> 16)),
};
Entries[Index].Trap = gate;
break;
}
case AVAILABLE_16BIT_TSS:
case LDT:
case BUSY_16BIT_TSS:
case CALL_GATE_16BIT:
case TASK_GATE:
case INTERRUPT_GATE_16BIT:
case TRAP_GATE_16BIT:
case AVAILABLE_32BIT_TSS:
case BUSY_32BIT_TSS:
case CALL_GATE_32BIT:
default:
{
assert(false);
break;
}
}
}
extern "C" __naked __used __no_stack_protector __aligned(16) void ExceptionHandlerStub()
{
asm("cld\n"
"cli\n"
"pusha\n"
"push %esp\n"
"call ExceptionHandler\n"
"pop %esp\n"
"popa\n"
"add $8, %esp\n"
"iret");
}
extern "C" __naked __used __no_stack_protector __aligned(16) void InterruptHandlerStub()
{
asm("cld\n"
"cli\n"
"pusha\n"
"push %esp\n"
"call MainInterruptHandler\n"
"pop %esp\n"
"popa\n"
"add $8, %esp\n"
"sti\n"
"iret");
}
#pragma region Exceptions
#define EXCEPTION_HANDLER(num) \
__naked __used __no_stack_protector __aligned(16) static void InterruptHandler_##num() \
{ \
asm("push $0\npush $" #num "\n" \
"jmp ExceptionHandlerStub"); \
}
#define EXCEPTION_ERROR_HANDLER(num) \
__naked __used __no_stack_protector __aligned(16) static void InterruptHandler_##num() \
{ \
asm("push $" #num "\n" \
"jmp ExceptionHandlerStub"); \
}
#define INTERRUPT_HANDLER(num) \
__naked __used __no_stack_protector __aligned(16) void InterruptHandler_##num() \
{ \
asm("push $0\npush $" #num "\n" \
"jmp InterruptHandlerStub\n"); \
}
/* ISR */
EXCEPTION_HANDLER(0x0);
EXCEPTION_HANDLER(0x1);
EXCEPTION_HANDLER(0x2);
EXCEPTION_HANDLER(0x3);
EXCEPTION_HANDLER(0x4);
EXCEPTION_HANDLER(0x5);
EXCEPTION_HANDLER(0x6);
EXCEPTION_HANDLER(0x7);
EXCEPTION_ERROR_HANDLER(0x8);
EXCEPTION_HANDLER(0x9);
EXCEPTION_ERROR_HANDLER(0xa);
EXCEPTION_ERROR_HANDLER(0xb);
EXCEPTION_ERROR_HANDLER(0xc);
EXCEPTION_ERROR_HANDLER(0xd);
EXCEPTION_ERROR_HANDLER(0xe);
EXCEPTION_HANDLER(0xf);
EXCEPTION_ERROR_HANDLER(0x10);
EXCEPTION_HANDLER(0x11);
EXCEPTION_HANDLER(0x12);
EXCEPTION_HANDLER(0x13);
EXCEPTION_HANDLER(0x14);
EXCEPTION_HANDLER(0x15);
EXCEPTION_HANDLER(0x16);
EXCEPTION_HANDLER(0x17);
EXCEPTION_HANDLER(0x18);
EXCEPTION_HANDLER(0x19);
EXCEPTION_HANDLER(0x1a);
EXCEPTION_HANDLER(0x1b);
EXCEPTION_HANDLER(0x1c);
EXCEPTION_HANDLER(0x1d);
EXCEPTION_HANDLER(0x1e);
EXCEPTION_HANDLER(0x1f);
/* IRQ */
INTERRUPT_HANDLER(0x20)
INTERRUPT_HANDLER(0x21)
INTERRUPT_HANDLER(0x22)
INTERRUPT_HANDLER(0x23)
INTERRUPT_HANDLER(0x24)
INTERRUPT_HANDLER(0x25)
INTERRUPT_HANDLER(0x26)
INTERRUPT_HANDLER(0x27)
INTERRUPT_HANDLER(0x28)
INTERRUPT_HANDLER(0x29)
INTERRUPT_HANDLER(0x2a)
INTERRUPT_HANDLER(0x2b)
INTERRUPT_HANDLER(0x2c)
INTERRUPT_HANDLER(0x2d)
INTERRUPT_HANDLER(0x2e)
INTERRUPT_HANDLER(0x2f)
/* Reserved by OS */
INTERRUPT_HANDLER(0x30)
INTERRUPT_HANDLER(0x31)
INTERRUPT_HANDLER(0x32)
INTERRUPT_HANDLER(0x33)
INTERRUPT_HANDLER(0x34)
INTERRUPT_HANDLER(0x35)
INTERRUPT_HANDLER(0x36)
INTERRUPT_HANDLER(0x37)
INTERRUPT_HANDLER(0x38)
INTERRUPT_HANDLER(0x39)
INTERRUPT_HANDLER(0x3a)
INTERRUPT_HANDLER(0x3b)
INTERRUPT_HANDLER(0x3c)
INTERRUPT_HANDLER(0x3d)
/* Free */
INTERRUPT_HANDLER(0x3e)
INTERRUPT_HANDLER(0x3f)
INTERRUPT_HANDLER(0x40)
INTERRUPT_HANDLER(0x41)
INTERRUPT_HANDLER(0x42)
INTERRUPT_HANDLER(0x43)
INTERRUPT_HANDLER(0x44)
INTERRUPT_HANDLER(0x45)
INTERRUPT_HANDLER(0x46)
INTERRUPT_HANDLER(0x47)
INTERRUPT_HANDLER(0x48)
INTERRUPT_HANDLER(0x49)
INTERRUPT_HANDLER(0x4a)
INTERRUPT_HANDLER(0x4b)
INTERRUPT_HANDLER(0x4c)
INTERRUPT_HANDLER(0x4d)
INTERRUPT_HANDLER(0x4e)
INTERRUPT_HANDLER(0x4f)
INTERRUPT_HANDLER(0x50)
INTERRUPT_HANDLER(0x51)
INTERRUPT_HANDLER(0x52)
INTERRUPT_HANDLER(0x53)
INTERRUPT_HANDLER(0x54)
INTERRUPT_HANDLER(0x55)
INTERRUPT_HANDLER(0x56)
INTERRUPT_HANDLER(0x57)
INTERRUPT_HANDLER(0x58)
INTERRUPT_HANDLER(0x59)
INTERRUPT_HANDLER(0x5a)
INTERRUPT_HANDLER(0x5b)
INTERRUPT_HANDLER(0x5c)
INTERRUPT_HANDLER(0x5d)
INTERRUPT_HANDLER(0x5e)
INTERRUPT_HANDLER(0x5f)
INTERRUPT_HANDLER(0x60)
INTERRUPT_HANDLER(0x61)
INTERRUPT_HANDLER(0x62)
INTERRUPT_HANDLER(0x63)
INTERRUPT_HANDLER(0x64)
INTERRUPT_HANDLER(0x65)
INTERRUPT_HANDLER(0x66)
INTERRUPT_HANDLER(0x67)
INTERRUPT_HANDLER(0x68)
INTERRUPT_HANDLER(0x69)
INTERRUPT_HANDLER(0x6a)
INTERRUPT_HANDLER(0x6b)
INTERRUPT_HANDLER(0x6c)
INTERRUPT_HANDLER(0x6d)
INTERRUPT_HANDLER(0x6e)
INTERRUPT_HANDLER(0x6f)
INTERRUPT_HANDLER(0x70)
INTERRUPT_HANDLER(0x71)
INTERRUPT_HANDLER(0x72)
INTERRUPT_HANDLER(0x73)
INTERRUPT_HANDLER(0x74)
INTERRUPT_HANDLER(0x75)
INTERRUPT_HANDLER(0x76)
INTERRUPT_HANDLER(0x77)
INTERRUPT_HANDLER(0x78)
INTERRUPT_HANDLER(0x79)
INTERRUPT_HANDLER(0x7a)
INTERRUPT_HANDLER(0x7b)
INTERRUPT_HANDLER(0x7c)
INTERRUPT_HANDLER(0x7d)
INTERRUPT_HANDLER(0x7e)
INTERRUPT_HANDLER(0x7f)
INTERRUPT_HANDLER(0x80)
INTERRUPT_HANDLER(0x81)
INTERRUPT_HANDLER(0x82)
INTERRUPT_HANDLER(0x83)
INTERRUPT_HANDLER(0x84)
INTERRUPT_HANDLER(0x85)
INTERRUPT_HANDLER(0x86)
INTERRUPT_HANDLER(0x87)
INTERRUPT_HANDLER(0x88)
INTERRUPT_HANDLER(0x89)
INTERRUPT_HANDLER(0x8a)
INTERRUPT_HANDLER(0x8b)
INTERRUPT_HANDLER(0x8c)
INTERRUPT_HANDLER(0x8d)
INTERRUPT_HANDLER(0x8e)
INTERRUPT_HANDLER(0x8f)
INTERRUPT_HANDLER(0x90)
INTERRUPT_HANDLER(0x91)
INTERRUPT_HANDLER(0x92)
INTERRUPT_HANDLER(0x93)
INTERRUPT_HANDLER(0x94)
INTERRUPT_HANDLER(0x95)
INTERRUPT_HANDLER(0x96)
INTERRUPT_HANDLER(0x97)
INTERRUPT_HANDLER(0x98)
INTERRUPT_HANDLER(0x99)
INTERRUPT_HANDLER(0x9a)
INTERRUPT_HANDLER(0x9b)
INTERRUPT_HANDLER(0x9c)
INTERRUPT_HANDLER(0x9d)
INTERRUPT_HANDLER(0x9e)
INTERRUPT_HANDLER(0x9f)
INTERRUPT_HANDLER(0xa0)
INTERRUPT_HANDLER(0xa1)
INTERRUPT_HANDLER(0xa2)
INTERRUPT_HANDLER(0xa3)
INTERRUPT_HANDLER(0xa4)
INTERRUPT_HANDLER(0xa5)
INTERRUPT_HANDLER(0xa6)
INTERRUPT_HANDLER(0xa7)
INTERRUPT_HANDLER(0xa8)
INTERRUPT_HANDLER(0xa9)
INTERRUPT_HANDLER(0xaa)
INTERRUPT_HANDLER(0xab)
INTERRUPT_HANDLER(0xac)
INTERRUPT_HANDLER(0xad)
INTERRUPT_HANDLER(0xae)
INTERRUPT_HANDLER(0xaf)
INTERRUPT_HANDLER(0xb0)
INTERRUPT_HANDLER(0xb1)
INTERRUPT_HANDLER(0xb2)
INTERRUPT_HANDLER(0xb3)
INTERRUPT_HANDLER(0xb4)
INTERRUPT_HANDLER(0xb5)
INTERRUPT_HANDLER(0xb6)
INTERRUPT_HANDLER(0xb7)
INTERRUPT_HANDLER(0xb8)
INTERRUPT_HANDLER(0xb9)
INTERRUPT_HANDLER(0xba)
INTERRUPT_HANDLER(0xbb)
INTERRUPT_HANDLER(0xbc)
INTERRUPT_HANDLER(0xbd)
INTERRUPT_HANDLER(0xbe)
INTERRUPT_HANDLER(0xbf)
INTERRUPT_HANDLER(0xc0)
INTERRUPT_HANDLER(0xc1)
INTERRUPT_HANDLER(0xc2)
INTERRUPT_HANDLER(0xc3)
INTERRUPT_HANDLER(0xc4)
INTERRUPT_HANDLER(0xc5)
INTERRUPT_HANDLER(0xc6)
INTERRUPT_HANDLER(0xc7)
INTERRUPT_HANDLER(0xc8)
INTERRUPT_HANDLER(0xc9)
INTERRUPT_HANDLER(0xca)
INTERRUPT_HANDLER(0xcb)
INTERRUPT_HANDLER(0xcc)
INTERRUPT_HANDLER(0xcd)
INTERRUPT_HANDLER(0xce)
INTERRUPT_HANDLER(0xcf)
INTERRUPT_HANDLER(0xd0)
INTERRUPT_HANDLER(0xd1)
INTERRUPT_HANDLER(0xd2)
INTERRUPT_HANDLER(0xd3)
INTERRUPT_HANDLER(0xd4)
INTERRUPT_HANDLER(0xd5)
INTERRUPT_HANDLER(0xd6)
INTERRUPT_HANDLER(0xd7)
INTERRUPT_HANDLER(0xd8)
INTERRUPT_HANDLER(0xd9)
INTERRUPT_HANDLER(0xda)
INTERRUPT_HANDLER(0xdb)
INTERRUPT_HANDLER(0xdc)
INTERRUPT_HANDLER(0xdd)
INTERRUPT_HANDLER(0xde)
INTERRUPT_HANDLER(0xdf)
INTERRUPT_HANDLER(0xe0)
INTERRUPT_HANDLER(0xe1)
INTERRUPT_HANDLER(0xe2)
INTERRUPT_HANDLER(0xe3)
INTERRUPT_HANDLER(0xe4)
INTERRUPT_HANDLER(0xe5)
INTERRUPT_HANDLER(0xe6)
INTERRUPT_HANDLER(0xe7)
INTERRUPT_HANDLER(0xe8)
INTERRUPT_HANDLER(0xe9)
INTERRUPT_HANDLER(0xea)
INTERRUPT_HANDLER(0xeb)
INTERRUPT_HANDLER(0xec)
INTERRUPT_HANDLER(0xed)
INTERRUPT_HANDLER(0xee)
INTERRUPT_HANDLER(0xef)
INTERRUPT_HANDLER(0xf0)
INTERRUPT_HANDLER(0xf1)
INTERRUPT_HANDLER(0xf2)
INTERRUPT_HANDLER(0xf3)
INTERRUPT_HANDLER(0xf4)
INTERRUPT_HANDLER(0xf5)
INTERRUPT_HANDLER(0xf6)
INTERRUPT_HANDLER(0xf7)
INTERRUPT_HANDLER(0xf8)
INTERRUPT_HANDLER(0xf9)
INTERRUPT_HANDLER(0xfa)
INTERRUPT_HANDLER(0xfb)
INTERRUPT_HANDLER(0xfc)
INTERRUPT_HANDLER(0xfd)
INTERRUPT_HANDLER(0xfe)
INTERRUPT_HANDLER(0xff)
#pragma endregion Exceptions
void Init(int Core)
{
if (Core == 0) /* Remap PIC using BSP */
{
// PIC
outb(0x20, 0x10 | 0x1);
outb(0x80, 0);
outb(0xA0, 0x10 | 0x10);
outb(0x80, 0);
outb(0x21, 0x20);
outb(0x80, 0);
outb(0xA1, 0x28);
outb(0x80, 0);
outb(0x21, 0x04);
outb(0x80, 0);
outb(0xA1, 0x02);
outb(0x80, 0);
outb(0x21, 1);
outb(0x80, 0);
outb(0xA1, 1);
outb(0x80, 0);
// Masking and disabling PIC
outb(0x21, 0xff);
outb(0x80, 0);
outb(0xA1, 0xff);
}
/* ISR */
bool EnableISRs = true;
// #ifdef DEBUG
EnableISRs = !DebuggerIsAttached;
if (!EnableISRs)
KPrint("\x1b[34mThe debugger is attached, disabling all ISRs.");
// #endif
SetEntry(0x0, InterruptHandler_0x0, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1, InterruptHandler_0x1, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x2, InterruptHandler_0x2, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x3, InterruptHandler_0x3, TRAP_GATE_32BIT, RING3, (!DebuggerIsAttached), GDT_KERNEL_CODE); /* Do not handle breakpoints if we are debugging the kernel. */
SetEntry(0x4, InterruptHandler_0x4, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x5, InterruptHandler_0x5, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x6, InterruptHandler_0x6, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x7, InterruptHandler_0x7, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x8, InterruptHandler_0x8, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x9, InterruptHandler_0x9, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xa, InterruptHandler_0xa, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xb, InterruptHandler_0xb, TRAP_GATE_32BIT, RING0, (!DebuggerIsAttached), GDT_KERNEL_CODE);
SetEntry(0xc, InterruptHandler_0xc, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xd, InterruptHandler_0xd, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xe, InterruptHandler_0xe, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0xf, InterruptHandler_0xf, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x10, InterruptHandler_0x10, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x11, InterruptHandler_0x11, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x12, InterruptHandler_0x12, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x13, InterruptHandler_0x13, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x14, InterruptHandler_0x14, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x15, InterruptHandler_0x15, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x16, InterruptHandler_0x16, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x17, InterruptHandler_0x17, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x18, InterruptHandler_0x18, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x19, InterruptHandler_0x19, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1a, InterruptHandler_0x1a, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1b, InterruptHandler_0x1b, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1c, InterruptHandler_0x1c, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1d, InterruptHandler_0x1d, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1e, InterruptHandler_0x1e, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
SetEntry(0x1f, InterruptHandler_0x1f, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
/* IRQ */
SetEntry(0x20, InterruptHandler_0x20, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x21, InterruptHandler_0x21, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x22, InterruptHandler_0x22, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x23, InterruptHandler_0x23, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x24, InterruptHandler_0x24, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x25, InterruptHandler_0x25, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x26, InterruptHandler_0x26, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x27, InterruptHandler_0x27, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x28, InterruptHandler_0x28, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x29, InterruptHandler_0x29, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2a, InterruptHandler_0x2a, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2b, InterruptHandler_0x2b, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2c, InterruptHandler_0x2c, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2d, InterruptHandler_0x2d, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2e, InterruptHandler_0x2e, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x2f, InterruptHandler_0x2f, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
/* Reserved by OS */
SetEntry(0x30, InterruptHandler_0x30, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x31, InterruptHandler_0x31, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x32, InterruptHandler_0x32, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x33, InterruptHandler_0x33, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x34, InterruptHandler_0x34, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x35, InterruptHandler_0x35, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x36, InterruptHandler_0x36, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x37, InterruptHandler_0x37, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x38, InterruptHandler_0x38, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x39, InterruptHandler_0x39, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3a, InterruptHandler_0x3a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3b, InterruptHandler_0x3b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3c, InterruptHandler_0x3c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3d, InterruptHandler_0x3d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
/* Free */
SetEntry(0x3e, InterruptHandler_0x3e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x3f, InterruptHandler_0x3f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x40, InterruptHandler_0x40, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x41, InterruptHandler_0x41, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x42, InterruptHandler_0x42, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x43, InterruptHandler_0x43, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x44, InterruptHandler_0x44, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x45, InterruptHandler_0x45, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x46, InterruptHandler_0x46, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x47, InterruptHandler_0x47, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x48, InterruptHandler_0x48, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x49, InterruptHandler_0x49, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4a, InterruptHandler_0x4a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4b, InterruptHandler_0x4b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4c, InterruptHandler_0x4c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4d, InterruptHandler_0x4d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4e, InterruptHandler_0x4e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x4f, InterruptHandler_0x4f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x50, InterruptHandler_0x50, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x51, InterruptHandler_0x51, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x52, InterruptHandler_0x52, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x53, InterruptHandler_0x53, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x54, InterruptHandler_0x54, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x55, InterruptHandler_0x55, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x56, InterruptHandler_0x56, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x57, InterruptHandler_0x57, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x58, InterruptHandler_0x58, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x59, InterruptHandler_0x59, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5a, InterruptHandler_0x5a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5b, InterruptHandler_0x5b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5c, InterruptHandler_0x5c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5d, InterruptHandler_0x5d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5e, InterruptHandler_0x5e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x5f, InterruptHandler_0x5f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x60, InterruptHandler_0x60, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x61, InterruptHandler_0x61, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x62, InterruptHandler_0x62, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x63, InterruptHandler_0x63, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x64, InterruptHandler_0x64, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x65, InterruptHandler_0x65, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x66, InterruptHandler_0x66, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x67, InterruptHandler_0x67, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x68, InterruptHandler_0x68, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x69, InterruptHandler_0x69, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6a, InterruptHandler_0x6a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6b, InterruptHandler_0x6b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6c, InterruptHandler_0x6c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6d, InterruptHandler_0x6d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6e, InterruptHandler_0x6e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x6f, InterruptHandler_0x6f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x70, InterruptHandler_0x70, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x71, InterruptHandler_0x71, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x72, InterruptHandler_0x72, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x73, InterruptHandler_0x73, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x74, InterruptHandler_0x74, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x75, InterruptHandler_0x75, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x76, InterruptHandler_0x76, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x77, InterruptHandler_0x77, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x78, InterruptHandler_0x78, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x79, InterruptHandler_0x79, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7a, InterruptHandler_0x7a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7b, InterruptHandler_0x7b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7c, InterruptHandler_0x7c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7d, InterruptHandler_0x7d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7e, InterruptHandler_0x7e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x7f, InterruptHandler_0x7f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x80, InterruptHandler_0x80, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x81, InterruptHandler_0x81, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x82, InterruptHandler_0x82, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x83, InterruptHandler_0x83, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x84, InterruptHandler_0x84, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x85, InterruptHandler_0x85, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x86, InterruptHandler_0x86, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x87, InterruptHandler_0x87, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x88, InterruptHandler_0x88, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x89, InterruptHandler_0x89, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8a, InterruptHandler_0x8a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8b, InterruptHandler_0x8b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8c, InterruptHandler_0x8c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8d, InterruptHandler_0x8d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8e, InterruptHandler_0x8e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x8f, InterruptHandler_0x8f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x90, InterruptHandler_0x90, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x91, InterruptHandler_0x91, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x92, InterruptHandler_0x92, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x93, InterruptHandler_0x93, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x94, InterruptHandler_0x94, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x95, InterruptHandler_0x95, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x96, InterruptHandler_0x96, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x97, InterruptHandler_0x97, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x98, InterruptHandler_0x98, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x99, InterruptHandler_0x99, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9a, InterruptHandler_0x9a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9b, InterruptHandler_0x9b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9c, InterruptHandler_0x9c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9d, InterruptHandler_0x9d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9e, InterruptHandler_0x9e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0x9f, InterruptHandler_0x9f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa0, InterruptHandler_0xa0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa1, InterruptHandler_0xa1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa2, InterruptHandler_0xa2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa3, InterruptHandler_0xa3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa4, InterruptHandler_0xa4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa5, InterruptHandler_0xa5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa6, InterruptHandler_0xa6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa7, InterruptHandler_0xa7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa8, InterruptHandler_0xa8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xa9, InterruptHandler_0xa9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xaa, InterruptHandler_0xaa, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xab, InterruptHandler_0xab, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xac, InterruptHandler_0xac, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xad, InterruptHandler_0xad, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xae, InterruptHandler_0xae, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xaf, InterruptHandler_0xaf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb0, InterruptHandler_0xb0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb1, InterruptHandler_0xb1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb2, InterruptHandler_0xb2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb3, InterruptHandler_0xb3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb4, InterruptHandler_0xb4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb5, InterruptHandler_0xb5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb6, InterruptHandler_0xb6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb7, InterruptHandler_0xb7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb8, InterruptHandler_0xb8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xb9, InterruptHandler_0xb9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xba, InterruptHandler_0xba, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbb, InterruptHandler_0xbb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbc, InterruptHandler_0xbc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbd, InterruptHandler_0xbd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbe, InterruptHandler_0xbe, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xbf, InterruptHandler_0xbf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc0, InterruptHandler_0xc0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc1, InterruptHandler_0xc1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc2, InterruptHandler_0xc2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc3, InterruptHandler_0xc3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc4, InterruptHandler_0xc4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc5, InterruptHandler_0xc5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc6, InterruptHandler_0xc6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc7, InterruptHandler_0xc7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc8, InterruptHandler_0xc8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xc9, InterruptHandler_0xc9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xca, InterruptHandler_0xca, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcb, InterruptHandler_0xcb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcc, InterruptHandler_0xcc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcd, InterruptHandler_0xcd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xce, InterruptHandler_0xce, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xcf, InterruptHandler_0xcf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd0, InterruptHandler_0xd0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd1, InterruptHandler_0xd1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd2, InterruptHandler_0xd2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd3, InterruptHandler_0xd3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd4, InterruptHandler_0xd4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd5, InterruptHandler_0xd5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd6, InterruptHandler_0xd6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd7, InterruptHandler_0xd7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd8, InterruptHandler_0xd8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xd9, InterruptHandler_0xd9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xda, InterruptHandler_0xda, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdb, InterruptHandler_0xdb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdc, InterruptHandler_0xdc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdd, InterruptHandler_0xdd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xde, InterruptHandler_0xde, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xdf, InterruptHandler_0xdf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe0, InterruptHandler_0xe0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe1, InterruptHandler_0xe1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe2, InterruptHandler_0xe2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe3, InterruptHandler_0xe3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe4, InterruptHandler_0xe4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe5, InterruptHandler_0xe5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe6, InterruptHandler_0xe6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe7, InterruptHandler_0xe7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe8, InterruptHandler_0xe8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xe9, InterruptHandler_0xe9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xea, InterruptHandler_0xea, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xeb, InterruptHandler_0xeb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xec, InterruptHandler_0xec, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xed, InterruptHandler_0xed, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xee, InterruptHandler_0xee, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xef, InterruptHandler_0xef, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf0, InterruptHandler_0xf0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf1, InterruptHandler_0xf1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf2, InterruptHandler_0xf2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf3, InterruptHandler_0xf3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf4, InterruptHandler_0xf4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf5, InterruptHandler_0xf5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf6, InterruptHandler_0xf6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf7, InterruptHandler_0xf7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf8, InterruptHandler_0xf8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xf9, InterruptHandler_0xf9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfa, InterruptHandler_0xfa, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfb, InterruptHandler_0xfb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfc, InterruptHandler_0xfc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfd, InterruptHandler_0xfd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xfe, InterruptHandler_0xfe, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
SetEntry(0xff, InterruptHandler_0xff, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
CPU::x32::lidt(&IDTr);
}
}

View File

@ -0,0 +1,144 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_IDT_H__
#define __FENNIX_KERNEL_IDT_H__
#include <types.h>
namespace InterruptDescriptorTable
{
/**
* Manual: AMD Architecture Programmer's Manual Volume 2: System Programming
* Subsection: 4.7.4 System Descriptors
* Table: 4-5
*
* @note Reserved values are not listed in the table.
*/
enum GateType
{
AVAILABLE_16BIT_TSS = 0b0001,
LDT = 0b0010,
BUSY_16BIT_TSS = 0b0011,
CALL_GATE_16BIT = 0b0100,
TASK_GATE = 0b0101,
INTERRUPT_GATE_16BIT = 0b0110,
TRAP_GATE_16BIT = 0b0111,
AVAILABLE_32BIT_TSS = 0b1001,
BUSY_32BIT_TSS = 0b1011,
CALL_GATE_32BIT = 0b1100,
INTERRUPT_GATE_32BIT = 0b1110,
TRAP_GATE_32BIT = 0b1111,
};
enum PrivilegeLevelType
{
RING0 = 0b0,
RING1 = 0b1,
RING2 = 0b10,
RING3 = 0b11,
};
struct LDTDescriptor
{
/* +0 */
uint32_t SegmentLimitLow : 16;
uint32_t BaseAddressLow : 16;
/* +4 */
uint32_t BaseAddressMiddle : 8;
uint32_t Type : 4;
uint32_t Zero : 1;
uint32_t DescriptorPrivilegeLevel : 2;
uint32_t Present : 1;
uint32_t SegmentLimitHigh : 4;
uint32_t Available : 1;
uint32_t Zero1 : 2;
uint32_t Granularity : 1;
uint32_t BaseAddressHigh : 8;
} __packed;
typedef LDTDescriptor TSSDescriptor;
struct CallGate
{
/* +0 */
uint32_t TargetCodeSegmentOffsetLow : 16;
uint32_t TargetCodeSegmentSelector : 16;
/* +4 */
uint32_t ParameterCount : 4;
uint32_t Reserved0 : 3;
uint32_t Type : 4;
uint32_t Zero : 1;
uint32_t DescriptorPrivilegeLevel : 2;
uint32_t Present : 1;
uint32_t TargetCodeSegmentOffsetHigh : 16;
} __packed;
struct InterruptGate
{
/* +0 */
uint32_t TargetCodeSegmentOffsetLow : 16;
uint32_t TargetCodeSegmentSelector : 16;
/* +4 */
uint32_t Reserved0 : 8;
uint32_t Type : 4;
uint32_t Zero : 1;
uint32_t DescriptorPrivilegeLevel : 2;
uint32_t Present : 1;
uint32_t TargetCodeSegmentOffsetHigh : 16;
} __packed;
typedef InterruptGate TrapGate;
struct TaskGate
{
/* +0 */
uint32_t Reserved0 : 16;
uint32_t TSSSelector : 16;
/* +4 */
uint32_t Reserved1 : 8;
uint32_t Type : 4;
uint32_t Zero : 1;
uint32_t DescriptorPrivilegeLevel : 2;
uint32_t Present : 1;
uint32_t Reserved2 : 16;
} __packed;
union IDTGateDescriptor
{
InterruptGate Interrupt;
TrapGate Trap;
CallGate Call;
};
struct IDTRegister
{
uint16_t Limit;
IDTGateDescriptor *BaseAddress;
} __packed;
void SetEntry(uint8_t Index,
void (*Base)(),
GateType Gate,
PrivilegeLevelType Ring,
bool Present,
uint16_t SegmentSelector);
void Init(int Core);
}
#endif // !__FENNIX_KERNEL_IDT_H__

View File

@ -0,0 +1,93 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <smp.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <ints.hpp>
#include <assert.h>
#include <cpu.hpp>
#include <atomic>
#include "../../../kernel.h"
#include "apic.hpp"
enum SMPTrampolineAddress
{
PAGE_TABLE = 0x500,
START_ADDR = 0x520,
STACK = 0x570,
GDT = 0x580,
IDT = 0x590,
CORE = 0x600,
TRAMPOLINE_START = 0x2000
};
std::atomic_bool CPUEnabled = false;
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
static __aligned(PAGE_SIZE) CPUData CPUs[MAX_CPU] = {0};
nsa CPUData *GetCPU(long id) { return &CPUs[id]; }
nsa CPUData *GetCurrentCPU()
{
if (unlikely(!Interrupts::apic[0]))
return &CPUs[0]; /* No APIC means we are on the BSP. */
APIC::APIC *apic = (APIC::APIC *)Interrupts::apic[0];
int CoreID = 0;
if (CPUEnabled.load(std::memory_order_acquire) == true)
{
if (apic->x2APIC)
CoreID = int(CPU::x32::rdmsr(CPU::x32::MSR_X2APIC_APICID));
else
CoreID = apic->Read(APIC::APIC_ID) >> 24;
}
if (unlikely((&CPUs[CoreID])->IsActive != true))
{
error("CPU %d is not active!", CoreID);
assert((&CPUs[0])->IsActive == true); /* We can't continue without the BSP. */
return &CPUs[0];
}
assert((&CPUs[CoreID])->Checksum == CPU_DATA_CHECKSUM); /* This should never happen. */
return &CPUs[CoreID];
}
namespace SMP
{
int CPUCores = 0;
void Initialize(void *_madt)
{
ACPI::MADT *madt = (ACPI::MADT *)_madt;
int Cores = madt->CPUCores + 1;
if (Config.Cores > madt->CPUCores + 1)
KPrint("More cores requested than available. Using %d cores", madt->CPUCores + 1);
else if (Config.Cores != 0)
Cores = Config.Cores;
CPUCores = Cores;
fixme("SMP::Initialize() is not implemented!");
}
}

View File

View File

@ -0,0 +1,140 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pic.hpp"
#include <io.h>
namespace PIC
{
PIC::PIC(uint8_t MasterCommandPort, uint8_t MasterDataPort, uint8_t SlaveCommandPort, uint8_t SlaveDataPort, uint8_t MasterOffset, uint8_t SlaveOffset)
{
this->MasterCommandPort = MasterCommandPort;
this->MasterDataPort = MasterDataPort;
this->SlaveCommandPort = SlaveCommandPort;
this->SlaveDataPort = SlaveDataPort;
this->MasterOffset = MasterOffset;
this->SlaveOffset = SlaveOffset;
MasterMask = 0xFF;
SlaveMask = 0xFF;
// ICW1
outb(MasterCommandPort, 0x11);
outb(SlaveCommandPort, 0x11);
// ICW2
outb(MasterDataPort, MasterOffset);
outb(SlaveDataPort, SlaveOffset);
// ICW3
outb(MasterDataPort, 0x04);
outb(SlaveDataPort, 0x02);
// ICW4
outb(MasterDataPort, 0x01);
outb(SlaveDataPort, 0x01);
// OCW1
outb(MasterDataPort, MasterMask);
outb(SlaveDataPort, SlaveMask);
}
PIC::~PIC()
{
outb(MasterDataPort, 0xFF);
outb(SlaveDataPort, 0xFF);
}
void PIC::Mask(uint8_t IRQ)
{
uint16_t Port;
uint8_t Value;
if (IRQ < 8)
{
Port = MasterDataPort;
Value = (uint8_t)(MasterMask & ~(1 << IRQ));
MasterMask = Value;
}
else
{
Port = SlaveDataPort;
Value = (uint8_t)(SlaveMask & ~(1 << (IRQ - 8)));
SlaveMask = Value;
}
outb(Port, Value);
}
void PIC::Unmask(uint8_t IRQ)
{
uint16_t Port;
uint8_t Value;
if (IRQ < 8)
{
Port = MasterDataPort;
Value = MasterMask | (1 << IRQ);
MasterMask = Value;
}
else
{
Port = SlaveDataPort;
Value = SlaveMask | (1 << (IRQ - 8));
SlaveMask = Value;
}
outb(Port, Value);
}
void PIC::SendEOI(uint8_t IRQ)
{
if (IRQ >= 8)
outb(SlaveCommandPort, 0x20);
outb(MasterCommandPort, 0x20);
}
PIT::PIT(uint16_t Port, uint16_t Frequency)
{
this->Port = Port;
this->Frequency = Frequency;
}
PIT::~PIT()
{
}
void PIT::PrepareSleep(uint32_t Milliseconds)
{
uint16_t Divisor = (uint16_t)(1193182 / Frequency);
uint8_t Low = (uint8_t)(Divisor & 0xFF);
uint8_t High = (uint8_t)((Divisor >> 8) & 0xFF);
outb(Port + 3, 0x36);
outb(Port + 0, Low);
outb(Port + 1, High);
}
void PIT::PerformSleep()
{
uint8_t Value = inb(Port + 0);
while (Value != 0)
Value = inb(Port + 0);
}
}

View File

@ -0,0 +1,59 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_8259PIC_H__
#define __FENNIX_KERNEL_8259PIC_H__
#include <types.h>
namespace PIC
{
class PIC
{
private:
uint8_t MasterCommandPort;
uint8_t MasterDataPort;
uint8_t SlaveCommandPort;
uint8_t SlaveDataPort;
uint8_t MasterOffset;
uint8_t SlaveOffset;
uint8_t MasterMask;
uint8_t SlaveMask;
public:
PIC(uint8_t MasterCommandPort, uint8_t MasterDataPort, uint8_t SlaveCommandPort, uint8_t SlaveDataPort, uint8_t MasterOffset, uint8_t SlaveOffset);
~PIC();
void Mask(uint8_t IRQ);
void Unmask(uint8_t IRQ);
void SendEOI(uint8_t IRQ);
};
class PIT
{
private:
uint16_t Port;
uint16_t Frequency;
public:
PIT(uint16_t Port, uint16_t Frequency);
~PIT();
void PrepareSleep(uint32_t Milliseconds);
void PerformSleep();
};
}
#endif // !__FENNIX_KERNEL_8259PIC_H__

129
Kernel/arch/i386/linker.ld Normal file
View File

@ -0,0 +1,129 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386)
ENTRY(_start)
PF_R = 0x4;
PF_W = 0x2;
PF_X = 0x1;
PHDRS
{
bootstrap PT_LOAD FLAGS( PF_R | PF_W /*| PF_X*/ );
text PT_LOAD FLAGS( PF_R | PF_X );
data PT_LOAD FLAGS( PF_R | PF_W );
rodata PT_LOAD FLAGS( PF_R );
bss PT_LOAD FLAGS( PF_R | PF_W );
}
KERNEL_VMA = 0xC0000000;
SECTIONS
{
. = 0x100000;
_bootstrap_start = .;
.bootstrap ALIGN(CONSTANT(MAXPAGESIZE)) :
{
*(.multiboot)
*(.multiboot2)
*(.bootstrap .bootstrap.*)
} :bootstrap
_bootstrap_end = ALIGN(CONSTANT(MAXPAGESIZE));
. += KERNEL_VMA;
_kernel_start = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_text_start = ALIGN(CONSTANT(MAXPAGESIZE));
.text ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.text) - KERNEL_VMA)
{
*(.text .text.*)
} :text
_kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_data_start = ALIGN(CONSTANT(MAXPAGESIZE));
.data ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.data) - KERNEL_VMA)
{
*(.data .data.*)
} :data
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RW
{
KEEP (*(.eh_frame .eh_frame.*))
} :data
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RW
{
KEEP (*(.gcc_except_table .gcc_except_table.*))
} :data
_kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_rodata_start = ALIGN(CONSTANT(MAXPAGESIZE));
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.rodata) - KERNEL_VMA)
{
*(.rodata .rodata.*)
} :rodata
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__init_array_start = .);
KEEP(*(.init_array .ctors))
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
PROVIDE_HIDDEN (__init_array_end = .);
} :rodata
.fini_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.fini_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__fini_array_start = .);
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP(*(.fini_array .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
} :rodata
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA)
{
*(.eh_frame_hdr .eh_frame_hdr.*)
} :rodata
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RO
{
KEEP (*(.eh_frame .eh_frame.*))
} :rodata
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RO
{
KEEP (*(.gcc_except_table .gcc_except_table.*))
} :rodata
_kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_bss_start = ALIGN(CONSTANT(MAXPAGESIZE));
.bss ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.bss) - KERNEL_VMA)
{
*(COMMON)
*(.bss .bss.*)
} :bss
_kernel_bss_end = ALIGN(CONSTANT(MAXPAGESIZE));
_kernel_end = ALIGN(CONSTANT(MAXPAGESIZE));
/DISCARD/ :
{
*(.comment*)
*(.note*)
}
}

91
Kernel/arch/i386/madt.cpp Normal file
View File

@ -0,0 +1,91 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "acpi.hpp"
#include <memory.hpp>
#include <debug.h>
#include "../../kernel.h"
namespace ACPI
{
MADT::MADT(ACPI::MADTHeader *madt)
{
trace("Initializing MADT");
CPUCores = 0;
LAPICAddress = (LAPIC *)(uintptr_t)madt->LocalControllerAddress;
for (uint8_t *ptr = (uint8_t *)(madt->Entries);
(uintptr_t)(ptr) < (uintptr_t)(madt) + madt->Header.Length;
ptr += *(ptr + 1))
{
switch (*(ptr))
{
case 0:
{
if (ptr[4] & 1)
{
lapic.push_back((LocalAPIC *)ptr);
KPrint("Local APIC %d (APIC %d) found.", lapic.back()->ACPIProcessorId, lapic.back()->APICId);
CPUCores++;
}
break;
}
case 1:
{
ioapic.push_back((MADTIOApic *)ptr);
KPrint("I/O APIC %d (Address %#lx) found.", ioapic.back()->APICID, ioapic.back()->Address);
Memory::Virtual(KernelPageTable).Map((void *)(uintptr_t)ioapic.back()->Address, (void *)(uintptr_t)ioapic.back()->Address, Memory::PTFlag::RW | Memory::PTFlag::PCD); // Make sure that the address is mapped.
break;
}
case 2:
{
iso.push_back((MADTIso *)ptr);
KPrint("ISO (IRQ:%#lx, BUS:%#lx, GSI:%#lx, %s/%s) found.",
iso.back()->IRQSource, iso.back()->BuSSource, iso.back()->GSI,
iso.back()->Flags & 0x00000004 ? "Active High" : "Active Low",
iso.back()->Flags & 0x00000100 ? "Edge Triggered" : "Level Triggered");
break;
}
case 4:
{
nmi.push_back((MADTNmi *)ptr);
KPrint("NMI %#lx (lint:%#lx) found.", nmi.back()->processor, nmi.back()->lint);
break;
}
case 5:
{
LAPICAddress = (LAPIC *)ptr;
KPrint("APIC found at %#lx", LAPICAddress);
break;
}
default:
{
KPrint("Unknown MADT entry %#lx", *(ptr));
break;
}
}
Memory::Virtual(KernelPageTable).Map((void *)LAPICAddress, (void *)LAPICAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD); // I should map more than one page?
}
CPUCores--; // We start at 0 (BSP) and end at 11 (APs), so we have 12 cores.
KPrint("Total CPU cores: %d", CPUCores + 1);
}
MADT::~MADT()
{
}
}

View File

@ -0,0 +1,226 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <convert.h>
#include <debug.h>
namespace Memory
{
bool Virtual::Check(void *VirtualAddress, PTFlag Flag, MapType Type)
{
// 0x1000 aligned
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
PageTableEntryPtr *PTE = nullptr;
if ((PDE->raw & Flag) > 0)
{
if (Type == MapType::FourMiB && PDE->PageSize)
return true;
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->GetAddress() << 12);
if (PTE)
{
if ((PTE->Entries[Index.PTEIndex].Present))
return true;
}
}
return false;
}
void *Virtual::GetPhysical(void *VirtualAddress)
{
// 0x1000 aligned
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
PageTableEntryPtr *PTE = nullptr;
if (PDE->Present)
{
if (PDE->PageSize)
return (void *)((uintptr_t)PDE->GetAddress() << 12);
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->GetAddress() << 12);
if (PTE)
{
if (PTE->Entries[Index.PTEIndex].Present)
return (void *)((uintptr_t)PTE->Entries[Index.PTEIndex].GetAddress() << 12);
}
}
return nullptr;
}
Virtual::MapType Virtual::GetMapType(void *VirtualAddress)
{
// 0x1000 aligned
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
PageTableEntryPtr *PTE = nullptr;
if (PDE->Present)
{
if (PDE->PageSize)
return MapType::FourMiB;
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->GetAddress() << 12);
if (PTE)
{
if (PTE->Entries[Index.PTEIndex].Present)
return MapType::FourKiB;
}
}
return MapType::NoMapType;
}
PageDirectoryEntry *Virtual::GetPDE(void *VirtualAddress, MapType Type)
{
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
if (PDE->Present)
return PDE;
return nullptr;
}
PageTableEntry *Virtual::GetPTE(void *VirtualAddress, MapType Type)
{
uintptr_t Address = (uintptr_t)VirtualAddress;
Address &= 0xFFFFF000;
PageMapIndexer Index = PageMapIndexer(Address);
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
if (!PDE->Present)
return nullptr;
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
if (PTE->Present)
return PTE;
return nullptr;
}
void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
{
SmartLock(this->MemoryLock);
if (unlikely(!this->Table))
{
error("No page table");
return;
}
Flags |= PTFlag::P;
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
uint64_t DirectoryFlags = Flags & 0x3F;
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
if (Type == MapType::FourMiB)
{
PDE->raw |= (uintptr_t)Flags;
PDE->PageSize = true;
PDE->SetAddress((uintptr_t)PhysicalAddress >> 12);
debug("Mapped 4MB page at %p to %p", VirtualAddress, PhysicalAddress);
return;
}
PageTableEntryPtr *PTEPtr = nullptr;
if (!PDE->Present)
{
PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1));
memset(PTEPtr, 0, sizeof(PageTableEntryPtr));
PDE->Present = true;
PDE->SetAddress((uintptr_t)PTEPtr >> 12);
}
else
PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
PDE->raw |= (uintptr_t)DirectoryFlags;
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
PTE->Present = true;
PTE->raw |= (uintptr_t)Flags;
PTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
CPU::x32::invlpg(VirtualAddress);
#ifdef DEBUG
/* https://stackoverflow.com/a/3208376/9352057 */
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
(byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \
(byte & 0x08 ? '1' : '0'), \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
if (!this->Check(VirtualAddress, (PTFlag)Flags, Type)) // quick workaround just to see where it fails
warn("Failed to map v:%#lx p:%#lx with flags: " BYTE_TO_BINARY_PATTERN, VirtualAddress, PhysicalAddress, BYTE_TO_BINARY(Flags));
#endif
}
void Virtual::Unmap(void *VirtualAddress, MapType Type)
{
SmartLock(this->MemoryLock);
if (!this->Table)
{
error("No page table");
return;
}
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
if (!PDE->Present)
{
warn("Page %#lx not present", PDE->GetAddress());
return;
}
if (Type == MapType::FourMiB && PDE->PageSize)
{
PDE->Present = false;
return;
}
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE->Address << 12);
PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex];
if (!PTE.Present)
{
warn("Page %#lx not present", PTE.GetAddress());
return;
}
PTE.Present = false;
PTEPtr->Entries[Index.PTEIndex] = PTE;
CPU::x32::invlpg(VirtualAddress);
}
}

View File

@ -0,0 +1,30 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <syscalls.hpp>
#include <cpu.hpp>
#include "cpu/gdt.hpp"
using namespace CPU::x32;
extern "C" uint32_t SystemCallsHandler(SyscallsFrame *regs);
void InitializeSystemCalls()
{
}