Update multiboot2 bootstrap code

This commit is contained in:
Alex
2023-03-29 10:56:23 +03:00
parent 298c6b3921
commit 73f38799eb
10 changed files with 281 additions and 81 deletions

View File

@@ -0,0 +1,41 @@
[bits 32]
section .bootstrap.text
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
ret
.NoCPUID:
mov word [0xb8F00], 0xF00F ; .
.Loop:
cli
hlt
jmp .Loop
global Detect64Bit
Detect64Bit:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .NoLongMode
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz .NoLongMode
ret
.NoLongMode:
mov word [0xb8F00], 0xF00A ; .
.Loop:
cli
hlt
jmp .Loop

View File

@@ -0,0 +1,139 @@
; https://wiki.osdev.org/Creating_a_64-bit_kernel
; https://wiki.osdev.org/Entering_Long_Mode_Directly
KERNEL_VIRTUAL_BASE equ 0xFFFFFFFF80000000 ; 512GB
KERNEL_LMA equ 0x1000000 ; 16MB
KERNEL_STACK_SIZE equ 0x4000 ; 16KB
extern Multiboot2Entry
extern BootPageTable
extern UpdatePageTable
extern UpdatePageTable64
extern DetectCPUID
extern Detect64Bit
extern LoadGDT32
global MB2_start
extern MB2_start_c
[bits 32]
section .bootstrap.text
MB2_start:
cli
mov word [0xb8F00], 0x072E ; .
; We need to check if the CPU supports 64-bit mode
call DetectCPUID
call Detect64Bit
mov word [0xb8F02], 0x072E ; .
mov ecx, cr0
and ecx, 0x7fffffff ; Clear PG
mov cr0, ecx
mov ecx, cr4
or ecx, 0x10 ; Set PSE
or ecx, 0x20 ; Set PAE
mov cr4, ecx
; Load the GDT and update the page table
call LoadGDT32
call UpdatePageTable
; Load the new page table
mov edi, BootPageTable
mov cr3, edi
mov word [0xb8F04], 0x072E ; .
; Enable long mode
mov ecx, 0xC0000080 ; EFER
rdmsr
or eax, 0x800 | 0x100 | 0x1 ; Set LME, LMA, SCE
wrmsr
mov ecx, cr0
or ecx, (0x80000000 | 0x1) ; Set PG and PE
mov cr0, ecx
lgdt [GDT64.Ptr]
; xor eax, eax
; sgdt [eax]
; test eax, eax
; jz .InvalidGDT
; .InvalidGDT:
; mov word [0xb8F07], 0x4 ; Red
; hlt
jmp GDT64.code:HigherHalfStart
[bits 64]
HigherHalfStart:
cli
mov word [0xb8F06], 0x072E ; .
call UpdatePageTable64
; Load the new page table
mov rdi, BootPageTable
mov cr3, rdi
mov ax, GDT64.data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov word [0xb8F08], 0x072E ; .
mov rsp, (KernelStack + KERNEL_STACK_SIZE)
push rax ; Multiboot2 Magic
add rbx, KERNEL_VIRTUAL_BASE
push rbx ; Multiboot2 Header
call Multiboot2Entry
.Loop:
hlt
jmp .Loop
section .bootstrap.bss
align 16
KernelStack:
resb KERNEL_STACK_SIZE
; Access bits
PRESENT equ 1 << 7
NOT_SYS equ 1 << 4
EXEC equ 1 << 3
DC equ 1 << 2
RW equ 1 << 1
ACCESSED equ 1 << 0
; Flags bits
GRAN_4K equ 1 << 7
SZ_32 equ 1 << 6
LONG_MODE equ 1 << 5
section .bootstrap.data
GDT64:
.null: equ $ - GDT64
dq 0
.code: equ $ - GDT64
dd 0xFFFF
db 0
db PRESENT | NOT_SYS | EXEC | RW
db GRAN_4K | LONG_MODE | 0xF
db 0
.data: equ $ - GDT64
dd 0xFFFF
db 0
db PRESENT | NOT_SYS | RW
db GRAN_4K | SZ_32 | 0xF
db 0
.tss: equ $ - GDT64
dd 0x00000068
dd 0x00CF8900
.Ptr:
dw $ - GDT64 - 1
dq GDT64

View File

@@ -0,0 +1,47 @@
[bits 32]
section .bootstrap.text
align 32
global gdtr
gdtr:
dw GDT32_END - GDT32 - 1
dd GDT32
align 32
GDT32:
dq 0x0
dw 0xffff
dw 0x0000
db 0x00
dw 0xcf9a
db 0x00
dw 0xffff
dw 0x0000
db 0x00
dw 0xcf92
db 0x00
dw 0x0100
dw 0x1000
db 0x00
dw 0x4092
db 0x00
GDT32_END:
global LoadGDT32
LoadGDT32:
lgdt [gdtr]
jmp 0x8:ActivateGDT
ActivateGDT:
mov cx, 0x10
mov ss, cx
mov ds, cx
mov es, cx
mov fs, cx
mov cx, 0x18
mov gs, cx
ret

View File

@@ -0,0 +1,42 @@
[bits 32]
section .multiboot2
align 4096
HEADER_START:
dd 0xE85250D6
dd 0
dd (HEADER_END - HEADER_START)
dd 0x100000000 - (HEADER_END - HEADER_START) - 0 - 0xE85250D6
align 8
MB2_INFO_REQUEST_TAG_START:
dw 1
dw 0
dd MB2_INFO_REQUEST_TAG_END - MB2_INFO_REQUEST_TAG_START
dd 1 ; Command Line
dd 2 ; Boot Loader Name
dd 3 ; Module
dd 4 ; Basic Memory Information
dd 5 ; BIOS Boot Device
dd 6 ; Memory Map
dd 7 ; VBE
dd 8 ; Framebuffer
dd 9 ; ELF Sections
dd 10 ; APM Table
dd 11 ; EFI 32-bit System Table Pointer
dd 12 ; EFI 64-bit System Table Pointer
; dd 13 ; SMBIOS
dd 14 ; ACPI Old
dd 15 ; ACPI New
dd 16 ; Network
dd 17 ; EFI Memory Map
dd 18 ; EFI Boot Services Notifier
dd 19 ; EFI 32-bit Image Handle Pointer
dd 20 ; EFI 64-bit Image Handle Pointer
dd 21 ; Load Base Address
MB2_INFO_REQUEST_TAG_END:
align 8
MB2_TAG_START:
dw 0
dw 0
dd MB2_TAG_END - MB2_TAG_START
MB2_TAG_END:
HEADER_END:

View File

@@ -0,0 +1,339 @@
#include <types.h>
#include <boot/protocols/multiboot2.h>
#include <memory.hpp>
#include <io.h>
#include "../../../../kernel.h"
BootInfo mb2binfo;
enum VideoType
{
VIDEO_TYPE_NONE = 0x00,
VIDEO_TYPE_COLOUR = 0x20,
VIDEO_TYPE_MONOCHROME = 0x30,
};
uint16_t GetBiosAreaHardware()
{
const uint16_t *BIOSDataAreaDetectedHardware = (const uint16_t *)0x410;
return *BIOSDataAreaDetectedHardware;
}
enum VideoType GetVideoType() { return (enum VideoType)(GetBiosAreaHardware() & 0x30); }
void GetSMBIOS()
{
unsigned char *SMBIOSAddress = (unsigned char *)0xF0000;
while ((unsigned int)(unsigned long)SMBIOSAddress < 0x100000)
{
if (SMBIOSAddress[0] == '_' &&
SMBIOSAddress[1] == 'S' &&
SMBIOSAddress[2] == 'M' &&
SMBIOSAddress[3] == '_')
{
unsigned char Checksum = 0;
int Length = SMBIOSAddress[5];
for (int i = 0; i < Length; i++)
Checksum += SMBIOSAddress[i];
if (Checksum == 0)
break;
}
SMBIOSAddress += 16;
}
if ((unsigned int)(unsigned long)SMBIOSAddress == 0x100000)
{
// No SMBIOS found
}
}
void ProcessMB2(unsigned long Info)
{
uint8_t *VideoBuffer = (uint8_t *)0xB8F00 + 0xC0000000;
int pos = 0;
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)))
{
VideoBuffer[pos++] = '.';
VideoBuffer[pos++] = 0x2;
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 *)module->mod_start;
mb2binfo.Modules[module_count++].Size = module->size;
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);
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;
uint32_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry);
mb2binfo.Memory.Entries = EntryCount;
for (uint32_t i = 0; i < EntryCount; i++)
{
if (EntryCount > 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;
mb2binfo.Framebuffer[fb_count].MemoryModel = fb->common.framebuffer_type;
switch (fb->common.framebuffer_type)
{
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
{
fixme("indexed");
break;
}
case MULTIBOOT_FRAMEBUFFER_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:
{
fixme("ega_text");
break;
}
}
debug("Framebuffer %d: %dx%d %d bpp", Tag, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp);
debug("More info:\nAddress: %p\nPitch: %lld\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;
fixme("elf_sections->[num=%d, size=%d, entsize=%d, shndx=%d]",
elf->num, elf->size, elf->entsize, elf->shndx);
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 *)load_base_addr->load_base_addr;
mb2binfo.Kernel.VirtualBase = (void *)(load_base_addr->load_base_addr + 0xC0000000);
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
break;
}
default:
{
error("Unknown multiboot2 tag type: %d", Tag->type);
break;
}
}
}
}
EXTERNC void Multiboot2Entry(unsigned long Info, unsigned int Magic)
{
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();
}
uint64_t div = 1193180 / 1000;
outb(0x43, 0xB6);
outb(0x42, (uint8_t)div);
outb(0x42, (uint8_t)(div >> 8));
uint8_t tmp = inb(0x61);
if (tmp != (tmp | 3))
outb(0x61, tmp | 3);
ProcessMB2(Info);
tmp = inb(0x61) & 0xFC;
outb(0x61, tmp);
CPU::Stop();
Entry(&mb2binfo);
}

View File

@@ -0,0 +1,85 @@
PAGE_TABLE_SIZE equ 0x4 ; 1GB
[bits 32]
section .bootstrap.data
align 0x1000
global BootPageTable
BootPageTable:
times (0x10000) dq 0 ; 0x4000
section .bootstrap.text
global UpdatePageTable
UpdatePageTable:
mov edi, (BootPageTable + 0x0000)
mov eax, (BootPageTable + 0x1000)
or eax, 0x3
mov dword [edi], eax
mov ecx, PAGE_TABLE_SIZE
mov edi, (BootPageTable + 0x1000)
mov eax, (BootPageTable + 0x2000)
or eax, 0x3
mov ebx, 0x0
.FillPageTableLevel3:
mov dword [edi], eax
mov dword [edi + 4], ebx
add eax, 1 << 12
adc ebx, 0
add edi, 8
loop .FillPageTableLevel3
mov ecx, (512 * PAGE_TABLE_SIZE)
mov edi, (BootPageTable + 0x2000)
mov eax, 0x0 | 0x3 | 1 << 7
mov ebx, 0x0
.FillPageTableLevel2:
mov dword [edi], eax
mov dword [edi + 4], ebx
add eax, 1 << 21
adc ebx, 0
add edi, 8
loop .FillPageTableLevel2
ret
[bits 64]
section .bootstrap.text
global UpdatePageTable64
UpdatePageTable64:
mov rdi, (BootPageTable + 0x0000)
mov rax, (BootPageTable + 0x1000)
or rax, 0x3
mov [rdi], rax
mov rcx, PAGE_TABLE_SIZE
mov rdi, (BootPageTable + 0x1000)
mov rax, (BootPageTable + 0x2000)
or rax, 0x3
mov rbx, 0x0
.FillPageTableLevel3:
mov [rdi], rax
mov [rdi + 4], rbx
add rax, 1 << 12
adc rbx, 0
add rdi, 8
loop .FillPageTableLevel3
mov rcx, (512 * PAGE_TABLE_SIZE)
mov rdi, (BootPageTable + 0x2000)
mov rax, 0x0 | 0x3 | 1 << 7
mov rbx, 0x0
.FillPageTableLevel2:
mov [rdi], rax
mov [rdi + 4], rbx
add rax, 1 << 21
adc rbx, 0
add rdi, 8
loop .FillPageTableLevel2
ret