mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3e49590a7 | ||
|
|
358837ed69 | ||
|
|
2a353830c2 | ||
|
|
14f51436d7 | ||
|
|
57e5465c2d | ||
|
|
d9619e65a2 | ||
|
|
9754994e0c | ||
|
|
34c894b15d | ||
|
|
a2665d9247 | ||
|
|
1e3ae67646 | ||
|
|
25b9625635 | ||
|
|
2404b22c1f | ||
|
|
bed882f41c | ||
|
|
fd9e0944cb | ||
|
|
7e462319c9 | ||
|
|
94de87ef86 | ||
|
|
eb13f1f4fb | ||
|
|
0a6c39ded4 | ||
|
|
ff1aac64c1 | ||
|
|
ef24894211 | ||
|
|
1113164505 | ||
|
|
1de73de2e3 | ||
|
|
571cc5a1da | ||
|
|
8cb0803605 | ||
|
|
95d52b87f4 | ||
|
|
07fd3abe2c | ||
|
|
5dedd2e0e0 | ||
|
|
57abb03deb | ||
|
|
4a38a74b16 | ||
|
|
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
|
*.bak
|
||||||
tags
|
tags
|
||||||
.gdbinit
|
.gdbinit
|
||||||
|
popcorn.log
|
||||||
|
|||||||
80
Makefile
80
Makefile
@@ -3,14 +3,15 @@ ARCH ?= x86_64
|
|||||||
include src/arch/$(ARCH)/config.mk
|
include src/arch/$(ARCH)/config.mk
|
||||||
|
|
||||||
BUILD_D := build
|
BUILD_D := build
|
||||||
|
KERN_D := src/kernel
|
||||||
ARCH_D := src/arch/$(ARCH)
|
ARCH_D := src/arch/$(ARCH)
|
||||||
VERSION ?= $(shell git describe --dirty --always)
|
VERSION ?= $(shell git describe --dirty --always)
|
||||||
GITSHA ?= $(shell git rev-parse --short HEAD)
|
GITSHA ?= $(shell git rev-parse --short HEAD)
|
||||||
|
|
||||||
KERNEL_FILENAME:= popcorn.bin
|
KERNEL_FILENAME:= popcorn.elf
|
||||||
KERNEL_FONT := assets/fonts/tamsyn8x16r.psf
|
KERNEL_FONT := assets/fonts/tamsyn8x16r.psf
|
||||||
|
|
||||||
MODULES := main
|
MODULES := kutil
|
||||||
|
|
||||||
|
|
||||||
EFI_DIR := external/gnu-efi
|
EFI_DIR := external/gnu-efi
|
||||||
@@ -50,20 +51,21 @@ WARNFLAGS += -Wno-attributes -Wno-sign-compare -Wno-multichar
|
|||||||
WARNFLAGS += -Wno-div-by-zero -Wno-endif-labels -Wno-pragmas
|
WARNFLAGS += -Wno-div-by-zero -Wno-endif-labels -Wno-pragmas
|
||||||
WARNFLAGS += -Wno-format-extra-args -Wno-unused-result
|
WARNFLAGS += -Wno-format-extra-args -Wno-unused-result
|
||||||
WARNFLAGS += -Wno-deprecated-declarations -Wno-unused-function
|
WARNFLAGS += -Wno-deprecated-declarations -Wno-unused-function
|
||||||
WARNFLAGS += -Wno-unused-but-set-parameter
|
|
||||||
|
|
||||||
ASFLAGS ?=
|
ASFLAGS ?=
|
||||||
ASFLAGS += -p $(BUILD_D)/versions.s
|
ASFLAGS += -p $(BUILD_D)/versions.s
|
||||||
|
|
||||||
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||||
CFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
CFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
||||||
CFLAGS += -std=c11
|
CFLAGS += -std=c11 -mcmodel=large
|
||||||
|
|
||||||
CXXFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
CXXFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||||
CXXFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
CXXFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
||||||
CXXFLAGS += -std=c++14
|
CXXFLAGS += -std=c++14 -mcmodel=large
|
||||||
|
CXXFLAGS += -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
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 += -DKERNEL_FILENAME="L\"$(KERNEL_FILENAME)\""
|
||||||
BOOT_CFLAGS += -DGIT_VERSION_WIDE="L\"$(VERSION)\""
|
BOOT_CFLAGS += -DGIT_VERSION_WIDE="L\"$(VERSION)\""
|
||||||
BOOT_CFLAGS += -DGNU_EFI_USE_MS_ABI -DHAVE_USE_MS_ABI
|
BOOT_CFLAGS += -DGNU_EFI_USE_MS_ABI -DHAVE_USE_MS_ABI
|
||||||
@@ -74,7 +76,7 @@ ifdef MAX_HRES
|
|||||||
BOOT_CFLAGS += -DMAX_HRES=$(MAX_HRES)
|
BOOT_CFLAGS += -DMAX_HRES=$(MAX_HRES)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LDFLAGS := -L $(BUILD_D) -ggdb
|
LDFLAGS := -L $(BUILD_D) -g
|
||||||
LDFLAGS += -nostdlib -znocombreloc -Bsymbolic -nostartfiles
|
LDFLAGS += -nostdlib -znocombreloc -Bsymbolic -nostartfiles
|
||||||
|
|
||||||
BOOT_LDFLAGS := $(LDFLAGS) -shared
|
BOOT_LDFLAGS := $(LDFLAGS) -shared
|
||||||
@@ -92,12 +94,15 @@ INIT_DEP := $(BUILD_D)/.builddir
|
|||||||
|
|
||||||
BOOT_SRCS := $(wildcard src/boot/*.c)
|
BOOT_SRCS := $(wildcard src/boot/*.c)
|
||||||
BOBJS += $(patsubst src/boot/%,$(BUILD_D)/boot/%,$(patsubst %,%.o,$(BOOT_SRCS)))
|
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)
|
KERN_SRCS := $(wildcard $(KERN_D)/*.s)
|
||||||
ARCH_SRCS += $(wildcard $(ARCH_D)/*.c)
|
KERN_SRCS += $(wildcard $(KERN_D)/*.c)
|
||||||
KOBJS += $(patsubst $(ARCH_D)/%,$(BUILD_D)/arch/%,$(patsubst %,%.o,$(ARCH_SRCS)))
|
KERN_SRCS += $(wildcard $(KERN_D)/*.cpp)
|
||||||
DEPS := $(patsubst $(ARCH_D)/%,$(BUILD_D)/arch/%,$(patsubst %,%.d,$(ARCH_SRCS)))
|
KERN_SRCS += $(wildcard $(ARCH_D)/*.s)
|
||||||
|
KERN_SRCS += $(wildcard $(ARCH_D)/*.c)
|
||||||
|
|
||||||
|
KERN_OBJS := $(patsubst src/%, $(BUILD_D)/%, $(patsubst %,%.o,$(KERN_SRCS)))
|
||||||
|
|
||||||
MOD_TARGETS :=
|
MOD_TARGETS :=
|
||||||
|
|
||||||
PARTED ?= /sbin/parted
|
PARTED ?= /sbin/parted
|
||||||
@@ -108,9 +113,9 @@ OVMF ?= assets/ovmf/x64/OVMF.fd
|
|||||||
|
|
||||||
QEMUOPTS := -pflash $(BUILD_D)/flash.img
|
QEMUOPTS := -pflash $(BUILD_D)/flash.img
|
||||||
QEMUOPTS += -drive file=$(BUILD_D)/fs.img,format=raw
|
QEMUOPTS += -drive file=$(BUILD_D)/fs.img,format=raw
|
||||||
QEMUOPTS += -smp $(CPUS)
|
QEMUOPTS += -smp $(CPUS) -m 512
|
||||||
QEMUOPTS += -m 512
|
QEMUOPTS += -d mmu,guest_errors,int -D popcorn.log
|
||||||
QEMUOPTS += -d guest_errors
|
QEMUOPTS += -no-reboot
|
||||||
QEMUOPTS += $(QEMUEXTRA)
|
QEMUOPTS += $(QEMUEXTRA)
|
||||||
|
|
||||||
|
|
||||||
@@ -120,19 +125,21 @@ init: $(INIT_DEP)
|
|||||||
$(INIT_DEP):
|
$(INIT_DEP):
|
||||||
mkdir -p $(BUILD_D) $(patsubst %,$(BUILD_D)/d.%,$(MODULES))
|
mkdir -p $(BUILD_D) $(patsubst %,$(BUILD_D)/d.%,$(MODULES))
|
||||||
mkdir -p $(BUILD_D)/boot
|
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)
|
touch $(INIT_DEP)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_D)/* $(BUILD_D)/.version $(BUILD_D)/.builddir
|
rm -rf $(BUILD_D)/* $(BUILD_D)/.version $(BUILD_D)/.builddir
|
||||||
|
|
||||||
|
vars:
|
||||||
|
@echo "KERN_SRCS: " $(KERN_SRCS)
|
||||||
|
@echo "KERN_OBJS: " $(KERN_OBJS)
|
||||||
|
|
||||||
dist-clean: clean
|
dist-clean: clean
|
||||||
make -C external/gnu-efi clean
|
make -C external/gnu-efi clean
|
||||||
|
|
||||||
dump: $(BUILD_D)/kernel.dump
|
.PHONY: all clean dist-clean init vars
|
||||||
vim $<
|
|
||||||
|
|
||||||
.PHONY: all clean dist-clean init dump
|
|
||||||
|
|
||||||
$(BUILD_D)/.version:
|
$(BUILD_D)/.version:
|
||||||
echo '$(VERSION)' | cmp -s - $@ || echo '$(VERSION)' > $@
|
echo '$(VERSION)' | cmp -s - $@ || echo '$(VERSION)' > $@
|
||||||
@@ -141,7 +148,7 @@ $(BUILD_D)/versions.s:
|
|||||||
./parse_version.py "$(VERSION)" "$(GITSHA)" > $@
|
./parse_version.py "$(VERSION)" "$(GITSHA)" > $@
|
||||||
|
|
||||||
-include x $(patsubst %,src/modules/%/module.mk,$(MODULES))
|
-include x $(patsubst %,src/modules/%/module.mk,$(MODULES))
|
||||||
-include x $(DEPS)
|
-include x $(shell find $(BUILD_D) -type f -name '*.d')
|
||||||
|
|
||||||
$(EFI_LIB):
|
$(EFI_LIB):
|
||||||
make -C external/gnu-efi all
|
make -C external/gnu-efi all
|
||||||
@@ -162,38 +169,36 @@ $(BUILD_D)/boot.debug.efi: $(BUILD_D)/boot.elf
|
|||||||
-j .debug_aranges -j .debug_line -j .debug_macinfo \
|
-j .debug_aranges -j .debug_line -j .debug_macinfo \
|
||||||
--target=efi-app-$(ARCH) $^ $@
|
--target=efi-app-$(ARCH) $^ $@
|
||||||
|
|
||||||
$(BUILD_D)/%.bin: $(BUILD_D)/%.elf
|
|
||||||
$(OBJC) $< -O binary $@
|
|
||||||
|
|
||||||
$(BUILD_D)/boot.dump: $(BUILD_D)/boot.efi
|
$(BUILD_D)/boot.dump: $(BUILD_D)/boot.efi
|
||||||
$(OBJD) -D -S $< > $@
|
$(OBJD) -D -S $< > $@
|
||||||
|
|
||||||
$(BUILD_D)/boot/%.s.o: src/boot/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
$(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)
|
$(BUILD_D)/boot/%.c.o: src/boot/%.c $(INIT_DEP)
|
||||||
$(CC) $(BOOT_CFLAGS) -c -o $@ $<
|
$(CC) $(BOOT_CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(BUILD_D)/kernel.elf: $(KOBJS) $(MOD_TARGETS) $(ARCH_D)/kernel.ld
|
$(BUILD_D)/kernel.elf: $(KERN_OBJS) $(MOD_TARGETS) $(ARCH_D)/kernel.ld
|
||||||
$(LD) $(LDFLAGS) -u _header -T $(ARCH_D)/kernel.ld -o $@ $(patsubst %,-l%,$(MODULES)) $(KOBJS)
|
$(LD) $(LDFLAGS) -u _header -T $(ARCH_D)/kernel.ld -o $@ $(KERN_OBJS) $(patsubst %,-l%,$(MODULES))
|
||||||
$(OBJC) --only-keep-debug $@ $@.sym
|
$(OBJC) --only-keep-debug $@ $@.sym
|
||||||
|
$(OBJD) -D -S $@ > $@.dump
|
||||||
|
|
||||||
$(BUILD_D)/kernel.dump: $(BUILD_D)/kernel.elf
|
$(BUILD_D)/%.s.o: src/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||||
$(OBJD) -D -S $< > $@
|
$(AS) $(ASFLAGS) -i $(ARCH_D)/ -i $(KERN_D)/ -o $@ $<
|
||||||
|
|
||||||
$(BUILD_D)/arch/%.s.o: $(ARCH_D)/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
$(BUILD_D)/%.c.o: src/%.c $(INIT_DEP)
|
||||||
$(AS) $(ASFLAGS) -o $@ $<
|
|
||||||
|
|
||||||
$(BUILD_D)/arch/%.c.o: $(ARCH_D)/%.c $(INIT_DEP)
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(BUILD_D)/%.cpp.o: src/%.cpp $(INIT_DEP)
|
||||||
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(BUILD_D)/flash.img: $(OVMF)
|
$(BUILD_D)/flash.img: $(OVMF)
|
||||||
cp $^ $@
|
cp $^ $@
|
||||||
|
|
||||||
$(BUILD_D)/screenfont.psf: $(KERNEL_FONT)
|
$(BUILD_D)/screenfont.psf: $(KERNEL_FONT)
|
||||||
cp $^ $@
|
cp $^ $@
|
||||||
|
|
||||||
$(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.bin $(BUILD_D)/screenfont.psf
|
$(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.elf $(BUILD_D)/screenfont.psf
|
||||||
$(eval TEMPFILE := $(shell mktemp --suffix=.img))
|
$(eval TEMPFILE := $(shell mktemp --suffix=.img))
|
||||||
dd if=/dev/zero of=$@.tmp bs=512 count=93750
|
dd if=/dev/zero of=$@.tmp bs=512 count=93750
|
||||||
$(PARTED) $@.tmp -s -a minimal mklabel gpt
|
$(PARTED) $@.tmp -s -a minimal mklabel gpt
|
||||||
@@ -204,7 +209,7 @@ $(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.bin $(BUILD_D)/screenfo
|
|||||||
mmd -i $(TEMPFILE) ::/EFI
|
mmd -i $(TEMPFILE) ::/EFI
|
||||||
mmd -i $(TEMPFILE) ::/EFI/BOOT
|
mmd -i $(TEMPFILE) ::/EFI/BOOT
|
||||||
mcopy -i $(TEMPFILE) $(BUILD_D)/boot.efi ::/EFI/BOOT/BOOTX64.efi
|
mcopy -i $(TEMPFILE) $(BUILD_D)/boot.efi ::/EFI/BOOT/BOOTX64.efi
|
||||||
mcopy -i $(TEMPFILE) $(BUILD_D)/kernel.bin ::$(KERNEL_FILENAME)
|
mcopy -i $(TEMPFILE) $(BUILD_D)/kernel.elf ::$(KERNEL_FILENAME)
|
||||||
mcopy -i $(TEMPFILE) $(BUILD_D)/screenfont.psf ::screenfont.psf
|
mcopy -i $(TEMPFILE) $(BUILD_D)/screenfont.psf ::screenfont.psf
|
||||||
mlabel -i $(TEMPFILE) ::Popcorn_OS
|
mlabel -i $(TEMPFILE) ::Popcorn_OS
|
||||||
dd if=$(TEMPFILE) of=$@.tmp bs=512 count=91669 seek=2048 conv=notrunc
|
dd if=$(TEMPFILE) of=$@.tmp bs=512 count=91669 seek=2048 conv=notrunc
|
||||||
@@ -217,12 +222,15 @@ $(BUILD_D)/fs.iso: $(BUILD_D)/fs.img
|
|||||||
xorriso -as mkisofs -R -f -e fs.img -no-emul-boot -o $@ $(BUILD_D)/iso
|
xorriso -as mkisofs -R -f -e fs.img -no-emul-boot -o $@ $(BUILD_D)/iso
|
||||||
|
|
||||||
qemu: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
|
qemu: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
|
||||||
|
-rm popcorn.log
|
||||||
"$(QEMU)" $(QEMUOPTS) -nographic
|
"$(QEMU)" $(QEMUOPTS) -nographic
|
||||||
|
|
||||||
qemu-window: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
|
qemu-window: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
|
||||||
|
-rm popcorn.log
|
||||||
"$(QEMU)" $(QEMUOPTS)
|
"$(QEMU)" $(QEMUOPTS)
|
||||||
|
|
||||||
qemu-gdb: $(BUILD_D)/fs.img $(BUILD_D)/boot.debug.efi $(BUILD_D)/flash.img $(BUILD_D)/kernel.elf
|
qemu-gdb: $(BUILD_D)/fs.img $(BUILD_D)/boot.debug.efi $(BUILD_D)/flash.img $(BUILD_D)/kernel.elf
|
||||||
"$(QEMU)" $(QEMUOPTS) -d mmu,guest_errors,page -D popcorn.log -s -nographic
|
-rm popcorn.log
|
||||||
|
"$(QEMU)" $(QEMUOPTS) -s -nographic
|
||||||
|
|
||||||
# vim: ft=make ts=4
|
# vim: ft=make ts=4
|
||||||
|
|||||||
11
NOTES.md
11
NOTES.md
@@ -1,10 +1,9 @@
|
|||||||
# Design / WIP notes
|
# Design / WIP notes
|
||||||
|
|
||||||
## Bootloader / UEFI
|
## TODO
|
||||||
|
|
||||||
* 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?
|
|
||||||
|
|
||||||
|
- Better page-allocation model
|
||||||
|
- Allow for more than one IOAPIC in ACPI module
|
||||||
|
- Slab allocator for kernel structures
|
||||||
|
- mark kernel memory pages global
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ $(MOD_BUILD_D)/%.cpp.o: $(MOD_SRC_D)/%.cpp $(INIT_DEP)
|
|||||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(MOD_BUILD_D)/%.s.o: $(MOD_SRC_D)/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
$(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)))
|
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,mmu -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)
|
ENTRY(_start)
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
. = 0x100000;
|
OFFSET = 0xFFFF800000000000;
|
||||||
|
. = OFFSET + 0x100000;
|
||||||
|
|
||||||
.header : {
|
.header : {
|
||||||
header = .;
|
header = .;
|
||||||
|
|||||||
77
src/boot/elf.h
Normal file
77
src/boot/elf.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef ELF_VERSION
|
||||||
|
#define ELF_VERSION 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ELF_WORDSIZE
|
||||||
|
#define ELF_WORDSIZE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ELF_ENDIAN
|
||||||
|
#define ELF_ENDIAN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ELF_OSABI
|
||||||
|
#define ELF_OSABI 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ELF_MACHINE
|
||||||
|
#define ELF_MACHINE 0x3e
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const unsigned ELF_PT_LOAD = 1;
|
||||||
|
|
||||||
|
struct elf_ident
|
||||||
|
{
|
||||||
|
char magic[4];
|
||||||
|
|
||||||
|
uint8_t word_size;
|
||||||
|
uint8_t endianness;
|
||||||
|
uint8_t version;
|
||||||
|
uint8_t os_abi;
|
||||||
|
|
||||||
|
uint64_t reserved;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct elf_header
|
||||||
|
{
|
||||||
|
struct elf_ident ident;
|
||||||
|
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t machine;
|
||||||
|
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
uint64_t entrypoint;
|
||||||
|
uint64_t ph_offset;
|
||||||
|
uint64_t sh_offset;
|
||||||
|
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
uint16_t eh_size;
|
||||||
|
|
||||||
|
uint16_t ph_entsize;
|
||||||
|
uint16_t ph_num;
|
||||||
|
|
||||||
|
uint16_t sh_entsize;
|
||||||
|
uint16_t sh_num;
|
||||||
|
|
||||||
|
uint16_t sh_str_idx;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct elf_program_header
|
||||||
|
{
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t flags;
|
||||||
|
uint64_t offset;
|
||||||
|
|
||||||
|
uint64_t vaddr;
|
||||||
|
uint64_t paddr;
|
||||||
|
|
||||||
|
uint64_t file_size;
|
||||||
|
uint64_t mem_size;
|
||||||
|
|
||||||
|
uint64_t align;
|
||||||
|
} __attribute__ ((packed));
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include "elf.h"
|
||||||
#include "guids.h"
|
#include "guids.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
@@ -13,8 +14,7 @@ loader_alloc_pages(
|
|||||||
EFI_BOOT_SERVICES *bootsvc,
|
EFI_BOOT_SERVICES *bootsvc,
|
||||||
EFI_MEMORY_TYPE mem_type,
|
EFI_MEMORY_TYPE mem_type,
|
||||||
size_t *length,
|
size_t *length,
|
||||||
void **pages,
|
void **pages)
|
||||||
void **next)
|
|
||||||
{
|
{
|
||||||
EFI_STATUS status;
|
EFI_STATUS status;
|
||||||
|
|
||||||
@@ -33,31 +33,26 @@ loader_alloc_pages(
|
|||||||
|
|
||||||
*length = page_count * PAGE_SIZE;
|
*length = page_count * PAGE_SIZE;
|
||||||
*pages = (void *)addr;
|
*pages = (void *)addr;
|
||||||
*next = (void*)(addr + *length);
|
|
||||||
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
EFI_STATUS
|
EFI_STATUS
|
||||||
loader_load_file(
|
loader_load_font(
|
||||||
EFI_BOOT_SERVICES *bootsvc,
|
EFI_BOOT_SERVICES *bootsvc,
|
||||||
EFI_FILE_PROTOCOL *root,
|
EFI_FILE_PROTOCOL *root,
|
||||||
const CHAR16 *filename,
|
struct loader_data *data)
|
||||||
EFI_MEMORY_TYPE mem_type,
|
|
||||||
void **data,
|
|
||||||
size_t *length,
|
|
||||||
void **next)
|
|
||||||
{
|
{
|
||||||
EFI_STATUS status;
|
EFI_STATUS status;
|
||||||
|
|
||||||
EFI_FILE_PROTOCOL *file = NULL;
|
EFI_FILE_PROTOCOL *file = NULL;
|
||||||
status = root->Open(root, &file, (CHAR16 *)filename, EFI_FILE_MODE_READ,
|
status = root->Open(root, &file, (CHAR16 *)font_name, EFI_FILE_MODE_READ,
|
||||||
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
|
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
|
||||||
|
|
||||||
if (status == EFI_NOT_FOUND)
|
if (status == EFI_NOT_FOUND)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", filename);
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", font_name);
|
||||||
|
|
||||||
char info[sizeof(EFI_FILE_INFO) + 100];
|
char info[sizeof(EFI_FILE_INFO) + 100];
|
||||||
size_t info_length = sizeof(info);
|
size_t info_length = sizeof(info);
|
||||||
@@ -65,12 +60,16 @@ loader_load_file(
|
|||||||
status = file->GetInfo(file, &guid_file_info, &info_length, info);
|
status = file->GetInfo(file, &guid_file_info, &info_length, info);
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info");
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info");
|
||||||
|
|
||||||
*length = ((EFI_FILE_INFO *)info)->FileSize;
|
data->font_length = ((EFI_FILE_INFO *)info)->FileSize;
|
||||||
|
|
||||||
status = loader_alloc_pages(bootsvc, mem_type, length, data, next);
|
status = loader_alloc_pages(
|
||||||
|
bootsvc,
|
||||||
|
KERNEL_FONT_MEMTYPE,
|
||||||
|
&data->font_length,
|
||||||
|
&data->font);
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
|
||||||
|
|
||||||
status = file->Read(file, length, *data);
|
status = file->Read(file, &data->font_length, data->font);
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
|
||||||
|
|
||||||
status = file->Close(file);
|
status = file->Close(file);
|
||||||
@@ -78,7 +77,96 @@ loader_load_file(
|
|||||||
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EFI_STATUS
|
||||||
|
loader_load_elf(
|
||||||
|
EFI_BOOT_SERVICES *bootsvc,
|
||||||
|
EFI_FILE_PROTOCOL *root,
|
||||||
|
struct loader_data *data)
|
||||||
|
{
|
||||||
|
EFI_STATUS status;
|
||||||
|
|
||||||
|
EFI_FILE_PROTOCOL *file = NULL;
|
||||||
|
status = root->Open(root, &file, (CHAR16 *)kernel_name, EFI_FILE_MODE_READ,
|
||||||
|
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
|
||||||
|
|
||||||
|
if (status == EFI_NOT_FOUND)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
uint64_t length = 0;
|
||||||
|
data->kernel = 0;
|
||||||
|
data->kernel_entry = 0;
|
||||||
|
data->kernel_length = 0;
|
||||||
|
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", kernel_name);
|
||||||
|
|
||||||
|
struct elf_header header;
|
||||||
|
|
||||||
|
length = sizeof(struct elf_header);
|
||||||
|
status = file->Read(file, &length, &header);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF header");
|
||||||
|
|
||||||
|
if (length < sizeof(struct elf_header))
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"Incomplete read of ELF header");
|
||||||
|
|
||||||
|
static const char expected[] = {0x7f, 'E', 'L', 'F'};
|
||||||
|
for (int i = 0; i < sizeof(expected); ++i) {
|
||||||
|
if (header.ident.magic[i] != expected[i])
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"Bad ELF magic number");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.ident.word_size != ELF_WORDSIZE)
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: 32 bit ELF not supported");
|
||||||
|
|
||||||
|
if (header.ph_entsize != sizeof(struct elf_program_header))
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: program header size mismatch");
|
||||||
|
|
||||||
|
if (header.ident.version != ELF_VERSION ||
|
||||||
|
header.version != ELF_VERSION)
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: wrong ELF version");
|
||||||
|
|
||||||
|
if (header.ident.endianness != 1 ||
|
||||||
|
header.ident.os_abi != 0 ||
|
||||||
|
header.machine != 0x3e)
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: wrong machine architecture");
|
||||||
|
|
||||||
|
data->kernel_entry = (void *)header.entrypoint;
|
||||||
|
|
||||||
|
struct elf_program_header prog_header;
|
||||||
|
for (int i = 0; i < header.ph_num; ++i) {
|
||||||
|
status = file->SetPosition(file, header.ph_offset + i * header.ph_entsize);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position");
|
||||||
|
|
||||||
|
length = header.ph_entsize;
|
||||||
|
status = file->Read(file, &length, &prog_header);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF program header");
|
||||||
|
|
||||||
|
if (prog_header.type != ELF_PT_LOAD) continue;
|
||||||
|
|
||||||
|
status = file->SetPosition(file, prog_header.offset);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position");
|
||||||
|
|
||||||
|
length = prog_header.mem_size;
|
||||||
|
void *addr = (void *)(prog_header.vaddr - KERNEL_VIRT_ADDRESS);
|
||||||
|
status = loader_alloc_pages(bootsvc, KERNEL_MEMTYPE, &length, &addr);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating kernel pages");
|
||||||
|
|
||||||
|
if (data->kernel == 0)
|
||||||
|
data->kernel = addr;
|
||||||
|
data->kernel_length = (uint64_t)addr + length - (uint64_t)data->kernel;
|
||||||
|
|
||||||
|
length = prog_header.file_size;
|
||||||
|
status = file->Read(file, &length, addr);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
|
||||||
|
}
|
||||||
|
|
||||||
|
status = file->Close(file);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle");
|
||||||
|
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
EFI_STATUS
|
EFI_STATUS
|
||||||
loader_load_kernel(
|
loader_load_kernel(
|
||||||
@@ -105,54 +193,26 @@ loader_load_kernel(
|
|||||||
status = fileSystem->OpenVolume(fileSystem, &root);
|
status = fileSystem->OpenVolume(fileSystem, &root);
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"OpenVolume");
|
CHECK_EFI_STATUS_OR_RETURN(status, L"OpenVolume");
|
||||||
|
|
||||||
void *next = NULL;
|
status = loader_load_elf(bootsvc, root, data);
|
||||||
|
|
||||||
data->kernel = (void *)KERNEL_PHYS_ADDRESS;
|
|
||||||
status = loader_load_file(
|
|
||||||
bootsvc,
|
|
||||||
root,
|
|
||||||
kernel_name,
|
|
||||||
KERNEL_MEMTYPE,
|
|
||||||
&data->kernel,
|
|
||||||
&data->kernel_length,
|
|
||||||
&next);
|
|
||||||
if (status == EFI_NOT_FOUND)
|
if (status == EFI_NOT_FOUND)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name);
|
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name);
|
||||||
|
|
||||||
data->font = next;
|
data->font = (void *)((uint64_t)data->kernel + data->kernel_length);
|
||||||
status = loader_load_file(
|
status = loader_load_font(bootsvc, root, data);
|
||||||
bootsvc,
|
|
||||||
root,
|
|
||||||
font_name,
|
|
||||||
KERNEL_FONT_MEMTYPE,
|
|
||||||
&data->font,
|
|
||||||
&data->font_length,
|
|
||||||
&next);
|
|
||||||
|
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name);
|
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name);
|
||||||
|
|
||||||
data->data = next;
|
data->data = (void *)((uint64_t)data->font + data->font_length);
|
||||||
data->data_length += PAGE_SIZE; // extra page for map growth
|
data->data_length += PAGE_SIZE; // extra page for map growth
|
||||||
status = loader_alloc_pages(
|
status = loader_alloc_pages(
|
||||||
bootsvc,
|
bootsvc,
|
||||||
KERNEL_DATA_MEMTYPE,
|
KERNEL_DATA_MEMTYPE,
|
||||||
&data->data_length,
|
&data->data_length,
|
||||||
&data->data,
|
&data->data);
|
||||||
&next);
|
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data");
|
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data");
|
||||||
|
|
||||||
data->log = next;
|
|
||||||
data->log_length = KERNEL_LOG_PAGES * PAGE_SIZE;
|
|
||||||
status = loader_alloc_pages(
|
|
||||||
bootsvc,
|
|
||||||
KERNEL_LOG_MEMTYPE,
|
|
||||||
&data->log_length,
|
|
||||||
&data->log,
|
|
||||||
&next);
|
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel log");
|
|
||||||
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#define KERNEL_PHYS_ADDRESS 0x100000
|
#define KERNEL_PHYS_ADDRESS 0x100000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef KERNEL_VIRT_ADDRESS
|
||||||
|
#define KERNEL_VIRT_ADDRESS 0xFFFF800000000000
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef VIRTUAL_OFFSET
|
#ifndef VIRTUAL_OFFSET
|
||||||
#define VIRTUAL_OFFSET 0xf00000000
|
#define VIRTUAL_OFFSET 0xf00000000
|
||||||
#endif
|
#endif
|
||||||
@@ -24,16 +28,12 @@
|
|||||||
#define KERNEL_DATA_MEMTYPE 0x80000002
|
#define KERNEL_DATA_MEMTYPE 0x80000002
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef KERNEL_LOG_MEMTYPE
|
#ifndef KERNEL_PT_MEMTYPE
|
||||||
#define KERNEL_LOG_MEMTYPE 0x80000003
|
#define KERNEL_PT_MEMTYPE 0x80000004
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef KERNEL_LOG_PAGES
|
|
||||||
#define KERNEL_LOG_PAGES 4
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef KERNEL_FILENAME
|
#ifndef KERNEL_FILENAME
|
||||||
#define KERNEL_FILENAME L"kernel.bin"
|
#define KERNEL_FILENAME L"kernel.elf"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef KERNEL_FONT
|
#ifndef KERNEL_FONT
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
struct loader_data {
|
struct loader_data {
|
||||||
void *kernel;
|
void *kernel;
|
||||||
|
void *kernel_entry;
|
||||||
size_t kernel_length;
|
size_t kernel_length;
|
||||||
|
|
||||||
void *font;
|
void *font;
|
||||||
@@ -49,9 +50,6 @@ struct loader_data {
|
|||||||
|
|
||||||
void *data;
|
void *data;
|
||||||
size_t data_length;
|
size_t data_length;
|
||||||
|
|
||||||
void *log;
|
|
||||||
size_t log_length;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EFI_STATUS loader_load_kernel(EFI_BOOT_SERVICES *bootsvc, struct loader_data *data);
|
EFI_STATUS loader_load_kernel(EFI_BOOT_SERVICES *bootsvc, struct loader_data *data);
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ struct kernel_header {
|
|||||||
uint8_t minor;
|
uint8_t minor;
|
||||||
uint16_t patch;
|
uint16_t patch;
|
||||||
uint32_t gitsha;
|
uint32_t gitsha;
|
||||||
|
|
||||||
void *entrypoint;
|
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
@@ -37,6 +35,7 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
{
|
{
|
||||||
EFI_STATUS status;
|
EFI_STATUS status;
|
||||||
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
|
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
|
||||||
|
EFI_RUNTIME_SERVICES *runsvc = system_table->RuntimeServices;
|
||||||
|
|
||||||
// When checking console initialization, use CHECK_EFI_STATUS_OR_RETURN
|
// When checking console initialization, use CHECK_EFI_STATUS_OR_RETURN
|
||||||
// because we can't be sure if the console was fully set up
|
// because we can't be sure if the console was fully set up
|
||||||
@@ -44,6 +43,8 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
|
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
|
||||||
// From here on out, we can use CHECK_EFI_STATUS_OR_FAIL instead
|
// 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.
|
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
|
||||||
//
|
//
|
||||||
void *acpi_table = NULL;
|
void *acpi_table = NULL;
|
||||||
@@ -74,8 +75,6 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
|
|
||||||
// Load the kernel image from disk and check it
|
// Load the kernel image from disk and check it
|
||||||
//
|
//
|
||||||
void *kernel_image = NULL, *kernel_data = NULL;
|
|
||||||
uint64_t kernel_length = 0;
|
|
||||||
con_printf(L"Loading kernel into memory...\r\n");
|
con_printf(L"Loading kernel into memory...\r\n");
|
||||||
|
|
||||||
struct loader_data load;
|
struct loader_data load;
|
||||||
@@ -86,7 +85,6 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
|
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
|
||||||
con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font);
|
con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font);
|
||||||
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
|
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
|
||||||
con_printf(L" %u log bytes at 0x%x\r\n", load.log_length, load.log);
|
|
||||||
|
|
||||||
struct kernel_header *version = (struct kernel_header *)load.kernel;
|
struct kernel_header *version = (struct kernel_header *)load.kernel;
|
||||||
if (version->magic != KERNEL_HEADER_MAGIC) {
|
if (version->magic != KERNEL_HEADER_MAGIC) {
|
||||||
@@ -97,13 +95,15 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
con_printf(L" Kernel version %d.%d.%d %x%s\r\n",
|
con_printf(L" Kernel version %d.%d.%d %x%s\r\n",
|
||||||
version->major, version->minor, version->patch, version->gitsha & 0x0fffffff,
|
version->major, version->minor, version->patch, version->gitsha & 0x0fffffff,
|
||||||
version->gitsha & 0xf0000000 ? "*" : "");
|
version->gitsha & 0xf0000000 ? "*" : "");
|
||||||
con_printf(L" Entrypoint 0x%x\r\n", version->entrypoint);
|
con_printf(L" Entrypoint 0x%x\r\n", load.kernel_entry);
|
||||||
|
|
||||||
void (*kernel_main)() = version->entrypoint;
|
void (*kernel_main)() = load.kernel_entry;
|
||||||
|
memory_mark_pointer_fixup((void **)&kernel_main);
|
||||||
|
|
||||||
// Set up the kernel data pages to pass to the kernel
|
// Set up the kernel data pages to pass to the kernel
|
||||||
//
|
//
|
||||||
struct popcorn_data *data_header = (struct popcorn_data *)load.data;
|
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->magic = DATA_HEADER_MAGIC;
|
||||||
data_header->version = DATA_HEADER_VERSION;
|
data_header->version = DATA_HEADER_VERSION;
|
||||||
@@ -113,16 +113,20 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
|
|
||||||
data_header->font = load.font;
|
data_header->font = load.font;
|
||||||
data_header->font_length = load.font_length;
|
data_header->font_length = load.font_length;
|
||||||
|
memory_mark_pointer_fixup((void **)&data_header->font);
|
||||||
|
|
||||||
data_header->data = load.data;
|
data_header->data = load.data;
|
||||||
data_header->data_length = load.data_length;
|
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;
|
|
||||||
|
|
||||||
data_header->memory_map = (EFI_MEMORY_DESCRIPTOR *)(data_header + 1);
|
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;
|
data_header->acpi_table = acpi_table;
|
||||||
|
memory_mark_pointer_fixup((void **)&data_header->acpi_table);
|
||||||
|
|
||||||
data_header->_reserved0 = 0;
|
data_header->_reserved0 = 0;
|
||||||
data_header->_reserved1 = 0;
|
data_header->_reserved1 = 0;
|
||||||
@@ -132,13 +136,14 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
status = con_get_framebuffer(
|
status = con_get_framebuffer(
|
||||||
bootsvc,
|
bootsvc,
|
||||||
&data_header->frame_buffer,
|
&data_header->frame_buffer,
|
||||||
&data_header->frame_buffer_size,
|
&data_header->frame_buffer_length,
|
||||||
&data_header->hres,
|
&data_header->hres,
|
||||||
&data_header->vres,
|
&data_header->vres,
|
||||||
&data_header->rmask,
|
&data_header->rmask,
|
||||||
&data_header->gmask,
|
&data_header->gmask,
|
||||||
&data_header->bmask);
|
&data_header->bmask);
|
||||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
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.
|
// Save the memory map and tell the firmware we're taking control.
|
||||||
//
|
//
|
||||||
@@ -149,11 +154,16 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
|||||||
status = memory_get_map(bootsvc, &map);
|
status = memory_get_map(bootsvc, &map);
|
||||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||||
|
|
||||||
|
data_header->memory_map_length = map.length;
|
||||||
|
data_header->memory_map_desc_size = map.size;
|
||||||
|
|
||||||
// bootsvc->Stall(5000000);
|
// bootsvc->Stall(5000000);
|
||||||
|
|
||||||
status = bootsvc->ExitBootServices(image_handle, map.key);
|
status = bootsvc->ExitBootServices(image_handle, map.key);
|
||||||
CHECK_EFI_STATUS_OR_ASSERT(status, 0);
|
CHECK_EFI_STATUS_OR_ASSERT(status, 0);
|
||||||
|
|
||||||
|
memory_virtualize(runsvc, &map);
|
||||||
|
|
||||||
// Hand control to the kernel
|
// Hand control to the kernel
|
||||||
//
|
//
|
||||||
kernel_main(data_header);
|
kernel_main(data_header);
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b))
|
#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[] = {
|
const CHAR16 *memory_type_names[] = {
|
||||||
L"EfiReservedMemoryType",
|
L"EfiReservedMemoryType",
|
||||||
L"EfiLoaderCode",
|
L"EfiLoaderCode",
|
||||||
@@ -37,6 +41,51 @@ memory_type_name(UINT32 value)
|
|||||||
return memory_type_names[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, plus some pages for the kernel to use
|
||||||
|
// as page tables while it gets started.
|
||||||
|
EFI_PHYSICAL_ADDRESS addr = 0;
|
||||||
|
status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 16, &addr);
|
||||||
|
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate page table pages.");
|
||||||
|
|
||||||
|
new_pml4 = (uint64_t *)addr;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len)
|
copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len)
|
||||||
{
|
{
|
||||||
@@ -111,3 +160,47 @@ memory_dump_map(struct memory_map *map)
|
|||||||
|
|
||||||
return EFI_SUCCESS;
|
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:
|
||||||
|
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_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_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);
|
||||||
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
|
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
|
||||||
EFI_STATUS memory_dump_map(struct memory_map *map);
|
EFI_STATUS memory_dump_map(struct memory_map *map);
|
||||||
|
|
||||||
|
void memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map);
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ struct popcorn_data {
|
|||||||
void *data;
|
void *data;
|
||||||
size_t data_length;
|
size_t data_length;
|
||||||
|
|
||||||
void *log;
|
|
||||||
size_t log_length;
|
|
||||||
|
|
||||||
void *memory_map;
|
void *memory_map;
|
||||||
|
size_t memory_map_length;
|
||||||
|
size_t memory_map_desc_size;
|
||||||
|
|
||||||
void *runtime;
|
void *runtime;
|
||||||
|
|
||||||
void *acpi_table;
|
void *acpi_table;
|
||||||
|
|
||||||
void *frame_buffer;
|
void *frame_buffer;
|
||||||
size_t frame_buffer_size;
|
size_t frame_buffer_length;
|
||||||
uint32_t hres;
|
uint32_t hres;
|
||||||
uint32_t vres;
|
uint32_t vres;
|
||||||
uint32_t rmask;
|
uint32_t rmask;
|
||||||
|
|||||||
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 {}
|
||||||
@@ -6,17 +6,19 @@ global _header
|
|||||||
_header:
|
_header:
|
||||||
dd MAGIC ; Kernel header magic
|
dd MAGIC ; Kernel header magic
|
||||||
dw 1 ; Header version 1
|
dw 1 ; Header version 1
|
||||||
dw 1 ; Kernel header length
|
dw 16 ; Kernel header length
|
||||||
db VERSION_MAJOR ; Kernel version
|
db VERSION_MAJOR ; Kernel version
|
||||||
db VERSION_MINOR
|
db VERSION_MINOR
|
||||||
dw VERSION_PATCH
|
dw VERSION_PATCH
|
||||||
dd VERSION_GITSHA
|
dd VERSION_GITSHA
|
||||||
dq _start ; Kernel entrypoint
|
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
align 16
|
align 16
|
||||||
global _start:function (_start.end - _start)
|
global _start:function (_start.end - _start)
|
||||||
_start:
|
_start:
|
||||||
|
cli
|
||||||
|
|
||||||
|
mov rsp, stack_end
|
||||||
extern kernel_main
|
extern kernel_main
|
||||||
call kernel_main
|
call kernel_main
|
||||||
|
|
||||||
@@ -26,3 +28,18 @@ _start:
|
|||||||
hlt
|
hlt
|
||||||
jmp .hang
|
jmp .hang
|
||||||
.end:
|
.end:
|
||||||
|
|
||||||
|
global interrupts_enable
|
||||||
|
interrupts_enable:
|
||||||
|
sti
|
||||||
|
ret
|
||||||
|
|
||||||
|
global interrupts_disable
|
||||||
|
interrupts_disable:
|
||||||
|
cli
|
||||||
|
ret
|
||||||
|
|
||||||
|
section .bss
|
||||||
|
stack_begin:
|
||||||
|
resb 0x4000 ; 16KiB stack space
|
||||||
|
stack_end:
|
||||||
348
src/kernel/console.cpp
Normal file
348
src/kernel/console.cpp
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
#include "console.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
const char digits[] = "0123456789abcdef";
|
||||||
|
console g_console;
|
||||||
|
|
||||||
|
|
||||||
|
class console_out_screen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
console_out_screen(const font &f, const screen &s, void *scratch, size_t len) :
|
||||||
|
m_font(f),
|
||||||
|
m_screen(s),
|
||||||
|
m_size(s.width() / f.width(), s.height() / f.height()),
|
||||||
|
m_fg(0xffffff),
|
||||||
|
m_bg(0),
|
||||||
|
m_first(0),
|
||||||
|
m_length(len),
|
||||||
|
m_data(nullptr),
|
||||||
|
m_attrs(nullptr),
|
||||||
|
m_palette(nullptr)
|
||||||
|
{
|
||||||
|
const unsigned count = m_size.size();
|
||||||
|
const size_t palette_size = sizeof(screen::pixel_t) * 256;
|
||||||
|
const size_t attrs_size = 2 * count;
|
||||||
|
|
||||||
|
if (len >= count) {
|
||||||
|
// We have enough scratch space to keep the contents of the screen
|
||||||
|
m_data = static_cast<char *>(scratch);
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
m_data[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= count + palette_size + attrs_size) {
|
||||||
|
// We have enough scratch space to also keep the colors of the text
|
||||||
|
m_palette = reinterpret_cast<screen::pixel_t *>(m_data + count);
|
||||||
|
|
||||||
|
unsigned index = 0;
|
||||||
|
|
||||||
|
// Manually add the 16 basic ANSI colors
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0x00, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0xcd, 0x00, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0xcd, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0xcd, 0xcd, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0x00, 0xee);
|
||||||
|
m_palette[index++] = m_screen.color(0xcd, 0x00, 0xcd);
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0xcd, 0xcd);
|
||||||
|
m_palette[index++] = m_screen.color(0xe5, 0xe5, 0xe5);
|
||||||
|
m_palette[index++] = m_screen.color(0x7f, 0x7f, 0x7f);
|
||||||
|
m_palette[index++] = m_screen.color(0xff, 0x00, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0xff, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0xff, 0xff, 0x00);
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0x50, 0xff);
|
||||||
|
m_palette[index++] = m_screen.color(0xff, 0x00, 0xff);
|
||||||
|
m_palette[index++] = m_screen.color(0x00, 0xff, 0xff);
|
||||||
|
m_palette[index++] = m_screen.color(0xff, 0xff, 0xff);
|
||||||
|
|
||||||
|
// Build the high-color portion of the table
|
||||||
|
const uint32_t intensity[] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
||||||
|
const uint32_t intensities = sizeof(intensity) / sizeof(uint32_t);
|
||||||
|
|
||||||
|
for (uint32_t r = 0; r < intensities; ++r) {
|
||||||
|
for (uint32_t g = 0; g < intensities; ++g) {
|
||||||
|
for (uint32_t b = 0; b < intensities; ++b) {
|
||||||
|
m_palette[index++] = m_screen.color(
|
||||||
|
intensity[r], intensity[g], intensity[b]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the greyscale portion of the table
|
||||||
|
for (uint8_t i = 0x08; i <= 0xee; i += 10)
|
||||||
|
m_palette[index++] = m_screen.color(i, i, i);
|
||||||
|
|
||||||
|
set_color(7, 0); // Grey on black default
|
||||||
|
m_attrs = reinterpret_cast<uint16_t *>(m_data + count + palette_size);
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
m_attrs[i] = m_attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void repaint()
|
||||||
|
{
|
||||||
|
m_screen.fill(m_bg);
|
||||||
|
if (!m_data) return;
|
||||||
|
|
||||||
|
for (unsigned y = 0; y < m_size.y; ++y) {
|
||||||
|
const char *line = line_pointer(y);
|
||||||
|
const uint16_t *attrs = attr_pointer(y);
|
||||||
|
for (unsigned x = 0; x < m_size.x; ++x) {
|
||||||
|
const uint16_t attr = attrs[x];
|
||||||
|
|
||||||
|
set_color(static_cast<uint8_t>(attr),
|
||||||
|
static_cast<uint8_t>(attr >> 8));
|
||||||
|
|
||||||
|
m_font.draw_glyph(
|
||||||
|
m_screen,
|
||||||
|
line[x] ? line[x] : ' ',
|
||||||
|
m_fg,
|
||||||
|
m_bg,
|
||||||
|
x * m_font.width(),
|
||||||
|
y * m_font.height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scroll(unsigned lines)
|
||||||
|
{
|
||||||
|
if (!m_data) {
|
||||||
|
m_pos.x = 0;
|
||||||
|
m_pos.y = 0;
|
||||||
|
} else {
|
||||||
|
unsigned bytes = lines * m_size.x;
|
||||||
|
char *line = line_pointer(0);
|
||||||
|
for (unsigned i = 0; i < bytes; ++i)
|
||||||
|
*line++ = 0;
|
||||||
|
|
||||||
|
m_first = (m_first + lines) % m_size.y;
|
||||||
|
m_pos.y -= lines;
|
||||||
|
}
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_color(uint8_t fg, uint8_t bg)
|
||||||
|
{
|
||||||
|
if (!m_palette) return;
|
||||||
|
|
||||||
|
m_bg = m_palette[bg];
|
||||||
|
m_fg = m_palette[fg];
|
||||||
|
m_attr = (bg << 8) | fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putc(char c)
|
||||||
|
{
|
||||||
|
char *line = line_pointer(m_pos.y);
|
||||||
|
uint16_t *attrs = attr_pointer(m_pos.y);
|
||||||
|
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '\t':
|
||||||
|
m_pos.x = (m_pos.x + 4) / 4 * 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\r':
|
||||||
|
m_pos.x = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
m_pos.x = 0;
|
||||||
|
m_pos.y++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
if (line) line[m_pos.x] = c;
|
||||||
|
if (attrs) attrs[m_pos.x] = m_attr;
|
||||||
|
|
||||||
|
const unsigned x = m_pos.x * m_font.width();
|
||||||
|
const unsigned y = m_pos.y * m_font.height();
|
||||||
|
m_font.draw_glyph(m_screen, c, m_fg, m_bg, x, y);
|
||||||
|
|
||||||
|
m_pos.x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pos.x >= m_size.x) {
|
||||||
|
m_pos.x = m_pos.x % m_size.x;
|
||||||
|
m_pos.y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pos.y >= m_size.y) {
|
||||||
|
scroll(1);
|
||||||
|
line = line_pointer(m_pos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char * line_pointer(unsigned line)
|
||||||
|
{
|
||||||
|
if (!m_data) return nullptr;
|
||||||
|
return m_data + ((m_first + line) % m_size.y) * m_size.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t * attr_pointer(unsigned line)
|
||||||
|
{
|
||||||
|
if (!m_attrs) return nullptr;
|
||||||
|
return m_attrs + ((m_first + line) % m_size.y) * m_size.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
font m_font;
|
||||||
|
screen m_screen;
|
||||||
|
|
||||||
|
kutil::coord<unsigned> m_size;
|
||||||
|
kutil::coord<unsigned> m_pos;
|
||||||
|
screen::pixel_t m_fg, m_bg;
|
||||||
|
uint16_t m_attr;
|
||||||
|
|
||||||
|
size_t m_first;
|
||||||
|
size_t m_length;
|
||||||
|
|
||||||
|
char *m_data;
|
||||||
|
uint16_t *m_attrs;
|
||||||
|
screen::pixel_t *m_palette;
|
||||||
|
};
|
||||||
|
|
||||||
|
console_out_screen *
|
||||||
|
console_get_screen_out(const font &f, const screen &s, void *scratch, size_t len)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
//return new console_out_screen(f, s, scratch, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
serial_ready()
|
||||||
|
{
|
||||||
|
return (inb(COM1 + 5) & 0x20) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
serial_write(char c) {
|
||||||
|
while (!serial_ready());
|
||||||
|
outb(COM1, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console::console() :
|
||||||
|
m_screen(nullptr)
|
||||||
|
{
|
||||||
|
const char *fgseq = "\x1b[2J";
|
||||||
|
while (*fgseq)
|
||||||
|
serial_write(*fgseq++);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
console::set_color(uint8_t fg, uint8_t bg)
|
||||||
|
{
|
||||||
|
if (m_screen)
|
||||||
|
m_screen->set_color(fg, bg);
|
||||||
|
|
||||||
|
const char *fgseq = "\x1b[38;5;";
|
||||||
|
while (*fgseq)
|
||||||
|
serial_write(*fgseq++);
|
||||||
|
if (fg >= 100) serial_write('0' + (fg/100));
|
||||||
|
if (fg >= 10) serial_write('0' + (fg/10) % 10);
|
||||||
|
serial_write('0' + fg % 10);
|
||||||
|
serial_write('m');
|
||||||
|
|
||||||
|
const char *bgseq = "\x1b[48;5;";
|
||||||
|
while (*bgseq)
|
||||||
|
serial_write(*bgseq++);
|
||||||
|
if (bg >= 100) serial_write('0' + (bg/100));
|
||||||
|
if (bg >= 10) serial_write('0' + (bg/10) % 10);
|
||||||
|
serial_write('0' + bg % 10);
|
||||||
|
serial_write('m');
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
console::puts(const char *message)
|
||||||
|
{
|
||||||
|
while (message && *message) {
|
||||||
|
char c = *message++;
|
||||||
|
if (m_screen) m_screen->putc(c);
|
||||||
|
|
||||||
|
serial_write(c);
|
||||||
|
if (c == '\n') serial_write('\r');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::vprintf(const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
static const unsigned buf_size = 256;
|
||||||
|
char buffer[256];
|
||||||
|
|
||||||
|
const char *r = fmt;
|
||||||
|
char *w = buffer;
|
||||||
|
char *wend = buffer + buf_size;
|
||||||
|
|
||||||
|
#define flush() do { *w = 0; puts(buffer); w = buffer; } while(0)
|
||||||
|
|
||||||
|
while (r && *r) {
|
||||||
|
if (w == wend) flush();
|
||||||
|
|
||||||
|
if (*r != '%') {
|
||||||
|
*w++ = *r++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r++; // chomp the %
|
||||||
|
flush();
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
bool right = true;
|
||||||
|
int width = 0;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
char c = *r++;
|
||||||
|
switch (c) {
|
||||||
|
case '%': *w = '%'; done = true; break;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9': width = width * 10 + (c - '0'); break;
|
||||||
|
|
||||||
|
case '-': right = !right; break;
|
||||||
|
|
||||||
|
case 'x': put_hex<uint32_t>(va_arg(args, uint32_t), right ? width : -width); done = true; break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
case 'u': put_dec<uint32_t>(va_arg(args, uint32_t), right ? width : -width); done = true; break;
|
||||||
|
|
||||||
|
case 's': {
|
||||||
|
const char *s = va_arg(args, const char*);
|
||||||
|
if (s) puts(s);
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
switch (*r++) {
|
||||||
|
case 'x': put_hex<uint64_t>(va_arg(args, uint64_t), right ? width : -width); done = true; break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
case 'u': put_dec<uint32_t>(va_arg(args, uint64_t), right ? width : -width); done = true; break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flush();
|
||||||
|
}
|
||||||
84
src/kernel/console.h
Normal file
84
src/kernel/console.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "kutil/coord.h"
|
||||||
|
#include "font.h"
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
class console_out_screen;
|
||||||
|
|
||||||
|
class console
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
console();
|
||||||
|
|
||||||
|
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
||||||
|
|
||||||
|
void puts(const char *message);
|
||||||
|
void vprintf(const char *fmt, va_list args);
|
||||||
|
|
||||||
|
inline void printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vprintf(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void put_hex(T x, int width = 0);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void put_dec(T x, int width = 0);
|
||||||
|
|
||||||
|
void set_screen(console_out_screen *out) { m_screen = out; }
|
||||||
|
|
||||||
|
static console * get();
|
||||||
|
|
||||||
|
private:
|
||||||
|
console_out_screen *m_screen;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern console g_console;
|
||||||
|
inline console * console::get() { return &g_console; }
|
||||||
|
|
||||||
|
|
||||||
|
console_out_screen * console_get_screen_out(
|
||||||
|
const font &f, const screen &s, void *scratch, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
extern const char digits[];
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void console::put_hex(T x, int width)
|
||||||
|
{
|
||||||
|
static const int chars = sizeof(x) * 2;
|
||||||
|
char message[chars + 1];
|
||||||
|
for (int i=0; i<chars; ++i) {
|
||||||
|
message[chars - i - 1] = digits[(x >> (i*4)) & 0xf];
|
||||||
|
}
|
||||||
|
message[chars] = 0;
|
||||||
|
|
||||||
|
if (width > chars) for(int i=0; i<(width-chars); ++i) puts(" ");
|
||||||
|
puts(message);
|
||||||
|
if (-width > chars) for(int i=0; i<(-width-chars); ++i) puts(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void console::put_dec(T x, int width)
|
||||||
|
{
|
||||||
|
static const int chars = sizeof(x) * 3;
|
||||||
|
char message[chars + 1];
|
||||||
|
char *p = message + chars;
|
||||||
|
int length = 0;
|
||||||
|
*p-- = 0;
|
||||||
|
do {
|
||||||
|
*p-- = digits[x % 10];
|
||||||
|
x /= 10;
|
||||||
|
length += 1;
|
||||||
|
} while (x != 0);
|
||||||
|
|
||||||
|
if (width > length) for(int i=0; i<(width-length); ++i) puts(" ");
|
||||||
|
puts(++p);
|
||||||
|
if (-width > length) for(int i=0; i<(-width-length); ++i) puts(" ");
|
||||||
|
}
|
||||||
146
src/kernel/device_manager.cpp
Normal file
146
src/kernel/device_manager.cpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "kutil/memory.h"
|
||||||
|
#include "acpi_tables.h"
|
||||||
|
#include "assert.h"
|
||||||
|
#include "device_manager.h"
|
||||||
|
#include "log.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),
|
||||||
|
m_io_apic(nullptr),
|
||||||
|
m_global_interrupt_base(0)
|
||||||
|
{
|
||||||
|
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(char *into, uint32_t type)
|
||||||
|
{
|
||||||
|
for (int j=0; j<4; ++j) into[j] = reinterpret_cast<char *>(&type)[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
device_manager::load_xsdt(const acpi_xsdt *xsdt)
|
||||||
|
{
|
||||||
|
kassert(xsdt && acpi_validate(xsdt), "Invalid ACPI XSDT.");
|
||||||
|
|
||||||
|
char sig[5] = {0,0,0,0,0};
|
||||||
|
log::info(logs::devices, "ACPI 2.0 tables loading:");
|
||||||
|
|
||||||
|
put_sig(sig, xsdt->header.type);
|
||||||
|
log::info(logs::devices, " Found table %s", sig);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
put_sig(sig, header->type);
|
||||||
|
log::info(logs::devices, " Found table %s", sig);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
device_manager::load_apic(const acpi_apic *apic)
|
||||||
|
{
|
||||||
|
m_local_apic = reinterpret_cast<uint32_t *>(apic->local_address);
|
||||||
|
log::info(logs::devices, " APIC local address %lx", 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];
|
||||||
|
|
||||||
|
log::debug(logs::devices, " APIC entry type %d", 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);
|
||||||
|
log::info(logs::devices, " IO APIC address %lx base %d",
|
||||||
|
m_io_apic, 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
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "kutil/coord.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
class font
|
class font
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ private:
|
|||||||
font();
|
font();
|
||||||
font(unsigned height, unsigned width, unsigned count, uint8_t const *data);
|
font(unsigned height, unsigned width, unsigned count, uint8_t const *data);
|
||||||
|
|
||||||
coord<unsigned> m_size;
|
kutil::coord<unsigned> m_size;
|
||||||
unsigned m_count;
|
unsigned m_count;
|
||||||
uint8_t const *m_data;
|
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);
|
||||||
302
src/kernel/interrupts.cpp
Normal file
302
src/kernel/interrupts.cpp
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "kutil/memory.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
#include "log.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();
|
||||||
|
log::info(logs::interrupt, "Interrupts enabled.");
|
||||||
|
gdt_dump(g_gdtr);
|
||||||
|
idt_dump(g_idtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
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->printf(" %s: %lx\n", name, (value));
|
||||||
|
|
||||||
|
void
|
||||||
|
isr_handler(registers regs)
|
||||||
|
{
|
||||||
|
console *cons = console::get();
|
||||||
|
|
||||||
|
cons->set_color(9);
|
||||||
|
cons->puts("\nReceived ISR interrupt:\n");
|
||||||
|
cons->set_color();
|
||||||
|
|
||||||
|
uint64_t cr2 = 0;
|
||||||
|
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
|
print_reg("ISR", regs.interrupt);
|
||||||
|
print_reg("ERR", regs.errorcode);
|
||||||
|
print_reg("CR2", cr2);
|
||||||
|
cons->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);
|
||||||
|
cons->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->set_color(9);
|
||||||
|
cons->puts("\nReceived IRQ interrupt:\n");
|
||||||
|
cons->set_color();
|
||||||
|
|
||||||
|
print_reg("ISR", regs.interrupt);
|
||||||
|
print_reg("ERR", regs.errorcode);
|
||||||
|
cons->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);
|
||||||
|
cons->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)
|
||||||
|
{
|
||||||
|
log::info(logs::interrupt, "Loaded GDT at: %lx size: %d bytes", table.base, table.limit+1);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (gdt[i].flags & 0x80) {
|
||||||
|
log::debug(logs::interrupt,
|
||||||
|
" Entry %3d: Base %x limit %x privs %d flags %s%s%s%s%s%s",
|
||||||
|
i, base, limit, ((gdt[i].flags >> 5) & 0x03),
|
||||||
|
|
||||||
|
(gdt[i].flags & 0x80) ? "P " : " ",
|
||||||
|
(gdt[i].flags & 0x08) ? "ex " : " ",
|
||||||
|
(gdt[i].flags & 0x04) ? "dc " : " ",
|
||||||
|
(gdt[i].flags & 0x02) ? "rw " : " ",
|
||||||
|
|
||||||
|
(gdt[i].granularity & 0x80) ? "kb " : "b ",
|
||||||
|
(gdt[i].granularity & 0x60) == 0x60 ? "64" :
|
||||||
|
(gdt[i].granularity & 0x60) == 0x40 ? "32" : "16"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
idt_dump(const table_ptr &table)
|
||||||
|
{
|
||||||
|
log::info(logs::interrupt, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
|
||||||
|
|
||||||
|
int count = (table.limit + 1) / sizeof(idt_descriptor);
|
||||||
|
const idt_descriptor *idt =
|
||||||
|
reinterpret_cast<const idt_descriptor *>(table.base);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
char const *type;
|
||||||
|
switch (idt[i].flags & 0xf) {
|
||||||
|
case 0x5: type = " 32tsk "; break;
|
||||||
|
case 0x6: type = " 16int "; break;
|
||||||
|
case 0x7: type = " 16trp "; break;
|
||||||
|
case 0xe: type = " 32int "; break;
|
||||||
|
case 0xf: type = " 32trp "; break;
|
||||||
|
default: type = " ????? "; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idt[i].flags & 0x80) {
|
||||||
|
log::debug(logs::interrupt,
|
||||||
|
" Entry %3d: Base:%lx Sel(rpl %d, ti %d, %3d) IST:%d %s DPL:%d", i, base,
|
||||||
|
(idt[i].selector & 0x3),
|
||||||
|
((idt[i].selector & 0x4) >> 2),
|
||||||
|
(idt[i].selector >> 3),
|
||||||
|
idt[i].ist,
|
||||||
|
type,
|
||||||
|
((idt[i].flags >> 5) & 0x3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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"
|
||||||
16
src/kernel/io.cpp
Normal file
16
src/kernel/io.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
inb(uint16_t port)
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
outb(uint16_t port, uint8_t val)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
|
||||||
|
}
|
||||||
|
|
||||||
19
src/kernel/io.h
Normal file
19
src/kernel/io.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
/// Read a byte from an IO port.
|
||||||
|
/// \arg port The address of the IO port
|
||||||
|
/// \returns One byte read from the port
|
||||||
|
uint8_t inb(uint16_t port);
|
||||||
|
|
||||||
|
/// Write a byte to an IO port.
|
||||||
|
/// \arg port The addres of the IO port
|
||||||
|
/// \arg val The byte to write
|
||||||
|
void outb(uint16_t port, uint8_t val);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t COM1 = 0x03f8;
|
||||||
103
src/kernel/log.cpp
Normal file
103
src/kernel/log.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#include <type_traits>
|
||||||
|
#include "kutil/memory.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const uint64_t default_enabled[] = {0x09, 0x0f, 0x0f, 0x0f};
|
||||||
|
static const uint8_t level_colors[] = {0x07, 0x0f, 0x0b, 0x09};
|
||||||
|
|
||||||
|
static const char *levels[] = {"debug", " info", " warn", "error", "fatal"};
|
||||||
|
static const char *areas[] = {
|
||||||
|
"boot",
|
||||||
|
"mem ",
|
||||||
|
"intr",
|
||||||
|
"dev ",
|
||||||
|
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
log log::s_log;
|
||||||
|
|
||||||
|
log::log() :
|
||||||
|
m_cons(nullptr)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < sizeof(m_enabled) / sizeof(uint64_t); ++i)
|
||||||
|
m_enabled[i] = default_enabled[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
log::log(console *cons) :
|
||||||
|
m_cons(cons)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < sizeof(m_enabled) / sizeof(uint64_t); ++i)
|
||||||
|
m_enabled[i] = default_enabled[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
log::init(console *cons)
|
||||||
|
{
|
||||||
|
new (&s_log) log(cons);
|
||||||
|
log::info(logs::boot, "Logging system initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t
|
||||||
|
bit_mask(logs area) { return 1 << static_cast<uint64_t>(area); }
|
||||||
|
|
||||||
|
void
|
||||||
|
log::enable(logs type, level at_level)
|
||||||
|
{
|
||||||
|
using under_t = std::underlying_type<level>::type;
|
||||||
|
under_t at = static_cast<under_t>(at_level);
|
||||||
|
under_t max = sizeof(m_enabled) / sizeof(under_t);
|
||||||
|
|
||||||
|
for (under_t i = 0; i < max; ++i) {
|
||||||
|
if (i <= at)
|
||||||
|
s_log.m_enabled[i] |= bit_mask(type);
|
||||||
|
else
|
||||||
|
s_log.m_enabled[i] &= ~bit_mask(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void
|
||||||
|
log::trylog<log::level::fatal>(logs area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
s_log.output(level::fatal, area, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
while(1) __asm__ ("hlt");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <log::level L>
|
||||||
|
void
|
||||||
|
log::trylog(logs area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
auto i = static_cast<std::underlying_type<level>::type>(L);
|
||||||
|
if ((s_log.m_enabled[i] & bit_mask(area)) == 0) return;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
s_log.output(L, area, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
log::output(level severity, logs area, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
m_cons->set_color(level_colors[static_cast<int>(severity)]);
|
||||||
|
|
||||||
|
m_cons->printf("%s %s: ",
|
||||||
|
areas[static_cast<int>(area)],
|
||||||
|
levels[static_cast<int>(severity)]);
|
||||||
|
m_cons->vprintf(fmt, args);
|
||||||
|
m_cons->set_color();
|
||||||
|
m_cons->puts("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const log::trylog_p log::debug = &trylog<level::debug>;
|
||||||
|
const log::trylog_p log::info = &trylog<level::info>;
|
||||||
|
const log::trylog_p log::warn = &trylog<level::warn>;
|
||||||
|
const log::trylog_p log::error = &trylog<level::error>;
|
||||||
|
const log::trylog_p log::fatal = &trylog<level::fatal>;
|
||||||
48
src/kernel/log.h
Normal file
48
src/kernel/log.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class console;
|
||||||
|
|
||||||
|
|
||||||
|
enum class logs
|
||||||
|
{
|
||||||
|
boot,
|
||||||
|
memory,
|
||||||
|
interrupt,
|
||||||
|
devices,
|
||||||
|
|
||||||
|
max
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class log
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class level {debug, info, warn, error, fatal, max};
|
||||||
|
|
||||||
|
static void init(console *cons);
|
||||||
|
static void enable(logs type, level at_level);
|
||||||
|
|
||||||
|
template <level L>
|
||||||
|
static void trylog(logs area, const char *fmt, ...);
|
||||||
|
using trylog_p = void (*)(logs area, const char *fmt, ...);
|
||||||
|
|
||||||
|
static const trylog_p debug;
|
||||||
|
static const trylog_p info;
|
||||||
|
static const trylog_p warn;
|
||||||
|
static const trylog_p error;
|
||||||
|
static const trylog_p fatal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void output(level severity, logs area, const char *fmt, va_list args);
|
||||||
|
|
||||||
|
/// Bitmasks for what categories are enabled. fatal is
|
||||||
|
/// always enabled, so leave it out.
|
||||||
|
uint64_t m_enabled[static_cast<uint64_t>(level::max) - 1];
|
||||||
|
console *m_cons;
|
||||||
|
|
||||||
|
log();
|
||||||
|
log(console *cons);
|
||||||
|
static log s_log;
|
||||||
|
};
|
||||||
73
src/kernel/main.cpp
Normal file
73
src/kernel/main.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "kutil/memory.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "device_manager.h"
|
||||||
|
#include "font.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
#include "kernel_data.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "memory_pages.h"
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void do_the_set_registers(popcorn_data *header);
|
||||||
|
void kernel_main(popcorn_data *header);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
console
|
||||||
|
load_console(const popcorn_data *header)
|
||||||
|
{
|
||||||
|
console cons{
|
||||||
|
font::load(header->font),
|
||||||
|
screen{
|
||||||
|
header->frame_buffer,
|
||||||
|
header->hres,
|
||||||
|
header->vres,
|
||||||
|
header->rmask,
|
||||||
|
header->gmask,
|
||||||
|
header->bmask},
|
||||||
|
header->log,
|
||||||
|
header->log_length};
|
||||||
|
|
||||||
|
return cons;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
kernel_main(popcorn_data *header)
|
||||||
|
{
|
||||||
|
console *cons = new (&g_console) console();
|
||||||
|
cons->set_color(0x21, 0x00);
|
||||||
|
cons->puts("Popcorn OS ");
|
||||||
|
cons->set_color(0x08, 0x00);
|
||||||
|
cons->puts(GIT_VERSION " booting...\n");
|
||||||
|
|
||||||
|
log::init(cons);
|
||||||
|
|
||||||
|
page_manager *pager = new (&g_page_manager) page_manager;
|
||||||
|
pager->mark_offset_pointer(&header->frame_buffer, header->frame_buffer_length);
|
||||||
|
|
||||||
|
memory_initialize_managers(
|
||||||
|
header->memory_map,
|
||||||
|
header->memory_map_length,
|
||||||
|
header->memory_map_desc_size);
|
||||||
|
|
||||||
|
size_t n = 5000;
|
||||||
|
void *p = kalloc(n);
|
||||||
|
log::info(logs::memory, "kalloc'd %d bytes at %lx", n, p);
|
||||||
|
|
||||||
|
interrupts_init();
|
||||||
|
interrupts_enable();
|
||||||
|
|
||||||
|
device_manager devices(header->acpi_table);
|
||||||
|
|
||||||
|
// int x = 1 / 0;
|
||||||
|
// __asm__ __volatile__("int $15");
|
||||||
|
|
||||||
|
g_console.puts("boogity!");
|
||||||
|
do_the_set_registers(header);
|
||||||
|
}
|
||||||
51
src/kernel/memory.cpp
Normal file
51
src/kernel/memory.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "assert.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "memory_pages.h"
|
||||||
|
|
||||||
|
memory_manager g_kernel_memory_manager;
|
||||||
|
|
||||||
|
struct memory_allocation_header
|
||||||
|
{
|
||||||
|
uint64_t pages;
|
||||||
|
uint64_t reserved;
|
||||||
|
uint8_t data[0];
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
memory_manager::memory_manager() :
|
||||||
|
m_start(nullptr),
|
||||||
|
m_length(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_manager::memory_manager(void *start, size_t length) :
|
||||||
|
m_start(start),
|
||||||
|
m_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
memory_manager::allocate(size_t length)
|
||||||
|
{
|
||||||
|
length = page_align(length + sizeof(memory_allocation_header));
|
||||||
|
|
||||||
|
if (length > m_length) return nullptr;
|
||||||
|
|
||||||
|
m_length -= length;
|
||||||
|
memory_allocation_header *header =
|
||||||
|
reinterpret_cast<memory_allocation_header *>(m_start);
|
||||||
|
m_start = reinterpret_cast<uint8_t *>(m_start) + length;
|
||||||
|
|
||||||
|
size_t pages = length / page_manager::page_size;
|
||||||
|
g_page_manager.map_pages(
|
||||||
|
reinterpret_cast<page_manager::addr_t>(header),
|
||||||
|
pages);
|
||||||
|
|
||||||
|
header->pages = pages;
|
||||||
|
return &header->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
memory_manager::free(void *p)
|
||||||
|
{
|
||||||
|
// In this simple version, we don't care about freed pointers
|
||||||
|
}
|
||||||
46
src/kernel/memory.h
Normal file
46
src/kernel/memory.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file memory.h
|
||||||
|
/// The block memory manager and related definitions.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct memory_block_node;
|
||||||
|
|
||||||
|
/// Manager for allocation of virtual memory.
|
||||||
|
class memory_manager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
memory_manager();
|
||||||
|
memory_manager(void *start, size_t length);
|
||||||
|
|
||||||
|
/// Allocate memory from the area managed.
|
||||||
|
/// \arg length The amount of memory to allocate, in bytes
|
||||||
|
/// \returns A pointer to the allocated memory, or nullptr if
|
||||||
|
/// allocation failed.
|
||||||
|
void * allocate(size_t length);
|
||||||
|
|
||||||
|
/// Free a previous allocation.
|
||||||
|
/// \arg p A pointer previously retuned by allocate()
|
||||||
|
void free(void *p);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class page_manager;
|
||||||
|
|
||||||
|
// Simple incrementing pointer.
|
||||||
|
void *m_start;
|
||||||
|
size_t m_length;
|
||||||
|
|
||||||
|
memory_manager(const memory_manager &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern memory_manager g_kernel_memory_manager;
|
||||||
|
|
||||||
|
/// Bootstrap the memory managers.
|
||||||
|
void memory_initialize_managers(const void *memory_map, size_t map_length, size_t desc_length);
|
||||||
|
|
||||||
|
/// Allocate kernel space memory.
|
||||||
|
/// \arg length The amount of memory to allocate, in bytes
|
||||||
|
/// \returns A pointer to the allocated memory, or nullptr if
|
||||||
|
/// allocation failed.
|
||||||
|
inline void * kalloc(size_t length) { return g_kernel_memory_manager.allocate(length); }
|
||||||
531
src/kernel/memory_bootstrap.cpp
Normal file
531
src/kernel/memory_bootstrap.cpp
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
#include "kutil/memory.h"
|
||||||
|
#include "assert.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "memory_pages.h"
|
||||||
|
|
||||||
|
const unsigned efi_page_size = 0x1000;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 * page_manager::page_size)};
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
page_block *
|
||||||
|
remove_block_for(page_block **list, uint64_t phys_start, uint64_t pages, page_block **cache)
|
||||||
|
{
|
||||||
|
// This is basically just the removal portion of page_manager::unmap_pages,
|
||||||
|
// but with physical addresses, and only ever removing a single block.
|
||||||
|
|
||||||
|
page_block *prev = nullptr;
|
||||||
|
page_block *cur = *list;
|
||||||
|
while (cur && !cur->contains_physical(phys_start)) {
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
kassert(cur, "Couldn't find block to remove");
|
||||||
|
|
||||||
|
uint64_t size = page_manager::page_size * pages;
|
||||||
|
uint64_t end = phys_start + size;
|
||||||
|
uint64_t leading = phys_start - cur->physical_address;
|
||||||
|
uint64_t trailing = cur->physical_end() - end;
|
||||||
|
|
||||||
|
if (leading) {
|
||||||
|
uint64_t pages = leading / page_manager::page_size;
|
||||||
|
|
||||||
|
page_block *lead_block = *cache;
|
||||||
|
*cache = (*cache)->next;
|
||||||
|
|
||||||
|
lead_block->copy(cur);
|
||||||
|
lead_block->next = cur;
|
||||||
|
lead_block->count = pages;
|
||||||
|
|
||||||
|
cur->count -= pages;
|
||||||
|
cur->physical_address += leading;
|
||||||
|
|
||||||
|
if (cur->virtual_address)
|
||||||
|
cur->virtual_address += leading;
|
||||||
|
|
||||||
|
if (prev) {
|
||||||
|
prev->next = lead_block;
|
||||||
|
} else {
|
||||||
|
prev = lead_block;
|
||||||
|
*list = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailing) {
|
||||||
|
uint64_t pages = trailing / page_manager::page_size;
|
||||||
|
|
||||||
|
page_block *trail_block = *cache;
|
||||||
|
*cache = (*cache)->next;
|
||||||
|
|
||||||
|
trail_block->copy(cur);
|
||||||
|
trail_block->next = cur->next;
|
||||||
|
trail_block->count = pages;
|
||||||
|
trail_block->physical_address += size;
|
||||||
|
|
||||||
|
if (cur->virtual_address)
|
||||||
|
trail_block->virtual_address += size;
|
||||||
|
|
||||||
|
cur->count -= pages;
|
||||||
|
cur->next = trail_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev->next = cur->next;
|
||||||
|
cur->next = nullptr;
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
gather_block_lists(
|
||||||
|
uint64_t scratch_virt,
|
||||||
|
const void *memory_map,
|
||||||
|
size_t map_length,
|
||||||
|
size_t desc_length,
|
||||||
|
page_block **free_head,
|
||||||
|
page_block **used_head)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
page_block *free = nullptr;
|
||||||
|
page_block *used = nullptr;
|
||||||
|
|
||||||
|
page_block *block_list = reinterpret_cast<page_block *>(scratch_virt);
|
||||||
|
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) {
|
||||||
|
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:
|
||||||
|
block->flags = page_block_flags::free;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case efi_memory_type::acpi_reclaim:
|
||||||
|
block->flags =
|
||||||
|
page_block_flags::used |
|
||||||
|
page_block_flags::mapped |
|
||||||
|
page_block_flags::acpi_wait;
|
||||||
|
|
||||||
|
block->virtual_address = block->physical_address;
|
||||||
|
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 || !block->physical_address)
|
||||||
|
block->flags |= page_block_flags::mapped;
|
||||||
|
|
||||||
|
used = page_block::insert(used, block);
|
||||||
|
} else {
|
||||||
|
free = page_block::insert(free, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
desc = desc_incr(desc, desc_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
*free_head = free;
|
||||||
|
*used_head = used;
|
||||||
|
return reinterpret_cast<uint64_t>(&block_list[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
page_block *
|
||||||
|
fill_page_with_blocks(uint64_t start)
|
||||||
|
{
|
||||||
|
uint64_t end = page_align(start);
|
||||||
|
uint64_t count = (end - start) / sizeof(page_block);
|
||||||
|
if (count == 0) return nullptr;
|
||||||
|
|
||||||
|
page_block *blocks = reinterpret_cast<page_block *>(start);
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
blocks[i].zero(&blocks[i+1]);
|
||||||
|
blocks[count - 1].next = nullptr;
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_new_table(page_table *base, unsigned index, page_table *new_table)
|
||||||
|
{
|
||||||
|
uint64_t entry = base->entries[index];
|
||||||
|
|
||||||
|
// If this is a large page and not a a table, bail out.
|
||||||
|
if(entry & 0x80) return;
|
||||||
|
|
||||||
|
if (entry & 0x1) {
|
||||||
|
page_table *old_next = reinterpret_cast<page_table *>(
|
||||||
|
base->entries[index] & ~0xffful);
|
||||||
|
for (int i = 0; i < 512; ++i) new_table->entries[i] = old_next->entries[i];
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < 512; ++i) new_table->entries[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
base->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
find_efi_free_aligned_pages(const void *memory_map, size_t map_length, size_t desc_length, unsigned pages)
|
||||||
|
{
|
||||||
|
efi_memory_descriptor const *desc =
|
||||||
|
reinterpret_cast<efi_memory_descriptor const *>(memory_map);
|
||||||
|
efi_memory_descriptor const *end = desc_incr(desc, map_length);
|
||||||
|
|
||||||
|
const unsigned want_space = pages * page_manager::page_size;
|
||||||
|
uint64_t start_phys = 0;
|
||||||
|
|
||||||
|
for (; desc < end; desc = desc_incr(desc, desc_length)) {
|
||||||
|
if (desc->type != efi_memory_type::available)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// See if the first wanted pages fit in one page table. If we
|
||||||
|
// find free memory at zero, skip ahead because we're not ready
|
||||||
|
// to deal with 0 being a valid pointer yet.
|
||||||
|
start_phys = desc->physical_start;
|
||||||
|
if (start_phys == 0)
|
||||||
|
start_phys += efi_page_size;
|
||||||
|
|
||||||
|
const uint64_t desc_end =
|
||||||
|
desc->physical_start + desc->pages * efi_page_size;
|
||||||
|
|
||||||
|
uint64_t end = start_phys + want_space;
|
||||||
|
if (end < desc_end) {
|
||||||
|
page_table_indices start_idx{start_phys};
|
||||||
|
page_table_indices end_idx{end};
|
||||||
|
if (start_idx[0] == end_idx[0] &&
|
||||||
|
start_idx[1] == end_idx[1] &&
|
||||||
|
start_idx[2] == end_idx[2])
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Try seeing if the page-table-aligned version fits
|
||||||
|
start_phys = page_table_align(start_phys);
|
||||||
|
end = start_phys + want_space;
|
||||||
|
if (end < desc_end)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kassert(desc < end, "Couldn't find wanted pages of aligned scratch space.");
|
||||||
|
return start_phys;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
check_needs_page_ident(page_table *table, unsigned index, page_table **free_pages)
|
||||||
|
{
|
||||||
|
if (table->entries[index] & 0x1 == 1) return 0;
|
||||||
|
|
||||||
|
kassert(*free_pages, "check_needs_page_ident needed to allocate but had no free pages");
|
||||||
|
|
||||||
|
page_table *new_table = (*free_pages)++;
|
||||||
|
for (int i=0; i<512; ++i) new_table->entries[i] = 0;
|
||||||
|
table->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
page_in_ident(
|
||||||
|
page_table *pml4,
|
||||||
|
uint64_t phys_addr,
|
||||||
|
uint64_t virt_addr,
|
||||||
|
uint64_t count,
|
||||||
|
page_table *free_pages)
|
||||||
|
{
|
||||||
|
page_table_indices idx{virt_addr};
|
||||||
|
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
|
||||||
|
|
||||||
|
unsigned pages_consumed = 0;
|
||||||
|
for (; idx[0] < 512; idx[0] += 1) {
|
||||||
|
pages_consumed += check_needs_page_ident(tables[0], idx[0], &free_pages);
|
||||||
|
tables[1] = reinterpret_cast<page_table *>(
|
||||||
|
tables[0]->entries[idx[0]] & ~0xfffull);
|
||||||
|
|
||||||
|
for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 0) {
|
||||||
|
pages_consumed += check_needs_page_ident(tables[1], idx[1], &free_pages);
|
||||||
|
tables[2] = reinterpret_cast<page_table *>(
|
||||||
|
tables[1]->entries[idx[1]] & ~0xfffull);
|
||||||
|
|
||||||
|
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) {
|
||||||
|
pages_consumed += check_needs_page_ident(tables[2], idx[2], &free_pages);
|
||||||
|
tables[3] = reinterpret_cast<page_table *>(
|
||||||
|
tables[2]->entries[idx[2]] & ~0xfffull);
|
||||||
|
|
||||||
|
for (; idx[3] < 512; idx[3] += 1) {
|
||||||
|
tables[3]->entries[idx[3]] = phys_addr | 0xb;
|
||||||
|
phys_addr += page_manager::page_size;
|
||||||
|
if (--count == 0) return pages_consumed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kassert(0, "Ran to end of page_in_ident");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
memory_initialize_managers(const void *memory_map, size_t map_length, size_t desc_length)
|
||||||
|
{
|
||||||
|
// The bootloader reserved 16 pages for page tables, which we'll use to bootstrap.
|
||||||
|
// The first one is the already-installed PML4, so grab it from CR3.
|
||||||
|
uint64_t cr3;
|
||||||
|
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||||
|
page_table *tables = reinterpret_cast<page_table *>(cr3 & ~0xfffull);
|
||||||
|
|
||||||
|
// Now go through EFi's memory map and find a region of scratch space.
|
||||||
|
const unsigned want_pages = 32;
|
||||||
|
uint64_t free_region_start_phys =
|
||||||
|
find_efi_free_aligned_pages(memory_map, map_length, desc_length, want_pages);
|
||||||
|
|
||||||
|
// Offset-map this region into the higher half.
|
||||||
|
uint64_t free_region_start_virt =
|
||||||
|
free_region_start_phys + page_manager::high_offset;
|
||||||
|
|
||||||
|
uint64_t free_next = free_region_start_virt;
|
||||||
|
|
||||||
|
// 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_start_virt};
|
||||||
|
|
||||||
|
copy_new_table(&tables[0], fr_idx[0], &tables[1]);
|
||||||
|
copy_new_table(&tables[1], fr_idx[1], &tables[2]);
|
||||||
|
copy_new_table(&tables[2], fr_idx[2], &tables[3]);
|
||||||
|
page_in_ident(&tables[0], free_region_start_phys, free_region_start_virt, want_pages, nullptr);
|
||||||
|
|
||||||
|
// We now have pages starting at "free_next" to bootstrap ourselves. Start by
|
||||||
|
// taking inventory of free pages.
|
||||||
|
page_block *free_head = nullptr;
|
||||||
|
page_block *used_head = nullptr;
|
||||||
|
free_next = gather_block_lists(
|
||||||
|
free_next, memory_map, map_length, desc_length,
|
||||||
|
&free_head, &used_head);
|
||||||
|
|
||||||
|
// Unused page_block structs go here - finish out the current page with them
|
||||||
|
page_block *cache_head = fill_page_with_blocks(free_next);
|
||||||
|
free_next = page_align(free_next);
|
||||||
|
|
||||||
|
// Now go back through these lists and consolidate
|
||||||
|
page_block *freed = page_block::consolidate(free_head);
|
||||||
|
cache_head = page_block::append(cache_head, freed);
|
||||||
|
|
||||||
|
freed = page_block::consolidate(used_head);
|
||||||
|
cache_head = page_block::append(cache_head, freed);
|
||||||
|
|
||||||
|
|
||||||
|
// Pull out the block that represents the bootstrap pages we've used
|
||||||
|
uint64_t used = free_next - free_region_start_virt;
|
||||||
|
uint64_t used_pages = used / page_manager::page_size;
|
||||||
|
uint64_t remaining_pages = want_pages - used_pages;
|
||||||
|
|
||||||
|
page_block *removed = remove_block_for(
|
||||||
|
&free_head,
|
||||||
|
free_region_start_phys,
|
||||||
|
used_pages,
|
||||||
|
&cache_head);
|
||||||
|
|
||||||
|
kassert(removed, "remove_block_for didn't find the bootstrap region.");
|
||||||
|
kassert(removed->physical_address == free_region_start_phys,
|
||||||
|
"remove_block_for found the wrong region.");
|
||||||
|
|
||||||
|
// Add it to the used list
|
||||||
|
removed->virtual_address = free_region_start_virt;
|
||||||
|
removed->flags = page_block_flags::used | page_block_flags::mapped;
|
||||||
|
used_head = page_block::insert(used_head, removed);
|
||||||
|
|
||||||
|
// Pull out the block that represents the rest
|
||||||
|
uint64_t free_next_phys = free_region_start_phys + used;
|
||||||
|
|
||||||
|
removed = remove_block_for(
|
||||||
|
&free_head,
|
||||||
|
free_next_phys,
|
||||||
|
remaining_pages,
|
||||||
|
&cache_head);
|
||||||
|
|
||||||
|
kassert(removed, "remove_block_for didn't find the page table region.");
|
||||||
|
kassert(removed->physical_address == free_next_phys,
|
||||||
|
"remove_block_for found the wrong region.");
|
||||||
|
|
||||||
|
uint64_t pt_start_phys = removed->physical_address;
|
||||||
|
uint64_t pt_start_virt = removed->physical_address + page_manager::page_offset;
|
||||||
|
|
||||||
|
// Record that we're about to remap it into the page table address space
|
||||||
|
removed->virtual_address = pt_start_virt;
|
||||||
|
removed->flags = page_block_flags::used | page_block_flags::mapped;
|
||||||
|
used_head = page_block::insert(used_head, removed);
|
||||||
|
|
||||||
|
page_manager *pm = &g_page_manager;
|
||||||
|
|
||||||
|
// Actually remap them into page table space
|
||||||
|
pm->page_out(&tables[0], free_next, remaining_pages);
|
||||||
|
|
||||||
|
page_table_indices pg_idx{pt_start_virt};
|
||||||
|
copy_new_table(&tables[0], pg_idx[0], &tables[4]);
|
||||||
|
copy_new_table(&tables[4], pg_idx[1], &tables[5]);
|
||||||
|
copy_new_table(&tables[5], pg_idx[2], &tables[6]);
|
||||||
|
|
||||||
|
page_in_ident(&tables[0], pt_start_phys, pt_start_virt, remaining_pages, tables + 4);
|
||||||
|
|
||||||
|
// Finally, build an acutal set of kernel page tables that just contains
|
||||||
|
// what the kernel actually has mapped, but making everything writable
|
||||||
|
// (especially the page tables themselves)
|
||||||
|
page_table *pml4 = reinterpret_cast<page_table *>(pt_start_virt);
|
||||||
|
for (int i=0; i<512; ++i) pml4->entries[i] = 0;
|
||||||
|
|
||||||
|
// Give the rest to the page_manager's cache for use in page_in
|
||||||
|
pm->free_table_pages(pml4 + 1, remaining_pages - 1);
|
||||||
|
|
||||||
|
for (page_block *cur = used_head; cur; cur = cur->next) {
|
||||||
|
if (!cur->has_flag(page_block_flags::mapped)) continue;
|
||||||
|
pm->page_in(pml4, cur->physical_address, cur->virtual_address, cur->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put our new PML4 into CR3 to start using it
|
||||||
|
page_manager::set_pml4(pml4);
|
||||||
|
|
||||||
|
// We now have all used memory mapped ourselves. Let the page_manager take
|
||||||
|
// over from here.
|
||||||
|
g_page_manager.init(free_head, used_head, cache_head);
|
||||||
|
}
|
||||||
552
src/kernel/memory_pages.cpp
Normal file
552
src/kernel/memory_pages.cpp
Normal file
@@ -0,0 +1,552 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "assert.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "memory_pages.h"
|
||||||
|
|
||||||
|
page_manager g_page_manager;
|
||||||
|
|
||||||
|
using addr_t = page_manager::addr_t;
|
||||||
|
|
||||||
|
|
||||||
|
static addr_t
|
||||||
|
pt_to_phys(page_table *pt)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<addr_t>(pt) - page_manager::page_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static page_table *
|
||||||
|
pt_from_phys(addr_t p)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<page_table *>((p + page_manager::page_offset) & ~0xfffull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct free_page_header
|
||||||
|
{
|
||||||
|
free_page_header *next;
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t
|
||||||
|
page_block::length(page_block *list)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
for (page_block *b = list; b; b = b->next) ++i;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
page_block *
|
||||||
|
page_block::append(page_block *list, page_block *extra)
|
||||||
|
{
|
||||||
|
if (list == nullptr) return extra;
|
||||||
|
else if (extra == nullptr) return list;
|
||||||
|
|
||||||
|
page_block *cur = list;
|
||||||
|
while (cur->next)
|
||||||
|
cur = cur->next;
|
||||||
|
cur->next = extra;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
page_block *
|
||||||
|
page_block::insert(page_block *list, page_block *block)
|
||||||
|
{
|
||||||
|
if (list == nullptr) return block;
|
||||||
|
else if (block == nullptr) return list;
|
||||||
|
|
||||||
|
page_block *cur = list;
|
||||||
|
page_block *prev = nullptr;
|
||||||
|
while (cur && page_block::compare(block, cur) > 0) {
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->next = cur;
|
||||||
|
if (prev) {
|
||||||
|
prev->next = block;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
page_block::compare(const page_block *lhs, const page_block *rhs)
|
||||||
|
{
|
||||||
|
if (lhs->virtual_address < rhs->virtual_address)
|
||||||
|
return -1;
|
||||||
|
else if (lhs->virtual_address > rhs->virtual_address)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (lhs->physical_address < rhs->physical_address)
|
||||||
|
return -1;
|
||||||
|
else if (lhs->physical_address > rhs->physical_address)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
page_block *
|
||||||
|
page_block::consolidate(page_block *list)
|
||||||
|
{
|
||||||
|
page_block *freed = nullptr;
|
||||||
|
page_block *cur = list;
|
||||||
|
|
||||||
|
while (cur) {
|
||||||
|
page_block *next = cur->next;
|
||||||
|
|
||||||
|
if (next &&
|
||||||
|
cur->flags == next->flags &&
|
||||||
|
cur->physical_end() == next->physical_address &&
|
||||||
|
(!cur->has_flag(page_block_flags::mapped) ||
|
||||||
|
cur->virtual_end() == next->virtual_address)) {
|
||||||
|
|
||||||
|
cur->count += next->count;
|
||||||
|
cur->next = next->next;
|
||||||
|
|
||||||
|
next->zero(freed);
|
||||||
|
freed = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_block::dump(page_block *list, const char *name, bool show_unmapped)
|
||||||
|
{
|
||||||
|
log::debug(logs::memory, "Block list %s:", name);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (page_block *cur = list; cur; cur = cur->next) {
|
||||||
|
count += 1;
|
||||||
|
if (!(show_unmapped || cur->has_flag(page_block_flags::mapped)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cur->virtual_address) {
|
||||||
|
page_table_indices start{cur->virtual_address};
|
||||||
|
log::debug(logs::memory, " %lx %x [%6d] %lx (%d,%d,%d,%d)",
|
||||||
|
cur->physical_address,
|
||||||
|
cur->flags,
|
||||||
|
cur->count,
|
||||||
|
cur->virtual_address,
|
||||||
|
start[0], start[1], start[2], start[3]);
|
||||||
|
} else {
|
||||||
|
page_table_indices start{cur->virtual_address};
|
||||||
|
log::debug(logs::memory, " %lx %x [%6d]",
|
||||||
|
cur->physical_address,
|
||||||
|
cur->flags,
|
||||||
|
cur->count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug(logs::memory, " Total: %d", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_block::zero(page_block *set_next)
|
||||||
|
{
|
||||||
|
physical_address = 0;
|
||||||
|
virtual_address = 0;
|
||||||
|
count = 0;
|
||||||
|
flags = page_block_flags::free;
|
||||||
|
next = set_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_block::copy(page_block *other)
|
||||||
|
{
|
||||||
|
physical_address = other->physical_address;
|
||||||
|
virtual_address = other->virtual_address;
|
||||||
|
count = other->count;
|
||||||
|
flags = other->flags;
|
||||||
|
next = other->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
page_manager::page_manager() :
|
||||||
|
m_free(nullptr),
|
||||||
|
m_used(nullptr),
|
||||||
|
m_block_cache(nullptr),
|
||||||
|
m_page_cache(nullptr)
|
||||||
|
{
|
||||||
|
kassert(this == &g_page_manager, "Attempt to create another page_manager.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::init(
|
||||||
|
page_block *free,
|
||||||
|
page_block *used,
|
||||||
|
page_block *block_cache)
|
||||||
|
{
|
||||||
|
m_free = free;
|
||||||
|
m_used = used;
|
||||||
|
m_block_cache = block_cache;
|
||||||
|
|
||||||
|
// For now we're ignoring that we've got the scratch pages
|
||||||
|
// allocated, full of page_block structs. Eventually hand
|
||||||
|
// control of that to a slab allocator.
|
||||||
|
|
||||||
|
page_table *pml4 = get_pml4();
|
||||||
|
|
||||||
|
// Fix up the offset-marked pointers
|
||||||
|
for (unsigned i = 0; i < m_marked_pointer_count; ++i) {
|
||||||
|
addr_t *p = reinterpret_cast<addr_t *>(m_marked_pointers[i]);
|
||||||
|
addr_t v = *p + page_offset;
|
||||||
|
addr_t c = (m_marked_pointer_lengths[i] / page_size) + 1;
|
||||||
|
|
||||||
|
// TODO: cleanly search/split this as a block out of used/free if possible
|
||||||
|
page_block *block = get_block();
|
||||||
|
|
||||||
|
// TODO: page-align
|
||||||
|
block->physical_address = *p;
|
||||||
|
block->virtual_address = v;
|
||||||
|
block->count = c;
|
||||||
|
block->flags =
|
||||||
|
page_block_flags::used |
|
||||||
|
page_block_flags::mapped |
|
||||||
|
page_block_flags::mmio;
|
||||||
|
|
||||||
|
log::info(logs::memory, "Fixing up pointer %lx [%3d] -> %lx", *p, c, v);
|
||||||
|
|
||||||
|
m_used = page_block::insert(m_used, block);
|
||||||
|
page_in(pml4, *p, v, c);
|
||||||
|
*p = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
consolidate_blocks();
|
||||||
|
|
||||||
|
page_block::dump(m_used, "used", true);
|
||||||
|
page_block::dump(m_free, "free", true);
|
||||||
|
|
||||||
|
// Initialize the kernel memory manager
|
||||||
|
addr_t end = 0;
|
||||||
|
for (page_block *b = m_used; b; b = b->next) {
|
||||||
|
if (b->virtual_address < page_offset)
|
||||||
|
end = b->virtual_end();
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
new (&g_kernel_memory_manager) memory_manager(
|
||||||
|
reinterpret_cast<void *>(end),
|
||||||
|
page_offset - end);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::mark_offset_pointer(void **pointer, size_t length)
|
||||||
|
{
|
||||||
|
m_marked_pointers[m_marked_pointer_count] = pointer;
|
||||||
|
m_marked_pointer_lengths[m_marked_pointer_count++] = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
page_block *
|
||||||
|
page_manager::get_block()
|
||||||
|
{
|
||||||
|
page_block *block = m_block_cache;
|
||||||
|
if (block) {
|
||||||
|
m_block_cache = block->next;
|
||||||
|
block->next = 0;
|
||||||
|
return block;
|
||||||
|
} else {
|
||||||
|
kassert(0, "NYI: page_manager::get_block() needed to allocate.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::free_blocks(page_block *block)
|
||||||
|
{
|
||||||
|
if (!block) return;
|
||||||
|
|
||||||
|
page_block *cur = block;
|
||||||
|
while (cur) {
|
||||||
|
page_block *next = cur->next;
|
||||||
|
cur->zero(cur->next ? cur->next : m_block_cache);
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_block_cache = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
page_table *
|
||||||
|
page_manager::get_table_page()
|
||||||
|
{
|
||||||
|
if (!m_page_cache) {
|
||||||
|
addr_t phys = 0;
|
||||||
|
size_t n = pop_pages(32, &phys);
|
||||||
|
addr_t virt = phys + page_offset;
|
||||||
|
|
||||||
|
page_block *block = get_block();
|
||||||
|
block->physical_address = phys;
|
||||||
|
block->virtual_address = virt;
|
||||||
|
block->count = n;
|
||||||
|
page_block::insert(m_used, block);
|
||||||
|
|
||||||
|
page_in(get_pml4(), phys, virt, n);
|
||||||
|
|
||||||
|
m_page_cache = reinterpret_cast<free_page_header *>(virt);
|
||||||
|
|
||||||
|
// The last one needs to be null, so do n-1
|
||||||
|
addr_t end = virt + (n-1) * page_size;
|
||||||
|
while (virt < end) {
|
||||||
|
reinterpret_cast<free_page_header *>(virt)->next =
|
||||||
|
reinterpret_cast<free_page_header *>(virt + page_size);
|
||||||
|
virt += page_size;
|
||||||
|
}
|
||||||
|
reinterpret_cast<free_page_header *>(virt)->next = nullptr;
|
||||||
|
|
||||||
|
log::info(logs::memory, "Mappd %d new page table pages at %lx", n, phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_page_header *page = m_page_cache;
|
||||||
|
m_page_cache = page->next;
|
||||||
|
return reinterpret_cast<page_table *>(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::free_table_pages(void *pages, size_t count)
|
||||||
|
{
|
||||||
|
addr_t start = reinterpret_cast<addr_t>(pages);
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
addr_t addr = start + (i * page_size);
|
||||||
|
free_page_header *header = reinterpret_cast<free_page_header *>(addr);
|
||||||
|
header->count = 1;
|
||||||
|
header->next = m_page_cache;
|
||||||
|
m_page_cache = header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::consolidate_blocks()
|
||||||
|
{
|
||||||
|
m_block_cache = page_block::append(m_block_cache, page_block::consolidate(m_free));
|
||||||
|
m_block_cache = page_block::append(m_block_cache, page_block::consolidate(m_used));
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
page_manager::map_pages(addr_t address, size_t count)
|
||||||
|
{
|
||||||
|
void *ret = reinterpret_cast<void *>(address);
|
||||||
|
page_table *pml4 = get_pml4();
|
||||||
|
|
||||||
|
while (count) {
|
||||||
|
kassert(m_free, "page_manager::map_pages ran out of free pages!");
|
||||||
|
|
||||||
|
addr_t phys = 0;
|
||||||
|
size_t n = pop_pages(count, &phys);
|
||||||
|
|
||||||
|
page_block *block = get_block();
|
||||||
|
block->physical_address = phys;
|
||||||
|
block->virtual_address = address;
|
||||||
|
block->count = n;
|
||||||
|
page_block::insert(m_used, block);
|
||||||
|
|
||||||
|
page_in(pml4, phys, address, n);
|
||||||
|
|
||||||
|
address += n * page_size;
|
||||||
|
count -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::unmap_pages(addr_t address, size_t count)
|
||||||
|
{
|
||||||
|
page_block **prev = &m_used;
|
||||||
|
page_block *cur = m_used;
|
||||||
|
while (cur && !cur->contains(address)) {
|
||||||
|
prev = &cur->next;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
kassert(cur, "Couldn't find existing mapped pages to unmap");
|
||||||
|
|
||||||
|
size_t size = page_size * count;
|
||||||
|
addr_t end = address + size;
|
||||||
|
|
||||||
|
while (cur && cur->contains(address)) {
|
||||||
|
size_t leading = address - cur->virtual_address;
|
||||||
|
size_t trailing =
|
||||||
|
end > cur->virtual_end() ?
|
||||||
|
0 : (cur->virtual_end() - end);
|
||||||
|
|
||||||
|
if (leading) {
|
||||||
|
size_t pages = leading / page_size;
|
||||||
|
|
||||||
|
page_block *lead_block = get_block();
|
||||||
|
lead_block->copy(cur);
|
||||||
|
lead_block->next = cur;
|
||||||
|
lead_block->count = pages;
|
||||||
|
|
||||||
|
cur->count -= pages;
|
||||||
|
cur->physical_address += leading;
|
||||||
|
cur->virtual_address += leading;
|
||||||
|
|
||||||
|
*prev = lead_block;
|
||||||
|
prev = &lead_block->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailing) {
|
||||||
|
size_t pages = trailing / page_size;
|
||||||
|
|
||||||
|
page_block *trail_block = get_block();
|
||||||
|
trail_block->copy(cur);
|
||||||
|
trail_block->next = cur->next;
|
||||||
|
trail_block->count = pages;
|
||||||
|
trail_block->physical_address += size;
|
||||||
|
trail_block->virtual_address += size;
|
||||||
|
|
||||||
|
cur->count -= pages;
|
||||||
|
|
||||||
|
cur->next = trail_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
address += cur->count * page_size;
|
||||||
|
page_block *next = cur->next;
|
||||||
|
|
||||||
|
*prev = cur->next;
|
||||||
|
cur->next = nullptr;
|
||||||
|
cur->virtual_address = 0;
|
||||||
|
cur->flags = cur->flags & ~(page_block_flags::used | page_block_flags::mapped);
|
||||||
|
m_free = page_block::insert(m_free, cur);
|
||||||
|
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::check_needs_page(page_table *table, unsigned index)
|
||||||
|
{
|
||||||
|
if (table->entries[index] & 0x1 == 1) return;
|
||||||
|
|
||||||
|
page_table *new_table = get_table_page();
|
||||||
|
for (int i=0; i<512; ++i) new_table->entries[i] = 0;
|
||||||
|
table->entries[index] = pt_to_phys(new_table) | 0xb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count)
|
||||||
|
{
|
||||||
|
page_table_indices idx{virt_addr};
|
||||||
|
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
|
||||||
|
|
||||||
|
for (; idx[0] < 512; idx[0] += 1) {
|
||||||
|
check_needs_page(tables[0], idx[0]);
|
||||||
|
tables[1] = tables[0]->get(idx[0]);
|
||||||
|
|
||||||
|
for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 0) {
|
||||||
|
check_needs_page(tables[1], idx[1]);
|
||||||
|
tables[2] = tables[1]->get(idx[1]);
|
||||||
|
|
||||||
|
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) {
|
||||||
|
check_needs_page(tables[2], idx[2]);
|
||||||
|
tables[3] = tables[2]->get(idx[2]);
|
||||||
|
|
||||||
|
for (; idx[3] < 512; idx[3] += 1) {
|
||||||
|
tables[3]->entries[idx[3]] = phys_addr | 0xb;
|
||||||
|
phys_addr += page_manager::page_size;
|
||||||
|
if (--count == 0) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kassert(0, "Ran to end of page_in");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
page_manager::page_out(page_table *pml4, addr_t virt_addr, size_t count)
|
||||||
|
{
|
||||||
|
page_table_indices idx{virt_addr};
|
||||||
|
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
|
||||||
|
|
||||||
|
for (; idx[0] < 512; idx[0] += 1) {
|
||||||
|
tables[1] = reinterpret_cast<page_table *>(
|
||||||
|
tables[0]->entries[idx[0]] & ~0xfffull);
|
||||||
|
|
||||||
|
for (; idx[1] < 512; idx[1] += 1) {
|
||||||
|
tables[2] = reinterpret_cast<page_table *>(
|
||||||
|
tables[1]->entries[idx[1]] & ~0xfffull);
|
||||||
|
|
||||||
|
for (; idx[2] < 512; idx[2] += 1) {
|
||||||
|
tables[3] = reinterpret_cast<page_table *>(
|
||||||
|
tables[2]->entries[idx[2]] & ~0xfffull);
|
||||||
|
|
||||||
|
for (; idx[3] < 512; idx[3] += 1) {
|
||||||
|
tables[3]->entries[idx[3]] = 0;
|
||||||
|
if (--count == 0) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kassert(0, "Ran to end of page_out");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
page_manager::pop_pages(size_t count, addr_t *address)
|
||||||
|
{
|
||||||
|
kassert(m_free, "page_manager::pop_pages ran out of free pages!");
|
||||||
|
|
||||||
|
unsigned n = std::min(count, static_cast<size_t>(m_free->count));
|
||||||
|
*address = m_free->physical_address;
|
||||||
|
|
||||||
|
m_free->physical_address += n * page_size;
|
||||||
|
m_free->count -= n;
|
||||||
|
if (m_free->count == 0) {
|
||||||
|
page_block *block = m_free;
|
||||||
|
m_free = m_free->next;
|
||||||
|
|
||||||
|
block->zero(m_block_cache);
|
||||||
|
m_block_cache = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
page_table::dump(int level, uint64_t offset)
|
||||||
|
{
|
||||||
|
log::info(logs::memory, "Level %d page table @ %lx (off %lx):", level, this, offset);
|
||||||
|
for (int i=0; i<512; ++i) {
|
||||||
|
uint64_t ent = entries[i];
|
||||||
|
if (ent == 0) continue;
|
||||||
|
|
||||||
|
if (ent & 0x1 == 0) {
|
||||||
|
log::info(logs::memory, " %3d: %lx NOT PRESENT", i, ent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) {
|
||||||
|
log::info(logs::memory, " %3d: %lx -> Large page at %lx",
|
||||||
|
i, ent, ent & ~0xfffull);
|
||||||
|
continue;
|
||||||
|
} else if (level == 1) {
|
||||||
|
log::info(logs::memory, " %3d: %lx -> Page at %lx",
|
||||||
|
i, ent, ent & ~0xfffull);
|
||||||
|
} else {
|
||||||
|
log::info(logs::memory, " %3d: %lx -> Level %d table at %lx",
|
||||||
|
i, ent, level - 1, (ent & ~0xfffull) + offset);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--level > 0) {
|
||||||
|
for (int i=0; i<512; ++i) {
|
||||||
|
uint64_t ent = entries[i];
|
||||||
|
if ((ent & 0x1) == 0) continue;
|
||||||
|
if ((ent & 0x80)) continue;
|
||||||
|
|
||||||
|
page_table *next = reinterpret_cast<page_table *>((ent & ~0xffful) + offset);
|
||||||
|
next->dump(level, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
293
src/kernel/memory_pages.h
Normal file
293
src/kernel/memory_pages.h
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file memory_pages.h
|
||||||
|
/// The page memory manager and related definitions.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "kutil/enum_bitfields.h"
|
||||||
|
|
||||||
|
struct page_block;
|
||||||
|
struct page_table;
|
||||||
|
struct free_page_header;
|
||||||
|
|
||||||
|
|
||||||
|
/// Manager for allocation of physical pages.
|
||||||
|
class page_manager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using addr_t = uint64_t;
|
||||||
|
|
||||||
|
/// Size of a single page.
|
||||||
|
static const size_t page_size = 0x1000;
|
||||||
|
|
||||||
|
/// Start of the higher half.
|
||||||
|
static const addr_t high_offset = 0xffff800000000000;
|
||||||
|
|
||||||
|
/// Offset from physical where page tables are mapped.
|
||||||
|
static const addr_t page_offset = 0xffffff8000000000;
|
||||||
|
|
||||||
|
page_manager();
|
||||||
|
|
||||||
|
/// Allocate and map pages into virtual memory.
|
||||||
|
/// \arg address The virtual address at which to map the pages
|
||||||
|
/// \arg count The number of pages to map
|
||||||
|
/// \returns A pointer to the start of the mapped region
|
||||||
|
void * map_pages(addr_t address, size_t count);
|
||||||
|
|
||||||
|
/// Unmap existing pages from memory.
|
||||||
|
/// \arg address The virtual address of the memory to unmap
|
||||||
|
/// \arg count The number of pages to unmap
|
||||||
|
void unmap_pages(addr_t address, size_t count);
|
||||||
|
|
||||||
|
/// Mark a pointer and range to be offset-mapped. This pointer will
|
||||||
|
/// automatically get updated once page_manager::init() is called.
|
||||||
|
/// \arg pointer Pointer to a pointer to the memory area to be mapped
|
||||||
|
/// \arg length Length of the memory area to be mapped
|
||||||
|
void mark_offset_pointer(void **pointer, size_t length);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend void memory_initialize_managers(const void *, size_t, size_t);
|
||||||
|
|
||||||
|
/// Set up the memory manager from bootstraped memory
|
||||||
|
void init(
|
||||||
|
page_block *free,
|
||||||
|
page_block *used,
|
||||||
|
page_block *block_cache);
|
||||||
|
|
||||||
|
/// Initialize the virtual memory manager based on this object's state
|
||||||
|
void init_memory_manager();
|
||||||
|
|
||||||
|
/// Create a `page_block` struct or pull one from the cache.
|
||||||
|
/// \returns An empty `page_block` struct
|
||||||
|
page_block * get_block();
|
||||||
|
|
||||||
|
/// Return a list of `page_block` structs to the cache.
|
||||||
|
/// \arg block A list of `page_block` structs
|
||||||
|
void free_blocks(page_block *block);
|
||||||
|
|
||||||
|
/// Allocate a page for a page table, or pull one from the cache
|
||||||
|
/// \returns An empty page mapped in page space
|
||||||
|
page_table * get_table_page();
|
||||||
|
|
||||||
|
/// Return a set of mapped contiguous pages to the page cache.
|
||||||
|
/// \arg pages Pointer to the first page to be returned
|
||||||
|
/// \arg count Number of pages in the range
|
||||||
|
void free_table_pages(void *pages, size_t count);
|
||||||
|
|
||||||
|
/// Consolidate the free and used block lists. Return freed blocks
|
||||||
|
/// to the cache.
|
||||||
|
void consolidate_blocks();
|
||||||
|
|
||||||
|
/// Helper to read the PML4 table from CR3.
|
||||||
|
/// \returns A pointer to the current PML4 table.
|
||||||
|
static inline page_table * get_pml4()
|
||||||
|
{
|
||||||
|
addr_t pml4 = 0;
|
||||||
|
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (pml4) );
|
||||||
|
return reinterpret_cast<page_table *>((pml4 & ~0xfffull) + page_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to set the PML4 table pointer in CR3.
|
||||||
|
/// \arg pml4 A pointer to the PML4 table to install.
|
||||||
|
static inline void set_pml4(page_table *pml4)
|
||||||
|
{
|
||||||
|
addr_t p = reinterpret_cast<addr_t>(pml4) - page_offset;
|
||||||
|
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to allocate a new page table. If table entry `i` in
|
||||||
|
/// table `base` is empty, allocate a new page table and point `base[i]` at
|
||||||
|
/// it.
|
||||||
|
/// \arg base Existing page table being indexed into
|
||||||
|
/// \arg i Index into the existing table to check
|
||||||
|
void check_needs_page(page_table *base, unsigned i);
|
||||||
|
|
||||||
|
/// Low-level routine for mapping a number of pages into the given page table.
|
||||||
|
/// \arg pml4 The root page table to map into
|
||||||
|
/// \arg phys_addr The starting physical address of the pages to be mapped
|
||||||
|
/// \arg virt_addr The starting virtual address ot the memory to be mapped
|
||||||
|
/// \arg count The number of pages to map
|
||||||
|
void page_in(
|
||||||
|
page_table *pml4,
|
||||||
|
addr_t phys_addr,
|
||||||
|
addr_t virt_addr,
|
||||||
|
size_t count);
|
||||||
|
|
||||||
|
/// Low-level routine for unmapping a number of pages from the given page table.
|
||||||
|
/// \arg pml4 The root page table for this mapping
|
||||||
|
/// \arg virt_addr The starting virtual address ot the memory to be unmapped
|
||||||
|
/// \arg count The number of pages to unmap
|
||||||
|
void page_out(
|
||||||
|
page_table *pml4,
|
||||||
|
addr_t virt_addr,
|
||||||
|
size_t count);
|
||||||
|
|
||||||
|
/// Get free pages from the free list. Only pages from the first free block
|
||||||
|
/// are returned, so the number may be less than requested, but they will
|
||||||
|
/// be contiguous. Pages will not be mapped into virtual memory.
|
||||||
|
/// \arg count The maximum number of pages to get
|
||||||
|
/// \arg address [out] The address of the first page
|
||||||
|
/// \returns The number of pages retrieved
|
||||||
|
size_t pop_pages(size_t count, addr_t *address);
|
||||||
|
|
||||||
|
page_block *m_free; ///< Free pages list
|
||||||
|
page_block *m_used; ///< In-use pages list
|
||||||
|
|
||||||
|
page_block *m_block_cache; ///< Cache of unused page_block structs
|
||||||
|
free_page_header *m_page_cache; ///< Cache of free pages to use for tables
|
||||||
|
|
||||||
|
static const unsigned marked_pointer_max = 16;
|
||||||
|
unsigned m_marked_pointer_count;
|
||||||
|
void **m_marked_pointers[marked_pointer_max];
|
||||||
|
size_t m_marked_pointer_lengths[marked_pointer_max];
|
||||||
|
|
||||||
|
page_manager(const page_manager &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Global page manager.
|
||||||
|
extern page_manager g_page_manager;
|
||||||
|
|
||||||
|
/// Flags used by `page_block`.
|
||||||
|
enum class page_block_flags : uint32_t
|
||||||
|
{
|
||||||
|
free = 0x00000000, ///< Not a flag, value for free memory
|
||||||
|
used = 0x00000001, ///< Memory is in use
|
||||||
|
mapped = 0x00000002, ///< Memory is mapped to virtual address
|
||||||
|
|
||||||
|
mmio = 0x00000010, ///< Memory is a MMIO region
|
||||||
|
nonvolatile = 0x00000020, ///< Memory is non-volatile storage
|
||||||
|
|
||||||
|
pending_free = 0x10000000, ///< Memory should be freed
|
||||||
|
acpi_wait = 0x40000000, ///< Memory should be freed after ACPI init
|
||||||
|
permanent = 0x80000000, ///< Memory is permanently unusable
|
||||||
|
|
||||||
|
max_flags
|
||||||
|
};
|
||||||
|
IS_BITFIELD(page_block_flags);
|
||||||
|
|
||||||
|
|
||||||
|
/// A block of contiguous pages. Each `page_block` represents contiguous
|
||||||
|
/// physical pages with the same attributes. A `page_block *` is also a
|
||||||
|
/// linked list of such structures.
|
||||||
|
struct page_block
|
||||||
|
{
|
||||||
|
using addr_t = page_manager::addr_t;
|
||||||
|
|
||||||
|
addr_t physical_address;
|
||||||
|
addr_t virtual_address;
|
||||||
|
uint32_t count;
|
||||||
|
page_block_flags flags;
|
||||||
|
page_block *next;
|
||||||
|
|
||||||
|
inline bool has_flag(page_block_flags f) const { return bitfield_contains(flags, f); }
|
||||||
|
inline addr_t physical_end() const { return physical_address + (count * page_manager::page_size); }
|
||||||
|
inline addr_t virtual_end() const { return virtual_address + (count * page_manager::page_size); }
|
||||||
|
|
||||||
|
inline bool contains(addr_t vaddr) const { return vaddr >= virtual_address && vaddr < virtual_end(); }
|
||||||
|
inline bool contains_physical(addr_t addr) const { return addr >= physical_address && addr < physical_end(); }
|
||||||
|
|
||||||
|
/// Helper to zero out a block and optionally set the next pointer.
|
||||||
|
/// \arg next [optional] The value for the `next` pointer
|
||||||
|
void zero(page_block *set_next = nullptr);
|
||||||
|
|
||||||
|
/// Helper to copy a bock from another block
|
||||||
|
/// \arg other The block to copy from
|
||||||
|
void copy(page_block *other);
|
||||||
|
|
||||||
|
/// \name Page block linked list functions
|
||||||
|
/// Functions to act on a `page_block *` as a linked list
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Count the items in the given linked list.
|
||||||
|
/// \arg list The list to count
|
||||||
|
/// \returns The number of entries in the list.
|
||||||
|
static size_t length(page_block *list);
|
||||||
|
|
||||||
|
/// Append a block or list to the given list.
|
||||||
|
/// \arg list The list to append to
|
||||||
|
/// \arg extra The list or block to be appended
|
||||||
|
/// \returns The new list head
|
||||||
|
static page_block * append(page_block *list, page_block *extra);
|
||||||
|
|
||||||
|
/// Sorted-insert of a block into the list by address.
|
||||||
|
/// \arg list The list to insert into
|
||||||
|
/// \arg block The single block to insert
|
||||||
|
/// \returns The new list head
|
||||||
|
static page_block * insert(page_block *list, page_block *block);
|
||||||
|
|
||||||
|
/// Compare two blocks by address.
|
||||||
|
/// \arg lhs The left-hand comparator
|
||||||
|
/// \arg rhs The right-hand comparator
|
||||||
|
/// \returns <0 if lhs is sorts earlier, >0 if lhs sorts later, 0 for equal
|
||||||
|
static int compare(const page_block *lhs, const page_block *rhs);
|
||||||
|
|
||||||
|
/// Traverse the list, joining adjacent blocks where possible.
|
||||||
|
/// \arg list The list to consolidate
|
||||||
|
/// \returns A linked list of freed page_block structures.
|
||||||
|
static page_block * consolidate(page_block *list);
|
||||||
|
|
||||||
|
/// Traverse the list, printing debug info on this list.
|
||||||
|
/// \arg list The list to print
|
||||||
|
/// \arg name [optional] String to print as the name of this list
|
||||||
|
/// \arg show_permanent [optional] If false, hide unmapped blocks
|
||||||
|
static void dump(page_block *list, const char *name = nullptr, bool show_unmapped = false);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Struct to allow easy accessing of a memory page being used as a page table.
|
||||||
|
struct page_table
|
||||||
|
{
|
||||||
|
using pm = page_manager;
|
||||||
|
|
||||||
|
uint64_t entries[512];
|
||||||
|
|
||||||
|
inline page_table * get(int i) const {
|
||||||
|
uint64_t entry = entries[i];
|
||||||
|
if ((entry & 0x1) == 0) return nullptr;
|
||||||
|
return reinterpret_cast<page_table *>((entry & ~0xfffull) + pm::page_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set(int i, page_table *p, uint16_t flags) {
|
||||||
|
entries[i] = (reinterpret_cast<uint64_t>(p) - pm::page_offset) | (flags & 0xfff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(int level = 4, uint64_t offset = page_manager::page_offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Helper struct for computing page table indices of a given address.
|
||||||
|
struct page_table_indices
|
||||||
|
{
|
||||||
|
page_table_indices(uint64_t v = 0) :
|
||||||
|
index{
|
||||||
|
(v >> 39) & 0x1ff,
|
||||||
|
(v >> 30) & 0x1ff,
|
||||||
|
(v >> 21) & 0x1ff,
|
||||||
|
(v >> 12) & 0x1ff }
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// Get the index for a given level of page table.
|
||||||
|
uint64_t & operator[](size_t i) { return index[i]; }
|
||||||
|
uint64_t index[4]; ///< Indices for each level of tables.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Calculate a page-aligned address.
|
||||||
|
/// \arg p The address to align.
|
||||||
|
/// \returns The next page-aligned address _after_ `p`.
|
||||||
|
template <typename T> inline T page_align(T p)
|
||||||
|
{
|
||||||
|
return ((p - 1) & ~(page_manager::page_size - 1)) + page_manager::page_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate a page-table-aligned address. That is, an address that is
|
||||||
|
/// page-aligned to the first page in a page table.
|
||||||
|
/// \arg p The address to align.
|
||||||
|
/// \returns The next page-table-aligned address _after_ `p`.
|
||||||
|
template <typename T> inline T page_table_align(T p) { return ((p - 1) & ~0x1fffffull) + 0x200000; }
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "kutil/coord.h"
|
||||||
|
|
||||||
class screen
|
class screen
|
||||||
{
|
{
|
||||||
@@ -36,5 +36,5 @@ private:
|
|||||||
|
|
||||||
pixel_t *m_framebuffer;
|
pixel_t *m_framebuffer;
|
||||||
color_masks m_masks;
|
color_masks m_masks;
|
||||||
coord<unsigned> m_resolution;
|
kutil::coord<unsigned> m_resolution;
|
||||||
};
|
};
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace kutil {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct coord {
|
struct coord
|
||||||
|
{
|
||||||
T x, y;
|
T x, y;
|
||||||
coord() : x(T{}), y(T{}) {}
|
coord() : x(T{}), y(T{}) {}
|
||||||
coord(T x, T y) : x(x), y(y) {}
|
coord(T x, T y) : x(x), y(y) {}
|
||||||
T size() const { return x * y; }
|
T size() const { return x * y; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace kutil
|
||||||
3
src/modules/kutil/cpprt.cpp
Normal file
3
src/modules/kutil/cpprt.cpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
extern "C" {
|
||||||
|
void __cxa_pure_virtual() { while(1); }
|
||||||
|
}
|
||||||
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
|
||||||
17
src/modules/kutil/memory.h
Normal file
17
src/modules/kutil/memory.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
inline void * operator new (size_t, void *p) throw() { return p; }
|
||||||
|
|
||||||
|
|
||||||
|
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
|
include modules.mk
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
#include "console.h"
|
|
||||||
|
|
||||||
const char digits[] = "0123456789abcdef";
|
|
||||||
|
|
||||||
console::console(const font &f, const screen &s, void *scratch, size_t len) :
|
|
||||||
m_font(f),
|
|
||||||
m_screen(s),
|
|
||||||
m_size(s.width() / f.width(), s.height() / f.height()),
|
|
||||||
m_fg(0xffffff),
|
|
||||||
m_bg(0),
|
|
||||||
m_first(0),
|
|
||||||
m_length(len),
|
|
||||||
m_data(nullptr),
|
|
||||||
m_attrs(nullptr),
|
|
||||||
m_palette(nullptr)
|
|
||||||
{
|
|
||||||
const unsigned count = m_size.size();
|
|
||||||
const size_t palette_size = sizeof(screen::pixel_t) * 256;
|
|
||||||
const size_t attrs_size = 2 * count;
|
|
||||||
|
|
||||||
if (len >= count) {
|
|
||||||
// We have enough scratch space to keep the contents of the screen
|
|
||||||
m_data = static_cast<char *>(scratch);
|
|
||||||
for (unsigned i = 0; i < count; ++i)
|
|
||||||
m_data[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len >= count + palette_size + attrs_size) {
|
|
||||||
// We have enough scratch space to also keep the colors of the text
|
|
||||||
m_palette = reinterpret_cast<screen::pixel_t *>(m_data + count);
|
|
||||||
|
|
||||||
unsigned index = 0;
|
|
||||||
|
|
||||||
// Manually add the 16 basic ANSI colors
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0x00, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0xcd, 0x00, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0xcd, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0xcd, 0xcd, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0x00, 0xee);
|
|
||||||
m_palette[index++] = m_screen.color(0xcd, 0x00, 0xcd);
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0xcd, 0xcd);
|
|
||||||
m_palette[index++] = m_screen.color(0xe5, 0xe5, 0xe5);
|
|
||||||
m_palette[index++] = m_screen.color(0x7f, 0x7f, 0x7f);
|
|
||||||
m_palette[index++] = m_screen.color(0xff, 0x00, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0xff, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0xff, 0xff, 0x00);
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0x50, 0xff);
|
|
||||||
m_palette[index++] = m_screen.color(0xff, 0x00, 0xff);
|
|
||||||
m_palette[index++] = m_screen.color(0x00, 0xff, 0xff);
|
|
||||||
m_palette[index++] = m_screen.color(0xff, 0xff, 0xff);
|
|
||||||
|
|
||||||
// Build the high-color portion of the table
|
|
||||||
const uint32_t intensity[] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
|
|
||||||
const uint32_t intensities = sizeof(intensity) / sizeof(uint32_t);
|
|
||||||
|
|
||||||
for (uint32_t r = 0; r < intensities; ++r) {
|
|
||||||
for (uint32_t g = 0; g < intensities; ++g) {
|
|
||||||
for (uint32_t b = 0; b < intensities; ++b) {
|
|
||||||
m_palette[index++] = m_screen.color(
|
|
||||||
intensity[r], intensity[g], intensity[b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the greyscale portion of the table
|
|
||||||
for (uint8_t i = 0x08; i <= 0xee; i += 10)
|
|
||||||
m_palette[index++] = m_screen.color(i, i, i);
|
|
||||||
|
|
||||||
set_color(7, 0); // Grey on black default
|
|
||||||
m_attrs = reinterpret_cast<uint16_t *>(m_data + count + palette_size);
|
|
||||||
for (unsigned i = 0; i < count; ++i)
|
|
||||||
m_attrs[i] = m_attr;
|
|
||||||
}
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
console::line_pointer(unsigned line)
|
|
||||||
{
|
|
||||||
if (!m_data) return nullptr;
|
|
||||||
return m_data + ((m_first + line) % m_size.y) * m_size.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t *
|
|
||||||
console::attr_pointer(unsigned line)
|
|
||||||
{
|
|
||||||
if (!m_attrs) return nullptr;
|
|
||||||
return m_attrs + ((m_first + line) % m_size.y) * m_size.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
console::repaint()
|
|
||||||
{
|
|
||||||
m_screen.fill(m_bg);
|
|
||||||
if (!m_data) return;
|
|
||||||
|
|
||||||
for (unsigned y = 0; y < m_size.y; ++y) {
|
|
||||||
const char *line = line_pointer(y);
|
|
||||||
const uint16_t *attrs = attr_pointer(y);
|
|
||||||
for (unsigned x = 0; x < m_size.x; ++x) {
|
|
||||||
const uint16_t attr = attrs[x];
|
|
||||||
|
|
||||||
set_color(static_cast<uint8_t>(attr),
|
|
||||||
static_cast<uint8_t>(attr >> 8));
|
|
||||||
|
|
||||||
m_font.draw_glyph(
|
|
||||||
m_screen,
|
|
||||||
line[x] ? line[x] : ' ',
|
|
||||||
m_fg,
|
|
||||||
m_bg,
|
|
||||||
x * m_font.width(),
|
|
||||||
y * m_font.height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
console::set_color(uint8_t fg, uint8_t bg)
|
|
||||||
{
|
|
||||||
if (!m_palette) return;
|
|
||||||
|
|
||||||
m_bg = m_palette[bg];
|
|
||||||
m_fg = m_palette[fg];
|
|
||||||
m_attr = (bg << 8) | fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
console::scroll(unsigned lines)
|
|
||||||
{
|
|
||||||
if (!m_data) {
|
|
||||||
m_pos.x = 0;
|
|
||||||
m_pos.y = 0;
|
|
||||||
} else {
|
|
||||||
unsigned bytes = lines * m_size.x;
|
|
||||||
char *line = line_pointer(0);
|
|
||||||
for (unsigned i = 0; i < bytes; ++i)
|
|
||||||
*line++ = 0;
|
|
||||||
|
|
||||||
m_first = (m_first + lines) % m_size.y;
|
|
||||||
m_pos.y -= lines;
|
|
||||||
}
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
console::puts(const char *message)
|
|
||||||
{
|
|
||||||
char *line = line_pointer(m_pos.y);
|
|
||||||
uint16_t *attrs = attr_pointer(m_pos.y);
|
|
||||||
size_t count = 0;
|
|
||||||
|
|
||||||
while (message && *message) {
|
|
||||||
const unsigned x = m_pos.x * m_font.width();
|
|
||||||
const unsigned y = m_pos.y * m_font.height();
|
|
||||||
const char c = *message++;
|
|
||||||
++count;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case '\t':
|
|
||||||
m_pos.x = (m_pos.x + 4) / 4 * 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\r':
|
|
||||||
m_pos.x = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\n':
|
|
||||||
m_pos.x = 0;
|
|
||||||
m_pos.y++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (line) line[m_pos.x] = c;
|
|
||||||
if (attrs) attrs[m_pos.x] = m_attr;
|
|
||||||
m_font.draw_glyph(m_screen, c, m_fg, m_bg, x, y);
|
|
||||||
m_pos.x++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pos.x >= m_size.x) {
|
|
||||||
m_pos.x = m_pos.x % m_size.x;
|
|
||||||
m_pos.y++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pos.y >= m_size.y) {
|
|
||||||
scroll(1);
|
|
||||||
line = line_pointer(m_pos.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "font.h"
|
|
||||||
#include "screen.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
struct console_data;
|
|
||||||
|
|
||||||
class console
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
console(const font &f, const screen &s, void *scratch, size_t len);
|
|
||||||
|
|
||||||
void repaint();
|
|
||||||
void scroll(unsigned lines);
|
|
||||||
|
|
||||||
void set_color(uint8_t fg, uint8_t bg);
|
|
||||||
|
|
||||||
size_t puts(const char *message);
|
|
||||||
size_t printf(const char *fmt, ...);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void put_hex(T x);
|
|
||||||
|
|
||||||
void put_dec(uint32_t x);
|
|
||||||
|
|
||||||
private:
|
|
||||||
char * line_pointer(unsigned line);
|
|
||||||
uint16_t * attr_pointer(unsigned line);
|
|
||||||
|
|
||||||
font m_font;
|
|
||||||
screen m_screen;
|
|
||||||
|
|
||||||
coord<unsigned> m_size;
|
|
||||||
coord<unsigned> m_pos;
|
|
||||||
screen::pixel_t m_fg, m_bg;
|
|
||||||
uint16_t m_attr;
|
|
||||||
|
|
||||||
size_t m_first;
|
|
||||||
size_t m_length;
|
|
||||||
|
|
||||||
char *m_data;
|
|
||||||
uint16_t *m_attrs;
|
|
||||||
screen::pixel_t *m_palette;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const char digits[];
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void console::put_hex(T x)
|
|
||||||
{
|
|
||||||
static const int chars = sizeof(x) * 2;
|
|
||||||
char message[chars + 1];
|
|
||||||
for (int i=0; i<chars; ++i) {
|
|
||||||
message[chars - i - 1] = digits[(x >> (i*4)) & 0xf];
|
|
||||||
}
|
|
||||||
message[chars] = 0;
|
|
||||||
puts(message);
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "console.h"
|
|
||||||
#include "font.h"
|
|
||||||
#include "kernel_data.h"
|
|
||||||
#include "screen.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
void do_the_set_registers(popcorn_data *header);
|
|
||||||
void kernel_main(popcorn_data *header);
|
|
||||||
}
|
|
||||||
|
|
||||||
console
|
|
||||||
load_console(const popcorn_data *header)
|
|
||||||
{
|
|
||||||
return console{
|
|
||||||
font::load(header->font),
|
|
||||||
screen{
|
|
||||||
header->frame_buffer,
|
|
||||||
header->hres,
|
|
||||||
header->vres,
|
|
||||||
header->rmask,
|
|
||||||
header->gmask,
|
|
||||||
header->bmask},
|
|
||||||
header->log,
|
|
||||||
header->log_length};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
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");
|
|
||||||
|
|
||||||
do_the_set_registers(header);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user