Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a45ea562b | ||
|
|
da404f520d | ||
|
|
799ad8b264 | ||
|
|
818b51d57c | ||
|
|
e8866abc7a | ||
|
|
3b560c063a | ||
|
|
a27b8d6a3a | ||
|
|
2050b89334 | ||
|
|
504de44ff3 | ||
|
|
71a6f13fa5 | ||
|
|
f62fbefe54 | ||
|
|
6c29024eac | ||
|
|
b7f18c0d31 | ||
|
|
696c29086b | ||
|
|
bce281606e | ||
|
|
2388a92085 | ||
|
|
447991e82b | ||
|
|
a8984350da | ||
|
|
4d5da72e2e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ build
|
||||
*.bak
|
||||
tags
|
||||
.gdbinit
|
||||
popcorn.log
|
||||
|
||||
49
Makefile
49
Makefile
@@ -3,6 +3,7 @@ ARCH ?= x86_64
|
||||
include src/arch/$(ARCH)/config.mk
|
||||
|
||||
BUILD_D := build
|
||||
KERN_D := src/kernel
|
||||
ARCH_D := src/arch/$(ARCH)
|
||||
VERSION ?= $(shell git describe --dirty --always)
|
||||
GITSHA ?= $(shell git rev-parse --short HEAD)
|
||||
@@ -10,7 +11,7 @@ GITSHA ?= $(shell git rev-parse --short HEAD)
|
||||
KERNEL_FILENAME:= popcorn.bin
|
||||
KERNEL_FONT := assets/fonts/tamsyn8x16r.psf
|
||||
|
||||
MODULES := main
|
||||
MODULES := kutil
|
||||
|
||||
|
||||
EFI_DIR := external/gnu-efi
|
||||
@@ -57,13 +58,14 @@ ASFLAGS += -p $(BUILD_D)/versions.s
|
||||
|
||||
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||
CFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
||||
CFLAGS += -std=c11
|
||||
CFLAGS += -std=c11 -mcmodel=large
|
||||
|
||||
CXXFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||
CXXFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
||||
CXXFLAGS += -std=c++14
|
||||
CXXFLAGS += -std=c++14 -mcmodel=large
|
||||
|
||||
BOOT_CFLAGS := -I src/boot $(CFLAGS) -fPIC -fshort-wchar
|
||||
BOOT_CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||
BOOT_CFLAGS += -std=c11 -I src/boot -fPIC -fshort-wchar
|
||||
BOOT_CFLAGS += -DKERNEL_FILENAME="L\"$(KERNEL_FILENAME)\""
|
||||
BOOT_CFLAGS += -DGIT_VERSION_WIDE="L\"$(VERSION)\""
|
||||
BOOT_CFLAGS += -DGNU_EFI_USE_MS_ABI -DHAVE_USE_MS_ABI
|
||||
@@ -92,12 +94,15 @@ INIT_DEP := $(BUILD_D)/.builddir
|
||||
|
||||
BOOT_SRCS := $(wildcard src/boot/*.c)
|
||||
BOBJS += $(patsubst src/boot/%,$(BUILD_D)/boot/%,$(patsubst %,%.o,$(BOOT_SRCS)))
|
||||
BDEPS := $(patsubst src/boot/%,$(BUILD_D)/boot/%,$(patsubst %,%.d,$(BOOT_SRCS)))
|
||||
|
||||
ARCH_SRCS := $(wildcard $(ARCH_D)/*.s)
|
||||
ARCH_SRCS += $(wildcard $(ARCH_D)/*.c)
|
||||
KOBJS += $(patsubst $(ARCH_D)/%,$(BUILD_D)/arch/%,$(patsubst %,%.o,$(ARCH_SRCS)))
|
||||
DEPS := $(patsubst $(ARCH_D)/%,$(BUILD_D)/arch/%,$(patsubst %,%.d,$(ARCH_SRCS)))
|
||||
KERN_SRCS := $(wildcard $(KERN_D)/*.s)
|
||||
KERN_SRCS += $(wildcard $(KERN_D)/*.c)
|
||||
KERN_SRCS += $(wildcard $(KERN_D)/*.cpp)
|
||||
KERN_SRCS += $(wildcard $(ARCH_D)/*.s)
|
||||
KERN_SRCS += $(wildcard $(ARCH_D)/*.c)
|
||||
|
||||
KERN_OBJS := $(patsubst src/%, $(BUILD_D)/%, $(patsubst %,%.o,$(KERN_SRCS)))
|
||||
|
||||
MOD_TARGETS :=
|
||||
|
||||
PARTED ?= /sbin/parted
|
||||
@@ -120,19 +125,24 @@ init: $(INIT_DEP)
|
||||
$(INIT_DEP):
|
||||
mkdir -p $(BUILD_D) $(patsubst %,$(BUILD_D)/d.%,$(MODULES))
|
||||
mkdir -p $(BUILD_D)/boot
|
||||
mkdir -p $(BUILD_D)/arch
|
||||
mkdir -p $(patsubst src/%,$(BUILD_D)/%,$(ARCH_D))
|
||||
mkdir -p $(patsubst src/%,$(BUILD_D)/%,$(KERN_D))
|
||||
touch $(INIT_DEP)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_D)/* $(BUILD_D)/.version $(BUILD_D)/.builddir
|
||||
|
||||
vars:
|
||||
@echo "KERN_SRCS: " $(KERN_SRCS)
|
||||
@echo "KERN_OBJS: " $(KERN_OBJS)
|
||||
|
||||
dist-clean: clean
|
||||
make -C external/gnu-efi clean
|
||||
|
||||
dump: $(BUILD_D)/kernel.dump
|
||||
vim $<
|
||||
|
||||
.PHONY: all clean dist-clean init dump
|
||||
.PHONY: all clean dist-clean init dump vars
|
||||
|
||||
$(BUILD_D)/.version:
|
||||
echo '$(VERSION)' | cmp -s - $@ || echo '$(VERSION)' > $@
|
||||
@@ -141,7 +151,7 @@ $(BUILD_D)/versions.s:
|
||||
./parse_version.py "$(VERSION)" "$(GITSHA)" > $@
|
||||
|
||||
-include x $(patsubst %,src/modules/%/module.mk,$(MODULES))
|
||||
-include x $(DEPS)
|
||||
-include x $(shell find $(BUILD_D) -type f -name '*.d')
|
||||
|
||||
$(EFI_LIB):
|
||||
make -C external/gnu-efi all
|
||||
@@ -169,24 +179,27 @@ $(BUILD_D)/boot.dump: $(BUILD_D)/boot.efi
|
||||
$(OBJD) -D -S $< > $@
|
||||
|
||||
$(BUILD_D)/boot/%.s.o: src/boot/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
$(AS) $(ASFLAGS) -i src/boot/ -o $@ $<
|
||||
|
||||
$(BUILD_D)/boot/%.c.o: src/boot/%.c $(INIT_DEP)
|
||||
$(CC) $(BOOT_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_D)/kernel.elf: $(KOBJS) $(MOD_TARGETS) $(ARCH_D)/kernel.ld
|
||||
$(LD) $(LDFLAGS) -u _header -T $(ARCH_D)/kernel.ld -o $@ $(patsubst %,-l%,$(MODULES)) $(KOBJS)
|
||||
$(BUILD_D)/kernel.elf: $(KERN_OBJS) $(MOD_TARGETS) $(ARCH_D)/kernel.ld
|
||||
$(LD) $(LDFLAGS) -u _header -T $(ARCH_D)/kernel.ld -o $@ $(KERN_OBJS) $(patsubst %,-l%,$(MODULES))
|
||||
$(OBJC) --only-keep-debug $@ $@.sym
|
||||
|
||||
$(BUILD_D)/kernel.dump: $(BUILD_D)/kernel.elf
|
||||
$(OBJD) -D -S $< > $@
|
||||
|
||||
$(BUILD_D)/arch/%.s.o: $(ARCH_D)/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
$(BUILD_D)/%.s.o: src/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -i $(ARCH_D)/ -i $(KERN_D)/ -o $@ $<
|
||||
|
||||
$(BUILD_D)/arch/%.c.o: $(ARCH_D)/%.c $(INIT_DEP)
|
||||
$(BUILD_D)/%.c.o: src/%.c $(INIT_DEP)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_D)/%.cpp.o: src/%.cpp $(INIT_DEP)
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_D)/flash.img: $(OVMF)
|
||||
cp $^ $@
|
||||
|
||||
|
||||
9
NOTES.md
9
NOTES.md
@@ -1,10 +1,7 @@
|
||||
# Design / WIP notes
|
||||
|
||||
## Bootloader / UEFI
|
||||
|
||||
* What is the interface between the UEFI boot portion and the kernel?
|
||||
* Allocate pages, use UEFI reserved mapping types (0x70000000 - 0x7fffffff)
|
||||
* Passing memory map: Allocate earliest pages and fill with UEFI's memory map
|
||||
stuctures?
|
||||
## TODO
|
||||
|
||||
- Better page-allocation model
|
||||
- Allow for more than one IOAPIC in ACPI module
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ $(MOD_BUILD_D)/%.cpp.o: $(MOD_SRC_D)/%.cpp $(INIT_DEP)
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
$(MOD_BUILD_D)/%.s.o: $(MOD_SRC_D)/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
$(AS) $(ASFLAGS) -i $(MOD_SRC_D)/ -o $@ $<
|
||||
|
||||
DEPS += $(patsubst %.o,%.d,$(OBJS_$(MOD_NAME)))
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
call make.bat
|
||||
qemu-system-x86_64.exe -bios .\assets\ovmf\x64\OVMF.fd -hda .\build\fs.img -m 512 -vga cirrus
|
||||
4
qemu.bat
4
qemu.bat
@@ -1 +1,3 @@
|
||||
qemu-system-x86_64.exe -bios .\assets\ovmf\x64\OVMF.fd -hda .\build\fs.img -m 512 -nographic -echr 23
|
||||
call make.bat
|
||||
del popcorn.log
|
||||
qemu-system-x86_64.exe -bios .\assets\ovmf\x64\OVMF.fd -hda .\build\fs.img -m 512 -vga cirrus -d guest_errors,int -D popcorn.log -no-reboot
|
||||
|
||||
60
scripts/parse_memmap.py
Executable file
60
scripts/parse_memmap.py
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from struct import unpack_from, calcsize
|
||||
import sys
|
||||
|
||||
memory_type_names = {
|
||||
0: "EfiReservedMemoryType",
|
||||
1: "EfiLoaderCode",
|
||||
2: "EfiLoaderData",
|
||||
3: "EfiBootServicesCode",
|
||||
4: "EfiBootServicesData",
|
||||
5: "EfiRuntimeServicesCode",
|
||||
6: "EfiRuntimeServicesData",
|
||||
7: "EfiConventionalMemory",
|
||||
8: "EfiUnusableMemory",
|
||||
9: "EfiACPIReclaimMemory",
|
||||
10: "EfiACPIMemoryNVS",
|
||||
11: "EfiMemoryMappedIO",
|
||||
12: "EfiMemoryMappedIOPortSpace",
|
||||
13: "EfiPalCode",
|
||||
14: "EfiPersistentMemory",
|
||||
|
||||
0x80000000: "Kernel Image",
|
||||
0x80000001: "Kernel Data",
|
||||
}
|
||||
|
||||
EFI_MEMORY_UC = 0x0000000000000001
|
||||
EFI_MEMORY_WC = 0x0000000000000002
|
||||
EFI_MEMORY_WT = 0x0000000000000004
|
||||
EFI_MEMORY_WB = 0x0000000000000008
|
||||
EFI_MEMORY_UCE = 0x0000000000000010
|
||||
EFI_MEMORY_WP = 0x0000000000001000
|
||||
EFI_MEMORY_RP = 0x0000000000002000
|
||||
EFI_MEMORY_XP = 0x0000000000004000
|
||||
EFI_MEMORY_NV = 0x0000000000008000
|
||||
EFI_MEMORY_MORE_RELIABLE = 0x0000000000010000
|
||||
EFI_MEMORY_RO = 0x0000000000020000
|
||||
EFI_MEMORY_RUNTIME = 0x8000000000000000
|
||||
|
||||
fmt = "LQQQQQ"
|
||||
size = calcsize(fmt)
|
||||
|
||||
print("Descriptor size: {} bytes\n".format(size))
|
||||
if size != 48:
|
||||
sys.exit(1)
|
||||
|
||||
data = open(sys.argv[1], 'rb').read()
|
||||
length = len(data)
|
||||
consumed = 0
|
||||
|
||||
while length - consumed > size:
|
||||
memtype, phys, virt, pages, attr, pad = unpack_from(fmt, data, consumed)
|
||||
consumed += size
|
||||
if pages == 0: break
|
||||
|
||||
memtype = memory_type_names.get(memtype, "{:016x}".format(memtype))
|
||||
runtime = {EFI_MEMORY_RUNTIME: "*"}.get(attr & EFI_MEMORY_RUNTIME, " ")
|
||||
|
||||
print("{:>23}{} {:016x} {:016x} [{:4d}]".format(memtype, runtime, phys, virt, pages))
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
OFFSET = 0xFFFF800000000000;
|
||||
. = OFFSET + 0x100000;
|
||||
|
||||
.header : {
|
||||
header = .;
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#define KERNEL_PHYS_ADDRESS 0x100000
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_VIRT_ADDRESS
|
||||
#define KERNEL_VIRT_ADDRESS 0xFFFF800000000000
|
||||
#endif
|
||||
|
||||
#ifndef VIRTUAL_OFFSET
|
||||
#define VIRTUAL_OFFSET 0xf00000000
|
||||
#endif
|
||||
@@ -32,6 +36,10 @@
|
||||
#define KERNEL_LOG_PAGES 4
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_PT_MEMTYPE
|
||||
#define KERNEL_PT_MEMTYPE 0x80000004
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_FILENAME
|
||||
#define KERNEL_FILENAME L"kernel.bin"
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,7 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
|
||||
EFI_RUNTIME_SERVICES *runsvc = system_table->RuntimeServices;
|
||||
|
||||
// When checking console initialization, use CHECK_EFI_STATUS_OR_RETURN
|
||||
// because we can't be sure if the console was fully set up
|
||||
@@ -44,6 +45,8 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
|
||||
// From here on out, we can use CHECK_EFI_STATUS_OR_FAIL instead
|
||||
|
||||
memory_init_pointer_fixup(bootsvc, runsvc);
|
||||
|
||||
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
|
||||
//
|
||||
void *acpi_table = NULL;
|
||||
@@ -100,10 +103,12 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
con_printf(L" Entrypoint 0x%x\r\n", version->entrypoint);
|
||||
|
||||
void (*kernel_main)() = version->entrypoint;
|
||||
memory_mark_pointer_fixup((void **)&kernel_main);
|
||||
|
||||
// Set up the kernel data pages to pass to the kernel
|
||||
//
|
||||
struct popcorn_data *data_header = (struct popcorn_data *)load.data;
|
||||
memory_mark_pointer_fixup((void **)&data_header);
|
||||
|
||||
data_header->magic = DATA_HEADER_MAGIC;
|
||||
data_header->version = DATA_HEADER_VERSION;
|
||||
@@ -113,16 +118,24 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
|
||||
data_header->font = load.font;
|
||||
data_header->font_length = load.font_length;
|
||||
memory_mark_pointer_fixup((void **)&data_header->font);
|
||||
|
||||
data_header->data = load.data;
|
||||
data_header->data_length = load.data_length;
|
||||
memory_mark_pointer_fixup((void **)&data_header->data);
|
||||
|
||||
data_header->log = load.log;
|
||||
data_header->log_length = load.log_length;
|
||||
memory_mark_pointer_fixup((void **)&data_header->log);
|
||||
|
||||
data_header->memory_map = (EFI_MEMORY_DESCRIPTOR *)(data_header + 1);
|
||||
data_header->runtime = system_table->RuntimeServices;
|
||||
memory_mark_pointer_fixup((void **)&data_header->memory_map);
|
||||
|
||||
data_header->runtime = runsvc;
|
||||
memory_mark_pointer_fixup((void **)&data_header->runtime);
|
||||
|
||||
data_header->acpi_table = acpi_table;
|
||||
memory_mark_pointer_fixup((void **)&data_header->acpi_table);
|
||||
|
||||
data_header->_reserved0 = 0;
|
||||
data_header->_reserved1 = 0;
|
||||
@@ -139,6 +152,7 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
&data_header->gmask,
|
||||
&data_header->bmask);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
memory_mark_pointer_fixup((void **)&data_header->frame_buffer);
|
||||
|
||||
// Save the memory map and tell the firmware we're taking control.
|
||||
//
|
||||
@@ -149,11 +163,16 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
status = memory_get_map(bootsvc, &map);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
|
||||
data_header->memory_map_length = map.length;
|
||||
data_header->memory_map_desc_size = map.size;
|
||||
|
||||
// bootsvc->Stall(5000000);
|
||||
|
||||
status = bootsvc->ExitBootServices(image_handle, map.key);
|
||||
CHECK_EFI_STATUS_OR_ASSERT(status, 0);
|
||||
|
||||
memory_virtualize(runsvc, &map);
|
||||
|
||||
// Hand control to the kernel
|
||||
//
|
||||
kernel_main(data_header);
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b))
|
||||
|
||||
size_t fixup_pointer_index = 0;
|
||||
void **fixup_pointers[64];
|
||||
uint64_t *new_pml4 = 0;
|
||||
|
||||
const CHAR16 *memory_type_names[] = {
|
||||
L"EfiReservedMemoryType",
|
||||
L"EfiLoaderCode",
|
||||
@@ -37,6 +41,49 @@ memory_type_name(UINT32 value)
|
||||
return memory_type_names[value];
|
||||
}
|
||||
|
||||
void EFIAPI
|
||||
memory_update_marked_addresses(EFI_EVENT UNUSED *event, void *context)
|
||||
{
|
||||
EFI_RUNTIME_SERVICES *runsvc = (EFI_RUNTIME_SERVICES*)context;
|
||||
for (size_t i = 0; i < fixup_pointer_index; ++i) {
|
||||
if (fixup_pointers[i])
|
||||
runsvc->ConvertPointer(0, fixup_pointers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_EVENT event;
|
||||
|
||||
status = bootsvc->CreateEvent(
|
||||
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
|
||||
TPL_CALLBACK,
|
||||
(EFI_EVENT_NOTIFY)&memory_update_marked_addresses,
|
||||
runsvc,
|
||||
&event);
|
||||
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to initialize pointer update event.");
|
||||
|
||||
// Reserve a page for our replacement PML4
|
||||
EFI_PHYSICAL_ADDRESS addr = 0;
|
||||
status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 4, &addr);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate PML4 page.");
|
||||
|
||||
new_pml4 = (uint64_t *)addr;
|
||||
}
|
||||
|
||||
void
|
||||
memory_mark_pointer_fixup(void **p)
|
||||
{
|
||||
if (fixup_pointer_index == 0) {
|
||||
const size_t count = sizeof(fixup_pointers) / sizeof(void*);
|
||||
for (size_t i = 0; i < count; ++i) fixup_pointers[i] = 0;
|
||||
}
|
||||
fixup_pointers[fixup_pointer_index++] = p;
|
||||
}
|
||||
|
||||
void
|
||||
copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len)
|
||||
{
|
||||
@@ -111,3 +158,48 @@ memory_dump_map(struct memory_map *map)
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
|
||||
{
|
||||
memory_mark_pointer_fixup((void **)&runsvc);
|
||||
memory_mark_pointer_fixup((void **)&map);
|
||||
|
||||
// Get the pointer to the start of PML4
|
||||
uint64_t* cr3 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||
|
||||
// PML4 is indexed with bits 39:47 of the virtual address
|
||||
uint64_t offset = (KERNEL_VIRT_ADDRESS >> 39) & 0x1ff;
|
||||
|
||||
// Double map the lower half pages that are present into the higher half
|
||||
for (unsigned i = 0; i < offset; ++i) {
|
||||
if (cr3[i] & 0x1)
|
||||
new_pml4[i] = new_pml4[offset+i] = cr3[i];
|
||||
else
|
||||
new_pml4[i] = new_pml4[offset+i] = 0;
|
||||
}
|
||||
|
||||
// Write our new PML4 pointer back to CR3
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (new_pml4) );
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
|
||||
EFI_MEMORY_DESCRIPTOR *d = map->entries;
|
||||
while (d < end) {
|
||||
switch (d->Type) {
|
||||
case KERNEL_MEMTYPE:
|
||||
case KERNEL_FONT_MEMTYPE:
|
||||
case KERNEL_DATA_MEMTYPE:
|
||||
case KERNEL_LOG_MEMTYPE:
|
||||
d->Attribute |= EFI_MEMORY_RUNTIME;
|
||||
|
||||
default:
|
||||
if (d->Attribute & EFI_MEMORY_RUNTIME) {
|
||||
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
||||
}
|
||||
}
|
||||
d = INCREMENT_DESC(d, map->size);
|
||||
}
|
||||
|
||||
runsvc->SetVirtualAddressMap(map->length, map->size, map->version, map->entries);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@ struct memory_map {
|
||||
EFI_MEMORY_DESCRIPTOR *entries;
|
||||
};
|
||||
|
||||
EFI_STATUS memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc);
|
||||
void memory_mark_pointer_fixup(void **p);
|
||||
|
||||
EFI_STATUS memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);
|
||||
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
|
||||
EFI_STATUS memory_dump_map(struct memory_map *map);
|
||||
|
||||
void memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map);
|
||||
|
||||
@@ -26,6 +26,9 @@ struct popcorn_data {
|
||||
size_t log_length;
|
||||
|
||||
void *memory_map;
|
||||
size_t memory_map_length;
|
||||
size_t memory_map_desc_size;
|
||||
|
||||
void *runtime;
|
||||
|
||||
void *acpi_table;
|
||||
|
||||
52
src/kernel/acpi_tables.h
Normal file
52
src/kernel/acpi_tables.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/coord.h"
|
||||
#include "kutil/misc.h"
|
||||
|
||||
struct acpi_table_header
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t length;
|
||||
uint8_t revision;
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
char oem_table[8];
|
||||
uint32_t oem_revision;
|
||||
uint32_t creator_id;
|
||||
uint32_t creator_revision;
|
||||
|
||||
bool validate(uint32_t expected_type = 0) const;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define TABLE_HEADER(signature) \
|
||||
static const uint32_t type_id = kutil::byteswap(signature); \
|
||||
acpi_table_header header;
|
||||
|
||||
|
||||
template <typename T>
|
||||
bool acpi_validate(const T *t) { return t->header.validate(T::type_id); }
|
||||
|
||||
template <typename T>
|
||||
size_t acpi_table_entries(const T *t, size_t size)
|
||||
{
|
||||
return (t->header.length - sizeof(T)) / size;
|
||||
}
|
||||
|
||||
|
||||
struct acpi_xsdt
|
||||
{
|
||||
TABLE_HEADER('XSDT');
|
||||
acpi_table_header *headers[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi_apic
|
||||
{
|
||||
TABLE_HEADER('APIC');
|
||||
uint32_t local_address;
|
||||
uint32_t flags;
|
||||
uint8_t controller_data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
29
src/kernel/assert.cpp
Normal file
29
src/kernel/assert.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "assert.h"
|
||||
#include "console.h"
|
||||
|
||||
[[noreturn]] void
|
||||
__kernel_assert(const char *file, unsigned line, const char *message)
|
||||
{
|
||||
console *cons = console::get();
|
||||
if (cons) {
|
||||
cons->set_color(9 , 0);
|
||||
cons->puts("\n\n ERROR: ");
|
||||
cons->puts(file);
|
||||
cons->puts(":");
|
||||
cons->put_dec(line);
|
||||
cons->puts(": ");
|
||||
cons->puts(message);
|
||||
}
|
||||
|
||||
__asm__ __volatile__(
|
||||
"movq %0, %%r8;"
|
||||
"movq %1, %%r9;"
|
||||
"movq %2, %%r10;"
|
||||
"movq $0, %%rdx;"
|
||||
"divq %%rdx;"
|
||||
: // no outputs
|
||||
: "r"((uint64_t)line), "r"(file), "r"(message)
|
||||
: "rax", "rdx", "r8", "r9", "r10");
|
||||
|
||||
while (1);
|
||||
}
|
||||
5
src/kernel/assert.h
Normal file
5
src/kernel/assert.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
[[noreturn]] void __kernel_assert(const char *file, unsigned line, const char *message);
|
||||
|
||||
#define kassert(stmt, message) if(!(stmt)) { __kernel_assert(__FILE__, __LINE__, (message)); } else {}
|
||||
@@ -17,6 +17,8 @@ section .text
|
||||
align 16
|
||||
global _start:function (_start.end - _start)
|
||||
_start:
|
||||
cli
|
||||
|
||||
extern kernel_main
|
||||
call kernel_main
|
||||
|
||||
@@ -26,3 +28,13 @@ _start:
|
||||
hlt
|
||||
jmp .hang
|
||||
.end:
|
||||
|
||||
global interrupts_enable
|
||||
interrupts_enable:
|
||||
sti
|
||||
ret
|
||||
|
||||
global interrupts_disable
|
||||
interrupts_disable:
|
||||
cli
|
||||
ret
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
const char digits[] = "0123456789abcdef";
|
||||
|
||||
console *console::default_console = nullptr;
|
||||
|
||||
console::console(const font &f, const screen &s, void *scratch, size_t len) :
|
||||
m_font(f),
|
||||
m_screen(s),
|
||||
@@ -73,6 +75,9 @@ console::console(const font &f, const screen &s, void *scratch, size_t len) :
|
||||
}
|
||||
|
||||
repaint();
|
||||
|
||||
if (default_console == nullptr)
|
||||
default_console = this;
|
||||
}
|
||||
|
||||
char *
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "kutil/coord.h"
|
||||
#include "font.h"
|
||||
#include "screen.h"
|
||||
#include "util.h"
|
||||
|
||||
struct console_data;
|
||||
|
||||
@@ -22,7 +22,10 @@ public:
|
||||
template <typename T>
|
||||
void put_hex(T x);
|
||||
|
||||
void put_dec(uint32_t x);
|
||||
template <typename T>
|
||||
void put_dec(T x);
|
||||
|
||||
static console * get() { return default_console; }
|
||||
|
||||
private:
|
||||
char * line_pointer(unsigned line);
|
||||
@@ -31,8 +34,8 @@ private:
|
||||
font m_font;
|
||||
screen m_screen;
|
||||
|
||||
coord<unsigned> m_size;
|
||||
coord<unsigned> m_pos;
|
||||
kutil::coord<unsigned> m_size;
|
||||
kutil::coord<unsigned> m_pos;
|
||||
screen::pixel_t m_fg, m_bg;
|
||||
uint16_t m_attr;
|
||||
|
||||
@@ -42,6 +45,8 @@ private:
|
||||
char *m_data;
|
||||
uint16_t *m_attrs;
|
||||
screen::pixel_t *m_palette;
|
||||
|
||||
static console *default_console;
|
||||
};
|
||||
|
||||
extern const char digits[];
|
||||
@@ -57,3 +62,17 @@ void console::put_hex(T x)
|
||||
message[chars] = 0;
|
||||
puts(message);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void console::put_dec(T x)
|
||||
{
|
||||
static const int chars = sizeof(x) * 3;
|
||||
char message[chars + 1];
|
||||
char *p = message + chars;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[x % 10];
|
||||
x /= 10;
|
||||
} while (x != 0);
|
||||
puts(++p);
|
||||
}
|
||||
156
src/kernel/device_manager.cpp
Normal file
156
src/kernel/device_manager.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/memory.h"
|
||||
#include "acpi_tables.h"
|
||||
#include "assert.h"
|
||||
#include "device_manager.h"
|
||||
#include "console.h"
|
||||
|
||||
static const char expected_signature[] = "RSD PTR ";
|
||||
|
||||
struct acpi1_rsdp
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acpi2_rsdp
|
||||
{
|
||||
char signature[8];
|
||||
uint8_t checksum10;
|
||||
char oem_id[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_address;
|
||||
|
||||
uint32_t length;
|
||||
uint64_t xsdt_address;
|
||||
uint8_t checksum20;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
uint8_t
|
||||
acpi_checksum(const void *p, size_t len, size_t off = 0)
|
||||
{
|
||||
uint8_t sum = 0;
|
||||
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
|
||||
for (int i = off; i < len; ++i) sum += c[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
bool
|
||||
acpi_table_header::validate(uint32_t expected_type) const
|
||||
{
|
||||
if (acpi_checksum(this, length) != 0) return false;
|
||||
return !expected_type || (expected_type == type);
|
||||
}
|
||||
|
||||
device_manager::device_manager(const void *root_table) :
|
||||
m_local_apic(nullptr)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
kassert(root_table != 0, "ACPI root table pointer is null.");
|
||||
|
||||
const acpi1_rsdp *acpi1 =
|
||||
reinterpret_cast<const acpi1_rsdp *>(root_table);
|
||||
|
||||
for (int i = 0; i < sizeof(acpi1->signature); ++i)
|
||||
kassert(acpi1->signature[i] == expected_signature[i],
|
||||
"ACPI RSDP table signature mismatch");
|
||||
|
||||
uint8_t sum = acpi_checksum(acpi1, sizeof(acpi1_rsdp), 0);
|
||||
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
|
||||
|
||||
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
|
||||
|
||||
const acpi2_rsdp *acpi2 =
|
||||
reinterpret_cast<const acpi2_rsdp *>(acpi1);
|
||||
|
||||
sum = acpi_checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
|
||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||
|
||||
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
|
||||
}
|
||||
|
||||
static void
|
||||
put_sig(console *cons, uint32_t type)
|
||||
{
|
||||
char sig[5] = {0,0,0,0,0};
|
||||
for (int j=0; j<4; ++j)
|
||||
sig[j] = reinterpret_cast<char *>(&type)[j];
|
||||
cons->puts(sig);
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_xsdt(const acpi_xsdt *xsdt)
|
||||
{
|
||||
kassert(xsdt && acpi_validate(xsdt), "Invalid ACPI XSDT.");
|
||||
|
||||
console *cons = console::get();
|
||||
cons->puts("ACPI 2.0 tables loading: ");
|
||||
put_sig(cons, xsdt->header.type);
|
||||
|
||||
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
|
||||
for (size_t i = 0; i < num_tables; ++i) {
|
||||
const acpi_table_header *header = xsdt->headers[i];
|
||||
|
||||
cons->puts(" ");
|
||||
put_sig(cons, header->type);
|
||||
|
||||
kassert(header->validate(), "Table failed validation.");
|
||||
|
||||
switch (header->type) {
|
||||
case acpi_apic::type_id:
|
||||
load_apic(reinterpret_cast<const acpi_apic *>(header));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cons->puts("\n");
|
||||
}
|
||||
|
||||
void
|
||||
device_manager::load_apic(const acpi_apic *apic)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
m_local_apic = reinterpret_cast<uint32_t *>(apic->local_address);
|
||||
cons->puts(" ");
|
||||
cons->put_hex(apic->local_address);
|
||||
|
||||
|
||||
uint8_t const *p = apic->controller_data;
|
||||
uint8_t const *end = p + acpi_table_entries(apic, 1);
|
||||
|
||||
while (p < end) {
|
||||
const uint8_t type = p[0];
|
||||
const uint8_t length = p[1];
|
||||
|
||||
cons->puts(" ");
|
||||
cons->put_hex(type);
|
||||
|
||||
switch (type) {
|
||||
case 0: // Local APIC
|
||||
break;
|
||||
|
||||
case 1: // I/O APIC
|
||||
m_io_apic = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
|
||||
m_global_interrupt_base = kutil::read_from<uint32_t>(p+8);
|
||||
cons->puts(" ");
|
||||
cons->put_hex((uint64_t)m_io_apic);
|
||||
cons->puts(" ");
|
||||
cons->put_hex(m_global_interrupt_base);
|
||||
break;
|
||||
}
|
||||
|
||||
p += length;
|
||||
}
|
||||
}
|
||||
25
src/kernel/device_manager.h
Normal file
25
src/kernel/device_manager.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
struct acpi_xsdt;
|
||||
struct acpi_apic;
|
||||
|
||||
class device_manager
|
||||
{
|
||||
public:
|
||||
device_manager(const void *root_table);
|
||||
|
||||
device_manager() = delete;
|
||||
device_manager(const device_manager &) = delete;
|
||||
|
||||
uint8_t * local_apic() const;
|
||||
uint8_t * io_apic() const;
|
||||
|
||||
private:
|
||||
uint32_t *m_local_apic;
|
||||
uint32_t *m_io_apic;
|
||||
|
||||
uint32_t m_global_interrupt_base;
|
||||
|
||||
void load_xsdt(const acpi_xsdt *xsdt);
|
||||
void load_apic(const acpi_apic *apic);
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/coord.h"
|
||||
#include "screen.h"
|
||||
#include "util.h"
|
||||
|
||||
class font
|
||||
{
|
||||
@@ -28,7 +28,7 @@ private:
|
||||
font();
|
||||
font(unsigned height, unsigned width, unsigned count, uint8_t const *data);
|
||||
|
||||
coord<unsigned> m_size;
|
||||
kutil::coord<unsigned> m_size;
|
||||
unsigned m_count;
|
||||
uint8_t const *m_data;
|
||||
};
|
||||
51
src/kernel/interrupt_isrs.inc
Normal file
51
src/kernel/interrupt_isrs.inc
Normal file
@@ -0,0 +1,51 @@
|
||||
ISR ( 0, isr0);
|
||||
ISR ( 1, isr1);
|
||||
ISR ( 2, isr2);
|
||||
ISR ( 3, isr3);
|
||||
ISR ( 4, isr4);
|
||||
ISR ( 5, isr5);
|
||||
ISR ( 6, isr6);
|
||||
ISR ( 7, isr7);
|
||||
EISR( 8, isr8);
|
||||
ISR ( 9, isr9);
|
||||
EISR(10, isr10);
|
||||
EISR(11, isr11);
|
||||
EISR(12, isr12);
|
||||
EISR(13, isr13);
|
||||
EISR(14, isr14);
|
||||
ISR (15, isr15);
|
||||
ISR (16, isr16);
|
||||
ISR (17, isr17);
|
||||
ISR (18, isr18);
|
||||
ISR (19, isr19);
|
||||
ISR (20, isr20);
|
||||
ISR (21, isr21);
|
||||
ISR (22, isr22);
|
||||
ISR (23, isr23);
|
||||
ISR (24, isr24);
|
||||
ISR (25, isr25);
|
||||
ISR (26, isr26);
|
||||
ISR (27, isr27);
|
||||
ISR (28, isr28);
|
||||
ISR (29, isr29);
|
||||
ISR (30, isr30);
|
||||
ISR (31, isr31);
|
||||
|
||||
IRQ (64, 0, irq0);
|
||||
IRQ (65, 1, irq1);
|
||||
IRQ (66, 2, irq2);
|
||||
IRQ (67, 3, irq3);
|
||||
IRQ (68, 4, irq4);
|
||||
IRQ (69, 5, irq5);
|
||||
IRQ (70, 6, irq6);
|
||||
IRQ (71, 7, irq7);
|
||||
IRQ (72, 8, irq8);
|
||||
IRQ (73, 9, irq9);
|
||||
IRQ (74, 10, irq10);
|
||||
IRQ (75, 11, irq11);
|
||||
IRQ (76, 12, irq12);
|
||||
IRQ (77, 13, irq13);
|
||||
IRQ (78, 14, irq14);
|
||||
IRQ (79, 15, irq15);
|
||||
|
||||
ISR (0xff, isrSpurious);
|
||||
353
src/kernel/interrupts.cpp
Normal file
353
src/kernel/interrupts.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/memory.h"
|
||||
#include "console.h"
|
||||
#include "interrupts.h"
|
||||
|
||||
enum class gdt_flags : uint8_t
|
||||
{
|
||||
ac = 0x01,
|
||||
rw = 0x02,
|
||||
dc = 0x04,
|
||||
ex = 0x08,
|
||||
r1 = 0x20,
|
||||
r2 = 0x40,
|
||||
r3 = 0x60,
|
||||
pr = 0x80,
|
||||
|
||||
res_1 = 0x10
|
||||
};
|
||||
|
||||
inline gdt_flags
|
||||
operator | (gdt_flags lhs, gdt_flags rhs)
|
||||
{
|
||||
return static_cast<gdt_flags>(
|
||||
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
struct gdt_descriptor
|
||||
{
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
uint8_t flags;
|
||||
uint8_t granularity;
|
||||
uint8_t base_high;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct idt_descriptor
|
||||
{
|
||||
uint16_t base_low;
|
||||
uint16_t selector;
|
||||
uint8_t ist;
|
||||
uint8_t flags;
|
||||
uint16_t base_mid;
|
||||
uint32_t base_high;
|
||||
uint32_t reserved; // must be zero
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct table_ptr
|
||||
{
|
||||
uint16_t limit;
|
||||
uint64_t base;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
gdt_descriptor g_gdt_table[10];
|
||||
idt_descriptor g_idt_table[256];
|
||||
table_ptr g_gdtr;
|
||||
table_ptr g_idtr;
|
||||
|
||||
|
||||
struct registers;
|
||||
|
||||
extern "C" {
|
||||
void idt_write();
|
||||
void idt_load();
|
||||
|
||||
void gdt_write();
|
||||
void gdt_load();
|
||||
|
||||
void isr_handler(registers);
|
||||
void irq_handler(registers);
|
||||
|
||||
#define ISR(i, name) extern void name ()
|
||||
#define EISR(i, name) extern void name ()
|
||||
#define IRQ(i, q, name) extern void name ()
|
||||
#include "interrupt_isrs.inc"
|
||||
#undef IRQ
|
||||
#undef EISR
|
||||
#undef ISR
|
||||
}
|
||||
|
||||
void idt_dump(const table_ptr &table);
|
||||
void gdt_dump(const table_ptr &table);
|
||||
|
||||
void
|
||||
set_gdt_entry(uint8_t i, uint32_t base, uint32_t limit, bool is64, gdt_flags flags)
|
||||
{
|
||||
g_gdt_table[i].limit_low = limit & 0xffff;
|
||||
|
||||
g_gdt_table[i].base_low = base & 0xffff;
|
||||
g_gdt_table[i].base_mid = (base >> 16) & 0xff;
|
||||
g_gdt_table[i].base_high = (base >> 24) & 0xff;
|
||||
|
||||
g_gdt_table[i].granularity =
|
||||
((limit >> 16) & 0xf) | (is64 ? 0xa0 : 0xc0);
|
||||
|
||||
g_gdt_table[i].flags = static_cast<uint8_t>(flags | gdt_flags::res_1 | gdt_flags::pr);
|
||||
}
|
||||
|
||||
void
|
||||
set_idt_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
|
||||
{
|
||||
g_idt_table[i].base_low = addr & 0xffff;
|
||||
g_idt_table[i].base_mid = (addr >> 16) & 0xffff;
|
||||
g_idt_table[i].base_high = (addr >> 32) & 0xffffffff;
|
||||
g_idt_table[i].selector = selector;
|
||||
g_idt_table[i].flags = flags;
|
||||
g_idt_table[i].ist = 0;
|
||||
g_idt_table[i].reserved = 0;
|
||||
}
|
||||
|
||||
void
|
||||
interrupts_init()
|
||||
{
|
||||
kutil::memset(&g_gdt_table, 0, sizeof(g_gdt_table));
|
||||
kutil::memset(&g_idt_table, 0, sizeof(g_idt_table));
|
||||
|
||||
g_gdtr.limit = sizeof(g_gdt_table) - 1;
|
||||
g_gdtr.base = reinterpret_cast<uint64_t>(&g_gdt_table);
|
||||
|
||||
set_gdt_entry(1, 0, 0xfffff, false, gdt_flags::rw);
|
||||
set_gdt_entry(2, 0, 0xfffff, false, gdt_flags::rw | gdt_flags::ex | gdt_flags::dc);
|
||||
set_gdt_entry(3, 0, 0xfffff, false, gdt_flags::rw);
|
||||
set_gdt_entry(4, 0, 0xfffff, false, gdt_flags::rw | gdt_flags::ex);
|
||||
|
||||
set_gdt_entry(6, 0, 0xfffff, false, gdt_flags::rw);
|
||||
set_gdt_entry(7, 0, 0xfffff, true, gdt_flags::rw | gdt_flags::ex);
|
||||
|
||||
gdt_write();
|
||||
|
||||
g_idtr.limit = sizeof(g_idt_table) - 1;
|
||||
g_idtr.base = reinterpret_cast<uint64_t>(&g_idt_table);
|
||||
|
||||
#define ISR(i, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
|
||||
#define EISR(i, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
|
||||
#define IRQ(i, q, name) set_idt_entry(i, reinterpret_cast<uint64_t>(& name), 0x38, 0x8e);
|
||||
#include "interrupt_isrs.inc"
|
||||
#undef IRQ
|
||||
#undef EISR
|
||||
#undef ISR
|
||||
|
||||
idt_write();
|
||||
}
|
||||
|
||||
struct registers
|
||||
{
|
||||
uint64_t ds;
|
||||
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
|
||||
uint64_t interrupt, errorcode;
|
||||
uint64_t rip, cs, eflags, user_esp, ss;
|
||||
};
|
||||
|
||||
#define print_reg(name, value) \
|
||||
cons->puts(" " name ": "); \
|
||||
cons->put_hex((value)); \
|
||||
cons->puts("\n");
|
||||
|
||||
void
|
||||
isr_handler(registers regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
cons->puts("received ISR interrupt:\n");
|
||||
|
||||
print_reg("ISR", regs.interrupt);
|
||||
print_reg("ERR", regs.errorcode);
|
||||
console::get()->puts("\n");
|
||||
|
||||
print_reg(" ds", regs.ds);
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
print_reg("rbp", regs.rbp);
|
||||
print_reg("rsp", regs.rsp);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
print_reg("rax", regs.rax);
|
||||
console::get()->puts("\n");
|
||||
|
||||
print_reg("rip", regs.rip);
|
||||
print_reg(" cs", regs.cs);
|
||||
print_reg(" ef", regs.eflags);
|
||||
print_reg("esp", regs.user_esp);
|
||||
print_reg(" ss", regs.ss);
|
||||
while(1) asm("hlt");
|
||||
}
|
||||
|
||||
void
|
||||
irq_handler(registers regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
cons->puts("received IRQ interrupt:\n");
|
||||
|
||||
print_reg("ISR", regs.interrupt);
|
||||
print_reg("ERR", regs.errorcode);
|
||||
console::get()->puts("\n");
|
||||
|
||||
print_reg(" ds", regs.ds);
|
||||
print_reg("rdi", regs.rdi);
|
||||
print_reg("rsi", regs.rsi);
|
||||
print_reg("rbp", regs.rbp);
|
||||
print_reg("rsp", regs.rsp);
|
||||
print_reg("rbx", regs.rbx);
|
||||
print_reg("rdx", regs.rdx);
|
||||
print_reg("rcx", regs.rcx);
|
||||
print_reg("rax", regs.rax);
|
||||
console::get()->puts("\n");
|
||||
|
||||
print_reg("rip", regs.rip);
|
||||
print_reg(" cs", regs.cs);
|
||||
print_reg(" ef", regs.eflags);
|
||||
print_reg("esp", regs.user_esp);
|
||||
print_reg(" ss", regs.ss);
|
||||
while(1) asm("hlt");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gdt_dump(const table_ptr &table)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
cons->puts("Loaded GDT at: ");
|
||||
cons->put_hex(table.base);
|
||||
cons->puts(" size: ");
|
||||
cons->put_dec(table.limit + 1);
|
||||
cons->puts(" bytes\n");
|
||||
|
||||
int count = (table.limit + 1) / sizeof(gdt_descriptor);
|
||||
const gdt_descriptor *gdt =
|
||||
reinterpret_cast<const gdt_descriptor *>(table.base);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint32_t base =
|
||||
(gdt[i].base_high << 24) |
|
||||
(gdt[i].base_mid << 16) |
|
||||
gdt[i].base_low;
|
||||
|
||||
uint32_t limit =
|
||||
static_cast<uint32_t>(gdt[i].granularity & 0x0f) << 16 |
|
||||
gdt[i].limit_low;
|
||||
|
||||
cons->puts(" Entry ");
|
||||
cons->put_dec(i);
|
||||
|
||||
cons->puts(": Base ");
|
||||
cons->put_hex(base);
|
||||
|
||||
cons->puts(" Limit ");
|
||||
cons->put_hex(limit);
|
||||
|
||||
cons->puts(" Privs ");
|
||||
cons->put_dec((gdt[i].flags >> 5) & 0x03);
|
||||
|
||||
cons->puts(" Flags ");
|
||||
|
||||
if (gdt[i].flags & 0x80) {
|
||||
cons->puts("P ");
|
||||
|
||||
if (gdt[i].flags & 0x08)
|
||||
cons->puts("EX ");
|
||||
else
|
||||
cons->puts(" ");
|
||||
|
||||
if (gdt[i].flags & 0x04)
|
||||
cons->puts("DC ");
|
||||
else
|
||||
cons->puts(" ");
|
||||
|
||||
if (gdt[i].flags & 0x02)
|
||||
cons->puts("RW ");
|
||||
else
|
||||
cons->puts(" ");
|
||||
|
||||
if (gdt[i].granularity & 0x80)
|
||||
cons->puts("KB ");
|
||||
else
|
||||
cons->puts(" B ");
|
||||
|
||||
if ((gdt[i].granularity & 0x60) == 0x20)
|
||||
cons->puts("64");
|
||||
else if ((gdt[i].granularity & 0x60) == 0x40)
|
||||
cons->puts("32");
|
||||
else
|
||||
cons->puts("16");
|
||||
}
|
||||
|
||||
cons->puts("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
idt_dump(const table_ptr &table)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
cons->puts("Loaded IDT at: ");
|
||||
cons->put_hex(table.base);
|
||||
cons->puts(" size: ");
|
||||
cons->put_dec(table.limit + 1);
|
||||
cons->puts(" bytes\n");
|
||||
|
||||
int count = (table.limit + 1) / sizeof(idt_descriptor);
|
||||
const idt_descriptor *idt =
|
||||
reinterpret_cast<const idt_descriptor *>(table.base);
|
||||
|
||||
if (count > 32) count = 32;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
uint64_t base =
|
||||
(static_cast<uint64_t>(idt[i].base_high) << 32) |
|
||||
(static_cast<uint64_t>(idt[i].base_mid) << 16) |
|
||||
idt[i].base_low;
|
||||
|
||||
cons->puts(" Entry ");
|
||||
cons->put_dec(i);
|
||||
|
||||
cons->puts(": Base ");
|
||||
cons->put_hex(base);
|
||||
|
||||
cons->puts(" Sel(");
|
||||
cons->put_dec(idt[i].selector & 0x3);
|
||||
cons->puts(",");
|
||||
cons->put_dec((idt[i].selector & 0x4) >> 2);
|
||||
cons->puts(",");
|
||||
cons->put_dec(idt[i].selector >> 3);
|
||||
cons->puts(") ");
|
||||
|
||||
cons->puts("IST ");
|
||||
cons->put_dec(idt[i].ist);
|
||||
|
||||
switch (idt[i].flags & 0xf) {
|
||||
case 0x5: cons->puts(" 32tsk "); break;
|
||||
case 0x6: cons->puts(" 16int "); break;
|
||||
case 0x7: cons->puts(" 16trp "); break;
|
||||
case 0xe: cons->puts(" 32int "); break;
|
||||
case 0xf: cons->puts(" 32trp "); break;
|
||||
default:
|
||||
cons->puts(" ?");
|
||||
cons->put_hex(idt[i].flags & 0xf);
|
||||
cons->puts(" ");
|
||||
break;
|
||||
}
|
||||
|
||||
cons->puts("DPL ");
|
||||
cons->put_dec((idt[i].flags >> 5) & 0x3);
|
||||
|
||||
if (idt[i].flags & 0x80)
|
||||
cons->puts(" P");
|
||||
|
||||
cons->puts("\n");
|
||||
}
|
||||
}
|
||||
8
src/kernel/interrupts.h
Normal file
8
src/kernel/interrupts.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
void interrupts_enable();
|
||||
void interrupts_disable();
|
||||
}
|
||||
|
||||
void interrupts_init();
|
||||
121
src/kernel/interrupts.s
Normal file
121
src/kernel/interrupts.s
Normal file
@@ -0,0 +1,121 @@
|
||||
extern g_idtr
|
||||
extern g_gdtr
|
||||
|
||||
global idt_write
|
||||
idt_write:
|
||||
lidt [rel g_idtr]
|
||||
ret
|
||||
|
||||
global idt_load
|
||||
idt_load:
|
||||
sidt [rel g_idtr]
|
||||
ret
|
||||
|
||||
global gdt_write
|
||||
gdt_write:
|
||||
lgdt [rel g_gdtr]
|
||||
ret
|
||||
|
||||
global gdt_load
|
||||
gdt_load:
|
||||
sgdt [rel g_gdtr]
|
||||
ret
|
||||
|
||||
%macro push_all_and_segments 0
|
||||
push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rbx
|
||||
push rsp
|
||||
push rbp
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
mov ax, ds
|
||||
push rax
|
||||
%endmacro
|
||||
|
||||
%macro pop_all_and_segments 0
|
||||
pop rax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rbp
|
||||
pop rsp
|
||||
pop rbx
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
%endmacro
|
||||
|
||||
%macro load_kernel_segments 0
|
||||
mov ax, 0x10 ; load the kernel data segment
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
%endmacro
|
||||
|
||||
extern isr_handler
|
||||
global isr_handler_prelude
|
||||
isr_handler_prelude:
|
||||
push_all_and_segments
|
||||
load_kernel_segments
|
||||
|
||||
call isr_handler
|
||||
|
||||
pop_all_and_segments
|
||||
|
||||
add rsp, 16 ; because the ISRs added err/num
|
||||
sti
|
||||
iretq
|
||||
|
||||
extern irq_handler
|
||||
global irq_handler_prelude
|
||||
irq_handler_prelude:
|
||||
push_all_and_segments
|
||||
load_kernel_segments
|
||||
|
||||
call irq_handler
|
||||
|
||||
pop_all_and_segments
|
||||
|
||||
add rsp, 16 ; because the ISRs added err/num
|
||||
sti
|
||||
iretq
|
||||
|
||||
%macro EMIT_ISR 2
|
||||
global %1
|
||||
%1:
|
||||
cli
|
||||
push byte 0
|
||||
push byte %2
|
||||
jmp isr_handler_prelude
|
||||
%endmacro
|
||||
|
||||
%macro EMIT_EISR 2
|
||||
global %1
|
||||
%1:
|
||||
cli
|
||||
push byte %2
|
||||
jmp isr_handler_prelude
|
||||
%endmacro
|
||||
|
||||
%macro EMIT_IRQ 2
|
||||
global %1
|
||||
%1:
|
||||
cli
|
||||
push byte 0
|
||||
push byte %2
|
||||
jmp irq_handler_prelude
|
||||
%endmacro
|
||||
|
||||
%define EISR(i, name) EMIT_EISR name, i
|
||||
%define ISR(i, name) EMIT_ISR name, i
|
||||
%define IRQ(i, q, name) EMIT_IRQ name, i
|
||||
|
||||
%include "interrupt_isrs.inc"
|
||||
@@ -2,8 +2,11 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "console.h"
|
||||
#include "device_manager.h"
|
||||
#include "font.h"
|
||||
#include "interrupts.h"
|
||||
#include "kernel_data.h"
|
||||
#include "memory.h"
|
||||
#include "screen.h"
|
||||
|
||||
extern "C" {
|
||||
@@ -14,7 +17,7 @@ extern "C" {
|
||||
console
|
||||
load_console(const popcorn_data *header)
|
||||
{
|
||||
return console{
|
||||
console cons{
|
||||
font::load(header->font),
|
||||
screen{
|
||||
header->frame_buffer,
|
||||
@@ -25,6 +28,13 @@ load_console(const popcorn_data *header)
|
||||
header->bmask},
|
||||
header->log,
|
||||
header->log_length};
|
||||
|
||||
cons.set_color(0x21, 0x00);
|
||||
cons.puts("Popcorn OS ");
|
||||
cons.set_color(0x08, 0x00);
|
||||
cons.puts(GIT_VERSION " booting...\n");
|
||||
|
||||
return cons;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -32,10 +42,21 @@ kernel_main(popcorn_data *header)
|
||||
{
|
||||
console cons = load_console(header);
|
||||
|
||||
cons.set_color(0x21, 0x00);
|
||||
cons.puts("Popcorn OS ");
|
||||
cons.set_color(0x08, 0x00);
|
||||
cons.puts(GIT_VERSION " booting...\n");
|
||||
memory_manager::create(
|
||||
header->memory_map,
|
||||
header->memory_map_length,
|
||||
header->memory_map_desc_size);
|
||||
|
||||
interrupts_init();
|
||||
interrupts_enable();
|
||||
|
||||
cons.puts("Interrupts initialized.\n");
|
||||
|
||||
device_manager devices(header->acpi_table);
|
||||
|
||||
// int x = 1 / 0;
|
||||
// __asm__ __volatile__("int $15");
|
||||
|
||||
cons.puts("boogity!");
|
||||
do_the_set_registers(header);
|
||||
}
|
||||
10
src/kernel/memory.cpp
Normal file
10
src/kernel/memory.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "assert.h"
|
||||
#include "console.h"
|
||||
#include "memory.h"
|
||||
#include "memory_pages.h"
|
||||
|
||||
memory_manager *memory_manager::s_instance = nullptr;
|
||||
|
||||
memory_manager::memory_manager(page_block *free, page_block *used, void *scratch, size_t scratch_len)
|
||||
{
|
||||
}
|
||||
21
src/kernel/memory.h
Normal file
21
src/kernel/memory.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct page_block;
|
||||
|
||||
class memory_manager
|
||||
{
|
||||
public:
|
||||
|
||||
static void create(const void *memory_map, size_t map_length, size_t desc_length);
|
||||
static memory_manager * get() { return s_instance; }
|
||||
|
||||
private:
|
||||
memory_manager(page_block *free, page_block *used, void *scratch, size_t scratch_len);
|
||||
|
||||
memory_manager() = delete;
|
||||
memory_manager(const memory_manager &) = delete;
|
||||
|
||||
static memory_manager * s_instance;
|
||||
};
|
||||
296
src/kernel/memory_bootstrap.cpp
Normal file
296
src/kernel/memory_bootstrap.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
#include "assert.h"
|
||||
#include "console.h"
|
||||
#include "memory.h"
|
||||
#include "memory_pages.h"
|
||||
|
||||
enum class efi_memory_type : uint32_t
|
||||
{
|
||||
reserved,
|
||||
loader_code,
|
||||
loader_data,
|
||||
boot_services_code,
|
||||
boot_services_data,
|
||||
runtime_services_code,
|
||||
runtime_services_data,
|
||||
available,
|
||||
unusable,
|
||||
acpi_reclaim,
|
||||
acpi_nvs,
|
||||
mmio,
|
||||
mmio_port,
|
||||
pal_code,
|
||||
persistent,
|
||||
|
||||
efi_max,
|
||||
|
||||
popcorn_kernel = 0x80000000,
|
||||
popcorn_font,
|
||||
popcorn_data,
|
||||
popcorn_log,
|
||||
popcorn_pml4,
|
||||
|
||||
popcorn_max
|
||||
};
|
||||
|
||||
const char *efi_memory_type_names[] = {
|
||||
" reserved",
|
||||
" loader_code",
|
||||
" loader_data",
|
||||
" boot_services_code",
|
||||
" boot_services_data",
|
||||
"runtime_services_code",
|
||||
"runtime_services_data",
|
||||
" available",
|
||||
" unusable",
|
||||
" acpi_reclaim",
|
||||
" acpi_nvs",
|
||||
" mmio",
|
||||
" mmio_port",
|
||||
" pal_code",
|
||||
|
||||
" popcorn_kernel",
|
||||
" popcorn_font",
|
||||
" popcorn_data",
|
||||
" popcorn_log",
|
||||
" popcorn_pml4",
|
||||
};
|
||||
|
||||
static const char *
|
||||
get_efi_name(efi_memory_type t)
|
||||
{
|
||||
static const unsigned offset =
|
||||
(unsigned)efi_memory_type::popcorn_kernel - (unsigned)efi_memory_type::efi_max;
|
||||
|
||||
return t >= efi_memory_type::popcorn_kernel ?
|
||||
efi_memory_type_names[(unsigned)t - offset] :
|
||||
efi_memory_type_names[(unsigned)t];
|
||||
}
|
||||
|
||||
enum class efi_memory_flag : uint64_t
|
||||
{
|
||||
can_mark_uc = 0x0000000000000001, // uc = un-cacheable
|
||||
can_mark_wc = 0x0000000000000002, // wc = write-combining
|
||||
can_mark_wt = 0x0000000000000004, // wt = write through
|
||||
can_mark_wb = 0x0000000000000008, // wb = write back
|
||||
can_mark_uce = 0x0000000000000010, // uce = un-cacheable exported
|
||||
can_mark_wp = 0x0000000000001000, // wp = write protected
|
||||
can_mark_rp = 0x0000000000002000, // rp = read protected
|
||||
can_mark_xp = 0x0000000000004000, // xp = exceute protected
|
||||
can_mark_ro = 0x0000000000020000, // ro = read only
|
||||
|
||||
non_volatile = 0x0000000000008000,
|
||||
more_reliable = 0x0000000000010000,
|
||||
runtime = 0x8000000000000000
|
||||
};
|
||||
IS_BITFIELD(efi_memory_flag);
|
||||
|
||||
struct efi_memory_descriptor
|
||||
{
|
||||
efi_memory_type type;
|
||||
uint32_t pad;
|
||||
uint64_t physical_start;
|
||||
uint64_t virtual_start;
|
||||
uint64_t pages;
|
||||
efi_memory_flag flags;
|
||||
};
|
||||
|
||||
static const efi_memory_descriptor *
|
||||
desc_incr(const efi_memory_descriptor *d, size_t desc_length)
|
||||
{
|
||||
return reinterpret_cast<const efi_memory_descriptor *>(
|
||||
reinterpret_cast<const uint8_t *>(d) + desc_length);
|
||||
}
|
||||
|
||||
struct page_table
|
||||
{
|
||||
uint64_t entries[512];
|
||||
page_table * next(int i) const { return reinterpret_cast<page_table *>(entries[i] & ~0xfffull); }
|
||||
};
|
||||
|
||||
static unsigned
|
||||
count_table_pages_needed(page_block *used)
|
||||
{
|
||||
page_table_indices last_idx{~0ull};
|
||||
unsigned counts[] = {1, 0, 0, 0};
|
||||
|
||||
for (page_block *cur = used; cur; cur = cur->next) {
|
||||
if (!cur->has_flag(page_block_flags::mapped))
|
||||
continue;
|
||||
|
||||
page_table_indices start{cur->virtual_address};
|
||||
page_table_indices end{cur->virtual_address + (cur->count * 0x1000)};
|
||||
|
||||
counts[1] +=
|
||||
((start[0] == last_idx[0]) ? 0 : 1) +
|
||||
(end[0] - start[0]);
|
||||
|
||||
counts[2] +=
|
||||
((start[0] == last_idx[0] &&
|
||||
start[1] == last_idx[1]) ? 0 : 1) +
|
||||
(end[1] - start[1]);
|
||||
|
||||
counts[3] +=
|
||||
((start[0] == last_idx[0] &&
|
||||
start[1] == last_idx[1] &&
|
||||
start[2] == last_idx[2]) ? 0 : 1) +
|
||||
(end[2] - start[2]);
|
||||
|
||||
last_idx = end;
|
||||
}
|
||||
|
||||
return counts[0] + counts[1] + counts[2] + counts[3];
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
memory_manager::create(const void *memory_map, size_t map_length, size_t desc_length)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
// The bootloader reserved 4 pages for page tables, which we'll use to bootstrap.
|
||||
// The first one is the already-installed PML4, so grab it from CR3.
|
||||
page_table *tables = nullptr;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (tables) );
|
||||
|
||||
// Now go through EFi's memory map and find a 4MiB region of free space to
|
||||
// use as a scratch space. We'll use the 2MiB that fits naturally aligned
|
||||
// into a single page table.
|
||||
efi_memory_descriptor const *desc =
|
||||
reinterpret_cast<efi_memory_descriptor const *>(memory_map);
|
||||
efi_memory_descriptor const *end = desc_incr(desc, map_length);
|
||||
|
||||
while (desc < end) {
|
||||
if (desc->type == efi_memory_type::available && desc->pages >= 1024)
|
||||
break;
|
||||
|
||||
desc = desc_incr(desc, desc_length);
|
||||
}
|
||||
kassert(desc < end, "Couldn't find 4MiB of contiguous scratch space.");
|
||||
|
||||
uint64_t free_region = (desc->physical_start & 0x1fffff) == 0 ?
|
||||
desc->physical_start :
|
||||
desc->physical_start + 0x1fffff & ~0x1fffffull;
|
||||
|
||||
// Offset-map this region into the higher half.
|
||||
uint64_t next_free = free_region + 0xffff800000000000;
|
||||
|
||||
cons->puts("Found region: ");
|
||||
cons->put_hex(free_region);
|
||||
cons->puts("\n");
|
||||
|
||||
// We'll need to copy any existing tables (except the PML4 which the
|
||||
// bootloader gave us) into our 4 reserved pages so we can edit them.
|
||||
page_table_indices fr_idx{free_region};
|
||||
fr_idx[0] += 256; // Flip the highest bit of the address
|
||||
|
||||
if (tables[0].entries[fr_idx[0]] & 0x1) {
|
||||
page_table *old_pdpt = tables[0].next(fr_idx[0]);
|
||||
for (int i = 0; i < 512; ++i) tables[1].entries[i] = old_pdpt->entries[i];
|
||||
} else {
|
||||
for (int i = 0; i < 512; ++i) tables[1].entries[i] = 0;
|
||||
}
|
||||
tables[0].entries[fr_idx[0]] = reinterpret_cast<uint64_t>(&tables[1]) | 0xb;
|
||||
|
||||
if (tables[1].entries[fr_idx[1]] & 0x1) {
|
||||
page_table *old_pdt = tables[1].next(fr_idx[1]);
|
||||
for (int i = 0; i < 512; ++i) tables[2].entries[i] = old_pdt->entries[i];
|
||||
} else {
|
||||
for (int i = 0; i < 512; ++i) tables[2].entries[i] = 0;
|
||||
}
|
||||
tables[1].entries[fr_idx[1]] = reinterpret_cast<uint64_t>(&tables[2]) | 0xb;
|
||||
|
||||
for (int i = 0; i < 512; ++i)
|
||||
tables[3].entries[i] = (free_region + 0x1000 * i) | 0xb;
|
||||
tables[2].entries[fr_idx[2]] = reinterpret_cast<uint64_t>(&tables[3]) | 0xb;
|
||||
|
||||
// We now have 2MiB starting at "free_region" to bootstrap ourselves. Start by
|
||||
// taking inventory of free pages.
|
||||
page_block *block_list = reinterpret_cast<page_block *>(next_free);
|
||||
|
||||
int i = 0;
|
||||
page_block *free_head = nullptr, **free = &free_head;
|
||||
page_block *used_head = nullptr, **used = &used_head;
|
||||
|
||||
desc = reinterpret_cast<efi_memory_descriptor const *>(memory_map);
|
||||
while (desc < end) {
|
||||
page_block *block = &block_list[i++];
|
||||
block->physical_address = desc->physical_start;
|
||||
block->virtual_address = desc->virtual_start;
|
||||
block->count = desc->pages;
|
||||
block->next = nullptr;
|
||||
|
||||
switch (desc->type) {
|
||||
case efi_memory_type::loader_code:
|
||||
case efi_memory_type::loader_data:
|
||||
block->flags = page_block_flags::used | page_block_flags::pending_free;
|
||||
break;
|
||||
|
||||
case efi_memory_type::boot_services_code:
|
||||
case efi_memory_type::boot_services_data:
|
||||
case efi_memory_type::available:
|
||||
if (free_region >= block->physical_address && free_region < block->end()) {
|
||||
// This is the scratch memory block, split off what we're not using
|
||||
block->virtual_address = block->physical_address + 0xffff800000000000;
|
||||
|
||||
block->flags = page_block_flags::used
|
||||
| page_block_flags::mapped
|
||||
| page_block_flags::pending_free;
|
||||
|
||||
if (block->count > 1024) {
|
||||
page_block *rest = &block_list[i++];
|
||||
rest->physical_address = desc->physical_start + (1024*0x1000);
|
||||
rest->virtual_address = 0;
|
||||
rest->count = desc->pages - 1024;
|
||||
rest->next = nullptr;
|
||||
*free = rest;
|
||||
free = &rest->next;
|
||||
|
||||
block->count = 1024;
|
||||
}
|
||||
} else {
|
||||
block->flags = page_block_flags::free;
|
||||
}
|
||||
break;
|
||||
|
||||
case efi_memory_type::acpi_reclaim:
|
||||
block->flags = page_block_flags::used | page_block_flags::acpi_wait;
|
||||
break;
|
||||
|
||||
case efi_memory_type::persistent:
|
||||
block->flags = page_block_flags::nonvolatile;
|
||||
break;
|
||||
|
||||
default:
|
||||
block->flags = page_block_flags::used | page_block_flags::permanent;
|
||||
break;
|
||||
}
|
||||
|
||||
if (block->has_flag(page_block_flags::used)) {
|
||||
if (block->virtual_address != 0)
|
||||
block->flags |= page_block_flags::mapped;
|
||||
*used = block;
|
||||
used = &block->next;
|
||||
} else {
|
||||
*free = block;
|
||||
free = &block->next;
|
||||
}
|
||||
|
||||
desc = desc_incr(desc, desc_length);
|
||||
}
|
||||
|
||||
// Update the pointer to the next free page
|
||||
next_free += i * sizeof(page_block);
|
||||
next_free = ((next_free - 1) & ~0xfffull) + 0x1000;
|
||||
|
||||
// Now go back through these lists and consolidate
|
||||
free_head->list_consolidate();
|
||||
used_head->list_consolidate();
|
||||
|
||||
// Ok, now build an acutal set of kernel page tables that just contains
|
||||
// what the kernel actually has mapped.
|
||||
unsigned table_page_count = count_table_pages_needed(used_head);
|
||||
|
||||
cons->puts("To map currently-mapped pages, we need ");
|
||||
cons->put_dec(table_page_count);
|
||||
cons->puts(" pages of tables.\n");
|
||||
}
|
||||
74
src/kernel/memory_pages.cpp
Normal file
74
src/kernel/memory_pages.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "console.h"
|
||||
#include "memory_pages.h"
|
||||
|
||||
page_block *
|
||||
page_block::list_consolidate()
|
||||
{
|
||||
page_block *cur = this;
|
||||
page_block *freed_head = nullptr, **freed = &freed_head;
|
||||
|
||||
while (cur) {
|
||||
page_block *next = cur->next;
|
||||
|
||||
if (next && cur->flags == next->flags &&
|
||||
cur->end() == next->physical_address)
|
||||
{
|
||||
cur->count += next->count;
|
||||
cur->next = next->next;
|
||||
|
||||
next->next = 0;
|
||||
*freed = next;
|
||||
freed = &next->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
cur = next;
|
||||
}
|
||||
|
||||
return freed_head;
|
||||
}
|
||||
|
||||
void
|
||||
page_block::list_dump(const char *name)
|
||||
{
|
||||
console *cons = console::get();
|
||||
cons->puts("Block list");
|
||||
if (name) {
|
||||
cons->puts(" ");
|
||||
cons->puts(name);
|
||||
}
|
||||
cons->puts(":\n");
|
||||
|
||||
int count = 0;
|
||||
for (page_block *cur = this; cur; cur = cur->next) {
|
||||
cons->puts(" ");
|
||||
cons->put_hex(cur->physical_address);
|
||||
cons->puts(" ");
|
||||
cons->put_hex((uint32_t)cur->flags);
|
||||
if (cur->virtual_address) {
|
||||
cons->puts(" ");
|
||||
cons->put_hex(cur->virtual_address);
|
||||
}
|
||||
cons->puts(" [");
|
||||
cons->put_dec(cur->count);
|
||||
cons->puts("]\n");
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
cons->puts(" Total: ");
|
||||
cons->put_dec(count);
|
||||
cons->puts("\n");
|
||||
}
|
||||
|
||||
void
|
||||
page_table_indices::dump()
|
||||
{
|
||||
console *cons = console::get();
|
||||
cons->puts("{");
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i) cons->puts(", ");
|
||||
cons->put_dec(index[i]);
|
||||
}
|
||||
cons->puts("}");
|
||||
}
|
||||
54
src/kernel/memory_pages.h
Normal file
54
src/kernel/memory_pages.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "kutil/enum_bitfields.h"
|
||||
|
||||
enum class page_block_flags : uint32_t
|
||||
{
|
||||
// Not a flag value, but for comparison
|
||||
free = 0x00000000,
|
||||
|
||||
used = 0x00000001,
|
||||
mapped = 0x00000002,
|
||||
pending_free = 0x00000004,
|
||||
|
||||
nonvolatile = 0x00000010,
|
||||
acpi_wait = 0x00000020,
|
||||
|
||||
permanent = 0x80000000,
|
||||
|
||||
max_flags
|
||||
};
|
||||
IS_BITFIELD(page_block_flags);
|
||||
|
||||
struct page_block
|
||||
{
|
||||
uint64_t physical_address;
|
||||
uint64_t virtual_address;
|
||||
uint32_t count;
|
||||
page_block_flags flags;
|
||||
page_block *next;
|
||||
|
||||
bool has_flag(page_block_flags f) const { return bitfield_contains(flags, f); }
|
||||
uint64_t end() const { return physical_address + (count * 0x1000); }
|
||||
|
||||
page_block * list_consolidate();
|
||||
void list_dump(const char *name = nullptr);
|
||||
};
|
||||
|
||||
struct page_table_indices
|
||||
{
|
||||
page_table_indices(uint64_t v) :
|
||||
index{
|
||||
(v >> 39) & 0x1ff,
|
||||
(v >> 30) & 0x1ff,
|
||||
(v >> 21) & 0x1ff,
|
||||
(v >> 12) & 0x1ff }
|
||||
{}
|
||||
|
||||
uint64_t & operator[](size_t i) { return index[i]; }
|
||||
uint64_t index[4];
|
||||
|
||||
void dump();
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "kutil/coord.h"
|
||||
|
||||
class screen
|
||||
{
|
||||
@@ -36,5 +36,5 @@ private:
|
||||
|
||||
pixel_t *m_framebuffer;
|
||||
color_masks m_masks;
|
||||
coord<unsigned> m_resolution;
|
||||
kutil::coord<unsigned> m_resolution;
|
||||
};
|
||||
@@ -1,9 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace kutil {
|
||||
|
||||
template <typename T>
|
||||
struct coord {
|
||||
struct coord
|
||||
{
|
||||
T x, y;
|
||||
coord() : x(T{}), y(T{}) {}
|
||||
coord(T x, T y) : x(x), y(y) {}
|
||||
T size() const { return x * y; }
|
||||
};
|
||||
|
||||
} // namespace kutil
|
||||
90
src/modules/kutil/enum_bitfields.h
Normal file
90
src/modules/kutil/enum_bitfields.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template<typename E>
|
||||
struct is_enum_bitfield { static constexpr bool value = false; };
|
||||
|
||||
#define IS_BITFIELD(name) \
|
||||
template<> struct is_enum_bitfield<name> {static constexpr bool value=true;}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator & (E lhs, E rhs)
|
||||
{
|
||||
return static_cast<E> (
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) &
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator | (E lhs, E rhs)
|
||||
{
|
||||
return static_cast<E> (
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) |
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator ^ (E lhs, E rhs)
|
||||
{
|
||||
return static_cast<E> (
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) ^
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type
|
||||
operator ~ (E rhs)
|
||||
{
|
||||
return static_cast<E>(~static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||
operator |= (E &lhs, E rhs)
|
||||
{
|
||||
lhs = static_cast<E>(
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) |
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||
operator &= (E &lhs, E rhs)
|
||||
{
|
||||
lhs = static_cast<E>(
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) &
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,E>::type&
|
||||
operator ^= (E &lhs, E rhs)
|
||||
{
|
||||
lhs = static_cast<E>(
|
||||
static_cast<typename std::underlying_type<E>::type>(lhs) ^
|
||||
static_cast<typename std::underlying_type<E>::type>(rhs));
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
|
||||
operator ! (E rhs)
|
||||
{
|
||||
return static_cast<typename std::underlying_type<E>::type>(rhs) == 0;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
typename std::enable_if<is_enum_bitfield<E>::value,bool>::type
|
||||
bitfield_contains(E set, E flag)
|
||||
{
|
||||
return (set & flag) == flag;
|
||||
}
|
||||
13
src/modules/kutil/memory.cpp
Normal file
13
src/modules/kutil/memory.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "memory.h"
|
||||
|
||||
namespace kutil {
|
||||
|
||||
void *
|
||||
memset(void *s, uint8_t v, size_t n)
|
||||
{
|
||||
uint8_t *p = reinterpret_cast<uint8_t *>(s);
|
||||
for (int i = 0; i < n; ++i) p[i] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace kutil
|
||||
13
src/modules/kutil/memory.h
Normal file
13
src/modules/kutil/memory.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace kutil {
|
||||
|
||||
void * memset(void *p, uint8_t v, size_t n);
|
||||
|
||||
template <typename T>
|
||||
T read_from(const void *p) { return *reinterpret_cast<const T *>(p); }
|
||||
|
||||
} // namespace kutil
|
||||
12
src/modules/kutil/misc.h
Normal file
12
src/modules/kutil/misc.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
namespace kutil {
|
||||
|
||||
constexpr uint32_t
|
||||
byteswap(uint32_t x)
|
||||
{
|
||||
return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00)
|
||||
| ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
MOD_NAME := main
|
||||
MOD_NAME := kutil
|
||||
include modules.mk
|
||||
Reference in New Issue
Block a user