Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2be70976ca | ||
|
|
699fc57e0a | ||
|
|
f3bb2e5259 | ||
|
|
34120fc4c1 | ||
|
|
e52fd8eacf | ||
|
|
a97073848c | ||
|
|
03b2d0dac7 | ||
|
|
cfeeba4400 | ||
|
|
45b52633bb | ||
|
|
b9af081a44 | ||
|
|
d75b9c22d4 | ||
|
|
e20c53f193 | ||
|
|
6d4a32b6e8 | ||
|
|
63a5c2da00 | ||
|
|
bdcd0c2fda | ||
|
|
1be929b9d5 | ||
|
|
d11dd0c3f9 | ||
|
|
e08e00790f | ||
|
|
8b3356e9d8 | ||
|
|
cd30126f17 | ||
|
|
2c444dccd6 | ||
|
|
bd490c4c7b | ||
|
|
3a67e461de | ||
|
|
972ef39295 | ||
|
|
4c41205e73 | ||
|
|
f6b4a4a103 | ||
| 327cd93c04 | |||
| 0e6b27e741 | |||
| db8a14720b | |||
| 87a7293ac1 | |||
| 5787aff866 | |||
| 7bdde2d359 | |||
| 2e3d7b1656 | |||
| bf600a7608 | |||
| 6b00805d04 | |||
| 82b7082fc5 | |||
| 8bb9e22218 | |||
| 2ad90dcb5c | |||
| 97ea77bd27 | |||
| 1904e240cf | |||
| 4ccaa2dfea | |||
| da38006f44 | |||
| 87b0a93d32 | |||
| ff78c951f0 | |||
| 2d44e8112b | |||
| f7f8bb3f45 | |||
| 67ebc58812 | |||
| 13aee1755e | |||
| 0e0975e5f6 | |||
| d4283731e4 | |||
| 6780ab1b67 | |||
| 41eac2764a | |||
| 113d14c440 | |||
| abe523be77 | |||
| 1b392a0551 | |||
| deb2fa0a09 | |||
| 671a0ce0fb | |||
| ac67111b83 | |||
| 09575370ce | |||
| 9aa08a70cf | |||
| ca7f78565d | |||
| dcb4d1823f | |||
| e8564c755b | |||
| 9dee5e4138 | |||
| 245f260d67 | |||
| 5f27727e52 | |||
| 1238608430 | |||
| 71cd7af17b | |||
| 8490472581 | |||
| 8534d8d3c5 | |||
| 53d260cc6e | |||
| 53a4682418 | |||
| 42455873ff | |||
| 724b846ee4 | |||
| f27b133089 | |||
| 773617cbf3 | |||
| 44e29b3c4a | |||
| 95a35cd0bf | |||
| 28b800a497 | |||
| 838776d7df | |||
| e19fa377d7 | |||
| d63a30728c | |||
| 4819920b4e | |||
| bbb9aae198 | |||
| 0d94776c46 | |||
| 565a5ee960 | |||
| e7f9d8f1d7 | |||
| 55bc49598e | |||
| 579eaaf4a0 | |||
| b98334db28 | |||
| e1b1b5d357 | |||
| cf582c4ce4 | |||
| 911b4c0b10 | |||
| 94c1f0d3fc | |||
| 73221dfe34 | |||
| 4ffd4949ca | |||
| 3f137805bc | |||
| 6a00057817 | |||
| 58bc5acb1e | |||
| d3e9d92466 | |||
| ae3290c53d | |||
| 4cf222a5bb | |||
| c3abe035c8 | |||
| ef5c333030 | |||
| f4cbb9498f | |||
| 794c86f9b4 | |||
|
|
8687fe3786 | ||
|
|
0a28d2db07 | ||
|
|
6c468a134b | ||
|
|
9b67f87062 | ||
|
|
b4adc29d7f | ||
|
|
a10aca573d | ||
|
|
ea1224e213 | ||
|
|
64ad65fa1b | ||
|
|
b881b2639d | ||
|
|
a5f72edf82 | ||
|
|
ea2a3e6f16 | ||
|
|
2125f043b6 | ||
|
|
655b5c88d4 | ||
|
|
88b090fe94 | ||
|
|
c6c3a556b3 |
@@ -2,7 +2,7 @@ import gdb
|
|||||||
|
|
||||||
class PrintStackCommand(gdb.Command):
|
class PrintStackCommand(gdb.Command):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("popc_stack", gdb.COMMAND_DATA)
|
super().__init__("j6stack", gdb.COMMAND_DATA)
|
||||||
|
|
||||||
def invoke(self, arg, from_tty):
|
def invoke(self, arg, from_tty):
|
||||||
args = gdb.string_to_argv(arg)
|
args = gdb.string_to_argv(arg)
|
||||||
@@ -16,13 +16,48 @@ class PrintStackCommand(gdb.Command):
|
|||||||
depth = int(args[1])
|
depth = int(args[1])
|
||||||
|
|
||||||
for i in range(depth-1, -1, -1):
|
for i in range(depth-1, -1, -1):
|
||||||
|
try:
|
||||||
offset = i * 8
|
offset = i * 8
|
||||||
base_addr = gdb.parse_and_eval(base)
|
base_addr = gdb.parse_and_eval(base)
|
||||||
value = gdb.parse_and_eval(f"*(uint64_t*)({base} + {offset})")
|
value = gdb.parse_and_eval(f"*(uint64_t*)({base} + {offset})")
|
||||||
print("{:016x} (+{:04x}): {:016x}".format(int(base_addr) + offset, offset, int(value)))
|
print("{:016x} (+{:04x}): {:016x}".format(int(base_addr) + offset, offset, int(value)))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
class PrintBacktraceCommand(gdb.Command):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("j6bt", gdb.COMMAND_DATA)
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
args = gdb.string_to_argv(arg)
|
||||||
|
|
||||||
|
depth = 30
|
||||||
|
if len(args) > 0:
|
||||||
|
depth = int(args[0])
|
||||||
|
|
||||||
|
frame = "$rbp"
|
||||||
|
if len(args) > 1:
|
||||||
|
frame = args[1]
|
||||||
|
|
||||||
|
for i in range(depth-1, -1, -1):
|
||||||
|
ret = gdb.parse_and_eval(f"*(uint64_t*)({frame} + 8)")
|
||||||
|
frame = gdb.parse_and_eval(f"*(uint64_t*)({frame})")
|
||||||
|
|
||||||
|
name = ""
|
||||||
|
block = gdb.block_for_pc(int(ret))
|
||||||
|
if block:
|
||||||
|
name = block.function or ""
|
||||||
|
|
||||||
|
print("{:016x} {}".format(int(ret), name))
|
||||||
|
|
||||||
|
if frame == 0 or ret == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
PrintStackCommand()
|
PrintStackCommand()
|
||||||
|
PrintBacktraceCommand()
|
||||||
|
|
||||||
gdb.execute("target remote :1234")
|
gdb.execute("target remote :1234")
|
||||||
gdb.execute("display/i $rip")
|
gdb.execute("display/i $rip")
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
dest = "screenfont.psf"
|
dest = "screenfont.psf"
|
||||||
source = "../assets/fonts/tamsyn8x16r.psf"
|
source = "../assets/fonts/tamsyn8x16r.psf"
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
dest = "symbol_table.dat"
|
||||||
|
source = "symbol_table.dat"
|
||||||
|
symbols = true
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
dest = "nulldrv1"
|
dest = "nulldrv1"
|
||||||
source = "user/nulldrv"
|
source = "user/nulldrv"
|
||||||
|
|||||||
2263
external/catch/catch.hpp
vendored
2263
external/catch/catch.hpp
vendored
File diff suppressed because it is too large
Load Diff
260
modules.yaml
260
modules.yaml
@@ -6,27 +6,26 @@ modules:
|
|||||||
output: jsix.elf
|
output: jsix.elf
|
||||||
target: host
|
target: host
|
||||||
deps:
|
deps:
|
||||||
- elf
|
- cpu
|
||||||
- initrd
|
|
||||||
- kutil
|
- kutil
|
||||||
includes:
|
includes:
|
||||||
- src/kernel
|
- src/kernel
|
||||||
source:
|
source:
|
||||||
- src/kernel/crti.s
|
|
||||||
- src/kernel/apic.cpp
|
- src/kernel/apic.cpp
|
||||||
- src/kernel/assert.cpp
|
- src/kernel/assert.cpp
|
||||||
- src/kernel/boot.s
|
- src/kernel/boot.s
|
||||||
|
- src/kernel/clock.cpp
|
||||||
- src/kernel/console.cpp
|
- src/kernel/console.cpp
|
||||||
- src/kernel/cpprt.cpp
|
- src/kernel/cpprt.cpp
|
||||||
- src/kernel/cpu.cpp
|
- src/kernel/cpu.cpp
|
||||||
- src/kernel/debug.cpp
|
- src/kernel/debug.cpp
|
||||||
- src/kernel/debug.s
|
- src/kernel/debug.s
|
||||||
- src/kernel/device_manager.cpp
|
- src/kernel/device_manager.cpp
|
||||||
- src/kernel/font.cpp
|
|
||||||
- src/kernel/frame_allocator.cpp
|
- src/kernel/frame_allocator.cpp
|
||||||
- src/kernel/fs/gpt.cpp
|
- src/kernel/fs/gpt.cpp
|
||||||
- src/kernel/gdt.cpp
|
- src/kernel/gdt.cpp
|
||||||
- src/kernel/gdt.s
|
- src/kernel/gdt.s
|
||||||
|
- src/kernel/hpet.cpp
|
||||||
- src/kernel/interrupts.cpp
|
- src/kernel/interrupts.cpp
|
||||||
- src/kernel/interrupts.s
|
- src/kernel/interrupts.s
|
||||||
- src/kernel/io.cpp
|
- src/kernel/io.cpp
|
||||||
@@ -35,25 +34,37 @@ modules:
|
|||||||
- src/kernel/main.cpp
|
- src/kernel/main.cpp
|
||||||
- src/kernel/memory_bootstrap.cpp
|
- src/kernel/memory_bootstrap.cpp
|
||||||
- src/kernel/msr.cpp
|
- src/kernel/msr.cpp
|
||||||
- src/kernel/objects/handle.cpp
|
- src/kernel/objects/channel.cpp
|
||||||
|
- src/kernel/objects/endpoint.cpp
|
||||||
- src/kernel/objects/kobject.cpp
|
- src/kernel/objects/kobject.cpp
|
||||||
- src/kernel/page_manager.cpp
|
- src/kernel/objects/thread.cpp
|
||||||
|
- src/kernel/objects/process.cpp
|
||||||
|
- src/kernel/objects/system.cpp
|
||||||
|
- src/kernel/objects/vm_area.cpp
|
||||||
|
- src/kernel/page_table.cpp
|
||||||
- src/kernel/pci.cpp
|
- src/kernel/pci.cpp
|
||||||
- src/kernel/process.cpp
|
|
||||||
- src/kernel/scheduler.cpp
|
- src/kernel/scheduler.cpp
|
||||||
- src/kernel/screen.cpp
|
|
||||||
- src/kernel/serial.cpp
|
- src/kernel/serial.cpp
|
||||||
|
- src/kernel/symbol_table.cpp
|
||||||
- src/kernel/syscall.cpp
|
- src/kernel/syscall.cpp
|
||||||
- src/kernel/syscall.s
|
- src/kernel/syscall.s
|
||||||
|
- src/kernel/syscalls/channel.cpp
|
||||||
|
- src/kernel/syscalls/endpoint.cpp
|
||||||
- src/kernel/syscalls/object.cpp
|
- src/kernel/syscalls/object.cpp
|
||||||
- src/kernel/syscalls/process.cpp
|
- src/kernel/syscalls/process.cpp
|
||||||
|
- src/kernel/syscalls/system.cpp
|
||||||
|
- src/kernel/syscalls/thread.cpp
|
||||||
|
- src/kernel/syscalls/vm_area.cpp
|
||||||
- src/kernel/task.s
|
- src/kernel/task.s
|
||||||
- src/kernel/crtn.s
|
- src/kernel/vm_mapper.cpp
|
||||||
|
- src/kernel/vm_space.cpp
|
||||||
|
|
||||||
boot:
|
boot:
|
||||||
kind: exe
|
kind: exe
|
||||||
target: boot
|
target: boot
|
||||||
output: boot.efi
|
output: boot.efi
|
||||||
|
deps:
|
||||||
|
- cpu
|
||||||
source:
|
source:
|
||||||
- src/boot/main.cpp
|
- src/boot/main.cpp
|
||||||
- src/boot/console.cpp
|
- src/boot/console.cpp
|
||||||
@@ -63,35 +74,31 @@ modules:
|
|||||||
- src/boot/loader.cpp
|
- src/boot/loader.cpp
|
||||||
- src/boot/memory.cpp
|
- src/boot/memory.cpp
|
||||||
- src/boot/paging.cpp
|
- src/boot/paging.cpp
|
||||||
|
- src/boot/status.cpp
|
||||||
- src/boot/support.cpp
|
- src/boot/support.cpp
|
||||||
|
|
||||||
nulldrv:
|
nulldrv:
|
||||||
kind: exe
|
kind: exe
|
||||||
target: user
|
target: user
|
||||||
output: nulldrv
|
output: nulldrv.elf
|
||||||
|
deps:
|
||||||
|
- libc
|
||||||
source:
|
source:
|
||||||
|
- src/drivers/nulldrv/io.cpp
|
||||||
- src/drivers/nulldrv/main.cpp
|
- src/drivers/nulldrv/main.cpp
|
||||||
- src/drivers/nulldrv/main.s
|
- src/drivers/nulldrv/serial.cpp
|
||||||
|
|
||||||
elf:
|
fb:
|
||||||
kind: lib
|
kind: exe
|
||||||
output: libelf.a
|
target: user
|
||||||
|
output: fb.elf
|
||||||
deps:
|
deps:
|
||||||
- kutil
|
- libc
|
||||||
includes:
|
|
||||||
- src/libraries/elf/include
|
|
||||||
source:
|
source:
|
||||||
- src/libraries/elf/elf.cpp
|
- src/drivers/fb/font.cpp
|
||||||
|
- src/drivers/fb/main.cpp
|
||||||
initrd:
|
- src/drivers/fb/screen.cpp
|
||||||
kind: lib
|
- src/drivers/fb/scrollback.cpp
|
||||||
output: libinitrd.a
|
|
||||||
deps:
|
|
||||||
- kutil
|
|
||||||
includes:
|
|
||||||
- src/libraries/initrd/include
|
|
||||||
source:
|
|
||||||
- src/libraries/initrd/initrd.cpp
|
|
||||||
|
|
||||||
kutil:
|
kutil:
|
||||||
kind: lib
|
kind: lib
|
||||||
@@ -105,17 +112,193 @@ modules:
|
|||||||
- src/libraries/kutil/logger.cpp
|
- src/libraries/kutil/logger.cpp
|
||||||
- src/libraries/kutil/memory.cpp
|
- src/libraries/kutil/memory.cpp
|
||||||
- src/libraries/kutil/printf.c
|
- src/libraries/kutil/printf.c
|
||||||
- src/libraries/kutil/vm_space.cpp
|
|
||||||
|
|
||||||
makerd:
|
cpu:
|
||||||
kind: exe
|
kind: lib
|
||||||
target: native
|
output: libcpu.a
|
||||||
output: makerd
|
includes:
|
||||||
deps:
|
- src/libraries/cpu/include
|
||||||
- initrd
|
|
||||||
source:
|
source:
|
||||||
- src/tools/makerd/entry.cpp
|
- src/libraries/cpu/cpu.cpp
|
||||||
- src/tools/makerd/main.cpp
|
|
||||||
|
|
||||||
|
libc:
|
||||||
|
kind: lib
|
||||||
|
output: libc.a
|
||||||
|
includes:
|
||||||
|
- src/libraries/libc/include
|
||||||
|
target: user
|
||||||
|
defines:
|
||||||
|
- DISABLE_SSE
|
||||||
|
- LACKS_UNISTD_H
|
||||||
|
- LACKS_FCNTL_H
|
||||||
|
- LACKS_SYS_PARAM_H
|
||||||
|
- LACKS_SYS_MMAN_H
|
||||||
|
- LACKS_SCHED_H
|
||||||
|
- LACKS_STRINGS_H
|
||||||
|
- HAVE_MMAP=0
|
||||||
|
#- LACKS_STRING_H
|
||||||
|
#- LACKS_ERRNO_H
|
||||||
|
#- LACKS_STDLIB_H
|
||||||
|
#- LACKS_TIME_H
|
||||||
|
source:
|
||||||
|
- src/libraries/libc/arch/x86_64/_Exit.s
|
||||||
|
- src/libraries/libc/arch/x86_64/crt0.s
|
||||||
|
- src/libraries/libc/arch/x86_64/init_libc.c
|
||||||
|
- src/libraries/libc/arch/x86_64/syscalls.s
|
||||||
|
- src/libraries/libc/ctype/isalnum.c
|
||||||
|
- src/libraries/libc/ctype/isalpha.c
|
||||||
|
- src/libraries/libc/ctype/isblank.c
|
||||||
|
- src/libraries/libc/ctype/iscntrl.c
|
||||||
|
- src/libraries/libc/ctype/isdigit.c
|
||||||
|
- src/libraries/libc/ctype/isgraph.c
|
||||||
|
- src/libraries/libc/ctype/islower.c
|
||||||
|
- src/libraries/libc/ctype/isprint.c
|
||||||
|
- src/libraries/libc/ctype/ispunct.c
|
||||||
|
- src/libraries/libc/ctype/isspace.c
|
||||||
|
- src/libraries/libc/ctype/isupper.c
|
||||||
|
- src/libraries/libc/ctype/isxdigit.c
|
||||||
|
- src/libraries/libc/ctype/tolower.c
|
||||||
|
- src/libraries/libc/ctype/toupper.c
|
||||||
|
- src/libraries/libc/inttypes/imaxabs.c
|
||||||
|
- src/libraries/libc/inttypes/imaxdiv.c
|
||||||
|
- src/libraries/libc/inttypes/strtoimax.c
|
||||||
|
- src/libraries/libc/inttypes/strtoumax.c
|
||||||
|
- src/libraries/libc/locale/localeconv.c
|
||||||
|
- src/libraries/libc/locale/setlocale.c
|
||||||
|
- src/libraries/libc/j6libc/assert.c
|
||||||
|
- src/libraries/libc/j6libc/errno.c
|
||||||
|
- src/libraries/libc/j6libc/allocpages.c
|
||||||
|
- src/libraries/libc/j6libc/atomax.c
|
||||||
|
- src/libraries/libc/j6libc/closeall.c
|
||||||
|
- src/libraries/libc/j6libc/close.c
|
||||||
|
- src/libraries/libc/j6libc/digits.c
|
||||||
|
- src/libraries/libc/j6libc/filemode.c
|
||||||
|
- src/libraries/libc/j6libc/fillbuffer.c
|
||||||
|
- src/libraries/libc/j6libc/flushbuffer.c
|
||||||
|
- src/libraries/libc/j6libc/is_leap.c
|
||||||
|
- src/libraries/libc/j6libc/load_lc_collate.c
|
||||||
|
- src/libraries/libc/j6libc/load_lc_ctype.c
|
||||||
|
- src/libraries/libc/j6libc/load_lc_messages.c
|
||||||
|
- src/libraries/libc/j6libc/load_lc_monetary.c
|
||||||
|
- src/libraries/libc/j6libc/load_lc_numeric.c
|
||||||
|
- src/libraries/libc/j6libc/load_lc_time.c
|
||||||
|
- src/libraries/libc/j6libc/load_lines.c
|
||||||
|
- src/libraries/libc/j6libc/open.c
|
||||||
|
- src/libraries/libc/j6libc/prepread.c
|
||||||
|
- src/libraries/libc/j6libc/prepwrite.c
|
||||||
|
- src/libraries/libc/j6libc/print.c
|
||||||
|
- src/libraries/libc/j6libc/rename.c
|
||||||
|
- src/libraries/libc/j6libc/scan.c
|
||||||
|
- src/libraries/libc/j6libc/seed.c
|
||||||
|
- src/libraries/libc/j6libc/seek.c
|
||||||
|
- src/libraries/libc/j6libc/stdinit.c
|
||||||
|
- src/libraries/libc/j6libc/strtox_main.c
|
||||||
|
- src/libraries/libc/j6libc/strtox_prelim.c
|
||||||
|
- src/libraries/libc/j6libc/sbrk.c
|
||||||
|
- src/libraries/libc/signal/raise.c
|
||||||
|
- src/libraries/libc/signal/signal.c
|
||||||
|
- src/libraries/libc/stdio/clearerr.c
|
||||||
|
- src/libraries/libc/stdio/fclose.c
|
||||||
|
- src/libraries/libc/stdio/feof.c
|
||||||
|
- src/libraries/libc/stdio/ferror.c
|
||||||
|
- src/libraries/libc/stdio/fflush.c
|
||||||
|
- src/libraries/libc/stdio/fgetc.c
|
||||||
|
- src/libraries/libc/stdio/fgetpos.c
|
||||||
|
- src/libraries/libc/stdio/fgets.c
|
||||||
|
- src/libraries/libc/stdio/fopen.c
|
||||||
|
- src/libraries/libc/stdio/fprintf.c
|
||||||
|
- src/libraries/libc/stdio/fputc.c
|
||||||
|
- src/libraries/libc/stdio/fputs.c
|
||||||
|
- src/libraries/libc/stdio/fread.c
|
||||||
|
- src/libraries/libc/stdio/freopen.c
|
||||||
|
- src/libraries/libc/stdio/fscanf.c
|
||||||
|
- src/libraries/libc/stdio/fseek.c
|
||||||
|
- src/libraries/libc/stdio/fsetpos.c
|
||||||
|
- src/libraries/libc/stdio/ftell.c
|
||||||
|
- src/libraries/libc/stdio/fwrite.c
|
||||||
|
- src/libraries/libc/stdio/getc.c
|
||||||
|
- src/libraries/libc/stdio/getchar.c
|
||||||
|
- src/libraries/libc/stdio/perror.c
|
||||||
|
- src/libraries/libc/stdio/printf.c
|
||||||
|
- src/libraries/libc/stdio/putc.c
|
||||||
|
- src/libraries/libc/stdio/putchar.c
|
||||||
|
- src/libraries/libc/stdio/puts.c
|
||||||
|
- src/libraries/libc/stdio/remove.c
|
||||||
|
- src/libraries/libc/stdio/rename.c
|
||||||
|
- src/libraries/libc/stdio/rewind.c
|
||||||
|
- src/libraries/libc/stdio/scanf.c
|
||||||
|
- src/libraries/libc/stdio/setbuf.c
|
||||||
|
- src/libraries/libc/stdio/setvbuf.c
|
||||||
|
- src/libraries/libc/stdio/snprintf.c
|
||||||
|
- src/libraries/libc/stdio/sprintf.c
|
||||||
|
- src/libraries/libc/stdio/sscanf.c
|
||||||
|
- src/libraries/libc/stdio/tmpfile.c
|
||||||
|
- src/libraries/libc/stdio/tmpnam.c
|
||||||
|
- src/libraries/libc/stdio/ungetc.c
|
||||||
|
- src/libraries/libc/stdio/vfprintf.c
|
||||||
|
- src/libraries/libc/stdio/vfscanf.c
|
||||||
|
- src/libraries/libc/stdio/vprintf.c
|
||||||
|
- src/libraries/libc/stdio/vscanf.c
|
||||||
|
- src/libraries/libc/stdio/vsnprintf.c
|
||||||
|
- src/libraries/libc/stdio/vsprintf.c
|
||||||
|
- src/libraries/libc/stdio/vsscanf.c
|
||||||
|
- src/libraries/libc/stdlib/abort.c
|
||||||
|
- src/libraries/libc/stdlib/abs.c
|
||||||
|
- src/libraries/libc/stdlib/atexit.c
|
||||||
|
- src/libraries/libc/stdlib/atoi.c
|
||||||
|
- src/libraries/libc/stdlib/atol.c
|
||||||
|
- src/libraries/libc/stdlib/atoll.c
|
||||||
|
- src/libraries/libc/stdlib/bsearch.c
|
||||||
|
- src/libraries/libc/stdlib/div.c
|
||||||
|
- src/libraries/libc/stdlib/exit.c
|
||||||
|
- src/libraries/libc/stdlib/_Exit.c
|
||||||
|
- src/libraries/libc/stdlib/getenv.c
|
||||||
|
- src/libraries/libc/stdlib/labs.c
|
||||||
|
- src/libraries/libc/stdlib/ldiv.c
|
||||||
|
- src/libraries/libc/stdlib/llabs.c
|
||||||
|
- src/libraries/libc/stdlib/lldiv.c
|
||||||
|
- src/libraries/libc/stdlib/malloc.c
|
||||||
|
- src/libraries/libc/stdlib/qsort.c
|
||||||
|
- src/libraries/libc/stdlib/rand.c
|
||||||
|
- src/libraries/libc/stdlib/srand.c
|
||||||
|
- src/libraries/libc/stdlib/strtol.c
|
||||||
|
- src/libraries/libc/stdlib/strtoll.c
|
||||||
|
- src/libraries/libc/stdlib/strtoul.c
|
||||||
|
- src/libraries/libc/stdlib/strtoull.c
|
||||||
|
- src/libraries/libc/stdlib/system.c
|
||||||
|
- src/libraries/libc/string/memchr.c
|
||||||
|
- src/libraries/libc/string/memcmp.c
|
||||||
|
- src/libraries/libc/string/memcpy.c
|
||||||
|
- src/libraries/libc/string/memmove.c
|
||||||
|
- src/libraries/libc/string/memset.c
|
||||||
|
- src/libraries/libc/string/strcat.c
|
||||||
|
- src/libraries/libc/string/strchr.c
|
||||||
|
- src/libraries/libc/string/strcmp.c
|
||||||
|
- src/libraries/libc/string/strcoll.c
|
||||||
|
- src/libraries/libc/string/strcpy.c
|
||||||
|
- src/libraries/libc/string/strcspn.c
|
||||||
|
- src/libraries/libc/string/strerror.c
|
||||||
|
- src/libraries/libc/string/strlen.c
|
||||||
|
- src/libraries/libc/string/strncat.c
|
||||||
|
- src/libraries/libc/string/strncmp.c
|
||||||
|
- src/libraries/libc/string/strncpy.c
|
||||||
|
- src/libraries/libc/string/strpbrk.c
|
||||||
|
- src/libraries/libc/string/strrchr.c
|
||||||
|
- src/libraries/libc/string/strspn.c
|
||||||
|
- src/libraries/libc/string/strstr.c
|
||||||
|
- src/libraries/libc/string/strtok.c
|
||||||
|
- src/libraries/libc/string/strxfrm.c
|
||||||
|
- src/libraries/libc/time/asctime.c
|
||||||
|
- src/libraries/libc/time/clock.c
|
||||||
|
- src/libraries/libc/time/ctime.c
|
||||||
|
- src/libraries/libc/time/difftime.c
|
||||||
|
- src/libraries/libc/time/gmtime.c
|
||||||
|
- src/libraries/libc/time/localtime.c
|
||||||
|
- src/libraries/libc/time/mktime.c
|
||||||
|
- src/libraries/libc/time/strftime.c
|
||||||
|
- src/libraries/libc/time/time.c
|
||||||
|
- src/libraries/libc/time/timespec_get.c
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
kind: exe
|
kind: exe
|
||||||
@@ -124,12 +307,13 @@ modules:
|
|||||||
deps:
|
deps:
|
||||||
- kutil
|
- kutil
|
||||||
source:
|
source:
|
||||||
- src/tests/address_manager.cpp
|
|
||||||
- src/tests/constexpr_hash.cpp
|
- src/tests/constexpr_hash.cpp
|
||||||
- src/tests/linked_list.cpp
|
- src/tests/linked_list.cpp
|
||||||
- src/tests/logger.cpp
|
- src/tests/logger.cpp
|
||||||
- src/tests/heap_allocator.cpp
|
- src/tests/heap_allocator.cpp
|
||||||
- src/tests/main.cpp
|
- src/tests/main.cpp
|
||||||
|
- src/tests/map.cpp
|
||||||
|
- src/tests/vector.cpp
|
||||||
overlays:
|
overlays:
|
||||||
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-llvm8-20190706.tar.bz2
|
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-llvm8-20190706.tar.bz2
|
||||||
path: sysroot
|
path: sysroot
|
||||||
|
|||||||
11
qemu.sh
11
qemu.sh
@@ -6,6 +6,7 @@ debug=""
|
|||||||
debugtarget="${build}/jsix.elf"
|
debugtarget="${build}/jsix.elf"
|
||||||
flash_name="ovmf_vars"
|
flash_name="ovmf_vars"
|
||||||
gfx="-nographic"
|
gfx="-nographic"
|
||||||
|
vga="-vga none"
|
||||||
kvm=""
|
kvm=""
|
||||||
cpu="Broadwell,+pdpe1gb"
|
cpu="Broadwell,+pdpe1gb"
|
||||||
|
|
||||||
@@ -22,6 +23,10 @@ for arg in $@; do
|
|||||||
;;
|
;;
|
||||||
--gfx)
|
--gfx)
|
||||||
gfx=""
|
gfx=""
|
||||||
|
vga=""
|
||||||
|
;;
|
||||||
|
--vga)
|
||||||
|
vga=""
|
||||||
;;
|
;;
|
||||||
--kvm)
|
--kvm)
|
||||||
kvm="-enable-kvm"
|
kvm="-enable-kvm"
|
||||||
@@ -45,6 +50,8 @@ if [[ -n $TMUX ]]; then
|
|||||||
if [[ -n $debug ]]; then
|
if [[ -n $debug ]]; then
|
||||||
tmux split-window -h "gdb ${debugtarget}" &
|
tmux split-window -h "gdb ${debugtarget}" &
|
||||||
else
|
else
|
||||||
|
tmux split-window -h -l 80 "sleep 1; telnet localhost 45455" &
|
||||||
|
tmux last-pane
|
||||||
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
|
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
|
||||||
fi
|
fi
|
||||||
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
||||||
@@ -61,6 +68,8 @@ exec qemu-system-x86_64 \
|
|||||||
-drive "format=raw,file=${build}/jsix.img" \
|
-drive "format=raw,file=${build}/jsix.img" \
|
||||||
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
|
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
|
||||||
-monitor telnet:localhost:45454,server,nowait \
|
-monitor telnet:localhost:45454,server,nowait \
|
||||||
|
-serial stdio \
|
||||||
|
-serial telnet:localhost:45455,server,nowait \
|
||||||
-smp 4 \
|
-smp 4 \
|
||||||
-m 512 \
|
-m 512 \
|
||||||
-d mmu,int,guest_errors \
|
-d mmu,int,guest_errors \
|
||||||
@@ -68,4 +77,4 @@ exec qemu-system-x86_64 \
|
|||||||
-cpu "${cpu}" \
|
-cpu "${cpu}" \
|
||||||
-M q35 \
|
-M q35 \
|
||||||
-no-reboot \
|
-no-reboot \
|
||||||
$gfx $kvm $debug
|
$gfx $vga $kvm $debug
|
||||||
|
|||||||
75
scripts/build_symbol_table.py
Executable file
75
scripts/build_symbol_table.py
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Generate the jsix style symbol table. The format in memory of this table
|
||||||
|
# is as follows:
|
||||||
|
#
|
||||||
|
# <num_entires> : 8 bytes
|
||||||
|
# <index> : 16 * N bytes
|
||||||
|
# <name data> : variable
|
||||||
|
#
|
||||||
|
# Each index entry has the format
|
||||||
|
# <symbol address> : 8 bytes
|
||||||
|
# <offset of name> : 8 bytes
|
||||||
|
#
|
||||||
|
# Name offsets are from the start of the symbol table as a whole. (ie,
|
||||||
|
# where <num_entries> is located.)
|
||||||
|
|
||||||
|
def parse_syms(infile):
|
||||||
|
"""Take the output of the `nm` command, and parse it into a tuple
|
||||||
|
representing the symbols in the text segment of the binary. Returns
|
||||||
|
a list of (address, symbol_name)."""
|
||||||
|
|
||||||
|
from cxxfilt import demangle, InvalidName
|
||||||
|
|
||||||
|
syms = []
|
||||||
|
for line in sys.stdin:
|
||||||
|
addr, t, mangled = line.split()
|
||||||
|
if t not in "tTvVwW": continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
name = demangle(mangled)
|
||||||
|
except InvalidName:
|
||||||
|
continue
|
||||||
|
|
||||||
|
addr = int(addr, base=16)
|
||||||
|
syms.append((addr, name))
|
||||||
|
|
||||||
|
return sorted(syms)
|
||||||
|
|
||||||
|
|
||||||
|
def write_table(syms, outfile):
|
||||||
|
"""Write the given symbol table as generated by parse_syms()
|
||||||
|
to the outfile, index first, and then name character data."""
|
||||||
|
|
||||||
|
import struct
|
||||||
|
|
||||||
|
outfile.write(struct.pack("@Q", len(syms)))
|
||||||
|
index_pos = outfile.tell()
|
||||||
|
|
||||||
|
outfile.seek(struct.calcsize("@QQ") * len(syms), 1)
|
||||||
|
nul = b'\0'
|
||||||
|
|
||||||
|
positions = {}
|
||||||
|
for s in syms:
|
||||||
|
addr, name = s
|
||||||
|
positions[addr] = outfile.tell()
|
||||||
|
|
||||||
|
data = name.encode('utf-8')
|
||||||
|
outfile.write(name.encode('utf-8'))
|
||||||
|
outfile.write(nul)
|
||||||
|
|
||||||
|
outfile.seek(index_pos)
|
||||||
|
for s in syms:
|
||||||
|
addr = s[0]
|
||||||
|
pos = positions[addr]
|
||||||
|
outfile.write(struct.pack("@QQ", addr, pos))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print(f"Usage: {sys.argv[0]} <output>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
outfile = open(sys.argv[1], "wb")
|
||||||
|
write_table(parse_syms(sys.stdin), outfile)
|
||||||
75
scripts/fontpsf.py
Normal file
75
scripts/fontpsf.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
_MAGIC = (0x72, 0xb5, 0x4a, 0x86)
|
||||||
|
_VERSION = 0
|
||||||
|
|
||||||
|
class PSF2:
|
||||||
|
from collections import namedtuple
|
||||||
|
Header = namedtuple("PSF2Header",
|
||||||
|
["version", "offset", "flags", "count", "charsize", "height", "width"])
|
||||||
|
|
||||||
|
def __init__(self, filename, header, data):
|
||||||
|
self.__filename = filename
|
||||||
|
self.__header = header
|
||||||
|
self.__data = data
|
||||||
|
|
||||||
|
data = property(lambda self: self.__data)
|
||||||
|
header = property(lambda self: self.__header)
|
||||||
|
count = property(lambda self: self.__header.count)
|
||||||
|
charsize = property(lambda self: self.__header.charsize)
|
||||||
|
dimension = property(lambda self: (self.__header.width, self.__header.height))
|
||||||
|
filename = property(lambda self: self.__filename)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, filename):
|
||||||
|
from os.path import basename
|
||||||
|
from struct import unpack_from
|
||||||
|
|
||||||
|
data = open(filename, 'rb').read()
|
||||||
|
|
||||||
|
fmt = "BBBBIIIIIII"
|
||||||
|
values = unpack_from(fmt, data)
|
||||||
|
|
||||||
|
if values[:len(_MAGIC)] != _MAGIC:
|
||||||
|
raise Exception("Bad magic number in header")
|
||||||
|
|
||||||
|
header = PSF2.Header(*values[len(_MAGIC):])
|
||||||
|
if header.version != _VERSION:
|
||||||
|
raise Exception(f"Bad version {header.version} in header")
|
||||||
|
|
||||||
|
return cls(basename(filename), header, data)
|
||||||
|
|
||||||
|
class Glyph:
|
||||||
|
__slots__ = ['index', 'data']
|
||||||
|
def __init__(self, i, data):
|
||||||
|
self.index = i
|
||||||
|
self.data = data
|
||||||
|
def __index__(self):
|
||||||
|
return self.index
|
||||||
|
def empty(self):
|
||||||
|
return not bool([b for b in self.data if b != 0])
|
||||||
|
def description(self):
|
||||||
|
c = chr(self.index)
|
||||||
|
if c.isprintable():
|
||||||
|
return "Glyph {:02x}: '{}'".format(self.index, c)
|
||||||
|
else:
|
||||||
|
return "Glyph {:02x}".format(self.index)
|
||||||
|
|
||||||
|
def __getitem__(self, i):
|
||||||
|
c = self.__header.charsize
|
||||||
|
n = i * c + self.__header.offset
|
||||||
|
return PSF2.Glyph(i, self.__data[n:n+c])
|
||||||
|
|
||||||
|
class __iter:
|
||||||
|
__slots__ = ['font', 'n']
|
||||||
|
def __init__(self, font):
|
||||||
|
self.font = font
|
||||||
|
self.n = 0
|
||||||
|
def __next__(self):
|
||||||
|
if self.n < self.font.count:
|
||||||
|
glyph = self.font[self.n]
|
||||||
|
self.n += 1
|
||||||
|
return glyph
|
||||||
|
else:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return PSF2.__iter(self)
|
||||||
34
scripts/parse_font.py
Normal file → Executable file
34
scripts/parse_font.py
Normal file → Executable file
@@ -1,21 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
MAGIC = (0x72, 0xb5, 0x4a, 0x86)
|
from fontpsf import PSF2
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
PSF2 = namedtuple("PSF2", ["version", "offset", "flags", "count", "charsize", "height", "width"])
|
|
||||||
|
|
||||||
def read_header(data):
|
|
||||||
from struct import unpack_from, calcsize
|
|
||||||
|
|
||||||
fmt = "BBBBIIIIIII"
|
|
||||||
|
|
||||||
values = unpack_from(fmt, data)
|
|
||||||
if values[:len(MAGIC)] != MAGIC:
|
|
||||||
raise Exception("Bad magic number in header")
|
|
||||||
|
|
||||||
return PSF2(*values[len(MAGIC):])
|
|
||||||
|
|
||||||
|
|
||||||
def print_glyph(header, data):
|
def print_glyph(header, data):
|
||||||
bw = (header.width + 7) // 8
|
bw = (header.width + 7) // 8
|
||||||
@@ -28,16 +13,15 @@ def print_glyph(header, data):
|
|||||||
|
|
||||||
|
|
||||||
def display_font(filename):
|
def display_font(filename):
|
||||||
data = open(filename, 'rb').read()
|
font = PSF2.load(filename)
|
||||||
|
print(font.header)
|
||||||
|
|
||||||
header = read_header(data)
|
for glyph in font:
|
||||||
print(header)
|
if glyph.empty():
|
||||||
|
print("{}: BLANK".format(glyph.description()))
|
||||||
c = header.charsize
|
else:
|
||||||
for i in range(0, header.count):
|
print("{}:".format(glyph.description()))
|
||||||
n = i * c + header.offset
|
print_glyph(font.header, glyph.data)
|
||||||
print("Glyph {}:".format(i))
|
|
||||||
print_glyph(header, data[n:n+c])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
27
scripts/psf_to_cpp.py
Executable file
27
scripts/psf_to_cpp.py
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from fontpsf import PSF2
|
||||||
|
|
||||||
|
def print_header(filename):
|
||||||
|
font = PSF2.load(filename)
|
||||||
|
|
||||||
|
print("#pragma once")
|
||||||
|
print(f"// This file was autogenerated by psf_to_cpp.py from {font.filename}\n")
|
||||||
|
|
||||||
|
print(f"const uint8_t font_glyph_size = {font.charsize};")
|
||||||
|
print(f"const uint8_t font_glyph_width = {font.dimension[0]};")
|
||||||
|
print(f"const uint8_t font_glyph_height = {font.dimension[1]};")
|
||||||
|
print(f"const uint16_t font_glyph_count = {font.count};\n")
|
||||||
|
|
||||||
|
print('const uint8_t font_glyph_data[] = {')
|
||||||
|
|
||||||
|
for glyph in font:
|
||||||
|
print(" ", "".join([f"0x{b:02x}," for b in glyph.data]), end="")
|
||||||
|
print(" // {}".format(glyph.description()))
|
||||||
|
|
||||||
|
print("};")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
for filename in sys.argv[1:]:
|
||||||
|
print_header(filename)
|
||||||
@@ -47,10 +47,11 @@ asflags = $
|
|||||||
-DVERSION_MAJOR={{ version_major }} $
|
-DVERSION_MAJOR={{ version_major }} $
|
||||||
-DVERSION_MINOR={{ version_minor }} $
|
-DVERSION_MINOR={{ version_minor }} $
|
||||||
-DVERSION_PATCH={{ version_patch }} $
|
-DVERSION_PATCH={{ version_patch }} $
|
||||||
-DVERSION_GITSHA=0x{{ version_sha }}
|
-DVERSION_GITSHA=0x{{ version_sha }} $
|
||||||
|
-I${srcroot}/src/include
|
||||||
|
|
||||||
cflags = -std=c11
|
cflags = -std=c11
|
||||||
cxxflags = -std=c++14
|
cxxflags = -std=c++17
|
||||||
libs =
|
libs =
|
||||||
|
|
||||||
rule c
|
rule c
|
||||||
@@ -67,7 +68,7 @@ rule dump_c_run
|
|||||||
description = Dumping C arguments for $target
|
description = Dumping C arguments for $target
|
||||||
command = $
|
command = $
|
||||||
echo "#!/bin/bash" > $out; $
|
echo "#!/bin/bash" > $out; $
|
||||||
echo '$cc $ccflags $cflags $$*' > $out; $
|
echo '$cc $ccflags $cflags $$*' >> $out; $
|
||||||
chmod a+x $out
|
chmod a+x $out
|
||||||
|
|
||||||
rule cpp
|
rule cpp
|
||||||
@@ -84,7 +85,7 @@ rule dump_cpp_run
|
|||||||
description = Dumping C++ arguments for $target
|
description = Dumping C++ arguments for $target
|
||||||
command = $
|
command = $
|
||||||
echo "#!/bin/bash" > $out; $
|
echo "#!/bin/bash" > $out; $
|
||||||
echo '$cc $cxxflags $ccflags $$*' > $out; $
|
echo '$cc $cxxflags $ccflags $$*' >> $out; $
|
||||||
chmod a+x $out
|
chmod a+x $out
|
||||||
|
|
||||||
rule s
|
rule s
|
||||||
@@ -118,9 +119,9 @@ rule dump
|
|||||||
description = Dumping decompiled $name
|
description = Dumping decompiled $name
|
||||||
command = objdump -DSC -M intel $in > $out
|
command = objdump -DSC -M intel $in > $out
|
||||||
|
|
||||||
rule makerd
|
rule makest
|
||||||
description = Making init ramdisk
|
description = Making symbol table
|
||||||
command = $builddir/native/makerd $in $out
|
command = nm $in | ${srcroot}/scripts/build_symbol_table.py $out
|
||||||
|
|
||||||
rule makeefi
|
rule makeefi
|
||||||
description = Converting $name
|
description = Converting $name
|
||||||
@@ -186,12 +187,18 @@ build $builddir/fatroot/jsix.elf : cp $builddir/jsix.elf
|
|||||||
build $builddir/fatroot/efi/boot/bootx64.efi : cp $builddir/boot/boot.efi
|
build $builddir/fatroot/efi/boot/bootx64.efi : cp $builddir/boot/boot.efi
|
||||||
name = bootloader to FAT image
|
name = bootloader to FAT image
|
||||||
|
|
||||||
build $builddir/fatroot/initrd.img : makerd ${srcroot}/assets/initrd.toml | $
|
build $builddir/fatroot/nulldrv.elf : cp $builddir/user/nulldrv.elf
|
||||||
${builddir}/native/makerd $
|
name = null driver to FAT image
|
||||||
${builddir}/user/nulldrv
|
|
||||||
|
build $builddir/fatroot/fb.elf : cp $builddir/user/fb.elf
|
||||||
|
name = fb driver to FAT image
|
||||||
|
|
||||||
|
build ${builddir}/fatroot/symbol_table.dat : makest ${builddir}/jsix.elf
|
||||||
|
|
||||||
build $builddir/jsix.img : makefat | $
|
build $builddir/jsix.img : makefat | $
|
||||||
$builddir/fatroot/initrd.img $
|
$builddir/fatroot/symbol_table.dat $
|
||||||
|
$builddir/fatroot/nulldrv.elf $
|
||||||
|
$builddir/fatroot/fb.elf $
|
||||||
$builddir/fatroot/jsix.elf $
|
$builddir/fatroot/jsix.elf $
|
||||||
$builddir/fatroot/efi/boot/bootx64.efi
|
$builddir/fatroot/efi/boot/bootx64.efi
|
||||||
name = jsix.img
|
name = jsix.img
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
asflags = $asflags -I${srcroot}/src/kernel/
|
asflags = $asflags -I${srcroot}/src/kernel/
|
||||||
libs = $libs
|
libs = $libs
|
||||||
ldflags = $ldflags -T ${srcroot}/src/arch/x86_64/kernel.ld
|
ldflags = $ldflags -T ${srcroot}/src/arch/x86_64/kernel.ld
|
||||||
|
ccflags = $ccflags -I${srcroot}/external
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -22,5 +22,10 @@ build ${builddir}/cpp.defs : dump_cpp_defs | {{ buildfile }}
|
|||||||
build ${builddir}/c.run : dump_c_run | {{ buildfile }}
|
build ${builddir}/c.run : dump_c_run | {{ buildfile }}
|
||||||
build ${builddir}/cpp.run : dump_cpp_run | {{ buildfile }}
|
build ${builddir}/cpp.run : dump_cpp_run | {{ buildfile }}
|
||||||
|
|
||||||
|
default ${builddir}/c.defs
|
||||||
|
default ${builddir}/cpp.defs
|
||||||
|
default ${builddir}/c.run
|
||||||
|
default ${builddir}/cpp.run
|
||||||
|
|
||||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ ccflags = $ccflags $
|
|||||||
-D__ELF__ $
|
-D__ELF__ $
|
||||||
-D__JSIX__ $
|
-D__JSIX__ $
|
||||||
-isystem${srcroot}/sysroot/include $
|
-isystem${srcroot}/sysroot/include $
|
||||||
|
-isystem${srcroot}/src/libraries/libc/include $
|
||||||
--sysroot="${srcroot}/sysroot"
|
--sysroot="${srcroot}/sysroot"
|
||||||
|
|
||||||
cxxflags = $cxxflags $
|
cxxflags = $cxxflags $
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ ccflags = $ccflags $
|
|||||||
-D__ELF__ $
|
-D__ELF__ $
|
||||||
-D__JSIX__ $
|
-D__JSIX__ $
|
||||||
-isystem${srcroot}/sysroot/include $
|
-isystem${srcroot}/sysroot/include $
|
||||||
|
-isystem${srcroot}/src/libraries/libc/include $
|
||||||
--sysroot="${srcroot}/sysroot"
|
--sysroot="${srcroot}/sysroot"
|
||||||
|
|
||||||
cxxflags = $cxxflags $
|
cxxflags = $cxxflags $
|
||||||
@@ -38,9 +39,6 @@ ldflags = $ldflags $
|
|||||||
--sysroot="${srcroot}/sysroot" $
|
--sysroot="${srcroot}/sysroot" $
|
||||||
-L "${srcroot}/sysroot/lib" $
|
-L "${srcroot}/sysroot/lib" $
|
||||||
|
|
||||||
libs = $libs $
|
|
||||||
-lc
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
ENTRY(_start)
|
ENTRY(_kernel_start)
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
OFFSET = 0xFFFF800000000000;
|
. = 0xFFFF800000000000;
|
||||||
. = OFFSET + 0x100000;
|
|
||||||
|
|
||||||
.header : {
|
.header : {
|
||||||
__header_start = .;
|
__header_start = .;
|
||||||
@@ -20,6 +19,12 @@ SECTIONS
|
|||||||
*(.rodata)
|
*(.rodata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ctors : ALIGN(8) {
|
||||||
|
__ctors = .;
|
||||||
|
KEEP(*(.ctors))
|
||||||
|
__ctors_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
.bss ALIGN(4096) : {
|
.bss ALIGN(4096) : {
|
||||||
__bss_start = .;
|
__bss_start = .;
|
||||||
*(.bss)
|
*(.bss)
|
||||||
|
|||||||
@@ -17,23 +17,7 @@ namespace boot {
|
|||||||
size_t ROWS = 0;
|
size_t ROWS = 0;
|
||||||
size_t COLS = 0;
|
size_t COLS = 0;
|
||||||
|
|
||||||
static constexpr int level_ok = 0;
|
|
||||||
static constexpr int level_warn = 1;
|
|
||||||
static constexpr int level_fail = 2;
|
|
||||||
|
|
||||||
static const wchar_t *level_tags[] = {
|
|
||||||
L" ok ",
|
|
||||||
L" warn ",
|
|
||||||
L"failed"
|
|
||||||
};
|
|
||||||
static const uefi::attribute level_colors[] = {
|
|
||||||
uefi::attribute::green,
|
|
||||||
uefi::attribute::brown,
|
|
||||||
uefi::attribute::light_red
|
|
||||||
};
|
|
||||||
|
|
||||||
console *console::s_console = nullptr;
|
console *console::s_console = nullptr;
|
||||||
status_line *status_line::s_current = nullptr;
|
|
||||||
|
|
||||||
static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
|
static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
|
||||||
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
|
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
|
||||||
@@ -49,9 +33,10 @@ wstrlen(const wchar_t *s)
|
|||||||
|
|
||||||
|
|
||||||
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
|
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
|
||||||
m_rows(0),
|
m_rows {0},
|
||||||
m_cols(0),
|
m_cols {0},
|
||||||
m_out(out)
|
m_out {out},
|
||||||
|
m_fb {0, 0}
|
||||||
{
|
{
|
||||||
pick_mode(bs);
|
pick_mode(bs);
|
||||||
|
|
||||||
@@ -72,6 +57,25 @@ console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out)
|
|||||||
m_out->set_attribute(uefi::attribute::light_gray);
|
m_out->set_attribute(uefi::attribute::light_gray);
|
||||||
m_out->output_string(L" booting...\r\n\n");
|
m_out->output_string(L" booting...\r\n\n");
|
||||||
|
|
||||||
|
if (m_fb.type != kernel::args::fb_type::none) {
|
||||||
|
wchar_t const * type = nullptr;
|
||||||
|
switch (m_fb.type) {
|
||||||
|
case kernel::args::fb_type::rgb8:
|
||||||
|
type = L"rgb8";
|
||||||
|
break;
|
||||||
|
case kernel::args::fb_type::bgr8:
|
||||||
|
type = L"bgr8";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = L"unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(L"Found framebuffer: %dx%d[%d] type %s @0x%x\r\n",
|
||||||
|
m_fb.horizontal, m_fb.vertical, m_fb.scanline, type, m_fb.phys_addr);
|
||||||
|
} else {
|
||||||
|
printf(L"No framebuffer found.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
s_console = this;
|
s_console = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,9 +86,14 @@ console::pick_mode(uefi::boot_services *bs)
|
|||||||
uefi::protos::graphics_output *gfx_out_proto;
|
uefi::protos::graphics_output *gfx_out_proto;
|
||||||
uefi::guid guid = uefi::protos::graphics_output::guid;
|
uefi::guid guid = uefi::protos::graphics_output::guid;
|
||||||
|
|
||||||
try_or_raise(
|
m_fb.type = kernel::args::fb_type::none;
|
||||||
bs->locate_protocol(&guid, nullptr, (void **)&gfx_out_proto),
|
|
||||||
L"Failed to find a Graphics Output Protocol handle");
|
uefi::status has_gop = bs->locate_protocol(&guid, nullptr,
|
||||||
|
(void **)&gfx_out_proto);
|
||||||
|
|
||||||
|
if (has_gop != uefi::status::success)
|
||||||
|
// No video output found, skip it
|
||||||
|
return;
|
||||||
|
|
||||||
const uint32_t modes = gfx_out_proto->mode->max_mode;
|
const uint32_t modes = gfx_out_proto->mode->max_mode;
|
||||||
uint32_t best = gfx_out_proto->mode->mode;
|
uint32_t best = gfx_out_proto->mode->mode;
|
||||||
@@ -93,7 +102,7 @@ console::pick_mode(uefi::boot_services *bs)
|
|||||||
(uefi::graphics_output_mode_info *)gfx_out_proto->mode;
|
(uefi::graphics_output_mode_info *)gfx_out_proto->mode;
|
||||||
|
|
||||||
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
||||||
int is_fb = info->pixel_format != uefi::pixel_format::blt_only;
|
int pixmode = static_cast<int>(info->pixel_format);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < modes; ++i) {
|
for (uint32_t i = 0; i < modes; ++i) {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
@@ -107,17 +116,37 @@ console::pick_mode(uefi::boot_services *bs)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
const uint32_t new_res = info->horizontal_resolution * info->vertical_resolution;
|
const uint32_t new_res = info->horizontal_resolution * info->vertical_resolution;
|
||||||
const int new_is_fb = info->pixel_format != uefi::pixel_format::blt_only;
|
int new_pixmode = static_cast<int>(info->pixel_format);
|
||||||
|
|
||||||
if (new_is_fb > is_fb && new_res >= res) {
|
if (new_pixmode <= pixmode && new_res >= res) {
|
||||||
best = i;
|
best = i;
|
||||||
res = new_res;
|
res = new_res;
|
||||||
|
pixmode = new_pixmode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
gfx_out_proto->set_mode(best),
|
gfx_out_proto->set_mode(best),
|
||||||
L"Failed to set graphics mode");
|
L"Failed to set graphics mode");
|
||||||
|
|
||||||
|
if (pixmode <= static_cast<int>(uefi::pixel_format::bgr8)) {
|
||||||
|
m_fb.phys_addr = gfx_out_proto->mode->frame_buffer_base;
|
||||||
|
m_fb.size = gfx_out_proto->mode->frame_buffer_size;
|
||||||
|
m_fb.vertical = gfx_out_proto->mode->info->vertical_resolution;
|
||||||
|
m_fb.horizontal = gfx_out_proto->mode->info->horizontal_resolution;
|
||||||
|
m_fb.scanline = gfx_out_proto->mode->info->pixels_per_scanline;
|
||||||
|
|
||||||
|
switch (gfx_out_proto->mode->info->pixel_format) {
|
||||||
|
case uefi::pixel_format::rgb8:
|
||||||
|
m_fb.type = kernel::args::fb_type::rgb8;
|
||||||
|
break;
|
||||||
|
case uefi::pixel_format::bgr8:
|
||||||
|
m_fb.type = kernel::args::fb_type::bgr8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_fb.type = kernel::args::fb_type::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
@@ -272,176 +301,4 @@ console::print(const wchar_t *fmt, ...)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_line::status_line(const wchar_t *message, const wchar_t *context) :
|
|
||||||
m_level(level_ok)
|
|
||||||
{
|
|
||||||
auto out = console::get().m_out;
|
|
||||||
m_line = out->mode->cursor_row;
|
|
||||||
m_depth = (s_current ? 1 + s_current->m_depth : 0);
|
|
||||||
|
|
||||||
int indent = 2 * m_depth;
|
|
||||||
out->set_cursor_position(indent, m_line);
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
|
||||||
out->output_string(message);
|
|
||||||
|
|
||||||
if (context) {
|
|
||||||
out->output_string(L": ");
|
|
||||||
out->output_string(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
out->output_string(L"\r\n");
|
|
||||||
|
|
||||||
m_next = s_current;
|
|
||||||
s_current = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_line::~status_line()
|
|
||||||
{
|
|
||||||
if (s_current != this)
|
|
||||||
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
|
|
||||||
|
|
||||||
finish();
|
|
||||||
if (m_next && m_level > m_next->m_level) {
|
|
||||||
m_next->m_level = m_level;
|
|
||||||
m_next->print_status_tag();
|
|
||||||
}
|
|
||||||
s_current = m_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
status_line::print_status_tag()
|
|
||||||
{
|
|
||||||
auto out = console::get().m_out;
|
|
||||||
int row = out->mode->cursor_row;
|
|
||||||
int col = out->mode->cursor_column;
|
|
||||||
|
|
||||||
uefi::attribute color = level_colors[m_level];
|
|
||||||
const wchar_t *tag = level_tags[m_level];
|
|
||||||
|
|
||||||
out->set_cursor_position(50, m_line);
|
|
||||||
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
|
||||||
out->output_string(L"[");
|
|
||||||
out->set_attribute(color);
|
|
||||||
out->output_string(tag);
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
|
||||||
out->output_string(L"]\r\n");
|
|
||||||
|
|
||||||
out->set_cursor_position(col, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
status_line::do_warn(const wchar_t *message, const wchar_t *error)
|
|
||||||
{
|
|
||||||
auto out = console::get().m_out;
|
|
||||||
int row = out->mode->cursor_row;
|
|
||||||
|
|
||||||
if (m_level < level_warn) {
|
|
||||||
m_level = level_warn;
|
|
||||||
print_status_tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
int indent = 2 + 2 * m_depth;
|
|
||||||
out->set_cursor_position(indent, row);
|
|
||||||
out->set_attribute(uefi::attribute::yellow);
|
|
||||||
out->output_string(message);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
out->output_string(L": ");
|
|
||||||
out->output_string(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
|
||||||
out->output_string(L"\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
status_line::do_fail(const wchar_t *message, const wchar_t *error)
|
|
||||||
{
|
|
||||||
auto out = console::get().m_out;
|
|
||||||
int row = out->mode->cursor_row;
|
|
||||||
|
|
||||||
if (s_current->m_level < level_fail) {
|
|
||||||
m_level = level_fail;
|
|
||||||
print_status_tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
int indent = 2 + 2 * m_depth;
|
|
||||||
out->set_cursor_position(indent, row);
|
|
||||||
out->set_attribute(uefi::attribute::red);
|
|
||||||
out->output_string(message);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
out->output_string(L": ");
|
|
||||||
out->output_string(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
out->set_attribute(uefi::attribute::light_gray);
|
|
||||||
out->output_string(L"\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
status_line::finish()
|
|
||||||
{
|
|
||||||
if (m_level <= level_ok)
|
|
||||||
print_status_tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
uefi::status
|
|
||||||
con_get_framebuffer(
|
|
||||||
EFI_BOOT_SERVICES *bootsvc,
|
|
||||||
void **buffer,
|
|
||||||
size_t *buffer_size,
|
|
||||||
uint32_t *hres,
|
|
||||||
uint32_t *vres,
|
|
||||||
uint32_t *rmask,
|
|
||||||
uint32_t *gmask,
|
|
||||||
uint32_t *bmask)
|
|
||||||
{
|
|
||||||
uefi::status status;
|
|
||||||
|
|
||||||
uefi::protos::graphics_output *gop;
|
|
||||||
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gop);
|
|
||||||
if (status != EFI_NOT_FOUND) {
|
|
||||||
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
|
|
||||||
|
|
||||||
*buffer = (void *)gop->Mode->FrameBufferBase;
|
|
||||||
*buffer_size = gop->Mode->FrameBufferSize;
|
|
||||||
*hres = gop->Mode->Info->horizontal_resolution;
|
|
||||||
*vres = gop->Mode->Info->vertical_resolution;
|
|
||||||
|
|
||||||
switch (gop->Mode->Info->PixelFormat) {
|
|
||||||
case PixelRedGreenBlueReserved8BitPerColor:
|
|
||||||
*rmask = 0x0000ff;
|
|
||||||
*gmask = 0x00ff00;
|
|
||||||
*bmask = 0xff0000;
|
|
||||||
return EFI_SUCCESS;
|
|
||||||
|
|
||||||
case PixelBlueGreenRedReserved8BitPerColor:
|
|
||||||
*bmask = 0x0000ff;
|
|
||||||
*gmask = 0x00ff00;
|
|
||||||
*rmask = 0xff0000;
|
|
||||||
return EFI_SUCCESS;
|
|
||||||
|
|
||||||
case PixelBitMask:
|
|
||||||
*rmask = gop->Mode->Info->PixelInformation.RedMask;
|
|
||||||
*gmask = gop->Mode->Info->PixelInformation.GreenMask;
|
|
||||||
*bmask = gop->Mode->Info->PixelInformation.BlueMask;
|
|
||||||
return EFI_SUCCESS;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Not a framebuffer, fall through to zeroing out
|
|
||||||
// values below.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*buffer = NULL;
|
|
||||||
*buffer_size = *hres = *vres = 0;
|
|
||||||
*rmask = *gmask = *bmask = 0;
|
|
||||||
return EFI_SUCCESS;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
/// \file console.h
|
/// \file console.h
|
||||||
/// Text output and status message handling
|
/// Text output handler
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <uefi/boot_services.h>
|
#include <uefi/boot_services.h>
|
||||||
#include <uefi/protos/simple_text_output.h>
|
#include <uefi/protos/simple_text_output.h>
|
||||||
|
#include "kernel_args.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
|
|
||||||
@@ -12,6 +14,8 @@ namespace boot {
|
|||||||
class console
|
class console
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using framebuffer = kernel::args::framebuffer;
|
||||||
|
|
||||||
console(uefi::boot_services *bs, uefi::protos::simple_text_output *out);
|
console(uefi::boot_services *bs, uefi::protos::simple_text_output *out);
|
||||||
|
|
||||||
size_t print_hex(uint32_t n) const;
|
size_t print_hex(uint32_t n) const;
|
||||||
@@ -20,6 +24,8 @@ public:
|
|||||||
size_t print_long_dec(uint64_t n) const;
|
size_t print_long_dec(uint64_t n) const;
|
||||||
size_t printf(const wchar_t *fmt, ...) const;
|
size_t printf(const wchar_t *fmt, ...) const;
|
||||||
|
|
||||||
|
const framebuffer & fb() const { return m_fb; };
|
||||||
|
|
||||||
static console & get() { return *s_console; }
|
static console & get() { return *s_console; }
|
||||||
static size_t print(const wchar_t *fmt, ...);
|
static size_t print(const wchar_t *fmt, ...);
|
||||||
|
|
||||||
@@ -31,50 +37,9 @@ private:
|
|||||||
|
|
||||||
size_t m_rows, m_cols;
|
size_t m_rows, m_cols;
|
||||||
uefi::protos::simple_text_output *m_out;
|
uefi::protos::simple_text_output *m_out;
|
||||||
|
framebuffer m_fb;
|
||||||
|
|
||||||
static console *s_console;
|
static console *s_console;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Scoped status line reporter. Prints a message and an "OK" if no errors
|
|
||||||
/// or warnings were reported before destruction, otherwise reports the
|
|
||||||
/// error or warning.
|
|
||||||
class status_line
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Constructor.
|
|
||||||
/// \arg message Description of the operation in progress
|
|
||||||
/// \arg context If non-null, printed after `message` and a colon
|
|
||||||
status_line(const wchar_t *message, const wchar_t *context = nullptr);
|
|
||||||
~status_line();
|
|
||||||
|
|
||||||
/// Set the state to warning, and print a message. If the state is already at
|
|
||||||
/// warning or error, the state is unchanged but the message is still printed.
|
|
||||||
/// \arg message The warning message to print
|
|
||||||
/// \arg error If non-null, printed after `message`
|
|
||||||
inline static void warn(const wchar_t *message, const wchar_t *error = nullptr) {
|
|
||||||
if (s_current) s_current->do_warn(message, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the state to error, and print a message. If the state is already at
|
|
||||||
/// error, the state is unchanged but the message is still printed.
|
|
||||||
/// \arg message The error message to print
|
|
||||||
/// \arg error If non-null, printed after `message`
|
|
||||||
inline static void fail(const wchar_t *message, const wchar_t *error = nullptr) {
|
|
||||||
if (s_current) s_current->do_fail(message, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void print_status_tag();
|
|
||||||
void do_warn(const wchar_t *message, const wchar_t *error);
|
|
||||||
void do_fail(const wchar_t *message, const wchar_t *error);
|
|
||||||
void finish();
|
|
||||||
|
|
||||||
size_t m_line;
|
|
||||||
int m_level;
|
|
||||||
int m_depth;
|
|
||||||
|
|
||||||
status_line *m_next;
|
|
||||||
static status_line *s_current;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
#include "kernel_args.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace error {
|
namespace error {
|
||||||
|
|
||||||
handler *handler::s_current = nullptr;
|
|
||||||
|
|
||||||
struct error_code_desc {
|
struct error_code_desc {
|
||||||
uefi::status code;
|
uefi::status code;
|
||||||
const wchar_t *name;
|
const wchar_t *name;
|
||||||
@@ -20,8 +20,8 @@ struct error_code_desc error_table[] = {
|
|||||||
{ uefi::status::success, nullptr }
|
{ uefi::status::success, nullptr }
|
||||||
};
|
};
|
||||||
|
|
||||||
static const wchar_t *
|
const wchar_t *
|
||||||
error_message(uefi::status status)
|
message(uefi::status status)
|
||||||
{
|
{
|
||||||
int32_t i = -1;
|
int32_t i = -1;
|
||||||
while (error_table[++i].name != nullptr) {
|
while (error_table[++i].name != nullptr) {
|
||||||
@@ -34,56 +34,31 @@ error_message(uefi::status status)
|
|||||||
return L"Unknown Warning";
|
return L"Unknown Warning";
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ noreturn ]] void
|
[[ noreturn ]] static void
|
||||||
raise(uefi::status status, const wchar_t *message)
|
cpu_assert(uefi::status s, const wchar_t *message)
|
||||||
{
|
|
||||||
if (handler::s_current) {
|
|
||||||
handler::s_current->handle(status, message);
|
|
||||||
}
|
|
||||||
while (1) asm("hlt");
|
|
||||||
}
|
|
||||||
|
|
||||||
handler::handler() :
|
|
||||||
m_next(s_current)
|
|
||||||
{
|
|
||||||
s_current = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
handler::~handler()
|
|
||||||
{
|
|
||||||
if (s_current != this)
|
|
||||||
raise(uefi::status::warn_stale_data,
|
|
||||||
L"Non-current error handler destructing");
|
|
||||||
s_current = m_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
uefi_handler::uefi_handler(console &con) :
|
|
||||||
handler(),
|
|
||||||
m_con(con)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
uefi_handler::handle(uefi::status s, const wchar_t *message)
|
|
||||||
{
|
|
||||||
status_line::fail(message, error_message(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_assert_handler::cpu_assert_handler() : handler() {}
|
|
||||||
|
|
||||||
void
|
|
||||||
cpu_assert_handler::handle(uefi::status s, const wchar_t *message)
|
|
||||||
{
|
{
|
||||||
asm volatile (
|
asm volatile (
|
||||||
"movq $0xeeeeeeebadbadbad, %%r8;"
|
"movq $0xeeeeeeebadbadbad, %%r8;"
|
||||||
"movq %0, %%r9;"
|
"movq %0, %%r9;"
|
||||||
|
"movq %1, %%r10;"
|
||||||
"movq $0, %%rdx;"
|
"movq $0, %%rdx;"
|
||||||
"divq %%rdx;"
|
"divq %%rdx;"
|
||||||
:
|
:
|
||||||
: "r"((uint64_t)s)
|
: "r"((uint64_t)s), "r"(message)
|
||||||
: "rax", "rdx", "r8", "r9");
|
: "rax", "rdx", "r8", "r9", "r10");
|
||||||
|
while (1) asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[ noreturn ]] void
|
||||||
|
raise(uefi::status status, const wchar_t *message)
|
||||||
|
{
|
||||||
|
if(status_line::fail(message, status))
|
||||||
|
while (1) asm("hlt");
|
||||||
|
else
|
||||||
|
cpu_assert(status, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace error
|
} // namespace error
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|
||||||
|
|||||||
@@ -14,48 +14,7 @@ namespace error {
|
|||||||
/// Halt or exit the program with the given error status/message
|
/// Halt or exit the program with the given error status/message
|
||||||
[[ noreturn ]] void raise(uefi::status status, const wchar_t *message);
|
[[ noreturn ]] void raise(uefi::status status, const wchar_t *message);
|
||||||
|
|
||||||
/// Interface for error-handling functors
|
const wchar_t * message(uefi::status status);
|
||||||
class handler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Constructor must be called by implementing classes.
|
|
||||||
handler();
|
|
||||||
virtual ~handler();
|
|
||||||
|
|
||||||
/// Interface for implementations of error handling.
|
|
||||||
virtual void handle(uefi::status, const wchar_t*) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend void raise(uefi::status, const wchar_t *);
|
|
||||||
|
|
||||||
handler *m_next;
|
|
||||||
static handler *s_current;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Error handler using UEFI boot services. Integrates with `status_line`
|
|
||||||
/// to print formatted error messages to the screen.
|
|
||||||
class uefi_handler :
|
|
||||||
public handler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uefi_handler(console &con);
|
|
||||||
virtual ~uefi_handler() {}
|
|
||||||
void handle(uefi::status, const wchar_t*) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
console &m_con;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Error handler that doesn't rely on UEFI. Sets status into CPU
|
|
||||||
/// registers and then causes a CPU #DE exception.
|
|
||||||
class cpu_assert_handler :
|
|
||||||
public handler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
cpu_assert_handler();
|
|
||||||
virtual ~cpu_assert_handler() {}
|
|
||||||
void handle(uefi::status, const wchar_t*) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace error
|
} // namespace error
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
@@ -50,19 +51,19 @@ file::open(const wchar_t *path)
|
|||||||
return file(fh, m_bs);
|
return file(fh, m_bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
buffer
|
||||||
file::load(size_t *out_size, uefi::memory_type mem_type)
|
file::load(uefi::memory_type mem_type)
|
||||||
{
|
{
|
||||||
uint8_t buffer[sizeof(uefi::protos::file_info) + 100];
|
uint8_t info_buf[sizeof(uefi::protos::file_info) + 100];
|
||||||
size_t size = sizeof(buffer);
|
size_t size = sizeof(info_buf);
|
||||||
uefi::guid info_guid = uefi::protos::file_info::guid;
|
uefi::guid info_guid = uefi::protos::file_info::guid;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
m_file->get_info(&info_guid, &size, &buffer),
|
m_file->get_info(&info_guid, &size, &info_buf),
|
||||||
L"Could not get file info");
|
L"Could not get file info");
|
||||||
|
|
||||||
uefi::protos::file_info *info =
|
uefi::protos::file_info *info =
|
||||||
reinterpret_cast<uefi::protos::file_info*>(&buffer);
|
reinterpret_cast<uefi::protos::file_info*>(&info_buf);
|
||||||
|
|
||||||
size_t pages = memory::bytes_to_pages(info->file_size);
|
size_t pages = memory::bytes_to_pages(info->file_size);
|
||||||
void *data = nullptr;
|
void *data = nullptr;
|
||||||
@@ -77,8 +78,7 @@ file::load(size_t *out_size, uefi::memory_type mem_type)
|
|||||||
m_file->read(&size, data),
|
m_file->read(&size, data),
|
||||||
L"Could not read from file");
|
L"Could not read from file");
|
||||||
|
|
||||||
*out_size = size;
|
return { .data = data, .size = size };
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file
|
file
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <uefi/types.h>
|
#include <uefi/types.h>
|
||||||
#include <uefi/boot_services.h>
|
#include <uefi/boot_services.h>
|
||||||
#include <uefi/protos/file.h>
|
#include <uefi/protos/file.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace fs {
|
namespace fs {
|
||||||
@@ -22,13 +23,10 @@ public:
|
|||||||
file open(const wchar_t *path);
|
file open(const wchar_t *path);
|
||||||
|
|
||||||
/// Load the contents of this file into memory.
|
/// Load the contents of this file into memory.
|
||||||
/// \arg out_size _out:_ The number of bytes loaded
|
|
||||||
/// \arg mem_type The UEFI memory type to use for allocation
|
/// \arg mem_type The UEFI memory type to use for allocation
|
||||||
/// \returns A pointer to the loaded memory. Memory will be
|
/// \returns A buffer describing the loaded memory. The
|
||||||
/// page-aligned.
|
/// memory will be page-aligned.
|
||||||
void * load(
|
buffer load(uefi::memory_type mem_type = uefi::memory_type::loader_data);
|
||||||
size_t *out_size,
|
|
||||||
uefi::memory_type mem_type = uefi::memory_type::loader_data);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend file get_boot_volume(uefi::handle, uefi::boot_services*);
|
friend file get_boot_volume(uefi::handle, uefi::boot_services*);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace hw {
|
namespace hw {
|
||||||
|
|||||||
@@ -5,12 +5,33 @@
|
|||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "elf.h"
|
#include "elf.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "fs.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
|
namespace args = kernel::args;
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
|
||||||
|
buffer
|
||||||
|
load_file(
|
||||||
|
fs::file &disk,
|
||||||
|
const wchar_t *name,
|
||||||
|
const wchar_t *path,
|
||||||
|
uefi::memory_type type)
|
||||||
|
{
|
||||||
|
status_line status(L"Loading file", name);
|
||||||
|
|
||||||
|
fs::file file = disk.open(path);
|
||||||
|
buffer b = file.load(type);
|
||||||
|
|
||||||
|
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_elfheader_valid(const elf::header *header)
|
is_elfheader_valid(const elf::header *header)
|
||||||
{
|
{
|
||||||
@@ -26,48 +47,63 @@ is_elfheader_valid(const elf::header *header)
|
|||||||
header->header_version == elf::version;
|
header->header_version == elf::version;
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel::entrypoint
|
void
|
||||||
load(
|
load_program(
|
||||||
const void *data, size_t size,
|
args::program &program,
|
||||||
kernel::args::header *args,
|
const wchar_t *name,
|
||||||
|
buffer data,
|
||||||
uefi::boot_services *bs)
|
uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line status(L"Loading kernel ELF binary");
|
status_line status(L"Loading program:", name);
|
||||||
const elf::header *header = reinterpret_cast<const elf::header*>(data);
|
const elf::header *header = reinterpret_cast<const elf::header*>(data.data);
|
||||||
|
|
||||||
if (size < sizeof(elf::header) || !is_elfheader_valid(header))
|
if (data.size < sizeof(elf::header) || !is_elfheader_valid(header))
|
||||||
error::raise(uefi::status::load_error, L"Kernel ELF not valid");
|
error::raise(uefi::status::load_error, L"ELF file not valid");
|
||||||
|
|
||||||
paging::page_table *pml4 = reinterpret_cast<paging::page_table*>(args->pml4);
|
uintptr_t prog_base = uintptr_t(-1);
|
||||||
|
uintptr_t prog_end = 0;
|
||||||
|
|
||||||
for (int i = 0; i < header->ph_num; ++i) {
|
for (int i = 0; i < header->ph_num; ++i) {
|
||||||
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
|
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
|
||||||
const elf::program_header *pheader =
|
const elf::program_header *pheader =
|
||||||
offset_ptr<elf::program_header>(data, offset);
|
offset_ptr<elf::program_header>(data.data, offset);
|
||||||
|
|
||||||
if (pheader->type != elf::PT_LOAD)
|
if (pheader->type != elf::PT_LOAD)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
size_t num_pages = memory::bytes_to_pages(pheader->mem_size);
|
uintptr_t end = pheader->vaddr + pheader->mem_size;
|
||||||
|
if (pheader->vaddr < prog_base) prog_base = pheader->vaddr;
|
||||||
|
if (end > prog_end) prog_end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_size = prog_end - prog_base;
|
||||||
|
size_t num_pages = memory::bytes_to_pages(total_size);
|
||||||
void *pages = nullptr;
|
void *pages = nullptr;
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->allocate_pages(uefi::allocate_type::any_pages,
|
bs->allocate_pages(uefi::allocate_type::any_pages,
|
||||||
memory::kernel_type, num_pages, &pages),
|
uefi::memory_type::loader_data, num_pages, &pages),
|
||||||
L"Failed allocating space for kernel code");
|
L"Failed allocating space for program");
|
||||||
|
|
||||||
void *data_start = offset_ptr<void>(data, pheader->offset);
|
bs->set_mem(pages, total_size, 0);
|
||||||
bs->copy_mem(pages, data_start, pheader->mem_size);
|
|
||||||
|
|
||||||
console::print(L" section %d phys: 0x%lx\r\n", i, pages);
|
for (int i = 0; i < header->ph_num; ++i) {
|
||||||
console::print(L" section %d virt: 0x%lx\r\n", i, pheader->vaddr);
|
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
|
||||||
|
const elf::program_header *pheader =
|
||||||
|
offset_ptr<elf::program_header>(data.data, offset);
|
||||||
|
|
||||||
// TODO: set appropriate RWX permissions
|
if (pheader->type != elf::PT_LOAD)
|
||||||
paging::map_pages(pml4, args, reinterpret_cast<uintptr_t>(pages), pheader->vaddr, pheader->mem_size);
|
continue;
|
||||||
|
|
||||||
|
void *src_start = offset_ptr<void>(data.data, pheader->offset);
|
||||||
|
void *dest_start = offset_ptr<void>(pages, pheader->vaddr - prog_base);
|
||||||
|
bs->copy_mem(dest_start, src_start, pheader->file_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
console::print(L" entrypoint: 0x%lx\r\n", header->entrypoint);
|
program.phys_addr = reinterpret_cast<uintptr_t>(pages);
|
||||||
return reinterpret_cast<kernel::entrypoint>(header->entrypoint);
|
program.size = total_size;
|
||||||
|
program.virt_addr = prog_base;
|
||||||
|
program.entrypoint = header->entrypoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace loader
|
} // namespace loader
|
||||||
|
|||||||
@@ -5,18 +5,36 @@
|
|||||||
#include <uefi/boot_services.h>
|
#include <uefi/boot_services.h>
|
||||||
|
|
||||||
#include "kernel_args.h"
|
#include "kernel_args.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
|
|
||||||
|
namespace fs { class file; }
|
||||||
|
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
|
||||||
|
/// Load a file from disk into memory.
|
||||||
|
/// \arg disk The opened UEFI filesystem to load from
|
||||||
|
/// \arg name Name of the module (informational only)
|
||||||
|
/// \arg path Path on `disk` of the file to load
|
||||||
|
/// \arg type Memory type to use for allocation
|
||||||
|
buffer
|
||||||
|
load_file(
|
||||||
|
fs::file &disk,
|
||||||
|
const wchar_t *name,
|
||||||
|
const wchar_t *path,
|
||||||
|
uefi::memory_type type = uefi::memory_type::loader_data);
|
||||||
|
|
||||||
/// Parse and load an ELF file in memory into a loaded image.
|
/// Parse and load an ELF file in memory into a loaded image.
|
||||||
/// \arg data The start of the ELF file in memory
|
/// \arg program The program structure to fill
|
||||||
/// \arg size The size of the ELF file in memory
|
/// \arg data Buffer of the ELF file in memory
|
||||||
/// \arg args The kernel args, used for modifying page tables
|
/// \arg bs Boot services
|
||||||
/// \returns A descriptor defining the loaded image
|
void
|
||||||
kernel::entrypoint load(
|
load_program(
|
||||||
const void *data, size_t size,
|
kernel::args::program &program,
|
||||||
kernel::args::header *args,
|
const wchar_t *name,
|
||||||
|
buffer data,
|
||||||
uefi::boot_services *bs);
|
uefi::boot_services *bs);
|
||||||
|
|
||||||
} // namespace loader
|
} // namespace loader
|
||||||
|
|||||||
@@ -8,12 +8,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
#include "cpu/cpu.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
#include "kernel_args.h"
|
#include "kernel_args.h"
|
||||||
|
|
||||||
@@ -21,9 +23,24 @@ namespace kernel {
|
|||||||
#include "kernel_memory.h"
|
#include "kernel_memory.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace args = kernel::args;
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
|
|
||||||
constexpr int max_modules = 10; // Max modules to allocate room for
|
constexpr int max_modules = 5; // Max modules to allocate room for
|
||||||
|
constexpr int max_programs = 5; // Max programs to allocate room for
|
||||||
|
|
||||||
|
struct program_desc
|
||||||
|
{
|
||||||
|
const wchar_t *name;
|
||||||
|
const wchar_t *path;
|
||||||
|
};
|
||||||
|
|
||||||
|
const program_desc program_list[] = {
|
||||||
|
{L"kernel", L"jsix.elf"},
|
||||||
|
{L"null driver", L"nulldrv.elf"},
|
||||||
|
{L"fb driver", L"fb.elf"},
|
||||||
|
};
|
||||||
|
|
||||||
/// Change a pointer to point to the higher-half linear-offset version
|
/// Change a pointer to point to the higher-half linear-offset version
|
||||||
/// of the address it points to.
|
/// of the address it points to.
|
||||||
@@ -34,129 +51,158 @@ void change_pointer(T *&pointer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate space for kernel args. Allocates enough space from pool
|
/// Allocate space for kernel args. Allocates enough space from pool
|
||||||
/// memory for the args header and `max_modules` module headers.
|
/// memory for the args header and the module and program headers.
|
||||||
kernel::args::header *
|
args::header *
|
||||||
allocate_args_structure(
|
allocate_args_structure(
|
||||||
uefi::boot_services *bs,
|
uefi::boot_services *bs,
|
||||||
size_t max_modules)
|
size_t max_modules,
|
||||||
|
size_t max_programs)
|
||||||
{
|
{
|
||||||
status_line status(L"Setting up kernel args memory");
|
status_line status {L"Setting up kernel args memory"};
|
||||||
|
|
||||||
kernel::args::header *args = nullptr;
|
args::header *args = nullptr;
|
||||||
|
|
||||||
size_t args_size =
|
size_t args_size =
|
||||||
sizeof(kernel::args::header) + // The header itself
|
sizeof(args::header) + // The header itself
|
||||||
max_modules * sizeof(kernel::args::module); // The module structures
|
max_modules * sizeof(args::module) + // The module structures
|
||||||
|
max_programs * sizeof(args::program); // The program structures
|
||||||
|
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->allocate_pool(memory::args_type, args_size,
|
bs->allocate_pool(uefi::memory_type::loader_data, args_size,
|
||||||
reinterpret_cast<void**>(&args)),
|
reinterpret_cast<void**>(&args)),
|
||||||
L"Could not allocate argument memory");
|
L"Could not allocate argument memory");
|
||||||
|
|
||||||
bs->set_mem(args, args_size, 0);
|
bs->set_mem(args, args_size, 0);
|
||||||
|
|
||||||
args->modules =
|
args->modules =
|
||||||
reinterpret_cast<kernel::args::module*>(args + 1);
|
reinterpret_cast<args::module*>(args + 1);
|
||||||
args->num_modules = 0;
|
args->num_modules = 0;
|
||||||
|
|
||||||
|
args->programs =
|
||||||
|
reinterpret_cast<args::program*>(args->modules + max_modules);
|
||||||
|
args->num_programs = 0;
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load a file from disk into memory. Also adds an entry to the kernel
|
/// Add a module to the kernel args list
|
||||||
/// args module headers pointing at the loaded data.
|
inline void
|
||||||
/// \arg disk The opened UEFI filesystem to load from
|
add_module(args::header *args, args::mod_type type, buffer &data)
|
||||||
/// \arg args The kernel args header to update with module information
|
|
||||||
/// \arg name Name of the module (informational only)
|
|
||||||
/// \arg path Path on `disk` of the file to load
|
|
||||||
/// \arg type Type specifier of this module (eg, initrd or kernel)
|
|
||||||
kernel::args::module *
|
|
||||||
load_module(
|
|
||||||
fs::file &disk,
|
|
||||||
kernel::args::header *args,
|
|
||||||
const wchar_t *name,
|
|
||||||
const wchar_t *path,
|
|
||||||
kernel::args::mod_type type)
|
|
||||||
{
|
{
|
||||||
status_line status(L"Loading module", name);
|
args::module &m = args->modules[args->num_modules++];
|
||||||
|
m.type = type;
|
||||||
|
m.location = data.data;
|
||||||
|
m.size = data.size;
|
||||||
|
}
|
||||||
|
|
||||||
fs::file file = disk.open(path);
|
/// Check that all required cpu features are supported
|
||||||
kernel::args::module &module = args->modules[args->num_modules++];
|
void
|
||||||
module.type = type;
|
check_cpu_supported()
|
||||||
module.location = file.load(&module.size, memory::module_type);
|
{
|
||||||
|
status_line status {L"Checking CPU features"};
|
||||||
|
|
||||||
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, module.size);
|
cpu::cpu_id cpu;
|
||||||
return &module;
|
uint64_t missing = cpu.missing();
|
||||||
|
if (missing) {
|
||||||
|
#define CPU_FEATURE_OPT(...)
|
||||||
|
#define CPU_FEATURE_REQ(name, ...) \
|
||||||
|
if (!cpu.has_feature(cpu::feature::name)) { \
|
||||||
|
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
|
||||||
|
}
|
||||||
|
#include "cpu/features.inc"
|
||||||
|
#undef CPU_FEATURE_REQ
|
||||||
|
#undef CPU_FEATURE_OPT
|
||||||
|
|
||||||
|
error::raise(uefi::status::unsupported, L"CPU not supported");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main procedure for the portion of the loader that runs while
|
/// The main procedure for the portion of the loader that runs while
|
||||||
/// UEFI is still in control of the machine. (ie, while the loader still
|
/// UEFI is still in control of the machine. (ie, while the loader still
|
||||||
/// has access to boot services.
|
/// has access to boot services.
|
||||||
kernel::args::header *
|
args::header *
|
||||||
bootloader_main_uefi(
|
uefi_preboot(uefi::handle image, uefi::system_table *st)
|
||||||
uefi::handle image,
|
|
||||||
uefi::system_table *st,
|
|
||||||
console &con,
|
|
||||||
kernel::entrypoint *kentry)
|
|
||||||
{
|
{
|
||||||
error::uefi_handler handler(con);
|
status_line status {L"Performing UEFI pre-boot"};
|
||||||
status_line status(L"Performing UEFI pre-boot");
|
|
||||||
|
|
||||||
uefi::boot_services *bs = st->boot_services;
|
uefi::boot_services *bs = st->boot_services;
|
||||||
uefi::runtime_services *rs = st->runtime_services;
|
uefi::runtime_services *rs = st->runtime_services;
|
||||||
memory::init_pointer_fixup(bs, rs);
|
memory::init_pointer_fixup(bs, rs);
|
||||||
|
|
||||||
kernel::args::header *args =
|
args::header *args =
|
||||||
allocate_args_structure(bs, max_modules);
|
allocate_args_structure(bs, max_modules, max_programs);
|
||||||
|
|
||||||
args->magic = kernel::args::magic;
|
args->magic = args::magic;
|
||||||
args->version = kernel::args::version;
|
args->version = args::version;
|
||||||
args->runtime_services = rs;
|
args->runtime_services = rs;
|
||||||
args->acpi_table = hw::find_acpi_table(st);
|
args->acpi_table = hw::find_acpi_table(st);
|
||||||
|
paging::allocate_tables(args, bs);
|
||||||
|
|
||||||
memory::mark_pointer_fixup(&args->runtime_services);
|
memory::mark_pointer_fixup(&args->runtime_services);
|
||||||
|
|
||||||
fs::file disk = fs::get_boot_volume(image, bs);
|
fs::file disk = fs::get_boot_volume(image, bs);
|
||||||
load_module(disk, args, L"initrd", L"initrd.img", kernel::args::mod_type::initrd);
|
|
||||||
|
|
||||||
kernel::args::module *kernel =
|
buffer symbols = loader::load_file(disk, L"symbol table", L"symbol_table.dat",
|
||||||
load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::mod_type::kernel);
|
uefi::memory_type::loader_data);
|
||||||
|
add_module(args, args::mod_type::symbol_table, symbols);
|
||||||
|
|
||||||
paging::allocate_tables(args, bs);
|
for (auto &desc : program_list) {
|
||||||
*kentry = loader::load(kernel->location, kernel->size, args, bs);
|
buffer buf = loader::load_file(disk, desc.name, desc.path);
|
||||||
|
args::program &program = args->programs[args->num_programs++];
|
||||||
|
loader::load_program(program, desc.name, buf, bs);
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned i = 0; i < args->num_modules; ++i) {
|
for (unsigned i = 0; i < args->num_modules; ++i) {
|
||||||
kernel::args::module &mod = args->modules[i];
|
args::module &mod = args->modules[i];
|
||||||
change_pointer(mod.location);
|
change_pointer(mod.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memory::efi_mem_map
|
||||||
|
uefi_exit(args::header *args, uefi::handle image, uefi::boot_services *bs)
|
||||||
|
{
|
||||||
|
status_line status {L"Exiting UEFI"};
|
||||||
|
|
||||||
|
memory::efi_mem_map map =
|
||||||
|
memory::build_kernel_mem_map(args, bs);
|
||||||
|
|
||||||
|
try_or_raise(
|
||||||
|
bs->exit_boot_services(image, map.key),
|
||||||
|
L"Failed to exit boot services");
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace boot
|
} // namespace boot
|
||||||
|
|
||||||
/// The UEFI entrypoint for the loader.
|
/// The UEFI entrypoint for the loader.
|
||||||
extern "C" uefi::status
|
extern "C" uefi::status
|
||||||
efi_main(uefi::handle image_handle, uefi::system_table *st)
|
efi_main(uefi::handle image, uefi::system_table *st)
|
||||||
{
|
{
|
||||||
using namespace boot;
|
using namespace boot;
|
||||||
|
|
||||||
error::cpu_assert_handler handler;
|
|
||||||
console con(st->boot_services, st->con_out);
|
console con(st->boot_services, st->con_out);
|
||||||
|
check_cpu_supported();
|
||||||
|
|
||||||
kernel::entrypoint kentry = nullptr;
|
args::header *args = uefi_preboot(image, st);
|
||||||
kernel::args::header *args =
|
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
|
||||||
bootloader_main_uefi(image_handle, st, con, &kentry);
|
|
||||||
|
|
||||||
memory::efi_mem_map map =
|
args->video = con.fb();
|
||||||
memory::build_kernel_mem_map(args, st->boot_services);
|
status_bar status {con.fb()}; // Switch to fb status display
|
||||||
|
|
||||||
try_or_raise(
|
args::program &kernel = args->programs[0];
|
||||||
st->boot_services->exit_boot_services(image_handle, map.key),
|
paging::map_pages(args, kernel.phys_addr, kernel.virt_addr, kernel.size);
|
||||||
L"Failed to exit boot services");
|
kernel::entrypoint kentry =
|
||||||
|
reinterpret_cast<kernel::entrypoint>(kernel.entrypoint);
|
||||||
|
status.next();
|
||||||
|
|
||||||
memory::virtualize(args->pml4, map, st->runtime_services);
|
memory::virtualize(args->pml4, map, st->runtime_services);
|
||||||
|
status.next();
|
||||||
|
|
||||||
change_pointer(args->pml4);
|
change_pointer(args->pml4);
|
||||||
hw::setup_cr4();
|
hw::setup_cr4();
|
||||||
|
status.next();
|
||||||
|
|
||||||
kentry(args);
|
kentry(args);
|
||||||
debug_break();
|
debug_break();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
@@ -35,20 +36,28 @@ static const wchar_t *memory_type_names[] = {
|
|||||||
L"persistent memory"
|
L"persistent memory"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const wchar_t *kernel_memory_type_names[] = {
|
||||||
|
L"free",
|
||||||
|
L"pending",
|
||||||
|
L"acpi",
|
||||||
|
L"uefi_runtime",
|
||||||
|
L"mmio",
|
||||||
|
L"persistent"
|
||||||
|
};
|
||||||
|
|
||||||
static const wchar_t *
|
static const wchar_t *
|
||||||
memory_type_name(uefi::memory_type t)
|
memory_type_name(uefi::memory_type t)
|
||||||
{
|
{
|
||||||
if (t < uefi::memory_type::max_memory_type) {
|
if (t < uefi::memory_type::max_memory_type)
|
||||||
return memory_type_names[static_cast<uint32_t>(t)];
|
return memory_type_names[static_cast<uint32_t>(t)];
|
||||||
}
|
|
||||||
|
|
||||||
switch(t) {
|
return L"Bad Type Value";
|
||||||
case args_type: return L"jsix kernel args";
|
}
|
||||||
case module_type: return L"jsix bootloader module";
|
|
||||||
case kernel_type: return L"jsix kernel code";
|
static const wchar_t *
|
||||||
case table_type: return L"jsix page tables";
|
kernel_memory_type_name(kernel::args::mem_type t)
|
||||||
default: return L"Bad Type Value";
|
{
|
||||||
}
|
return kernel_memory_type_names[static_cast<uint32_t>(t)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -100,14 +109,15 @@ can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next)
|
|||||||
void
|
void
|
||||||
get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
|
get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line(L"Getting UEFI memory map");
|
size_t length = 0;
|
||||||
|
|
||||||
uefi::status status = bs->get_memory_map(
|
uefi::status status = bs->get_memory_map(
|
||||||
&map->length, nullptr, &map->key, &map->size, &map->version);
|
&length, nullptr, &map->key, &map->size, &map->version);
|
||||||
|
|
||||||
if (status != uefi::status::buffer_too_small)
|
if (status != uefi::status::buffer_too_small)
|
||||||
error::raise(status, L"Error getting memory map size");
|
error::raise(status, L"Error getting memory map size");
|
||||||
|
|
||||||
|
map->length = length;
|
||||||
|
|
||||||
if (allocate) {
|
if (allocate) {
|
||||||
map->length += 10*map->size;
|
map->length += 10*map->size;
|
||||||
|
|
||||||
@@ -126,29 +136,30 @@ get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
|
|||||||
efi_mem_map
|
efi_mem_map
|
||||||
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
||||||
{
|
{
|
||||||
status_line(L"Creating kernel memory map");
|
status_line status {L"Creating kernel memory map"};
|
||||||
|
|
||||||
efi_mem_map efi_map;
|
efi_mem_map map;
|
||||||
get_uefi_mappings(&efi_map, false, bs);
|
get_uefi_mappings(&map, false, bs);
|
||||||
|
|
||||||
size_t map_size = efi_map.num_entries() * sizeof(mem_entry);
|
size_t map_size = map.num_entries() * sizeof(mem_entry);
|
||||||
|
|
||||||
kernel::args::mem_entry *kernel_map = nullptr;
|
kernel::args::mem_entry *kernel_map = nullptr;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->allocate_pages(
|
bs->allocate_pages(
|
||||||
uefi::allocate_type::any_pages,
|
uefi::allocate_type::any_pages,
|
||||||
module_type,
|
uefi::memory_type::loader_data,
|
||||||
bytes_to_pages(map_size),
|
bytes_to_pages(map_size),
|
||||||
reinterpret_cast<void**>(&kernel_map)),
|
reinterpret_cast<void**>(&kernel_map)),
|
||||||
L"Error allocating kernel memory map module space");
|
L"Error allocating kernel memory map module space");
|
||||||
|
|
||||||
bs->set_mem(kernel_map, map_size, 0);
|
bs->set_mem(kernel_map, map_size, 0);
|
||||||
get_uefi_mappings(&efi_map, true, bs);
|
get_uefi_mappings(&map, true, bs);
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto desc : efi_map) {
|
for (auto desc : map) {
|
||||||
/*
|
/*
|
||||||
|
// EFI map dump
|
||||||
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
|
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
|
||||||
desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages);
|
desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages);
|
||||||
*/
|
*/
|
||||||
@@ -162,13 +173,16 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
case uefi::memory_type::loader_code:
|
case uefi::memory_type::loader_code:
|
||||||
case uefi::memory_type::loader_data:
|
|
||||||
case uefi::memory_type::boot_services_code:
|
case uefi::memory_type::boot_services_code:
|
||||||
case uefi::memory_type::boot_services_data:
|
case uefi::memory_type::boot_services_data:
|
||||||
case uefi::memory_type::conventional_memory:
|
case uefi::memory_type::conventional_memory:
|
||||||
type = mem_type::free;
|
type = mem_type::free;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case uefi::memory_type::loader_data:
|
||||||
|
type = mem_type::pending;
|
||||||
|
break;
|
||||||
|
|
||||||
case uefi::memory_type::runtime_services_code:
|
case uefi::memory_type::runtime_services_code:
|
||||||
case uefi::memory_type::runtime_services_data:
|
case uefi::memory_type::runtime_services_data:
|
||||||
type = mem_type::uefi_runtime;
|
type = mem_type::uefi_runtime;
|
||||||
@@ -187,22 +201,6 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
|||||||
type = mem_type::persistent;
|
type = mem_type::persistent;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case args_type:
|
|
||||||
type = mem_type::args;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case module_type:
|
|
||||||
type = mem_type::module;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kernel_type:
|
|
||||||
type = mem_type::kernel;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case table_type:
|
|
||||||
type = mem_type::table;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error::raise(
|
error::raise(
|
||||||
uefi::status::invalid_parameter,
|
uefi::status::invalid_parameter,
|
||||||
@@ -233,23 +231,18 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
|||||||
|
|
||||||
// Give just the actually-set entries in the header
|
// Give just the actually-set entries in the header
|
||||||
args->mem_map = kernel_map;
|
args->mem_map = kernel_map;
|
||||||
args->num_map_entries = i;
|
args->map_count = i;
|
||||||
|
|
||||||
// But pass the entire allocated area in a module as well
|
|
||||||
kernel::args::module &module = args->modules[args->num_modules++];
|
|
||||||
module.location = reinterpret_cast<void*>(kernel_map);
|
|
||||||
module.size = map_size;
|
|
||||||
module.type = kernel::args::mod_type::memory_map;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for (size_t i = 0; i<map.num_entries(); ++i) {
|
// kernel map dump
|
||||||
mem_entry &ent = kernel_map[i];
|
for (unsigned i = 0; i < args->map_count; ++i) {
|
||||||
console::print(L" Range %lx (%x) %d [%lu]\r\n",
|
const kernel::args::mem_entry &e = kernel_map[i];
|
||||||
ent.start, ent.attr, ent.type, ent.pages);
|
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
|
||||||
|
e.start, e.attr, e.type, kernel_memory_type_name(e.type), e.pages);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return efi_map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -18,28 +18,6 @@ inline constexpr size_t bytes_to_pages(size_t bytes) {
|
|||||||
return ((bytes - 1) / page_size) + 1;
|
return ((bytes - 1) / page_size) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \defgroup memory_types
|
|
||||||
/// Custom UEFI memory type values used for data being passed to the kernel
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Memory containing the kernel args structure
|
|
||||||
constexpr uefi::memory_type args_type =
|
|
||||||
static_cast<uefi::memory_type>(0x80000000);
|
|
||||||
|
|
||||||
/// Memory containing any loaded modules to be passed to the kernel
|
|
||||||
constexpr uefi::memory_type module_type =
|
|
||||||
static_cast<uefi::memory_type>(0x80000001);
|
|
||||||
|
|
||||||
/// Memory containing loaded kernel code and data sections
|
|
||||||
constexpr uefi::memory_type kernel_type =
|
|
||||||
static_cast<uefi::memory_type>(0x80000002);
|
|
||||||
|
|
||||||
/// Memory containing page tables set up by the loader
|
|
||||||
constexpr uefi::memory_type table_type =
|
|
||||||
static_cast<uefi::memory_type>(0x80000003);
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// \defgroup pointer_fixup
|
/// \defgroup pointer_fixup
|
||||||
/// Memory virtualization pointer fixup functions. Handles changing affected pointers
|
/// Memory virtualization pointer fixup functions. Handles changing affected pointers
|
||||||
/// when calling UEFI's `set_virtual_address_map` function to change the location of
|
/// when calling UEFI's `set_virtual_address_map` function to change the location of
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
#include "pointer_manipulation.h"
|
#include "pointer_manipulation.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
namespace boot {
|
namespace boot {
|
||||||
namespace paging {
|
namespace paging {
|
||||||
|
|
||||||
using memory::page_size;
|
using memory::page_size;
|
||||||
|
using ::memory::pml4e_kernel;
|
||||||
|
using ::memory::table_entries;
|
||||||
|
|
||||||
// Flags: 0 0 0 1 0 0 0 0 0 0 1 1 = 0x0103
|
// Flags: 0 0 0 1 0 0 0 0 0 0 1 1 = 0x0103
|
||||||
// IGN | | | | | | | | +- Present
|
// IGN | | | | | | | | +- Present
|
||||||
@@ -25,7 +28,7 @@ using memory::page_size;
|
|||||||
/// Page table entry flags for entries pointing at a page
|
/// Page table entry flags for entries pointing at a page
|
||||||
constexpr uint16_t page_flags = 0x103;
|
constexpr uint16_t page_flags = 0x103;
|
||||||
|
|
||||||
// Flags: 0 0 0 0 1 1 0 0 0 0 0 1 1 = 0x0183
|
// Flags: 0 0 0 0 1 1 0 0 0 1 0 1 1 = 0x018b
|
||||||
// | IGN | | | | | | | | +- Present
|
// | IGN | | | | | | | | +- Present
|
||||||
// | | | | | | | | +--- Writeable
|
// | | | | | | | | +--- Writeable
|
||||||
// | | | | | | | +----- Supervisor only
|
// | | | | | | | +----- Supervisor only
|
||||||
@@ -65,7 +68,7 @@ public:
|
|||||||
uintptr_t virt,
|
uintptr_t virt,
|
||||||
page_table *pml4,
|
page_table *pml4,
|
||||||
void *&page_cache,
|
void *&page_cache,
|
||||||
uint32_t &cache_count) :
|
size_t &cache_count) :
|
||||||
m_page_cache(page_cache),
|
m_page_cache(page_cache),
|
||||||
m_cache_count(cache_count)
|
m_cache_count(cache_count)
|
||||||
{
|
{
|
||||||
@@ -128,14 +131,14 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *&m_page_cache;
|
void *&m_page_cache;
|
||||||
uint32_t &m_cache_count;
|
size_t &m_cache_count;
|
||||||
page_table *m_table[D];
|
page_table *m_table[D];
|
||||||
uint16_t m_index[D];
|
uint16_t m_index[D];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_offset_mappings(page_table *pml4, void *&page_cache, uint32_t &num_pages)
|
add_offset_mappings(page_table *pml4, void *&page_cache, size_t &num_pages)
|
||||||
{
|
{
|
||||||
uintptr_t phys = 0;
|
uintptr_t phys = 0;
|
||||||
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
|
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
|
||||||
@@ -157,6 +160,16 @@ add_offset_mappings(page_table *pml4, void *&page_cache, uint32_t &num_pages)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_kernel_pds(page_table *pml4, void *&page_cache, size_t &num_pages)
|
||||||
|
{
|
||||||
|
for (unsigned i = pml4e_kernel; i < table_entries; ++i) {
|
||||||
|
pml4->set(i, page_cache, table_flags);
|
||||||
|
page_cache = offset_ptr<void>(page_cache, page_size);
|
||||||
|
num_pages--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
add_current_mappings(page_table *new_pml4)
|
add_current_mappings(page_table *new_pml4)
|
||||||
{
|
{
|
||||||
@@ -177,47 +190,53 @@ allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
|
|||||||
{
|
{
|
||||||
status_line status(L"Allocating initial page tables");
|
status_line status(L"Allocating initial page tables");
|
||||||
|
|
||||||
static constexpr size_t offset_map_tables = 128 + 1;
|
static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs
|
||||||
static constexpr size_t tables_needed = offset_map_tables + 49;
|
static constexpr size_t extra_tables = 49; // number of extra pages
|
||||||
|
|
||||||
|
// number of pages for kernelspace PDs + PML4
|
||||||
|
static constexpr size_t kernel_tables = pd_tables + 1;
|
||||||
|
|
||||||
|
static constexpr size_t tables_needed = kernel_tables + extra_tables;
|
||||||
|
|
||||||
void *addr = nullptr;
|
void *addr = nullptr;
|
||||||
try_or_raise(
|
try_or_raise(
|
||||||
bs->allocate_pages(
|
bs->allocate_pages(
|
||||||
uefi::allocate_type::any_pages,
|
uefi::allocate_type::any_pages,
|
||||||
memory::table_type,
|
uefi::memory_type::loader_data,
|
||||||
tables_needed,
|
tables_needed,
|
||||||
&addr),
|
&addr),
|
||||||
L"Error allocating page table pages.");
|
L"Error allocating page table pages.");
|
||||||
|
|
||||||
bs->set_mem(addr, tables_needed*page_size, 0);
|
bs->set_mem(addr, tables_needed*page_size, 0);
|
||||||
|
|
||||||
kernel::args::module &mod = args->modules[++args->num_modules];
|
|
||||||
mod.type = kernel::args::mod_type::page_tables;
|
|
||||||
mod.location = addr;
|
|
||||||
mod.size = tables_needed*page_size;
|
|
||||||
|
|
||||||
args->pml4 = addr;
|
|
||||||
args->num_free_tables = tables_needed - 1;
|
|
||||||
args->page_table_cache = offset_ptr<void>(addr, page_size);
|
|
||||||
|
|
||||||
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
||||||
add_offset_mappings(pml4, args->page_table_cache, args->num_free_tables);
|
|
||||||
|
|
||||||
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->num_free_tables);
|
args->pml4 = pml4;
|
||||||
|
args->table_count = tables_needed - 1;
|
||||||
|
args->page_tables = offset_ptr<void>(addr, page_size);
|
||||||
|
|
||||||
|
console::print(L" First page (pml4) at: 0x%lx\r\n", pml4);
|
||||||
|
|
||||||
|
add_kernel_pds(pml4, args->page_tables, args->table_count);
|
||||||
|
add_offset_mappings(pml4, args->page_tables, args->table_count);
|
||||||
|
|
||||||
|
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
map_pages(
|
map_pages(
|
||||||
page_table *pml4,
|
|
||||||
kernel::args::header *args,
|
kernel::args::header *args,
|
||||||
uintptr_t phys, uintptr_t virt,
|
uintptr_t phys, uintptr_t virt,
|
||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
|
paging::page_table *pml4 =
|
||||||
|
reinterpret_cast<paging::page_table*>(args->pml4);
|
||||||
|
|
||||||
size_t pages = memory::bytes_to_pages(size);
|
size_t pages = memory::bytes_to_pages(size);
|
||||||
page_entry_iterator<4> iterator{
|
page_entry_iterator<4> iterator{
|
||||||
virt, pml4,
|
virt, pml4,
|
||||||
args->page_table_cache,
|
args->page_tables,
|
||||||
args->num_free_tables};
|
args->table_count};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
*iterator = phys | page_flags;
|
*iterator = phys | page_flags;
|
||||||
|
|||||||
@@ -39,13 +39,11 @@ void allocate_tables(
|
|||||||
void add_current_mappings(page_table *new_pml4);
|
void add_current_mappings(page_table *new_pml4);
|
||||||
|
|
||||||
/// Map a physical address to a virtual address in the given page tables.
|
/// Map a physical address to a virtual address in the given page tables.
|
||||||
/// \arg pml4 The root of the set of page tables to be updated
|
/// \arg args The kernel args header, used for the page table cache and pml4
|
||||||
/// \arg args The kernel args header, used for the page table cache
|
|
||||||
/// \arg phys The phyiscal address to map in
|
/// \arg phys The phyiscal address to map in
|
||||||
/// \arg virt The virtual address to map in
|
/// \arg virt The virtual address to map in
|
||||||
/// \arg size The size in bytes of the mapping
|
/// \arg size The size in bytes of the mapping
|
||||||
void map_pages(
|
void map_pages(
|
||||||
page_table *pml4,
|
|
||||||
kernel::args::header *args,
|
kernel::args::header *args,
|
||||||
uintptr_t phys, uintptr_t virt,
|
uintptr_t phys, uintptr_t virt,
|
||||||
size_t bytes);
|
size_t bytes);
|
||||||
|
|||||||
263
src/boot/status.cpp
Normal file
263
src/boot/status.cpp
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
#include <uefi/types.h>
|
||||||
|
#include <uefi/graphics.h>
|
||||||
|
|
||||||
|
#include "console.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "kernel_args.h"
|
||||||
|
#include "status.h"
|
||||||
|
|
||||||
|
constexpr int num_boxes = 30;
|
||||||
|
|
||||||
|
namespace boot {
|
||||||
|
|
||||||
|
static constexpr int level_ok = 0;
|
||||||
|
static constexpr int level_warn = 1;
|
||||||
|
static constexpr int level_fail = 2;
|
||||||
|
|
||||||
|
static const wchar_t *level_tags[] = {
|
||||||
|
L" ok ",
|
||||||
|
L" warn ",
|
||||||
|
L"failed"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uefi::attribute level_colors[] = {
|
||||||
|
uefi::attribute::green,
|
||||||
|
uefi::attribute::brown,
|
||||||
|
uefi::attribute::light_red
|
||||||
|
};
|
||||||
|
|
||||||
|
status *status::s_current = nullptr;
|
||||||
|
unsigned status::s_current_type = 0;
|
||||||
|
unsigned status_bar::s_count = 0;
|
||||||
|
|
||||||
|
status_line::status_line(const wchar_t *message, const wchar_t *context) :
|
||||||
|
m_level(level_ok),
|
||||||
|
m_depth(0),
|
||||||
|
m_outer(nullptr)
|
||||||
|
{
|
||||||
|
if (status::s_current_type == status_line::type) {
|
||||||
|
m_outer = static_cast<status_line*>(s_current);
|
||||||
|
m_depth = (m_outer ? 1 + m_outer->m_depth : 0);
|
||||||
|
}
|
||||||
|
s_current = this;
|
||||||
|
s_current_type = status_line::type;
|
||||||
|
|
||||||
|
auto out = console::get().m_out;
|
||||||
|
m_line = out->mode->cursor_row;
|
||||||
|
|
||||||
|
int indent = 2 * m_depth;
|
||||||
|
out->set_cursor_position(indent, m_line);
|
||||||
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
|
out->output_string(message);
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
out->output_string(L": ");
|
||||||
|
out->output_string(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
out->output_string(L"\r\n");
|
||||||
|
print_status_tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
status_line::~status_line()
|
||||||
|
{
|
||||||
|
if (s_current != this)
|
||||||
|
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
|
||||||
|
|
||||||
|
if (m_outer && m_level > m_outer->m_level) {
|
||||||
|
m_outer->m_level = m_level;
|
||||||
|
m_outer->print_status_tag();
|
||||||
|
}
|
||||||
|
s_current = m_outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_line::print_status_tag()
|
||||||
|
{
|
||||||
|
auto out = console::get().m_out;
|
||||||
|
int row = out->mode->cursor_row;
|
||||||
|
int col = out->mode->cursor_column;
|
||||||
|
|
||||||
|
uefi::attribute color = level_colors[m_level];
|
||||||
|
const wchar_t *tag = level_tags[m_level];
|
||||||
|
|
||||||
|
out->set_cursor_position(50, m_line);
|
||||||
|
|
||||||
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
|
out->output_string(L"[");
|
||||||
|
out->set_attribute(color);
|
||||||
|
out->output_string(tag);
|
||||||
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
|
out->output_string(L"]\r\n");
|
||||||
|
|
||||||
|
out->set_cursor_position(col, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_line::do_warn(const wchar_t *message, uefi::status status)
|
||||||
|
{
|
||||||
|
auto out = console::get().m_out;
|
||||||
|
int row = out->mode->cursor_row;
|
||||||
|
|
||||||
|
if (m_level < level_warn) {
|
||||||
|
m_level = level_warn;
|
||||||
|
print_status_tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
int indent = 2 + 2 * m_depth;
|
||||||
|
out->set_cursor_position(indent, row);
|
||||||
|
out->set_attribute(uefi::attribute::yellow);
|
||||||
|
out->output_string(message);
|
||||||
|
|
||||||
|
const wchar_t *error = error::message(status);
|
||||||
|
if (error) {
|
||||||
|
out->output_string(L": ");
|
||||||
|
out->output_string(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
|
out->output_string(L"\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_line::do_fail(const wchar_t *message, uefi::status status)
|
||||||
|
{
|
||||||
|
auto out = console::get().m_out;
|
||||||
|
int row = out->mode->cursor_row;
|
||||||
|
|
||||||
|
if (m_level < level_fail) {
|
||||||
|
m_level = level_fail;
|
||||||
|
print_status_tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
int indent = 2 + 2 * m_depth;
|
||||||
|
out->set_cursor_position(indent, row);
|
||||||
|
out->set_attribute(uefi::attribute::red);
|
||||||
|
out->output_string(message);
|
||||||
|
|
||||||
|
const wchar_t *error = error::message(status);
|
||||||
|
if (error) {
|
||||||
|
out->output_string(L": ");
|
||||||
|
out->output_string(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
out->set_attribute(uefi::attribute::light_gray);
|
||||||
|
out->output_string(L"\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
status_bar::status_bar(kernel::args::framebuffer const &fb) :
|
||||||
|
m_outer(nullptr)
|
||||||
|
{
|
||||||
|
m_size = (fb.vertical / num_boxes) - 1;
|
||||||
|
m_top = fb.vertical - m_size;
|
||||||
|
m_horiz = fb.horizontal;
|
||||||
|
m_fb = reinterpret_cast<uint32_t*>(fb.phys_addr);
|
||||||
|
m_type = static_cast<uint16_t>(fb.type);
|
||||||
|
next();
|
||||||
|
|
||||||
|
if (status::s_current_type == status_bar::type)
|
||||||
|
m_outer = static_cast<status_bar*>(s_current);
|
||||||
|
s_current = this;
|
||||||
|
s_current_type = status_bar::type;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_bar::~status_bar()
|
||||||
|
{
|
||||||
|
if (s_current != this)
|
||||||
|
error::raise(uefi::status::unsupported, L"Destroying non-current status_bar");
|
||||||
|
draw_box();
|
||||||
|
s_current = m_outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_bar::do_warn(const wchar_t *message, uefi::status status)
|
||||||
|
{
|
||||||
|
m_status = status;
|
||||||
|
if (m_level < level_warn) {
|
||||||
|
m_level = level_warn;
|
||||||
|
draw_box();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_bar::do_fail(const wchar_t *message, uefi::status status)
|
||||||
|
{
|
||||||
|
m_status = status;
|
||||||
|
if (m_level < level_fail) {
|
||||||
|
m_level = level_fail;
|
||||||
|
draw_box();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
make_color(uint8_t r, uint8_t g, uint8_t b, uint16_t type)
|
||||||
|
{
|
||||||
|
switch (static_cast<kernel::args::fb_type>(type)) {
|
||||||
|
case kernel::args::fb_type::bgr8:
|
||||||
|
return
|
||||||
|
(static_cast<uint32_t>(b) << 0) |
|
||||||
|
(static_cast<uint32_t>(g) << 8) |
|
||||||
|
(static_cast<uint32_t>(r) << 16);
|
||||||
|
|
||||||
|
case kernel::args::fb_type::rgb8:
|
||||||
|
return
|
||||||
|
(static_cast<uint32_t>(r) << 0) |
|
||||||
|
(static_cast<uint32_t>(g) << 8) |
|
||||||
|
(static_cast<uint32_t>(b) << 16);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_bar::draw_box()
|
||||||
|
{
|
||||||
|
static const uint32_t colors[] = {0x909090, 0xf0f0f0};
|
||||||
|
constexpr unsigned ncolors = sizeof(colors) / sizeof(uint32_t);
|
||||||
|
|
||||||
|
if (m_fb == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned x0 = m_current * m_size;
|
||||||
|
unsigned x1 = x0 + m_size - 3;
|
||||||
|
unsigned y0 = m_top;
|
||||||
|
unsigned y1 = m_top + m_size - 3;
|
||||||
|
|
||||||
|
uint32_t color = 0;
|
||||||
|
switch (m_level) {
|
||||||
|
case level_ok:
|
||||||
|
color = colors[m_current % ncolors];
|
||||||
|
break;
|
||||||
|
case level_warn:
|
||||||
|
color = make_color(0xff, 0xb2, 0x34, m_type);
|
||||||
|
break;
|
||||||
|
case level_fail:
|
||||||
|
color = make_color(0xfb, 0x0a, 0x1e, m_type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned y = y0; y < y1; ++y)
|
||||||
|
for (unsigned x = x0; x < x1; ++x)
|
||||||
|
m_fb[y * m_horiz + x] = color;
|
||||||
|
|
||||||
|
if (m_level > level_ok) {
|
||||||
|
unsigned nbars = static_cast<uint64_t>(m_status) & 0xffff;
|
||||||
|
constexpr unsigned bar_height = 4;
|
||||||
|
|
||||||
|
for (unsigned i = 1; i <= nbars; ++i) {
|
||||||
|
y0 = m_top - 2 * i * bar_height;
|
||||||
|
y1 = y0 + bar_height;
|
||||||
|
|
||||||
|
for (unsigned y = y0; y < y1; ++y)
|
||||||
|
for (unsigned x = x0; x < x1; ++x)
|
||||||
|
m_fb[y * m_horiz + x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace boot
|
||||||
122
src/boot/status.h
Normal file
122
src/boot/status.h
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/// \file status.h
|
||||||
|
/// Status message and indicator handling
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <uefi/types.h>
|
||||||
|
|
||||||
|
namespace kernel {
|
||||||
|
namespace args {
|
||||||
|
class framebuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace boot {
|
||||||
|
|
||||||
|
// Abstract base class for status reporters.
|
||||||
|
class status
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void do_warn(const wchar_t *message, uefi::status status) = 0;
|
||||||
|
virtual void do_fail(const wchar_t *message, uefi::status status) = 0;
|
||||||
|
|
||||||
|
/// Set the state to warning, and print a message. If the state is already at
|
||||||
|
/// warning or error, the state is unchanged but the message is still printed.
|
||||||
|
/// \arg message The warning message to print, if text is supported
|
||||||
|
/// \arg status If set, the error or warning code that should be represented
|
||||||
|
/// \returns True if there was a status handler to display the warning
|
||||||
|
inline static bool warn(const wchar_t *message, uefi::status status = uefi::status::success) {
|
||||||
|
if (!s_current) return false;
|
||||||
|
s_current->do_warn(message, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the state to error, and print a message. If the state is already at
|
||||||
|
/// error, the state is unchanged but the message is still printed.
|
||||||
|
/// \arg message The error message to print, if text is supported
|
||||||
|
/// \arg status The error or warning code that should be represented
|
||||||
|
/// \returns True if there was a status handler to display the failure
|
||||||
|
inline static bool fail(const wchar_t *message, uefi::status status) {
|
||||||
|
if (!s_current) return false;
|
||||||
|
s_current->do_fail(message, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static status *s_current;
|
||||||
|
static unsigned s_current_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Scoped status line reporter. Prints a message and an "OK" if no errors
|
||||||
|
/// or warnings were reported before destruction, otherwise reports the
|
||||||
|
/// error or warning.
|
||||||
|
class status_line :
|
||||||
|
public status
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr static unsigned type = 1;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg message Description of the operation in progress
|
||||||
|
/// \arg context If non-null, printed after `message` and a colon
|
||||||
|
status_line(const wchar_t *message, const wchar_t *context = nullptr);
|
||||||
|
~status_line();
|
||||||
|
|
||||||
|
virtual void do_warn(const wchar_t *message, uefi::status status) override;
|
||||||
|
virtual void do_fail(const wchar_t *message, uefi::status status) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void print_status_tag();
|
||||||
|
|
||||||
|
size_t m_line;
|
||||||
|
int m_level;
|
||||||
|
int m_depth;
|
||||||
|
|
||||||
|
status_line *m_outer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Scoped status bar reporter. Draws a row of boxes along the bottom of
|
||||||
|
/// the screen, turning one red if there's an error in that step.
|
||||||
|
class status_bar :
|
||||||
|
public status
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr static unsigned type = 2;
|
||||||
|
|
||||||
|
using framebuffer = kernel::args::framebuffer;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg fb The framebuffer descriptor to draw to
|
||||||
|
status_bar(kernel::args::framebuffer const &fb);
|
||||||
|
~status_bar();
|
||||||
|
|
||||||
|
virtual void do_warn(const wchar_t *message, uefi::status status) override;
|
||||||
|
virtual void do_fail(const wchar_t *message, uefi::status status) override;
|
||||||
|
|
||||||
|
inline void next() {
|
||||||
|
m_current = s_count++;
|
||||||
|
m_level = 0;
|
||||||
|
m_status = uefi::status::success;
|
||||||
|
draw_box();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void draw_box();
|
||||||
|
|
||||||
|
uint32_t *m_fb;
|
||||||
|
uint32_t m_size;
|
||||||
|
uint32_t m_top;
|
||||||
|
uint32_t m_horiz;
|
||||||
|
|
||||||
|
int m_level;
|
||||||
|
uefi::status m_status;
|
||||||
|
|
||||||
|
uint16_t m_type;
|
||||||
|
uint16_t m_current;
|
||||||
|
|
||||||
|
status_bar *m_outer;
|
||||||
|
|
||||||
|
static unsigned s_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace boot
|
||||||
13
src/boot/types.h
Normal file
13
src/boot/types.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/// \file types.h
|
||||||
|
/// Definitions of shared types used throughout the bootloader
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace boot {
|
||||||
|
|
||||||
|
struct buffer
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
void *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace boot
|
||||||
390
src/drivers/fb/default_font.inc
Normal file
390
src/drivers/fb/default_font.inc
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
0x72, 0xb5, 0x4a, 0x86, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x7e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
|
||||||
|
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
|
||||||
|
0x22, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x72, 0x8c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,
|
||||||
|
0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x7e, 0x24, 0x24,
|
||||||
|
0x24, 0x7e, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
|
||||||
|
0x1e, 0x20, 0x20, 0x1c, 0x02, 0x02, 0x3c, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x30, 0x49, 0x4a, 0x34, 0x08, 0x16, 0x29, 0x49, 0x06,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x30, 0x31,
|
||||||
|
0x49, 0x46, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10,
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08,
|
||||||
|
0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x24, 0x18, 0x7e, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x18, 0x18, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08,
|
||||||
|
0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
|
||||||
|
0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x02, 0x04,
|
||||||
|
0x08, 0x10, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
|
||||||
|
0x04, 0x08, 0x1c, 0x02, 0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x04, 0x0c, 0x14, 0x24, 0x44, 0x7e, 0x04, 0x04, 0x04,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x7c, 0x02,
|
||||||
|
0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
|
||||||
|
0x20, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7e, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c,
|
||||||
|
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
|
||||||
|
0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x04, 0x38, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
|
||||||
|
0x00, 0x00, 0x18, 0x18, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04,
|
||||||
|
0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
|
||||||
|
0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42,
|
||||||
|
0x02, 0x04, 0x08, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1c, 0x22, 0x41, 0x4f, 0x51, 0x51, 0x51, 0x53, 0x4d, 0x40,
|
||||||
|
0x20, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42,
|
||||||
|
0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
|
||||||
|
0x42, 0x42, 0x42, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x1e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x44, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
|
||||||
|
0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x46,
|
||||||
|
0x42, 0x42, 0x22, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||||
|
0x02, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
|
||||||
|
0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x55, 0x49, 0x49,
|
||||||
|
0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
|
||||||
|
0x62, 0x52, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c,
|
||||||
|
0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
|
||||||
|
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x04, 0x02, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x48, 0x44, 0x42, 0x42,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x40, 0x20, 0x18,
|
||||||
|
0x04, 0x02, 0x02, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42,
|
||||||
|
0x24, 0x24, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
|
||||||
|
0x41, 0x41, 0x41, 0x49, 0x49, 0x49, 0x55, 0x63, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x41,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
|
||||||
|
0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x1e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||||
|
0x10, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10,
|
||||||
|
0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x78, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7c, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3c, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x02, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42,
|
||||||
|
0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x10,
|
||||||
|
0x10, 0x7e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
|
||||||
|
0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7c, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
|
||||||
|
0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x04, 0x04, 0x00, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||||
|
0x04, 0x04, 0x38, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x44, 0x48, 0x50,
|
||||||
|
0x70, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7c,
|
||||||
|
0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x2e, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x20, 0x18, 0x04, 0x02, 0x7c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x7e, 0x10, 0x10,
|
||||||
|
0x10, 0x10, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41,
|
||||||
|
0x49, 0x49, 0x55, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
|
||||||
|
0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x04, 0x08,
|
||||||
|
0x10, 0x20, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x10, 0x10,
|
||||||
|
0x10, 0x10, 0x10, 0xe0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e, 0x00, 0x00,
|
||||||
|
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x00, 0x00, 0x00, 0x70, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x49,
|
||||||
|
0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,
|
||||||
|
0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x08, 0x08,
|
||||||
|
0x1c, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1c, 0x08, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1c, 0x22, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x72, 0x8c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x24, 0x24,
|
||||||
|
0x24, 0x3c, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
|
||||||
|
0x22, 0x14, 0x08, 0x3e, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x4d, 0x51, 0x51, 0x4d, 0x41, 0x22,
|
||||||
|
0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x09, 0x12, 0x24, 0x48, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x08, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x24, 0x12, 0x09, 0x12, 0x24, 0x48,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00,
|
||||||
|
0x00, 0x08, 0x10, 0x20, 0x40, 0x42, 0x3c, 0x00, 0x20, 0x10, 0x00, 0x18,
|
||||||
|
0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x08, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24,
|
||||||
|
0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x18,
|
||||||
|
0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x24, 0x24, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x24,
|
||||||
|
0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
|
||||||
|
0x14, 0x14, 0x24, 0x27, 0x3c, 0x44, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x1e,
|
||||||
|
0x08, 0x08, 0x30, 0x00, 0x20, 0x10, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c,
|
||||||
|
0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x7e,
|
||||||
|
0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c,
|
||||||
|
0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x3e,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x08, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x3e,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x3c, 0x22, 0x21, 0x21, 0x79, 0x21, 0x21, 0x22, 0x3c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x42, 0x62, 0x52, 0x4a, 0x46,
|
||||||
|
0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x1c,
|
||||||
|
0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x08, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41,
|
||||||
|
0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x1c,
|
||||||
|
0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x24, 0x24, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x14,
|
||||||
|
0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c,
|
||||||
|
0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x42, 0x3c, 0x40, 0x00, 0x00, 0x00,
|
||||||
|
0x20, 0x10, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x24, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
|
||||||
|
0x40, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x3c, 0x42, 0x44, 0x4c, 0x42, 0x42, 0x42, 0x44, 0x58,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x3c, 0x02, 0x02,
|
||||||
|
0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08,
|
||||||
|
0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x02, 0x02,
|
||||||
|
0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24,
|
||||||
|
0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x24, 0x24, 0x18, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x09, 0x39,
|
||||||
|
0x4f, 0x48, 0x48, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x3c, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3c, 0x08, 0x08, 0x30, 0x00,
|
||||||
|
0x00, 0x00, 0x20, 0x10, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x3c, 0x42, 0x42,
|
||||||
|
0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00,
|
||||||
|
0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x24, 0x24, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x38, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08,
|
||||||
|
0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x18, 0x24, 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x38, 0x08, 0x08,
|
||||||
|
0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x1a,
|
||||||
|
0x01, 0x1d, 0x23, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x32, 0x4c, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x3c, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08,
|
||||||
|
0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24,
|
||||||
|
0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x18, 0x18,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c, 0x46, 0x4a,
|
||||||
|
0x52, 0x62, 0x42, 0x3c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10,
|
||||||
|
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x00, 0x42, 0x42, 0x42,
|
||||||
|
0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24,
|
||||||
|
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
|
||||||
|
0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x5c, 0x62, 0x41,
|
||||||
|
0x41, 0x41, 0x62, 0x5c, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x24, 0x24,
|
||||||
|
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00,
|
||||||
|
0xff, 0xff, 0xe2, 0x96, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xb0,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xe2, 0x94, 0x98, 0xff, 0xe2, 0x94, 0x90, 0xff,
|
||||||
|
0xe2, 0x94, 0x8c, 0xff, 0xe2, 0x94, 0x94, 0xff, 0xe2, 0x94, 0xbc, 0xff,
|
||||||
|
0xff, 0xff, 0xe2, 0x94, 0x80, 0xff, 0xff, 0xff, 0xe2, 0x94, 0x9c, 0xff,
|
||||||
|
0xe2, 0x94, 0xa4, 0xff, 0xe2, 0x94, 0xb4, 0xff, 0xe2, 0x94, 0xac, 0xff,
|
||||||
|
0xe2, 0x94, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xa3, 0xff, 0xff,
|
||||||
|
0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, 0x24, 0xff, 0x25, 0xff,
|
||||||
|
0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff, 0x2a, 0xff, 0x2b, 0xff,
|
||||||
|
0x2c, 0xff, 0x2d, 0xff, 0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff,
|
||||||
|
0x32, 0xff, 0x33, 0xff, 0x34, 0xff, 0x35, 0xff, 0x36, 0xff, 0x37, 0xff,
|
||||||
|
0x38, 0xff, 0x39, 0xff, 0x3a, 0xff, 0x3b, 0xff, 0x3c, 0xff, 0x3d, 0xff,
|
||||||
|
0x3e, 0xff, 0x3f, 0xff, 0x40, 0xff, 0x41, 0xff, 0x42, 0xff, 0x43, 0xff,
|
||||||
|
0x44, 0xff, 0x45, 0xff, 0x46, 0xff, 0x47, 0xff, 0x48, 0xff, 0x49, 0xff,
|
||||||
|
0x4a, 0xff, 0x4b, 0xff, 0x4c, 0xff, 0x4d, 0xff, 0x4e, 0xff, 0x4f, 0xff,
|
||||||
|
0x50, 0xff, 0x51, 0xff, 0x52, 0xff, 0x53, 0xff, 0x54, 0xff, 0x55, 0xff,
|
||||||
|
0x56, 0xff, 0x57, 0xff, 0x58, 0xff, 0x59, 0xff, 0x5a, 0xff, 0x5b, 0xff,
|
||||||
|
0x5c, 0xff, 0x5d, 0xff, 0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff,
|
||||||
|
0x62, 0xff, 0x63, 0xff, 0x64, 0xff, 0x65, 0xff, 0x66, 0xff, 0x67, 0xff,
|
||||||
|
0x68, 0xff, 0x69, 0xff, 0x6a, 0xff, 0x6b, 0xff, 0x6c, 0xff, 0x6d, 0xff,
|
||||||
|
0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff, 0x72, 0xff, 0x73, 0xff,
|
||||||
|
0x74, 0xff, 0x75, 0xff, 0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff,
|
||||||
|
0x7a, 0xff, 0x7b, 0xff, 0x7c, 0xff, 0x7d, 0xff, 0x7e, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xa0, 0xff, 0xc2, 0xa1,
|
||||||
|
0xff, 0xc2, 0xa2, 0xff, 0xc2, 0xa3, 0xff, 0xc2, 0xa4, 0xff, 0xc2, 0xa5,
|
||||||
|
0xff, 0xc2, 0xa6, 0xff, 0xff, 0xc2, 0xa8, 0xff, 0xc2, 0xa9, 0xff, 0xff,
|
||||||
|
0xc2, 0xab, 0xff, 0xff, 0xc2, 0xad, 0xff, 0xff, 0xff, 0xc2, 0xb0, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xb8, 0xff, 0xff, 0xff,
|
||||||
|
0xc2, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xbf, 0xff, 0xc3, 0x80, 0xff,
|
||||||
|
0xc3, 0x81, 0xff, 0xc3, 0x82, 0xff, 0xc3, 0x83, 0xff, 0xc3, 0x84, 0xff,
|
||||||
|
0xc3, 0x85, 0xff, 0xc3, 0x86, 0xff, 0xc3, 0x87, 0xff, 0xc3, 0x88, 0xff,
|
||||||
|
0xc3, 0x89, 0xff, 0xc3, 0x8a, 0xff, 0xc3, 0x8b, 0xff, 0xc3, 0x8c, 0xff,
|
||||||
|
0xc3, 0x8d, 0xff, 0xc3, 0x8e, 0xff, 0xc3, 0x8f, 0xff, 0xc3, 0x90, 0xff,
|
||||||
|
0xc3, 0x91, 0xff, 0xc3, 0x92, 0xff, 0xc3, 0x93, 0xff, 0xc3, 0x94, 0xff,
|
||||||
|
0xc3, 0x95, 0xff, 0xc3, 0x96, 0xff, 0xc3, 0x97, 0xff, 0xc3, 0x98, 0xff,
|
||||||
|
0xc3, 0x99, 0xff, 0xc3, 0x9a, 0xff, 0xc3, 0x9b, 0xff, 0xc3, 0x9c, 0xff,
|
||||||
|
0xc3, 0x9d, 0xff, 0xc3, 0x9e, 0xff, 0xc3, 0x9f, 0xff, 0xc3, 0xa0, 0xff,
|
||||||
|
0xc3, 0xa1, 0xff, 0xc3, 0xa2, 0xff, 0xc3, 0xa3, 0xff, 0xc3, 0xa4, 0xff,
|
||||||
|
0xc3, 0xa5, 0xff, 0xc3, 0xa6, 0xff, 0xc3, 0xa7, 0xff, 0xc3, 0xa8, 0xff,
|
||||||
|
0xc3, 0xa9, 0xff, 0xc3, 0xaa, 0xff, 0xc3, 0xab, 0xff, 0xc3, 0xac, 0xff,
|
||||||
|
0xc3, 0xad, 0xff, 0xc3, 0xae, 0xff, 0xc3, 0xaf, 0xff, 0xc3, 0xb0, 0xff,
|
||||||
|
0xc3, 0xb1, 0xff, 0xc3, 0xb2, 0xff, 0xc3, 0xb3, 0xff, 0xc3, 0xb4, 0xff,
|
||||||
|
0xc3, 0xb5, 0xff, 0xc3, 0xb6, 0xff, 0xc3, 0xb7, 0xff, 0xc3, 0xb8, 0xff,
|
||||||
|
0xc3, 0xb9, 0xff, 0xc3, 0xba, 0xff, 0xc3, 0xbb, 0xff, 0xc3, 0xbc, 0xff,
|
||||||
|
0xc3, 0xbd, 0xff, 0xc3, 0xbe, 0xff, 0xc3, 0xbf, 0xff
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "kutil/assert.h"
|
#include <assert.h>
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
|
||||||
|
|
||||||
/* PSF2 header format
|
/* PSF2 header format
|
||||||
* Taken from the Linux KBD documentation
|
* Taken from the Linux KBD documentation
|
||||||
* http://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
|
* http://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
|
||||||
@@ -27,43 +28,51 @@ struct psf2_header {
|
|||||||
uint32_t height, width; // max dimensions of glyphs
|
uint32_t height, width; // max dimensions of glyphs
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const uint8_t default_font[] = {
|
||||||
|
// xxd -i < font_file.psf > default_font.inc
|
||||||
|
#include "default_font.inc"
|
||||||
|
};
|
||||||
|
|
||||||
font::font(void const *data) :
|
font::font(void const *data) :
|
||||||
m_size(0, 0),
|
m_sizex {0},
|
||||||
m_count(0),
|
m_sizey {0},
|
||||||
m_data(nullptr)
|
m_count {0},
|
||||||
|
m_data {nullptr}
|
||||||
{
|
{
|
||||||
|
if (!data)
|
||||||
|
data = default_font;
|
||||||
|
|
||||||
psf2_header const *psf2 = static_cast<psf2_header const *>(data);
|
psf2_header const *psf2 = static_cast<psf2_header const *>(data);
|
||||||
for (int i = 0; i < sizeof(magic); ++i) {
|
for (int i = 0; i < sizeof(magic); ++i) {
|
||||||
kassert(psf2->magic[i] == magic[i], "Bad font magic number.");
|
assert(psf2->magic[i] == magic[i] && "Bad font magic number.");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data = static_cast<uint8_t const *>(data) + psf2->header_size;
|
m_data = static_cast<uint8_t const *>(data) + psf2->header_size;
|
||||||
m_size.x = psf2->width;
|
m_sizex = psf2->width;
|
||||||
m_size.y = psf2->height;
|
m_sizey = psf2->height;
|
||||||
m_count = psf2->length;
|
m_count = psf2->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
font::draw_glyph(
|
font::draw_glyph(
|
||||||
screen *s,
|
screen &s,
|
||||||
uint32_t glyph,
|
uint32_t glyph,
|
||||||
screen::pixel_t fg,
|
screen::pixel_t fg,
|
||||||
screen::pixel_t bg,
|
screen::pixel_t bg,
|
||||||
unsigned x,
|
unsigned x,
|
||||||
unsigned y) const
|
unsigned y) const
|
||||||
{
|
{
|
||||||
unsigned bwidth = (m_size.x+7)/8;
|
unsigned bwidth = (m_sizex+7)/8;
|
||||||
uint8_t const *data = m_data + (glyph * glyph_bytes());
|
uint8_t const *data = m_data + (glyph * glyph_bytes());
|
||||||
|
|
||||||
for (int dy = 0; dy < m_size.y; ++dy) {
|
for (int dy = 0; dy < m_sizey; ++dy) {
|
||||||
for (int dx = 0; dx < bwidth; ++dx) {
|
for (int dx = 0; dx < bwidth; ++dx) {
|
||||||
uint8_t byte = data[dy * bwidth + dx];
|
uint8_t byte = data[dy * bwidth + dx];
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if (dx*8 + i >= m_size.x) continue;
|
if (dx*8 + i >= m_sizex) break;
|
||||||
const uint8_t mask = 1 << (7-i);
|
const uint8_t mask = 1 << (7-i);
|
||||||
uint32_t c = (byte & mask) ? fg : bg;
|
uint32_t c = (byte & mask) ? fg : bg;
|
||||||
s->draw_pixel(x + dx*8 + i, y + dy, c);
|
s.draw_pixel(x + dx*8 + i, y + dy, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
33
src/drivers/fb/font.h
Normal file
33
src/drivers/fb/font.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
class font
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg data The font data to load. If null, will load the default
|
||||||
|
/// built-in font.
|
||||||
|
font(void const *data = nullptr);
|
||||||
|
|
||||||
|
unsigned glyph_bytes() const { return m_sizey * ((m_sizex + 7) / 8); }
|
||||||
|
unsigned count() const { return m_count; }
|
||||||
|
unsigned width() const { return m_sizex; }
|
||||||
|
unsigned height() const { return m_sizey; }
|
||||||
|
bool valid() const { return m_count > 0; }
|
||||||
|
|
||||||
|
void draw_glyph(
|
||||||
|
screen &s,
|
||||||
|
uint32_t glyph,
|
||||||
|
screen::pixel_t fg,
|
||||||
|
screen::pixel_t bg,
|
||||||
|
unsigned x,
|
||||||
|
unsigned y) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned m_sizex, m_sizey;
|
||||||
|
unsigned m_count;
|
||||||
|
uint8_t const *m_data;
|
||||||
|
};
|
||||||
|
|
||||||
105
src/drivers/fb/main.cpp
Normal file
105
src/drivers/fb/main.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "j6/init.h"
|
||||||
|
#include "j6/errors.h"
|
||||||
|
#include "j6/signals.h"
|
||||||
|
#include "j6/types.h"
|
||||||
|
|
||||||
|
#include <j6libc/syscalls.h>
|
||||||
|
|
||||||
|
#include "font.h"
|
||||||
|
#include "screen.h"
|
||||||
|
#include "scrollback.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
int main(int, const char **);
|
||||||
|
void _get_init(size_t *initc, struct j6_init_value **initv);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern j6_handle_t __handle_sys;
|
||||||
|
|
||||||
|
struct entry
|
||||||
|
{
|
||||||
|
uint8_t bytes;
|
||||||
|
uint8_t area;
|
||||||
|
uint8_t severity;
|
||||||
|
uint8_t sequence;
|
||||||
|
char message[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
_syscall_system_log("fb driver starting");
|
||||||
|
|
||||||
|
size_t initc = 0;
|
||||||
|
j6_init_value *initv = nullptr;
|
||||||
|
_get_init(&initc, &initv);
|
||||||
|
|
||||||
|
j6_init_framebuffer *fb = nullptr;
|
||||||
|
for (unsigned i = 0; i < initc; ++i) {
|
||||||
|
if (initv[i].type == j6_init_desc_framebuffer) {
|
||||||
|
fb = reinterpret_cast<j6_init_framebuffer*>(initv[i].value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fb || fb->addr == nullptr) {
|
||||||
|
_syscall_system_log("fb driver didn't find a framebuffer, exiting");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const screen::pixel_order order = (fb->flags & 1) ?
|
||||||
|
screen::pixel_order::bgr8 : screen::pixel_order::rgb8;
|
||||||
|
|
||||||
|
screen scr(fb->addr, fb->horizontal, fb->vertical, order);
|
||||||
|
font fnt;
|
||||||
|
|
||||||
|
screen::pixel_t fg = scr.color(0xb0, 0xb0, 0xb0);
|
||||||
|
screen::pixel_t bg = scr.color(49, 79, 128);
|
||||||
|
scr.fill(bg);
|
||||||
|
scr.update();
|
||||||
|
|
||||||
|
constexpr int margin = 2;
|
||||||
|
const unsigned xstride = (margin + fnt.width());
|
||||||
|
const unsigned ystride = (margin + fnt.height());
|
||||||
|
const unsigned rows = (scr.height() - margin) / ystride;
|
||||||
|
const unsigned cols = (scr.width() - margin) / xstride;
|
||||||
|
|
||||||
|
scrollback scroll(rows, cols);
|
||||||
|
|
||||||
|
int pending = 0;
|
||||||
|
constexpr int pending_threshold = 10;
|
||||||
|
|
||||||
|
char message_buffer[256];
|
||||||
|
while (true) {
|
||||||
|
size_t size = sizeof(message_buffer);
|
||||||
|
_syscall_system_get_log(__handle_sys, message_buffer, &size);
|
||||||
|
if (size != 0) {
|
||||||
|
entry *e = reinterpret_cast<entry*>(&message_buffer);
|
||||||
|
|
||||||
|
size_t eom = e->bytes - sizeof(entry);
|
||||||
|
e->message[eom] = 0;
|
||||||
|
|
||||||
|
scroll.add_line(e->message, eom);
|
||||||
|
if (++pending > pending_threshold) {
|
||||||
|
scroll.render(scr, fnt);
|
||||||
|
scr.update();
|
||||||
|
pending = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pending) {
|
||||||
|
scroll.render(scr, fnt);
|
||||||
|
scr.update();
|
||||||
|
pending = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_syscall_system_log("fb driver done, exiting");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
50
src/drivers/fb/screen.cpp
Normal file
50
src/drivers/fb/screen.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
screen::screen(void *addr, unsigned hres, unsigned vres, pixel_order order) :
|
||||||
|
m_fb(static_cast<pixel_t *>(addr)),
|
||||||
|
m_order(order),
|
||||||
|
m_resx(hres),
|
||||||
|
m_resy(vres)
|
||||||
|
{
|
||||||
|
m_back = reinterpret_cast<pixel_t*>(malloc(hres*vres*sizeof(pixel_t)));
|
||||||
|
}
|
||||||
|
|
||||||
|
screen::pixel_t
|
||||||
|
screen::color(uint8_t r, uint8_t g, uint8_t b) const
|
||||||
|
{
|
||||||
|
switch (m_order) {
|
||||||
|
case pixel_order::bgr8:
|
||||||
|
return
|
||||||
|
(static_cast<uint32_t>(b) << 0) |
|
||||||
|
(static_cast<uint32_t>(g) << 8) |
|
||||||
|
(static_cast<uint32_t>(r) << 16);
|
||||||
|
|
||||||
|
case pixel_order::rgb8:
|
||||||
|
return
|
||||||
|
(static_cast<uint32_t>(r) << 0) |
|
||||||
|
(static_cast<uint32_t>(g) << 8) |
|
||||||
|
(static_cast<uint32_t>(b) << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
screen::fill(pixel_t color)
|
||||||
|
{
|
||||||
|
const size_t len = m_resx * m_resy;
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
m_back[i] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
screen::draw_pixel(unsigned x, unsigned y, pixel_t color)
|
||||||
|
{
|
||||||
|
m_back[x + y * m_resx] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
screen::update()
|
||||||
|
{
|
||||||
|
memcpy(m_fb, m_back, m_resx*m_resy*sizeof(pixel_t));
|
||||||
|
}
|
||||||
30
src/drivers/fb/screen.h
Normal file
30
src/drivers/fb/screen.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class screen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using pixel_t = uint32_t;
|
||||||
|
|
||||||
|
enum class pixel_order : uint8_t { bgr8, rgb8, };
|
||||||
|
|
||||||
|
screen(void *addr, unsigned hres, unsigned vres, pixel_order order);
|
||||||
|
|
||||||
|
unsigned width() const { return m_resx; }
|
||||||
|
unsigned height() const { return m_resy; }
|
||||||
|
|
||||||
|
pixel_t color(uint8_t r, uint8_t g, uint8_t b) const;
|
||||||
|
|
||||||
|
void fill(pixel_t color);
|
||||||
|
void draw_pixel(unsigned x, unsigned y, pixel_t color);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
pixel_t *m_fb, *m_back;
|
||||||
|
pixel_order m_order;
|
||||||
|
unsigned m_resx, m_resy;
|
||||||
|
|
||||||
|
screen() = delete;
|
||||||
|
};
|
||||||
56
src/drivers/fb/scrollback.cpp
Normal file
56
src/drivers/fb/scrollback.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "font.h"
|
||||||
|
#include "screen.h"
|
||||||
|
#include "scrollback.h"
|
||||||
|
|
||||||
|
scrollback::scrollback(unsigned lines, unsigned cols, unsigned margin) :
|
||||||
|
m_rows {lines},
|
||||||
|
m_cols {cols},
|
||||||
|
m_count {0},
|
||||||
|
m_margin {margin}
|
||||||
|
{
|
||||||
|
m_data = reinterpret_cast<char*>(malloc(lines*cols));
|
||||||
|
m_lines = reinterpret_cast<char**>(malloc(lines*sizeof(char*)));
|
||||||
|
for (unsigned i = 0; i < lines; ++i)
|
||||||
|
m_lines[i] = &m_data[i*cols];
|
||||||
|
|
||||||
|
memset(m_data, ' ', lines*cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scrollback::add_line(const char *line, size_t len)
|
||||||
|
{
|
||||||
|
unsigned i = m_count++ % m_rows;
|
||||||
|
|
||||||
|
if (len > m_cols)
|
||||||
|
len = m_cols;
|
||||||
|
|
||||||
|
memcpy(m_lines[i], line, len);
|
||||||
|
if (len < m_cols)
|
||||||
|
memset(m_lines[i]+len, ' ', m_cols - len);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
scrollback::get_line(unsigned i)
|
||||||
|
{
|
||||||
|
return m_lines[(i+m_count)%m_rows];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scrollback::render(screen &scr, font &fnt)
|
||||||
|
{
|
||||||
|
screen::pixel_t fg = scr.color(0xb0, 0xb0, 0xb0);
|
||||||
|
screen::pixel_t bg = scr.color(49, 79, 128);
|
||||||
|
|
||||||
|
const unsigned xstride = (m_margin + fnt.width());
|
||||||
|
const unsigned ystride = (m_margin + fnt.height());
|
||||||
|
|
||||||
|
for (unsigned y = 0; y < m_rows; ++y) {
|
||||||
|
char *line = get_line(y);
|
||||||
|
for (unsigned x = 0; x < m_cols; ++x) {
|
||||||
|
fnt.draw_glyph(scr, line[x], fg, bg, m_margin+x*xstride, m_margin+y*ystride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/drivers/fb/scrollback.h
Normal file
26
src/drivers/fb/scrollback.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file scrollback.h
|
||||||
|
|
||||||
|
class screen;
|
||||||
|
class font;
|
||||||
|
|
||||||
|
class scrollback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
scrollback(unsigned lines, unsigned cols, unsigned margin = 2);
|
||||||
|
|
||||||
|
void add_line(const char *line, size_t len);
|
||||||
|
|
||||||
|
char * get_line(unsigned i);
|
||||||
|
|
||||||
|
void render(screen &scr, font &fnt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *m_data;
|
||||||
|
char **m_lines;
|
||||||
|
unsigned m_rows, m_cols;
|
||||||
|
unsigned m_start;
|
||||||
|
unsigned m_count;
|
||||||
|
unsigned m_margin;
|
||||||
|
};
|
||||||
|
|
||||||
22
src/drivers/nulldrv/io.cpp
Normal file
22
src/drivers/nulldrv/io.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#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) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
io_wait(unsigned times)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < times; ++i)
|
||||||
|
outb(0x80, 0);
|
||||||
|
}
|
||||||
24
src/drivers/nulldrv/io.h
Normal file
24
src/drivers/nulldrv/io.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
/// Pause briefly by doing IO to port 0x80
|
||||||
|
/// \arg times Number of times to delay by writing
|
||||||
|
void io_wait(unsigned times = 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t COM1 = 0x03f8;
|
||||||
|
constexpr uint16_t COM2 = 0x02f8;
|
||||||
@@ -3,36 +3,138 @@
|
|||||||
|
|
||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
#include "j6/errors.h"
|
#include "j6/errors.h"
|
||||||
|
#include "j6/signals.h"
|
||||||
|
|
||||||
|
#include <j6libc/syscalls.h>
|
||||||
|
|
||||||
|
#include "io.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
char inbuf[1024];
|
||||||
|
extern j6_handle_t __handle_sys;
|
||||||
|
j6_handle_t endp = j6_handle_invalid;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
j6_status_t getpid(uint64_t *);
|
void _init_libc(j6_process_init *);
|
||||||
j6_status_t fork(uint64_t *);
|
|
||||||
j6_status_t sleep(uint64_t til);
|
|
||||||
j6_status_t debug();
|
|
||||||
j6_status_t message(const char *msg);
|
|
||||||
|
|
||||||
int main(int, const char **);
|
int main(int, const char **);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread_proc()
|
||||||
|
{
|
||||||
|
_syscall_system_log("sub thread starting");
|
||||||
|
|
||||||
|
char buffer[512];
|
||||||
|
size_t len = sizeof(buffer);
|
||||||
|
j6_tag_t tag = 0;
|
||||||
|
j6_status_t result = _syscall_endpoint_receive(endp, &tag, &len, (void*)buffer);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
_syscall_thread_exit(result);
|
||||||
|
|
||||||
|
_syscall_system_log("sub thread received message");
|
||||||
|
|
||||||
|
for (int i = 0; i < len; ++i)
|
||||||
|
if (buffer[i] >= 'A' && buffer[i] <= 'Z')
|
||||||
|
buffer[i] += 0x20;
|
||||||
|
|
||||||
|
tag++;
|
||||||
|
result = _syscall_endpoint_send(endp, tag, len, (void*)buffer);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
_syscall_thread_exit(result);
|
||||||
|
|
||||||
|
_syscall_system_log("sub thread sent message");
|
||||||
|
|
||||||
|
for (int i = 1; i < 5; ++i)
|
||||||
|
_syscall_thread_sleep(i*10);
|
||||||
|
|
||||||
|
_syscall_system_log("sub thread exiting");
|
||||||
|
_syscall_thread_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, const char **argv)
|
main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
uint64_t pid = 0;
|
j6_handle_t child = j6_handle_invalid;
|
||||||
uint64_t child = 0;
|
j6_signal_t out = 0;
|
||||||
|
|
||||||
j6_status_t result = fork(&child);
|
_syscall_system_log("main thread starting");
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; ++i)
|
||||||
|
_syscall_system_log(argv[i]);
|
||||||
|
|
||||||
|
void *base = malloc(0x1000);
|
||||||
|
if (!base)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
uint64_t *vma_ptr = reinterpret_cast<uint64_t*>(base);
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
vma_ptr[i*100] = uint64_t(i);
|
||||||
|
|
||||||
|
_syscall_system_log("main thread wrote to memory area");
|
||||||
|
|
||||||
|
j6_status_t result = _syscall_endpoint_create(&endp);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
message("hello from nulldrv!");
|
_syscall_system_log("main thread created endpoint");
|
||||||
|
|
||||||
result = getpid(&pid);
|
result = _syscall_thread_create(reinterpret_cast<void*>(&thread_proc), &child);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
for (int i = 1; i < 5; ++i)
|
_syscall_system_log("main thread created sub thread");
|
||||||
sleep(i*10);
|
|
||||||
|
|
||||||
return pid;
|
char message[] = "MAIN THREAD SUCCESSFULLY CALLED SENDRECV IF THIS IS LOWERCASE";
|
||||||
|
size_t size = sizeof(message);
|
||||||
|
j6_tag_t tag = 16;
|
||||||
|
result = _syscall_endpoint_sendrecv(endp, &tag, &size, (void*)message);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (tag != 17)
|
||||||
|
_syscall_system_log("GOT WRONG TAG FROM SENDRECV");
|
||||||
|
|
||||||
|
result = _syscall_system_bind_irq(__handle_sys, endp, 3);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
_syscall_system_log(message);
|
||||||
|
|
||||||
|
_syscall_system_log("main thread waiting on child");
|
||||||
|
|
||||||
|
result = _syscall_object_wait(child, -1ull, &out);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
_syscall_system_log("main testing irqs");
|
||||||
|
|
||||||
|
|
||||||
|
serial_port com2(COM2);
|
||||||
|
|
||||||
|
const char *fgseq = "\x1b[2J";
|
||||||
|
while (*fgseq)
|
||||||
|
com2.write(*fgseq++);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
com2.write('%');
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
while (true) {
|
||||||
|
result = _syscall_endpoint_receive(endp, &tag, &len, nullptr);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (j6_tag_is_irq(tag))
|
||||||
|
_syscall_system_log("main thread got irq!");
|
||||||
|
}
|
||||||
|
|
||||||
|
_syscall_system_log("main thread closing endpoint");
|
||||||
|
|
||||||
|
result = _syscall_object_close(endp);
|
||||||
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
_syscall_system_log("main thread done, exiting");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
section .bss
|
|
||||||
mymessage:
|
|
||||||
resq 1024
|
|
||||||
|
|
||||||
extern main
|
|
||||||
extern exit
|
|
||||||
|
|
||||||
section .text
|
|
||||||
global getpid
|
|
||||||
getpid:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
; address of out var should already be in rdi
|
|
||||||
mov rax, 0x13 ; getpid syscall
|
|
||||||
syscall ; result is now already in rax, so just return
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
global debug
|
|
||||||
debug:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
mov rax, 0x00 ; debug syscall
|
|
||||||
syscall
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
global sleep
|
|
||||||
sleep:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
mov rax, 0x16 ; sleep syscall
|
|
||||||
syscall
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
global fork
|
|
||||||
fork:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
; address of out var should already be in rdi
|
|
||||||
mov rax, 0x12
|
|
||||||
syscall ; result left in rax
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
global message
|
|
||||||
message:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
; message should already be in rdi
|
|
||||||
mov rax, 0x14
|
|
||||||
syscall
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
global _start
|
|
||||||
_start:
|
|
||||||
xor rbp, rbp ; Sentinel rbp
|
|
||||||
push rbp
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
mov rdi, 0
|
|
||||||
mov rsi, 0
|
|
||||||
call main
|
|
||||||
|
|
||||||
mov rdi, rax
|
|
||||||
call exit
|
|
||||||
41
src/drivers/nulldrv/serial.cpp
Normal file
41
src/drivers/nulldrv/serial.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "io.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
|
||||||
|
serial_port::serial_port() :
|
||||||
|
m_port(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_port::serial_port(uint16_t port) :
|
||||||
|
m_port(port)
|
||||||
|
{
|
||||||
|
outb(port + 1, 0x00); // Disable all interrupts
|
||||||
|
outb(port + 3, 0x80); // Enable the Divisor Latch Access Bit
|
||||||
|
outb(port + 0, 0x01); // Divisor low bit: 1 (115200 baud)
|
||||||
|
outb(port + 1, 0x00); // Divisor high bit
|
||||||
|
outb(port + 3, 0x03); // 8-N-1
|
||||||
|
outb(port + 2, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
||||||
|
outb(port + 4, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
|
||||||
|
outb(port + 1, 0x03); // Enable interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
bool serial_port::read_ready() { return (inb(m_port + 5) & 0x01) != 0; }
|
||||||
|
|
||||||
|
bool serial_port::write_ready() {
|
||||||
|
uint8_t lsr = inb(m_port + 5);
|
||||||
|
return (lsr & 0x20) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char
|
||||||
|
serial_port::read() {
|
||||||
|
while (!read_ready());
|
||||||
|
return inb(m_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
serial_port::write(char c) {
|
||||||
|
while (!write_ready());
|
||||||
|
outb(m_port, c);
|
||||||
|
}
|
||||||
|
|
||||||
26
src/drivers/nulldrv/serial.h
Normal file
26
src/drivers/nulldrv/serial.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file serial.h
|
||||||
|
/// Declarations related to serial ports.
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define serial_port nulldrv_serial_port
|
||||||
|
|
||||||
|
class serial_port
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg port The IO address of the serial port
|
||||||
|
serial_port(uint16_t port);
|
||||||
|
|
||||||
|
serial_port();
|
||||||
|
|
||||||
|
void write(char c);
|
||||||
|
char read();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t m_port;
|
||||||
|
|
||||||
|
bool read_ready();
|
||||||
|
bool write_ready();
|
||||||
|
};
|
||||||
|
|
||||||
@@ -8,9 +8,13 @@
|
|||||||
|
|
||||||
#define j6_status_ok 0x0000
|
#define j6_status_ok 0x0000
|
||||||
|
|
||||||
|
#define j6_status_closed 0x1000
|
||||||
#define j6_status_destroyed 0x1001
|
#define j6_status_destroyed 0x1001
|
||||||
|
#define j6_status_exists 0x1002
|
||||||
|
|
||||||
#define j6_err_nyi j6_err(0x0001)
|
#define j6_err_nyi j6_err(0x0001)
|
||||||
#define j6_err_unexpected j6_err(0x0002)
|
#define j6_err_unexpected j6_err(0x0002)
|
||||||
#define j6_err_invalid_arg j6_err(0x0003)
|
#define j6_err_invalid_arg j6_err(0x0003)
|
||||||
|
#define j6_err_not_ready j6_err(0x0004)
|
||||||
|
#define j6_err_insufficient j6_err(0x0005)
|
||||||
|
|
||||||
|
|||||||
30
src/include/j6/init.h
Normal file
30
src/include/j6/init.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file init.h
|
||||||
|
/// Types used in process and thread initialization
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum j6_init_type { // `value` is a:
|
||||||
|
j6_init_handle_system, // Handle to the system
|
||||||
|
j6_init_handle_process, // Handle to this process
|
||||||
|
j6_init_handle_thread, // Handle to this thread
|
||||||
|
j6_init_handle_space, // Handle to this process' address space
|
||||||
|
j6_init_desc_framebuffer // Pointer to a j6_init_framebuffer descriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
struct j6_init_value {
|
||||||
|
enum j6_init_type type;
|
||||||
|
uint64_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Structure defining a framebuffer.
|
||||||
|
/// `flags` has the following bits:
|
||||||
|
/// 0-3: Pixel layout. 0000: rgb8, 0001: bgr8
|
||||||
|
struct j6_init_framebuffer {
|
||||||
|
void* addr;
|
||||||
|
size_t size;
|
||||||
|
uint32_t vertical;
|
||||||
|
uint32_t horizontal;
|
||||||
|
uint32_t scanline;
|
||||||
|
uint32_t flags;
|
||||||
|
};
|
||||||
@@ -2,17 +2,40 @@
|
|||||||
/// \file signals.h
|
/// \file signals.h
|
||||||
/// Collection of constants for the j6_signal_t type
|
/// Collection of constants for the j6_signal_t type
|
||||||
|
|
||||||
// Signals 0-7 are common to all types
|
// Signals 0-15 are common to all types
|
||||||
#define j6_signal_no_handles (1 << 0)
|
#define j6_signal_no_handles (1ull << 0)
|
||||||
|
#define j6_signal_closed (1ull << 1)
|
||||||
|
|
||||||
// Signals 8-15 are user-defined signals
|
// Signals 16-47 are defined per-object-type
|
||||||
#define j6_signal_user0 (1 << 8)
|
|
||||||
#define j6_signal_user1 (1 << 9)
|
|
||||||
#define j6_signal_user2 (1 << 10)
|
|
||||||
#define j6_signal_user3 (1 << 11)
|
|
||||||
#define j6_signal_user4 (1 << 12)
|
|
||||||
#define j6_signal_user5 (1 << 13)
|
|
||||||
#define j6_signal_user6 (1 << 14)
|
|
||||||
#define j6_signal_user7 (1 << 15)
|
|
||||||
|
|
||||||
// All other signals are type-specific
|
// Process signals
|
||||||
|
|
||||||
|
// Thread signals
|
||||||
|
|
||||||
|
// Channel signals
|
||||||
|
#define j6_signal_channel_can_send (1ull << 16)
|
||||||
|
#define j6_signal_channel_can_recv (1ull << 17)
|
||||||
|
|
||||||
|
// Endpoint signals
|
||||||
|
#define j6_signal_endpoint_can_send (1ull << 16)
|
||||||
|
#define j6_signal_endpoint_can_recv (1ull << 17)
|
||||||
|
|
||||||
|
// Signals 48-63 are user-defined signals
|
||||||
|
#define j6_signal_user0 (1ull << 48)
|
||||||
|
#define j6_signal_user1 (1ull << 49)
|
||||||
|
#define j6_signal_user2 (1ull << 50)
|
||||||
|
#define j6_signal_user3 (1ull << 51)
|
||||||
|
#define j6_signal_user4 (1ull << 52)
|
||||||
|
#define j6_signal_user5 (1ull << 53)
|
||||||
|
#define j6_signal_user6 (1ull << 54)
|
||||||
|
#define j6_signal_user7 (1ull << 55)
|
||||||
|
#define j6_signal_user8 (1ull << 56)
|
||||||
|
#define j6_signal_user9 (1ull << 57)
|
||||||
|
#define j6_signal_user10 (1ull << 58)
|
||||||
|
#define j6_signal_user11 (1ull << 59)
|
||||||
|
#define j6_signal_user12 (1ull << 60)
|
||||||
|
#define j6_signal_user13 (1ull << 61)
|
||||||
|
#define j6_signal_user14 (1ull << 62)
|
||||||
|
#define j6_signal_user15 (1ull << 63)
|
||||||
|
|
||||||
|
#define j6_signal_user_mask (0xffffull << 48)
|
||||||
|
|||||||
@@ -10,11 +10,41 @@ typedef uint64_t j6_koid_t;
|
|||||||
/// Syscalls return status as this type
|
/// Syscalls return status as this type
|
||||||
typedef uint64_t j6_status_t;
|
typedef uint64_t j6_status_t;
|
||||||
|
|
||||||
/// Handles are references and capabilities to other objects
|
|
||||||
typedef uint32_t j6_handle_t;
|
|
||||||
|
|
||||||
/// Some objects have signals, which are a bitmap of 64 possible signals
|
/// Some objects have signals, which are a bitmap of 64 possible signals
|
||||||
typedef uint64_t j6_signal_t;
|
typedef uint64_t j6_signal_t;
|
||||||
|
|
||||||
/// The rights of a handle/capability are a bitmap of 64 possible rights
|
/// The first word of IPC messages are the tag. Tags with the high bit
|
||||||
typedef uint64_t j6_rights_t;
|
/// set are reserved for the system.
|
||||||
|
typedef uint64_t j6_tag_t;
|
||||||
|
|
||||||
|
#define j6_tag_system_flag 0x8000000000000000
|
||||||
|
#define j6_tag_invalid 0x0000000000000000
|
||||||
|
|
||||||
|
/// If all high bits except the last 16 are set, then the tag represents
|
||||||
|
/// an IRQ.
|
||||||
|
#define j6_tag_irq_base 0xffffffffffff0000
|
||||||
|
#define j6_tag_is_irq(x) (((x) & j6_tag_irq_base) == j6_tag_irq_base)
|
||||||
|
#define j6_tag_from_irq(x) ((x) | j6_tag_irq_base)
|
||||||
|
#define j6_tag_to_irq(x) ((x) & ~j6_tag_irq_base)
|
||||||
|
|
||||||
|
/// Handles are references and capabilities to other objects. The least
|
||||||
|
/// significant 32 bits are an identifier, and the most significant 32
|
||||||
|
/// bits are a bitmask of capabilities this handle has on that object.
|
||||||
|
typedef uint64_t j6_handle_t;
|
||||||
|
|
||||||
|
#define j6_handle_rights_shift 4
|
||||||
|
#define j6_handle_id_mask 0xffffffffull
|
||||||
|
#define j6_handle_invalid 0xffffffffull
|
||||||
|
|
||||||
|
/// A process' initial data structure for communicating with the system
|
||||||
|
struct j6_process_init
|
||||||
|
{
|
||||||
|
j6_handle_t process;
|
||||||
|
j6_handle_t handles[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A thread's initial data structure
|
||||||
|
struct j6_thread_init
|
||||||
|
{
|
||||||
|
j6_handle_t thread;
|
||||||
|
};
|
||||||
|
|||||||
@@ -10,44 +10,41 @@ namespace args {
|
|||||||
constexpr uint32_t magic = 0x600dda7a;
|
constexpr uint32_t magic = 0x600dda7a;
|
||||||
constexpr uint16_t version = 1;
|
constexpr uint16_t version = 1;
|
||||||
|
|
||||||
enum class mod_flags : uint32_t
|
|
||||||
{
|
|
||||||
debug = 0x00000001
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class mod_type : uint32_t {
|
enum class mod_type : uint32_t {
|
||||||
unknown,
|
symbol_table
|
||||||
|
|
||||||
kernel,
|
|
||||||
initrd,
|
|
||||||
|
|
||||||
memory_map,
|
|
||||||
page_tables,
|
|
||||||
framebuffer,
|
|
||||||
|
|
||||||
max
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class mode : uint8_t {
|
|
||||||
normal,
|
|
||||||
debug
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct module {
|
struct module {
|
||||||
void *location;
|
void *location;
|
||||||
size_t size;
|
size_t size;
|
||||||
mod_type type;
|
mod_type type;
|
||||||
mod_flags flags;
|
};
|
||||||
}
|
|
||||||
__attribute__((packed));
|
|
||||||
|
|
||||||
|
enum class fb_type : uint16_t {
|
||||||
|
none,
|
||||||
|
rgb8,
|
||||||
|
bgr8
|
||||||
|
};
|
||||||
|
|
||||||
|
struct framebuffer {
|
||||||
|
uintptr_t phys_addr;
|
||||||
|
size_t size;
|
||||||
|
uint32_t vertical;
|
||||||
|
uint32_t horizontal;
|
||||||
|
uint16_t scanline;
|
||||||
|
fb_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct program {
|
||||||
|
uintptr_t phys_addr;
|
||||||
|
uintptr_t virt_addr;
|
||||||
|
uintptr_t entrypoint;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
enum class mem_type : uint32_t {
|
enum class mem_type : uint32_t {
|
||||||
free,
|
free,
|
||||||
args,
|
pending,
|
||||||
kernel,
|
|
||||||
module,
|
|
||||||
table,
|
|
||||||
acpi,
|
acpi,
|
||||||
uefi_runtime,
|
uefi_runtime,
|
||||||
mmio,
|
mmio,
|
||||||
@@ -61,33 +58,37 @@ struct mem_entry
|
|||||||
size_t pages;
|
size_t pages;
|
||||||
mem_type type;
|
mem_type type;
|
||||||
uint32_t attr;
|
uint32_t attr;
|
||||||
}
|
};
|
||||||
__attribute__((packed));
|
|
||||||
|
|
||||||
|
enum class boot_flags : uint16_t {
|
||||||
|
none = 0x0000,
|
||||||
|
debug = 0x0001
|
||||||
|
};
|
||||||
|
|
||||||
struct header {
|
struct header {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
|
boot_flags flags;
|
||||||
mode mode;
|
|
||||||
|
|
||||||
uint8_t _reserved0;
|
|
||||||
|
|
||||||
void *pml4;
|
void *pml4;
|
||||||
void *page_table_cache;
|
void *page_tables;
|
||||||
uint32_t num_free_tables;
|
size_t table_count;
|
||||||
|
|
||||||
|
program *programs;
|
||||||
|
size_t num_programs;
|
||||||
|
|
||||||
uint32_t num_modules;
|
|
||||||
module *modules;
|
module *modules;
|
||||||
|
size_t num_modules;
|
||||||
|
|
||||||
mem_entry *mem_map;
|
mem_entry *mem_map;
|
||||||
size_t num_map_entries;
|
size_t map_count;
|
||||||
|
|
||||||
void *runtime_services;
|
void *runtime_services;
|
||||||
void *acpi_table;
|
void *acpi_table;
|
||||||
|
|
||||||
|
framebuffer video;
|
||||||
}
|
}
|
||||||
__attribute__((aligned(alignof(max_align_t))));
|
__attribute__((aligned(alignof(max_align_t))));
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
} // namespace args
|
} // namespace args
|
||||||
|
|
||||||
|
|||||||
@@ -11,23 +11,35 @@ namespace memory {
|
|||||||
constexpr size_t frame_size = 0x1000;
|
constexpr size_t frame_size = 0x1000;
|
||||||
|
|
||||||
/// Start of kernel memory.
|
/// Start of kernel memory.
|
||||||
constexpr uintptr_t kernel_offset = 0xffff800000000000;
|
constexpr uintptr_t kernel_offset = 0xffff800000000000ull;
|
||||||
|
|
||||||
/// Offset from physical where page tables are mapped.
|
/// Offset from physical where page tables are mapped.
|
||||||
constexpr uintptr_t page_offset = 0xffffc00000000000;
|
constexpr uintptr_t page_offset = 0xffffc00000000000ull;
|
||||||
|
|
||||||
/// Initial process thread's stack address
|
/// Max number of pages for a kernel stack
|
||||||
constexpr uintptr_t initial_stack = 0x0000800000000000;
|
constexpr unsigned kernel_stack_pages = 4;
|
||||||
|
|
||||||
/// Initial process thread's stack size, in pages
|
/// Max number of pages for a kernel buffer
|
||||||
constexpr unsigned initial_stack_pages = 1;
|
constexpr unsigned kernel_buffer_pages = 16;
|
||||||
|
|
||||||
/// Max size of the kernel heap
|
/// Max size of the kernel heap
|
||||||
constexpr size_t kernel_max_heap = 0x8000000000; // 512GiB
|
constexpr size_t kernel_max_heap = 0x8000000000ull; // 512GiB
|
||||||
|
|
||||||
/// Start of the kernel heap
|
/// Start of the kernel heap
|
||||||
constexpr uintptr_t heap_start = page_offset - kernel_max_heap;
|
constexpr uintptr_t heap_start = page_offset - kernel_max_heap;
|
||||||
|
|
||||||
|
/// Max size of the kernel stacks area
|
||||||
|
constexpr size_t kernel_max_stacks = 0x8000000000ull; // 512GiB
|
||||||
|
|
||||||
|
/// Start of the kernel stacks
|
||||||
|
constexpr uintptr_t stacks_start = heap_start - kernel_max_stacks;
|
||||||
|
|
||||||
|
/// Max size of kernel buffers area
|
||||||
|
constexpr size_t kernel_max_buffers = 0x10000000000ull; // 1TiB
|
||||||
|
|
||||||
|
/// Start of kernel buffers
|
||||||
|
constexpr uintptr_t buffers_start = stacks_start - kernel_max_buffers;
|
||||||
|
|
||||||
/// First kernel space PML4 entry
|
/// First kernel space PML4 entry
|
||||||
constexpr unsigned pml4e_kernel = 256;
|
constexpr unsigned pml4e_kernel = 256;
|
||||||
|
|
||||||
@@ -41,4 +53,22 @@ namespace memory {
|
|||||||
/// through the page_offset area.
|
/// through the page_offset area.
|
||||||
inline bool page_mappable(uintptr_t a) { return (a & page_offset) == 0; }
|
inline bool page_mappable(uintptr_t a) { return (a & page_offset) == 0; }
|
||||||
|
|
||||||
|
/// Convert a physical address to a virtual one (in the offset-mapped area)
|
||||||
|
template <typename T> T * to_virtual(uintptr_t a) {
|
||||||
|
return reinterpret_cast<T*>(a|page_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of pages needed for a given number of bytes.
|
||||||
|
/// \arg bytes The number of bytes desired
|
||||||
|
/// \returns The number of pages needed to contain the desired bytes
|
||||||
|
inline size_t page_count(size_t bytes) {
|
||||||
|
return ((bytes - 1) >> 12) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the given address, aligned to the next lowest page
|
||||||
|
inline uintptr_t page_align_down(uintptr_t a) { return a & ~(frame_size-1); }
|
||||||
|
|
||||||
|
/// Get the given address, aligned to the next page
|
||||||
|
inline uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + frame_size; }
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
LOG(apic, info);
|
LOG(apic, info);
|
||||||
LOG(device, info);
|
LOG(device, debug);
|
||||||
LOG(paging, warn);
|
LOG(paging, warn);
|
||||||
LOG(driver, info);
|
LOG(driver, info);
|
||||||
LOG(memory, info);
|
LOG(memory, info);
|
||||||
LOG(fs, info);
|
LOG(fs, info);
|
||||||
LOG(task, info);
|
LOG(task, debug);
|
||||||
|
LOG(loader, info);
|
||||||
LOG(boot, debug);
|
LOG(boot, debug);
|
||||||
LOG(syscall,debug);
|
LOG(syscall,debug);
|
||||||
LOG(vmem, debug);
|
LOG(vmem, debug);
|
||||||
LOG(objs, debug);
|
LOG(objs, debug);
|
||||||
|
LOG(timer, debug);
|
||||||
|
LOG(clock, debug);
|
||||||
|
|||||||
31
src/include/syscalls.inc
Normal file
31
src/include/syscalls.inc
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
SYSCALL(0x00, system_log, const char *)
|
||||||
|
SYSCALL(0x01, system_noop, void)
|
||||||
|
SYSCALL(0x02, system_get_log, j6_handle_t, char *, size_t *)
|
||||||
|
SYSCALL(0x03, system_bind_irq, j6_handle_t, j6_handle_t, unsigned)
|
||||||
|
|
||||||
|
SYSCALL(0x08, object_koid, j6_handle_t, j6_koid_t *)
|
||||||
|
SYSCALL(0x09, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *)
|
||||||
|
SYSCALL(0x0a, object_signal, j6_handle_t, j6_signal_t)
|
||||||
|
SYSCALL(0x0b, object_close, j6_handle_t)
|
||||||
|
|
||||||
|
SYSCALL(0x10, process_exit, int64_t)
|
||||||
|
|
||||||
|
SYSCALL(0x18, thread_create, void *, j6_handle_t *)
|
||||||
|
SYSCALL(0x19, thread_exit, int64_t)
|
||||||
|
SYSCALL(0x1a, thread_pause, void)
|
||||||
|
SYSCALL(0x1b, thread_sleep, uint64_t)
|
||||||
|
|
||||||
|
SYSCALL(0x20, channel_create, j6_handle_t *)
|
||||||
|
SYSCALL(0x21, channel_send, j6_handle_t, size_t *, void *)
|
||||||
|
SYSCALL(0x22, channel_receive, j6_handle_t, size_t *, void *)
|
||||||
|
|
||||||
|
SYSCALL(0x28, endpoint_create, j6_handle_t *)
|
||||||
|
SYSCALL(0x29, endpoint_send, j6_handle_t, j6_tag_t, size_t, void *)
|
||||||
|
SYSCALL(0x2a, endpoint_receive, j6_handle_t, j6_tag_t *, size_t *, void *)
|
||||||
|
SYSCALL(0x2b, endpoint_sendrecv, j6_handle_t, j6_tag_t *, size_t *, void *)
|
||||||
|
|
||||||
|
SYSCALL(0x30, vma_create, j6_handle_t *, size_t, uint32_t)
|
||||||
|
SYSCALL(0x31, vma_create_map, j6_handle_t *, size_t, uintptr_t, uint32_t)
|
||||||
|
SYSCALL(0x32, vma_map, j6_handle_t, uintptr_t)
|
||||||
|
SYSCALL(0x33, vma_unmap, j6_handle_t)
|
||||||
|
SYSCALL(0x34, vma_resize, j6_handle_t, size_t *)
|
||||||
@@ -188,3 +188,13 @@ struct acpi_mcfg
|
|||||||
acpi_mcfg_entry entries[0];
|
acpi_mcfg_entry entries[0];
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct acpi_hpet
|
||||||
|
{
|
||||||
|
TABLE_HEADER('HPET');
|
||||||
|
uint32_t hardware_id;
|
||||||
|
acpi_gas base_address;
|
||||||
|
uint8_t index;
|
||||||
|
uint16_t periodic_min;
|
||||||
|
uint8_t attributes;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
#include "kutil/assert.h"
|
#include "kutil/assert.h"
|
||||||
#include "apic.h"
|
#include "apic.h"
|
||||||
|
#include "clock.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
|
||||||
|
|
||||||
|
static constexpr uint16_t lapic_spurious = 0x00f0;
|
||||||
|
|
||||||
|
static constexpr uint16_t lapic_lvt_timer = 0x0320;
|
||||||
|
static constexpr uint16_t lapic_lvt_lint0 = 0x0350;
|
||||||
|
static constexpr uint16_t lapic_lvt_lint1 = 0x0360;
|
||||||
|
|
||||||
|
static constexpr uint16_t lapic_timer_init = 0x0380;
|
||||||
|
static constexpr uint16_t lapic_timer_cur = 0x0390;
|
||||||
|
static constexpr uint16_t lapic_timer_div = 0x03e0;
|
||||||
|
|
||||||
static uint32_t
|
static uint32_t
|
||||||
apic_read(uint32_t volatile *apic, uint16_t offset)
|
apic_read(uint32_t volatile *apic, uint16_t offset)
|
||||||
@@ -32,21 +42,17 @@ ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value)
|
|||||||
*(base + 4) = value;
|
*(base + 4) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
apic::apic(uint32_t *base) :
|
apic::apic(uintptr_t base) :
|
||||||
m_base(base)
|
m_base(memory::to_virtual<uint32_t>(base))
|
||||||
{
|
{
|
||||||
// Map 1MiB of space for the APIC registers and
|
|
||||||
// MSI area
|
|
||||||
page_manager::get()->map_offset_pointer(
|
|
||||||
reinterpret_cast<void **>(&m_base),
|
|
||||||
0x100000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lapic::lapic(uint32_t *base, isr spurious) :
|
lapic::lapic(uintptr_t base, isr spurious) :
|
||||||
apic(base)
|
apic(base),
|
||||||
|
m_divisor(0)
|
||||||
{
|
{
|
||||||
apic_write(m_base, 0xf0, static_cast<uint32_t>(spurious));
|
apic_write(m_base, lapic_spurious, static_cast<uint32_t>(spurious));
|
||||||
log::info(logs::apic, "LAPIC created, base %lx", m_base);
|
log::info(logs::apic, "LAPIC created, base %lx", m_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,40 +63,25 @@ lapic::calibrate_timer()
|
|||||||
|
|
||||||
log::info(logs::apic, "Calibrating APIC timer...");
|
log::info(logs::apic, "Calibrating APIC timer...");
|
||||||
|
|
||||||
// Set up PIT sleep
|
|
||||||
uint8_t command = 0x30; // channel 0, loybyte/highbyte, mode 0
|
|
||||||
outb(0x43, command);
|
|
||||||
|
|
||||||
const uint32_t initial = -1u;
|
const uint32_t initial = -1u;
|
||||||
enable_timer_internal(isr::isrSpurious, 1, initial, false);
|
enable_timer(isr::isrSpurious);
|
||||||
|
set_divisor(1);
|
||||||
|
apic_write(m_base, lapic_timer_init, initial);
|
||||||
|
|
||||||
const int iterations = 5;
|
uint64_t us = 200000;
|
||||||
for (int i=0; i<iterations; ++i) {
|
clock::get().spinwait(us);
|
||||||
const uint16_t pit_33ms = 39375;
|
|
||||||
uint16_t pit_count = pit_33ms;
|
|
||||||
outb(0x40, pit_count & 0xff);
|
|
||||||
io_wait();
|
|
||||||
outb(0x40, (pit_count >> 8) & 0xff);
|
|
||||||
|
|
||||||
|
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
|
||||||
|
uint32_t ticks_total = initial - remaining;
|
||||||
|
m_ticks_per_us = ticks_total / us;
|
||||||
|
|
||||||
while (pit_count <= pit_33ms) {
|
log::info(logs::apic, "APIC timer ticks %d times per microsecond.", m_ticks_per_us);
|
||||||
outb(0x43, 0); // latch counter values
|
|
||||||
pit_count =
|
|
||||||
static_cast<uint16_t>(inb(0x40)) |
|
|
||||||
static_cast<uint16_t>(inb(0x40)) << 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t remain = stop_timer();
|
|
||||||
uint32_t ticks_total = initial - remain;
|
|
||||||
m_ticks_per_us = ticks_total / (iterations * 33000);
|
|
||||||
log::info(logs::apic, "APIC timer ticks %d times per nanosecond.", m_ticks_per_us);
|
|
||||||
|
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
void
|
||||||
lapic::enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat)
|
lapic::set_divisor(uint8_t divisor)
|
||||||
{
|
{
|
||||||
uint32_t divbits = 0;
|
uint32_t divbits = 0;
|
||||||
|
|
||||||
@@ -107,39 +98,37 @@ lapic::enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool r
|
|||||||
kassert(0, "Invalid divisor passed to lapic::enable_timer");
|
kassert(0, "Invalid divisor passed to lapic::enable_timer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apic_write(m_base, lapic_timer_div, divbits);
|
||||||
|
m_divisor = divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
lapic::enable_timer(isr vector, bool repeat)
|
||||||
|
{
|
||||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||||
if (repeat)
|
if (repeat)
|
||||||
lvte |= 0x20000;
|
lvte |= 0x20000;
|
||||||
|
apic_write(m_base, lapic_lvt_timer, lvte);
|
||||||
|
|
||||||
log::debug(logs::apic, "Enabling APIC timer count %ld, divisor %d, isr %02x",
|
log::debug(logs::apic, "Enabling APIC timer at isr %02x", vector);
|
||||||
count, divisor, vector);
|
|
||||||
|
|
||||||
apic_write(m_base, 0x320, lvte);
|
|
||||||
apic_write(m_base, 0x3e0, divbits);
|
|
||||||
|
|
||||||
reset_timer(count);
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
lapic::enable_timer(isr vector, uint64_t interval, bool repeat)
|
lapic::reset_timer(uint64_t interval)
|
||||||
{
|
{
|
||||||
uint64_t ticks = interval * m_ticks_per_us;
|
uint64_t remaining = ticks_to_us(apic_read(m_base, lapic_timer_cur));
|
||||||
|
uint64_t ticks = us_to_ticks(interval);
|
||||||
|
|
||||||
int divisor = 1;
|
int divisor = 1;
|
||||||
while (ticks > -1u) {
|
while (ticks > 0xffffffffull) {
|
||||||
ticks /= 2;
|
ticks >>= 1;
|
||||||
divisor *= 2;
|
divisor <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return enable_timer_internal(vector, divisor, static_cast<uint32_t>(ticks), repeat);
|
if (divisor != m_divisor)
|
||||||
}
|
set_divisor(divisor);
|
||||||
|
|
||||||
uint32_t
|
apic_write(m_base, lapic_timer_init, ticks);
|
||||||
lapic::reset_timer(uint32_t count)
|
|
||||||
{
|
|
||||||
uint32_t remaining = apic_read(m_base, 0x390);
|
|
||||||
apic_write(m_base, 0x380, count);
|
|
||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +137,7 @@ lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
|
|||||||
{
|
{
|
||||||
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
|
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
|
||||||
|
|
||||||
uint16_t off = num ? 0x360 : 0x350;
|
uint16_t off = num ? lapic_lvt_lint1 : lapic_lvt_lint0;
|
||||||
uint32_t lvte = static_cast<uint8_t>(vector);
|
uint32_t lvte = static_cast<uint8_t>(vector);
|
||||||
|
|
||||||
uint16_t polarity = flags & 0x3;
|
uint16_t polarity = flags & 0x3;
|
||||||
@@ -169,21 +158,21 @@ lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
|
|||||||
void
|
void
|
||||||
lapic::enable()
|
lapic::enable()
|
||||||
{
|
{
|
||||||
apic_write(m_base, 0xf0,
|
apic_write(m_base, lapic_spurious,
|
||||||
apic_read(m_base, 0xf0) | 0x100);
|
apic_read(m_base, lapic_spurious) | 0x100);
|
||||||
log::debug(logs::apic, "LAPIC enabled!");
|
log::debug(logs::apic, "LAPIC enabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lapic::disable()
|
lapic::disable()
|
||||||
{
|
{
|
||||||
apic_write(m_base, 0xf0,
|
apic_write(m_base, lapic_spurious,
|
||||||
apic_read(m_base, 0xf0) & ~0x100);
|
apic_read(m_base, lapic_spurious) & ~0x100);
|
||||||
log::debug(logs::apic, "LAPIC disabled.");
|
log::debug(logs::apic, "LAPIC disabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ioapic::ioapic(uint32_t *base, uint32_t base_gsi) :
|
ioapic::ioapic(uintptr_t base, uint32_t base_gsi) :
|
||||||
apic(base),
|
apic(base),
|
||||||
m_base_gsi(base_gsi)
|
m_base_gsi(base_gsi)
|
||||||
{
|
{
|
||||||
@@ -206,6 +195,9 @@ ioapic::ioapic(uint32_t *base, uint32_t base_gsi) :
|
|||||||
void
|
void
|
||||||
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
|
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
|
||||||
{
|
{
|
||||||
|
log::debug(logs::apic, "IOAPIC %d redirecting irq %3d to vector %3d [%04x]%s",
|
||||||
|
m_id, irq, vector, flags, masked ? " (masked)" : "");
|
||||||
|
|
||||||
uint64_t entry = static_cast<uint64_t>(vector);
|
uint64_t entry = static_cast<uint64_t>(vector);
|
||||||
|
|
||||||
uint16_t polarity = flags & 0x3;
|
uint16_t polarity = flags & 0x3;
|
||||||
@@ -226,6 +218,9 @@ ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
|
|||||||
void
|
void
|
||||||
ioapic::mask(uint8_t irq, bool masked)
|
ioapic::mask(uint8_t irq, bool masked)
|
||||||
{
|
{
|
||||||
|
log::debug(logs::apic, "IOAPIC %d %smasking irq %3d",
|
||||||
|
m_id, masked ? "" : "un", irq);
|
||||||
|
|
||||||
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
|
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
|
||||||
if (masked)
|
if (masked)
|
||||||
entry |= (1 << 16);
|
entry |= (1 << 16);
|
||||||
|
|||||||
@@ -6,14 +6,13 @@
|
|||||||
|
|
||||||
enum class isr : uint8_t;
|
enum class isr : uint8_t;
|
||||||
|
|
||||||
|
|
||||||
/// Base class for other APIC types
|
/// Base class for other APIC types
|
||||||
class apic
|
class apic
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg base Base virtual address of the APIC's MMIO registers
|
/// \arg base Physical base address of the APIC's MMIO registers
|
||||||
apic(uint32_t *base);
|
apic(uintptr_t base);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t *m_base;
|
uint32_t *m_base;
|
||||||
@@ -26,24 +25,22 @@ class lapic :
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg base Base virtual address of the APIC's MMIO registers
|
/// \arg base Physicl base address of the APIC's MMIO registers
|
||||||
/// \arg spurious Vector of the spurious interrupt handler
|
/// \arg spurious Vector of the spurious interrupt handler
|
||||||
lapic(uint32_t *base, isr spurious);
|
lapic(uintptr_t base, isr spurious);
|
||||||
|
|
||||||
/// Enable interrupts for the LAPIC timer.
|
/// Enable interrupts for the LAPIC timer.
|
||||||
/// \arg vector Interrupt vector the timer should use
|
/// \arg vector Interrupt vector the timer should use
|
||||||
/// \arg interval The timer interval, in microseconds
|
|
||||||
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
/// \arg repeat If false, this timer is one-off, otherwise repeating
|
||||||
/// \returns The count of ticks the timer is set for
|
void enable_timer(isr vector, bool repeat = true);
|
||||||
uint32_t enable_timer(isr vector, uint64_t interval, bool repeat = true);
|
|
||||||
|
|
||||||
/// Reset the timer countdown.
|
/// Reset the timer countdown.
|
||||||
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
|
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
|
||||||
/// \returns The count of ticks that were remaining before reset
|
/// \returns The interval in us that was remaining before reset
|
||||||
uint32_t reset_timer(uint32_t count);
|
uint32_t reset_timer(uint64_t interval);
|
||||||
|
|
||||||
/// Stop the timer.
|
/// Stop the timer.
|
||||||
/// \returns The count of ticks remaining before an interrupt was to happen
|
/// \returns The interval in us remaining before an interrupt was to happen
|
||||||
inline uint32_t stop_timer() { return reset_timer(0); }
|
inline uint32_t stop_timer() { return reset_timer(0); }
|
||||||
|
|
||||||
/// Enable interrupts for the LAPIC LINT0 pin.
|
/// Enable interrupts for the LAPIC LINT0 pin.
|
||||||
@@ -56,12 +53,22 @@ public:
|
|||||||
void enable(); ///< Enable servicing of interrupts
|
void enable(); ///< Enable servicing of interrupts
|
||||||
void disable(); ///< Disable (temporarily) servicing of interrupts
|
void disable(); ///< Disable (temporarily) servicing of interrupts
|
||||||
|
|
||||||
/// Calibrate the timer speed against the PIT
|
/// Calibrate the timer speed against the clock
|
||||||
void calibrate_timer();
|
void calibrate_timer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat);
|
inline uint64_t ticks_to_us(uint32_t ticks) const {
|
||||||
|
return static_cast<uint64_t>(ticks) / m_ticks_per_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t us_to_ticks(uint64_t interval) const {
|
||||||
|
return interval * m_ticks_per_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_divisor(uint8_t divisor);
|
||||||
|
void set_repeat(bool repeat);
|
||||||
|
|
||||||
|
uint32_t m_divisor;
|
||||||
uint32_t m_ticks_per_us;
|
uint32_t m_ticks_per_us;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,9 +79,9 @@ class ioapic :
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// \arg base Base virtual address of the APIC's MMIO registers
|
/// \arg base Physical base address of the APIC's MMIO registers
|
||||||
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
|
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
|
||||||
ioapic(uint32_t *base, uint32_t base_gsi);
|
ioapic(uintptr_t base, uint32_t base_gsi);
|
||||||
|
|
||||||
uint32_t get_base_gsi() const { return m_base_gsi; }
|
uint32_t get_base_gsi() const { return m_base_gsi; }
|
||||||
uint32_t get_num_gsi() const { return m_num_gsi; }
|
uint32_t get_num_gsi() const { return m_num_gsi; }
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ _header:
|
|||||||
|
|
||||||
section .text
|
section .text
|
||||||
align 16
|
align 16
|
||||||
global _start:function (_start.end - _start)
|
global _kernel_start:function (_kernel_start.end - _kernel_start)
|
||||||
_start:
|
_kernel_start:
|
||||||
cli
|
cli
|
||||||
|
|
||||||
mov rsp, idle_stack_end
|
mov rsp, idle_stack_end
|
||||||
|
|||||||
22
src/kernel/clock.cpp
Normal file
22
src/kernel/clock.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include "clock.h"
|
||||||
|
|
||||||
|
clock * clock::s_instance = nullptr;
|
||||||
|
|
||||||
|
clock::clock(uint64_t rate, clock::source source_func, void *data) :
|
||||||
|
m_rate(rate),
|
||||||
|
m_data(data),
|
||||||
|
m_source(source_func)
|
||||||
|
{
|
||||||
|
// TODO: make this atomic
|
||||||
|
if (s_instance == nullptr)
|
||||||
|
s_instance = this;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clock::spinwait(uint64_t us) const
|
||||||
|
{
|
||||||
|
uint64_t when = m_source(m_data) + us;
|
||||||
|
while (value() < when);
|
||||||
|
}
|
||||||
|
|
||||||
44
src/kernel/clock.h
Normal file
44
src/kernel/clock.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file clock.h
|
||||||
|
/// The kernel time keeping interface
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class clock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// A source is a function that returns the current
|
||||||
|
/// value of some clock source.
|
||||||
|
using source = uint64_t (*)(void*);
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg rate Number of source ticks per us
|
||||||
|
/// \arg source Function for the clock source
|
||||||
|
/// \arg data Data to pass to the source function
|
||||||
|
clock(uint64_t rate, source source_func, void *data);
|
||||||
|
|
||||||
|
/// Get the current value of the clock.
|
||||||
|
/// \returns Current value of the source, in us
|
||||||
|
/// TODO: optimize divison by finding a multiply and
|
||||||
|
/// shift value instead
|
||||||
|
inline uint64_t value() const { return m_source(m_data) / m_rate; }
|
||||||
|
|
||||||
|
/// Update the internal state via the source
|
||||||
|
/// \returns Current value of the clock
|
||||||
|
inline void update() { m_current = value(); }
|
||||||
|
|
||||||
|
/// Wait in a tight loop
|
||||||
|
/// \arg interval Time to wait, in us
|
||||||
|
void spinwait(uint64_t us) const;
|
||||||
|
|
||||||
|
/// Get the master clock
|
||||||
|
static clock & get() { return *s_instance; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t m_current; ///< current us count
|
||||||
|
uint64_t m_rate; ///< source ticks per us
|
||||||
|
void *m_data;
|
||||||
|
source m_source;
|
||||||
|
|
||||||
|
static clock *s_instance;
|
||||||
|
};
|
||||||
@@ -1,223 +1,24 @@
|
|||||||
#include "kutil/coord.h"
|
#include "kutil/coord.h"
|
||||||
#include "kutil/guid.h"
|
#include "kutil/guid.h"
|
||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
|
#include "kutil/no_construct.h"
|
||||||
#include "kutil/printf.h"
|
#include "kutil/printf.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "font.h"
|
|
||||||
#include "screen.h"
|
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
|
|
||||||
const char digits[] = "0123456789abcdef";
|
const char digits[] = "0123456789abcdef";
|
||||||
console g_console;
|
|
||||||
|
|
||||||
|
static kutil::no_construct<console> __g_console_storage;
|
||||||
class console::screen_out
|
console &g_console = __g_console_storage.value;
|
||||||
{
|
|
||||||
public:
|
|
||||||
screen_out(screen *s, font *f) :
|
|
||||||
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_data(nullptr),
|
|
||||||
m_attrs(nullptr),
|
|
||||||
m_palette(nullptr)
|
|
||||||
{
|
|
||||||
const unsigned count = m_size.size();
|
|
||||||
const size_t attrs_size = 2 * count;
|
|
||||||
|
|
||||||
m_data = new char[count];
|
|
||||||
kutil::memset(m_data, 0, count);
|
|
||||||
|
|
||||||
m_palette = new screen::pixel_t[256];
|
|
||||||
fill_palette();
|
|
||||||
|
|
||||||
m_attrs = new uint16_t[count];
|
|
||||||
set_color(7, 0); // Grey on black default
|
|
||||||
for (unsigned i = 0; i < count; ++i) m_attrs[i] = m_attr;
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
~screen_out()
|
|
||||||
{
|
|
||||||
delete [] m_data;
|
|
||||||
delete [] m_palette;
|
|
||||||
delete [] m_attrs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fill_palette()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
char *m_data;
|
|
||||||
uint16_t *m_attrs;
|
|
||||||
screen::pixel_t *m_palette;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
console::console() :
|
console::console() :
|
||||||
m_screen(nullptr),
|
|
||||||
m_serial(nullptr)
|
m_serial(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
console::console(serial_port *serial) :
|
console::console(serial_port *serial) :
|
||||||
m_screen(nullptr),
|
|
||||||
m_serial(serial)
|
m_serial(serial)
|
||||||
{
|
{
|
||||||
if (m_serial) {
|
if (m_serial) {
|
||||||
@@ -236,9 +37,6 @@ console::echo()
|
|||||||
void
|
void
|
||||||
console::set_color(uint8_t fg, uint8_t bg)
|
console::set_color(uint8_t fg, uint8_t bg)
|
||||||
{
|
{
|
||||||
if (m_screen)
|
|
||||||
m_screen->set_color(fg, bg);
|
|
||||||
|
|
||||||
if (m_serial) {
|
if (m_serial) {
|
||||||
const char *fgseq = "\x1b[38;5;";
|
const char *fgseq = "\x1b[38;5;";
|
||||||
while (*fgseq)
|
while (*fgseq)
|
||||||
@@ -258,21 +56,24 @@ console::set_color(uint8_t fg, uint8_t bg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
size_t
|
||||||
console::puts(const char *message)
|
console::puts(const char *message)
|
||||||
{
|
{
|
||||||
while (message && *message)
|
size_t n = 0;
|
||||||
|
while (message && *message) {
|
||||||
|
n++;
|
||||||
putc(*message++);
|
putc(*message++);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console::putc(char c)
|
console::putc(char c)
|
||||||
{
|
{
|
||||||
if (m_screen) m_screen->putc(c);
|
|
||||||
|
|
||||||
if (m_serial) {
|
if (m_serial) {
|
||||||
|
if (c == '\n') m_serial->write('\r');
|
||||||
m_serial->write(c);
|
m_serial->write(c);
|
||||||
if (c == '\r') m_serial->write('\n');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,9 +84,3 @@ void console::vprintf(const char *fmt, va_list args)
|
|||||||
vsnprintf_(buffer, buf_size, fmt, args);
|
vsnprintf_(buffer, buf_size, fmt, args);
|
||||||
puts(buffer);
|
puts(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
console::init_screen(screen *s, font *f)
|
|
||||||
{
|
|
||||||
m_screen = new screen_out(s, f);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <algorithm>
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class font;
|
|
||||||
struct kernel_data;
|
struct kernel_data;
|
||||||
class screen;
|
|
||||||
class serial_port;
|
class serial_port;
|
||||||
|
|
||||||
class console
|
class console
|
||||||
@@ -17,7 +14,7 @@ public:
|
|||||||
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
void set_color(uint8_t fg = 7, uint8_t bg = 0);
|
||||||
|
|
||||||
void putc(char c);
|
void putc(char c);
|
||||||
void puts(const char *message);
|
size_t puts(const char *message);
|
||||||
void vprintf(const char *fmt, va_list args);
|
void vprintf(const char *fmt, va_list args);
|
||||||
|
|
||||||
inline void printf(const char *fmt, ...)
|
inline void printf(const char *fmt, ...)
|
||||||
@@ -36,17 +33,13 @@ public:
|
|||||||
|
|
||||||
void echo();
|
void echo();
|
||||||
|
|
||||||
void init_screen(screen *s, font *f);
|
|
||||||
|
|
||||||
static console * get();
|
static console * get();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class screen_out;
|
|
||||||
screen_out *m_screen;
|
|
||||||
serial_port *m_serial;
|
serial_port *m_serial;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern console g_console;
|
extern console &g_console;
|
||||||
inline console * console::get() { return &g_console; }
|
inline console * console::get() { return &g_console; }
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +54,7 @@ void console::put_hex(T x, int width, char pad)
|
|||||||
int len = 1;
|
int len = 1;
|
||||||
for (int i = chars - 1; i >= 0; --i) {
|
for (int i = chars - 1; i >= 0; --i) {
|
||||||
uint8_t nibble = (x >> (i*4)) & 0xf;
|
uint8_t nibble = (x >> (i*4)) & 0xf;
|
||||||
if (nibble) len = std::max(i + 1, len);
|
if (nibble) len = len > i + 1 ? len : i + 1;
|
||||||
message[chars - i - 1] = digits[nibble];
|
message[chars - i - 1] = digits[nibble];
|
||||||
}
|
}
|
||||||
message[chars] = 0;
|
message[chars] = 0;
|
||||||
|
|||||||
@@ -1,116 +1,31 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "kutil/assert.h"
|
||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
#include "cpu/cpu.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
cpu_data bsp_cpu_data;
|
cpu_data bsp_cpu_data;
|
||||||
|
|
||||||
static constexpr uint32_t cpuid_extended = 0x80000000;
|
|
||||||
|
|
||||||
|
|
||||||
inline static void
|
|
||||||
__cpuid(
|
|
||||||
uint32_t leaf,
|
|
||||||
uint32_t subleaf,
|
|
||||||
uint32_t *eax,
|
|
||||||
uint32_t *ebx = nullptr,
|
|
||||||
uint32_t *ecx = nullptr,
|
|
||||||
uint32_t *edx = nullptr)
|
|
||||||
{
|
|
||||||
uint32_t a, b, c, d;
|
|
||||||
__asm__ __volatile__ ( "cpuid"
|
|
||||||
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
|
|
||||||
: "a"(leaf), "c"(subleaf)
|
|
||||||
);
|
|
||||||
if (eax) *eax = a;
|
|
||||||
if (ebx) *ebx = b;
|
|
||||||
if (ecx) *ecx = c;
|
|
||||||
if (edx) *edx = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_id::cpu_id() :
|
|
||||||
m_features(0)
|
|
||||||
{
|
|
||||||
__cpuid(0, 0,
|
|
||||||
&m_high_basic,
|
|
||||||
reinterpret_cast<uint32_t *>(&m_vendor_id[0]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
|
|
||||||
|
|
||||||
__cpuid(cpuid_extended, 0, &m_high_ext);
|
|
||||||
|
|
||||||
if (m_high_ext >= cpuid_extended + 4) {
|
|
||||||
__cpuid(cpuid_extended + 2, 0,
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[0]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[4]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[8]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[12]));
|
|
||||||
__cpuid(cpuid_extended + 3, 0,
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[16]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[20]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[24]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[28]));
|
|
||||||
__cpuid(cpuid_extended + 4, 0,
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[32]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[36]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[40]),
|
|
||||||
reinterpret_cast<uint32_t *>(&m_brand_name[44]));
|
|
||||||
} else {
|
|
||||||
m_brand_name[0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_id::regs
|
|
||||||
cpu_id::get(uint32_t leaf, uint32_t sub) const
|
|
||||||
{
|
|
||||||
regs ret {0, 0, 0, 0};
|
|
||||||
|
|
||||||
if ((leaf & cpuid_extended) == 0 && leaf > m_high_basic) return ret;
|
|
||||||
if ((leaf & cpuid_extended) != 0 && leaf > m_high_ext) return ret;
|
|
||||||
|
|
||||||
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
cpu_id::validate()
|
cpu_validate()
|
||||||
{
|
{
|
||||||
bool fail = false;
|
cpu::cpu_id cpu;
|
||||||
uint32_t leaf = 0;
|
|
||||||
uint32_t sub = 0;
|
|
||||||
regs r;
|
|
||||||
|
|
||||||
log::info(logs::boot, "CPU: %s", brand_name());
|
log::info(logs::boot, "CPU: %s", cpu.brand_name());
|
||||||
log::debug(logs::boot, " Vendor is %s", vendor_id());
|
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
|
||||||
|
|
||||||
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", highest_basic());
|
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
|
||||||
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", highest_ext() & ~cpuid_extended);
|
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
|
||||||
|
|
||||||
#define CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit) \
|
#define CPU_FEATURE_OPT(name, ...) \
|
||||||
if (leaf != feat_leaf || sub != feat_sub) { \
|
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no");
|
||||||
leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \
|
|
||||||
} \
|
|
||||||
if (r.regname & (1 << bit)) \
|
|
||||||
m_features |= (1 << static_cast<uint64_t>(cpu_feature::name)); \
|
|
||||||
log::debug(logs::boot, " Supports %9s: %s", #name, (r.regname & (1<<bit)) ? "yes" : "no");
|
|
||||||
|
|
||||||
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
|
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
|
||||||
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
|
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
|
||||||
if ((r.regname & (1 << bit)) == 0) { \
|
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name );
|
||||||
log::error(logs::boot, "CPU missing required feature " #name); \
|
|
||||||
fail = true; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "cpu_features.inc"
|
#include "cpu/features.inc"
|
||||||
#undef CPU_FEATURE_OPT
|
#undef CPU_FEATURE_OPT
|
||||||
#undef CPU_FEATURE_REQ
|
#undef CPU_FEATURE_REQ
|
||||||
|
|
||||||
if (fail)
|
|
||||||
log::fatal(logs::boot, "CPU not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
cpu_id::has_feature(cpu_feature feat)
|
|
||||||
{
|
|
||||||
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct process;
|
struct TCB;
|
||||||
|
class thread;
|
||||||
|
class process;
|
||||||
|
|
||||||
struct cpu_state
|
struct cpu_state
|
||||||
{
|
{
|
||||||
@@ -18,74 +20,13 @@ struct cpu_data
|
|||||||
{
|
{
|
||||||
uintptr_t rsp0;
|
uintptr_t rsp0;
|
||||||
uintptr_t rsp3;
|
uintptr_t rsp3;
|
||||||
process *tcb;
|
TCB *tcb;
|
||||||
|
thread *t;
|
||||||
|
process *p;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern cpu_data bsp_cpu_data;
|
extern cpu_data bsp_cpu_data;
|
||||||
|
|
||||||
/// Enum of the cpu features jsix cares about
|
// We already validated the required options in the bootloader,
|
||||||
enum class cpu_feature {
|
// but iterate the options and log about them.
|
||||||
#define CPU_FEATURE_REQ(name, ...) name,
|
void cpu_validate();
|
||||||
#define CPU_FEATURE_OPT(name, ...) name,
|
|
||||||
#include "cpu_features.inc"
|
|
||||||
#undef CPU_FEATURE_OPT
|
|
||||||
#undef CPU_FEATURE_REQ
|
|
||||||
max
|
|
||||||
};
|
|
||||||
|
|
||||||
class cpu_id
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// CPUID result register values
|
|
||||||
struct regs {
|
|
||||||
union {
|
|
||||||
uint32_t reg[4];
|
|
||||||
uint32_t eax, ebx, ecx, edx;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Return true if bit |bit| of EAX is set
|
|
||||||
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
|
|
||||||
|
|
||||||
/// Return true if bit |bit| of EBX is set
|
|
||||||
bool ebx_bit(unsigned bit) { return (ebx >> bit) & 0x1; }
|
|
||||||
|
|
||||||
/// Return true if bit |bit| of ECX is set
|
|
||||||
bool ecx_bit(unsigned bit) { return (ecx >> bit) & 0x1; }
|
|
||||||
|
|
||||||
/// Return true if bit |bit| of EDX is set
|
|
||||||
bool edx_bit(unsigned bit) { return (edx >> bit) & 0x1; }
|
|
||||||
};
|
|
||||||
|
|
||||||
cpu_id();
|
|
||||||
|
|
||||||
/// The the result of a given CPUID leaf/subleaf
|
|
||||||
/// \arg leaf The leaf selector (initial EAX)
|
|
||||||
/// \arg subleaf The subleaf selector (initial ECX)
|
|
||||||
/// \returns A |regs| struct of the values retuned
|
|
||||||
regs get(uint32_t leaf, uint32_t sub = 0) const;
|
|
||||||
|
|
||||||
/// Get the name of the cpu vendor (eg, "GenuineIntel")
|
|
||||||
inline const char * vendor_id() const { return m_vendor_id; }
|
|
||||||
|
|
||||||
/// Get the brand name of this processor model
|
|
||||||
inline const char * brand_name() const { return m_brand_name; }
|
|
||||||
|
|
||||||
/// Get the highest basic CPUID leaf supported
|
|
||||||
inline uint32_t highest_basic() const { return m_high_basic; }
|
|
||||||
|
|
||||||
/// Get the highest extended CPUID leaf supported
|
|
||||||
inline uint32_t highest_ext() const { return m_high_ext; }
|
|
||||||
|
|
||||||
/// Validate the CPU supports the necessary options for jsix
|
|
||||||
void validate();
|
|
||||||
|
|
||||||
/// Return true if the CPU claims to support the given feature
|
|
||||||
bool has_feature(cpu_feature feat);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32_t m_high_basic;
|
|
||||||
uint32_t m_high_ext;
|
|
||||||
char m_vendor_id[13];
|
|
||||||
char m_brand_name[48];
|
|
||||||
uint64_t m_features;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
CPU_FEATURE_OPT(pcid, 0x00000001, 0, ecx, 17)
|
|
||||||
CPU_FEATURE_OPT(x2apic, 0x00000001, 0, ecx, 21)
|
|
||||||
CPU_FEATURE_REQ(fpu, 0x00000001, 0, edx, 0)
|
|
||||||
CPU_FEATURE_REQ(pse, 0x00000001, 0, edx, 3)
|
|
||||||
CPU_FEATURE_OPT(tsc, 0x00000001, 0, edx, 4)
|
|
||||||
CPU_FEATURE_REQ(msr, 0x00000001, 0, edx, 5)
|
|
||||||
CPU_FEATURE_REQ(apic, 0x00000001, 0, edx, 9)
|
|
||||||
CPU_FEATURE_REQ(pge, 0x00000001, 0, edx, 13)
|
|
||||||
CPU_FEATURE_OPT(pat, 0x00000001, 0, edx, 16)
|
|
||||||
CPU_FEATURE_REQ(fxsr, 0x00000001, 0, edx, 24)
|
|
||||||
CPU_FEATURE_OPT(fsgsbase, 0x00000007, 0, ebx, 0)
|
|
||||||
CPU_FEATURE_OPT(invpcid, 0x00000007, 0, ebx, 10)
|
|
||||||
CPU_FEATURE_OPT(pku, 0x00000007, 0, ecx, 3)
|
|
||||||
CPU_FEATURE_REQ(syscall, 0x80000001, 0, edx, 11)
|
|
||||||
CPU_FEATURE_REQ(pdpe1gb, 0x80000001, 0, edx, 26)
|
|
||||||
CPU_FEATURE_OPT(extapic, 0x80000001, 0, ecx, 3)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
section .init
|
|
||||||
global _init:function
|
|
||||||
_init:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
; Control flow falls through to other .init sections
|
|
||||||
|
|
||||||
section .fini
|
|
||||||
global _fini:function
|
|
||||||
_fini:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
; Control flow falls through to other .fini sections
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
section .init
|
|
||||||
; Control flow falls through to here from other .init sections
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
section .fini
|
|
||||||
; Control flow falls through to here from other .fini sections
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
@@ -2,7 +2,9 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
#include "page_manager.h"
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
|
#include "symbol_table.h"
|
||||||
|
|
||||||
size_t __counter_syscall_enter = 0;
|
size_t __counter_syscall_enter = 0;
|
||||||
size_t __counter_syscall_sysret = 0;
|
size_t __counter_syscall_sysret = 0;
|
||||||
@@ -15,6 +17,12 @@ print_regs(const cpu_state ®s)
|
|||||||
uint64_t cr2 = 0;
|
uint64_t cr2 = 0;
|
||||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
|
uintptr_t cr3 = 0;
|
||||||
|
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||||
|
|
||||||
|
cons->printf(" process: %llx", bsp_cpu_data.p->koid());
|
||||||
|
cons->printf(" thread: %llx\n", bsp_cpu_data.t->koid());
|
||||||
|
|
||||||
print_regL("rax", regs.rax);
|
print_regL("rax", regs.rax);
|
||||||
print_regM("rbx", regs.rbx);
|
print_regM("rbx", regs.rbx);
|
||||||
print_regR("rcx", regs.rcx);
|
print_regR("rcx", regs.rcx);
|
||||||
@@ -38,7 +46,7 @@ print_regs(const cpu_state ®s)
|
|||||||
print_regR("sp0", bsp_cpu_data.rsp0);
|
print_regR("sp0", bsp_cpu_data.rsp0);
|
||||||
|
|
||||||
print_regL("rip", regs.rip);
|
print_regL("rip", regs.rip);
|
||||||
print_regM("cr3", page_manager::get()->get_pml4());
|
print_regM("cr3", cr3);
|
||||||
print_regR("cr2", cr2);
|
print_regR("cr2", cr2);
|
||||||
|
|
||||||
cons->puts("\n");
|
cons->puts("\n");
|
||||||
@@ -54,14 +62,26 @@ void
|
|||||||
print_stacktrace(int skip)
|
print_stacktrace(int skip)
|
||||||
{
|
{
|
||||||
console *cons = console::get();
|
console *cons = console::get();
|
||||||
|
symbol_table *syms = symbol_table::get();
|
||||||
|
|
||||||
frame *fp = nullptr;
|
frame *fp = nullptr;
|
||||||
int fi = -skip;
|
int fi = -skip;
|
||||||
__asm__ __volatile__ ( "mov %%rbp, %0" : "=r" (fp) );
|
__asm__ __volatile__ ( "mov %%rbp, %0" : "=r" (fp) );
|
||||||
|
|
||||||
while (fp && fp->return_addr) {
|
while (fp && fp->return_addr) {
|
||||||
if (fi++ >= 0)
|
if (fi++ >= 0) {
|
||||||
cons->printf(" frame %2d: %lx\n", fi-1, fp->return_addr);
|
const symbol_table::entry *e = syms ? syms->find_symbol(fp->return_addr) : nullptr;
|
||||||
|
const char *name = e ? e->name : "";
|
||||||
|
cons->printf(" frame %2d:", fi-1);
|
||||||
|
|
||||||
|
cons->set_color(5);
|
||||||
|
cons->printf(" %016llx", fp->return_addr);
|
||||||
|
cons->set_color();
|
||||||
|
|
||||||
|
cons->set_color(6);
|
||||||
|
cons->printf(" %s\n", name);
|
||||||
|
cons->set_color();
|
||||||
|
}
|
||||||
fp = fp->prev;
|
fp = fp->prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
uintptr_t get_rsp();
|
uintptr_t get_rsp();
|
||||||
uintptr_t get_rip();
|
uintptr_t get_rip();
|
||||||
|
uintptr_t get_caller();
|
||||||
uintptr_t get_frame(int frame);
|
uintptr_t get_frame(int frame);
|
||||||
uintptr_t get_gsbase();
|
uintptr_t get_gsbase();
|
||||||
void _halt();
|
void _halt();
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ get_rip:
|
|||||||
pop rax ; do the same thing as 'ret', except with 'jmp'
|
pop rax ; do the same thing as 'ret', except with 'jmp'
|
||||||
jmp rax ; with the return address still in rax
|
jmp rax ; with the return address still in rax
|
||||||
|
|
||||||
|
global get_caller
|
||||||
|
get_caller:
|
||||||
|
; No prelude - don't touch rsp or rbp
|
||||||
|
mov rax, [rbp+8]
|
||||||
|
ret
|
||||||
|
|
||||||
global get_gsbase
|
global get_gsbase
|
||||||
get_gsbase:
|
get_gsbase:
|
||||||
rdgsbase rax
|
rdgsbase rax
|
||||||
|
|||||||
@@ -5,16 +5,20 @@
|
|||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
#include "acpi_tables.h"
|
#include "acpi_tables.h"
|
||||||
#include "apic.h"
|
#include "apic.h"
|
||||||
|
#include "clock.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "device_manager.h"
|
#include "device_manager.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
#include "objects/endpoint.h"
|
||||||
|
|
||||||
|
|
||||||
|
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
|
||||||
|
|
||||||
static const char expected_signature[] = "RSD PTR ";
|
static const char expected_signature[] = "RSD PTR ";
|
||||||
|
|
||||||
device_manager device_manager::s_instance(nullptr, kutil::allocator::invalid);
|
device_manager device_manager::s_instance;
|
||||||
|
|
||||||
struct acpi1_rsdp
|
struct acpi1_rsdp
|
||||||
{
|
{
|
||||||
@@ -46,7 +50,6 @@ acpi_table_header::validate(uint32_t expected_type) const
|
|||||||
return !expected_type || (expected_type == type);
|
return !expected_type || (expected_type == type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void irq2_callback(void *)
|
void irq2_callback(void *)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -59,13 +62,19 @@ void irq4_callback(void *)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
device_manager::device_manager(const void *root_table, kutil::allocator &alloc) :
|
device_manager::device_manager() :
|
||||||
m_lapic(nullptr),
|
m_lapic(0)
|
||||||
m_ioapics(alloc),
|
{
|
||||||
m_pci(alloc),
|
m_irqs.ensure_capacity(32);
|
||||||
m_devices(alloc),
|
m_irqs.set_size(16);
|
||||||
m_irqs(alloc),
|
for (int i = 0; i < 16; ++i)
|
||||||
m_blockdevs(alloc)
|
m_irqs[i] = nullptr;
|
||||||
|
|
||||||
|
m_irqs[2] = ignore_endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
device_manager::parse_acpi(const void *root_table)
|
||||||
{
|
{
|
||||||
kassert(root_table != 0, "ACPI root table pointer is null.");
|
kassert(root_table != 0, "ACPI root table pointer is null.");
|
||||||
|
|
||||||
@@ -88,11 +97,6 @@ device_manager::device_manager(const void *root_table, kutil::allocator &alloc)
|
|||||||
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
|
||||||
|
|
||||||
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
|
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
|
||||||
|
|
||||||
m_irqs.ensure_capacity(32);
|
|
||||||
m_irqs.set_size(16);
|
|
||||||
m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr};
|
|
||||||
m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ioapic *
|
ioapic *
|
||||||
@@ -136,6 +140,10 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
|
|||||||
load_mcfg(reinterpret_cast<const acpi_mcfg *>(header));
|
load_mcfg(reinterpret_cast<const acpi_mcfg *>(header));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case acpi_hpet::type_id:
|
||||||
|
load_hpet(reinterpret_cast<const acpi_hpet *>(header));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -145,8 +153,7 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
|
|||||||
void
|
void
|
||||||
device_manager::load_apic(const acpi_apic *apic)
|
device_manager::load_apic(const acpi_apic *apic)
|
||||||
{
|
{
|
||||||
uint32_t *local = reinterpret_cast<uint32_t *>(apic->local_address);
|
uintptr_t local = apic->local_address;
|
||||||
|
|
||||||
m_lapic = new lapic(local, isr::isrSpurious);
|
m_lapic = new lapic(local, isr::isrSpurious);
|
||||||
|
|
||||||
size_t count = acpi_table_entries(apic, 1);
|
size_t count = acpi_table_entries(apic, 1);
|
||||||
@@ -170,7 +177,7 @@ device_manager::load_apic(const acpi_apic *apic)
|
|||||||
const uint8_t type = p[0];
|
const uint8_t type = p[0];
|
||||||
const uint8_t length = p[1];
|
const uint8_t length = p[1];
|
||||||
if (type == 1) {
|
if (type == 1) {
|
||||||
uint32_t *base = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
|
uintptr_t base = kutil::read_from<uint32_t>(p+4);
|
||||||
uint32_t base_gsr = kutil::read_from<uint32_t>(p+8);
|
uint32_t base_gsr = kutil::read_from<uint32_t>(p+8);
|
||||||
m_ioapics.emplace(base, base_gsr);
|
m_ioapics.emplace(base, base_gsr);
|
||||||
}
|
}
|
||||||
@@ -246,21 +253,13 @@ device_manager::load_mcfg(const acpi_mcfg *mcfg)
|
|||||||
m_pci.set_size(count);
|
m_pci.set_size(count);
|
||||||
m_devices.set_capacity(16);
|
m_devices.set_capacity(16);
|
||||||
|
|
||||||
page_manager *pm = page_manager::get();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < count; ++i) {
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
|
||||||
|
|
||||||
m_pci[i].group = mcfge.group;
|
m_pci[i].group = mcfge.group;
|
||||||
m_pci[i].bus_start = mcfge.bus_start;
|
m_pci[i].bus_start = mcfge.bus_start;
|
||||||
m_pci[i].bus_end = mcfge.bus_end;
|
m_pci[i].bus_end = mcfge.bus_end;
|
||||||
m_pci[i].base = reinterpret_cast<uint32_t *>(mcfge.base);
|
m_pci[i].base = memory::to_virtual<uint32_t>(mcfge.base);
|
||||||
|
|
||||||
int num_busses = m_pci[i].bus_end - m_pci[i].bus_start + 1;
|
|
||||||
|
|
||||||
/// Map the MMIO space into memory
|
|
||||||
pm->map_offset_pointer(reinterpret_cast<void **>(&m_pci[i].base),
|
|
||||||
(num_busses << 20));
|
|
||||||
|
|
||||||
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
|
||||||
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
|
||||||
@@ -269,6 +268,27 @@ device_manager::load_mcfg(const acpi_mcfg *mcfg)
|
|||||||
probe_pci();
|
probe_pci();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
device_manager::load_hpet(const acpi_hpet *hpet)
|
||||||
|
{
|
||||||
|
log::debug(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
|
||||||
|
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
|
||||||
|
|
||||||
|
uint32_t hwid = hpet->hardware_id;
|
||||||
|
uint8_t rev_id = hwid & 0xff;
|
||||||
|
uint8_t comparators = (hwid >> 8) & 0x1f;
|
||||||
|
uint8_t count_size_cap = (hwid >> 13) & 1;
|
||||||
|
uint8_t legacy_replacement = (hwid >> 15) & 1;
|
||||||
|
uint32_t pci_vendor_id = (hwid >> 16);
|
||||||
|
|
||||||
|
log::debug(logs::device, " rev:%02d comparators:%02d count_size_cap:%1d legacy_repl:%1d",
|
||||||
|
rev_id, comparators, count_size_cap, legacy_replacement);
|
||||||
|
log::debug(logs::device, " pci vendor id: %04x", pci_vendor_id);
|
||||||
|
|
||||||
|
m_hpets.emplace(hpet->index,
|
||||||
|
reinterpret_cast<uint64_t*>(hpet->base_address.address + ::memory::page_offset));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
device_manager::probe_pci()
|
device_manager::probe_pci()
|
||||||
{
|
{
|
||||||
@@ -309,11 +329,59 @@ device_manager::init_drivers()
|
|||||||
ahcid.register_device(&device);
|
ahcid.register_device(&device);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
if (m_hpets.count() > 0) {
|
||||||
|
hpet &h = m_hpets[0];
|
||||||
|
h.enable();
|
||||||
|
|
||||||
|
// becomes the singleton
|
||||||
|
clock *master_clock = new clock(h.rate(), hpet_clock_source, &h);
|
||||||
|
kassert(master_clock, "Failed to allocate master clock");
|
||||||
|
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
|
||||||
|
} else {
|
||||||
|
//TODO: APIC clock?
|
||||||
|
kassert(0, "No HPET master clock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
device_manager::dispatch_irq(unsigned irq)
|
||||||
|
{
|
||||||
|
if (irq >= m_irqs.count())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
endpoint *e = m_irqs[irq];
|
||||||
|
if (!e || e == ignore_endpoint)
|
||||||
|
return e == ignore_endpoint;
|
||||||
|
|
||||||
|
e->signal_irq(irq);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
device_manager::bind_irq(unsigned irq, endpoint *target)
|
||||||
|
{
|
||||||
|
// TODO: grow if under max size
|
||||||
|
if (irq >= m_irqs.count())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_irqs[irq]= target;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
device_manager::unbind_irqs(endpoint *target)
|
||||||
|
{
|
||||||
|
const size_t count = m_irqs.count();
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
if (m_irqs[i] == target)
|
||||||
|
m_irqs[i] = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
// TODO: find gaps to fill
|
// TODO: find gaps to fill
|
||||||
uint8_t irq = m_irqs.count();
|
uint8_t irq = m_irqs.count();
|
||||||
isr vector = isr::irq00 + irq;
|
isr vector = isr::irq00 + irq;
|
||||||
@@ -324,6 +392,7 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
|
|||||||
device.write_msi_regs(
|
device.write_msi_regs(
|
||||||
0xFEE00000,
|
0xFEE00000,
|
||||||
static_cast<uint16_t>(vector));
|
static_cast<uint16_t>(vector));
|
||||||
|
*/
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,15 @@
|
|||||||
/// The device manager definition
|
/// The device manager definition
|
||||||
#include "kutil/vector.h"
|
#include "kutil/vector.h"
|
||||||
#include "apic.h"
|
#include "apic.h"
|
||||||
|
#include "hpet.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
|
||||||
struct acpi_xsdt;
|
struct acpi_xsdt;
|
||||||
struct acpi_apic;
|
struct acpi_apic;
|
||||||
struct acpi_mcfg;
|
struct acpi_mcfg;
|
||||||
|
struct acpi_hpet;
|
||||||
class block_device;
|
class block_device;
|
||||||
|
class endpoint;
|
||||||
|
|
||||||
using irq_callback = void (*)(void *);
|
using irq_callback = void (*)(void *);
|
||||||
|
|
||||||
@@ -18,9 +21,7 @@ class device_manager
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg root_table Pointer to the ACPI RSDP
|
device_manager();
|
||||||
/// \arg alloc Allocator for device arrays
|
|
||||||
device_manager(const void *root_table, kutil::allocator &alloc);
|
|
||||||
|
|
||||||
/// Get the system global device manager.
|
/// Get the system global device manager.
|
||||||
/// \returns A reference to the system device manager
|
/// \returns A reference to the system device manager
|
||||||
@@ -36,9 +37,23 @@ public:
|
|||||||
/// otherwise nullptr.
|
/// otherwise nullptr.
|
||||||
ioapic * get_ioapic(int i);
|
ioapic * get_ioapic(int i);
|
||||||
|
|
||||||
|
/// Parse ACPI tables.
|
||||||
|
/// \arg root_table Pointer to the ACPI RSDP
|
||||||
|
void parse_acpi(const void *root_table);
|
||||||
|
|
||||||
/// Intialize drivers for the current device list.
|
/// Intialize drivers for the current device list.
|
||||||
void init_drivers();
|
void init_drivers();
|
||||||
|
|
||||||
|
/// Bind an IRQ to an endpoint
|
||||||
|
/// \arg irq The IRQ number to bind
|
||||||
|
/// \arg target The endpoint to recieve messages when the IRQ is signalled
|
||||||
|
/// \returns True on success
|
||||||
|
bool bind_irq(unsigned irq, endpoint *target);
|
||||||
|
|
||||||
|
/// Remove IRQ bindings for an endpoint
|
||||||
|
/// \arg target The endpoint to remove
|
||||||
|
void unbind_irqs(endpoint *target);
|
||||||
|
|
||||||
/// Allocate an MSI IRQ for a device
|
/// Allocate an MSI IRQ for a device
|
||||||
/// \arg name Name of the interrupt, for display to user
|
/// \arg name Name of the interrupt, for display to user
|
||||||
/// \arg device Device this MSI is being allocated for
|
/// \arg device Device this MSI is being allocated for
|
||||||
@@ -54,17 +69,7 @@ public:
|
|||||||
/// Dispatch an IRQ interrupt
|
/// Dispatch an IRQ interrupt
|
||||||
/// \arg irq The irq number of the interrupt
|
/// \arg irq The irq number of the interrupt
|
||||||
/// \returns True if the interrupt was handled
|
/// \returns True if the interrupt was handled
|
||||||
inline bool dispatch_irq(uint8_t irq)
|
bool dispatch_irq(unsigned irq);
|
||||||
{
|
|
||||||
if (irq < m_irqs.count()) {
|
|
||||||
irq_allocation &cba = m_irqs[irq];
|
|
||||||
if (cba.callback) {
|
|
||||||
cba.callback(cba.data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register the existance of a block device.
|
/// Register the existance of a block device.
|
||||||
/// \arg blockdev Pointer to the block device
|
/// \arg blockdev Pointer to the block device
|
||||||
@@ -83,6 +88,15 @@ public:
|
|||||||
m_blockdevs[i] : nullptr;
|
m_blockdevs[i] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an HPET device
|
||||||
|
/// \arg i Index of the device to get
|
||||||
|
/// \returns A pointer to the requested device, or nullptr
|
||||||
|
inline hpet * get_hpet(unsigned i)
|
||||||
|
{
|
||||||
|
return i < m_hpets.count() ?
|
||||||
|
&m_hpets[i] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Parse the ACPI XSDT and load relevant sub-tables.
|
/// Parse the ACPI XSDT and load relevant sub-tables.
|
||||||
/// \arg xsdt Pointer to the XSDT from the firmware
|
/// \arg xsdt Pointer to the XSDT from the firmware
|
||||||
@@ -96,6 +110,10 @@ private:
|
|||||||
/// \arg mcfg Pointer to the MCFG from the XSDT
|
/// \arg mcfg Pointer to the MCFG from the XSDT
|
||||||
void load_mcfg(const acpi_mcfg *mcfg);
|
void load_mcfg(const acpi_mcfg *mcfg);
|
||||||
|
|
||||||
|
/// Parse the ACPI HPET and initialize an HPET from it.
|
||||||
|
/// \arg hpet Pointer to the HPET from the XSDT
|
||||||
|
void load_hpet(const acpi_hpet *hpet);
|
||||||
|
|
||||||
/// Probe the PCIe busses and add found devices to our
|
/// Probe the PCIe busses and add found devices to our
|
||||||
/// device list. The device list is destroyed and rebuilt.
|
/// device list. The device list is destroyed and rebuilt.
|
||||||
void probe_pci();
|
void probe_pci();
|
||||||
@@ -106,23 +124,17 @@ private:
|
|||||||
|
|
||||||
lapic *m_lapic;
|
lapic *m_lapic;
|
||||||
kutil::vector<ioapic> m_ioapics;
|
kutil::vector<ioapic> m_ioapics;
|
||||||
|
kutil::vector<hpet> m_hpets;
|
||||||
|
|
||||||
kutil::vector<pci_group> m_pci;
|
kutil::vector<pci_group> m_pci;
|
||||||
kutil::vector<pci_device> m_devices;
|
kutil::vector<pci_device> m_devices;
|
||||||
|
|
||||||
struct irq_allocation
|
kutil::vector<endpoint*> m_irqs;
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
irq_callback callback;
|
|
||||||
void *data;
|
|
||||||
};
|
|
||||||
kutil::vector<irq_allocation> m_irqs;
|
|
||||||
|
|
||||||
kutil::vector<block_device *> m_blockdevs;
|
kutil::vector<block_device *> m_blockdevs;
|
||||||
|
|
||||||
static device_manager s_instance;
|
static device_manager s_instance;
|
||||||
|
|
||||||
device_manager() = delete;
|
|
||||||
device_manager(const device_manager &) = delete;
|
device_manager(const device_manager &) = delete;
|
||||||
device_manager operator=(const device_manager &) = delete;
|
device_manager operator=(const device_manager &) = delete;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "kutil/coord.h"
|
|
||||||
#include "screen.h"
|
|
||||||
|
|
||||||
class font
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
font(void const *data);
|
|
||||||
|
|
||||||
unsigned glyph_bytes() const { return m_size.y * ((m_size.x + 7) / 8); }
|
|
||||||
unsigned count() const { return m_count; }
|
|
||||||
unsigned width() const { return m_size.x; }
|
|
||||||
unsigned height() const { return m_size.y; }
|
|
||||||
bool valid() const { return m_count > 0; }
|
|
||||||
|
|
||||||
void draw_glyph(
|
|
||||||
screen *s,
|
|
||||||
uint32_t glyph,
|
|
||||||
screen::pixel_t fg,
|
|
||||||
screen::pixel_t bg,
|
|
||||||
unsigned x,
|
|
||||||
unsigned y) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
kutil::coord<unsigned> m_size;
|
|
||||||
unsigned m_count;
|
|
||||||
uint8_t const *m_data;
|
|
||||||
|
|
||||||
font() = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include "kernel_memory.h"
|
||||||
#include "kutil/assert.h"
|
#include "kutil/assert.h"
|
||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
#include "frame_allocator.h"
|
#include "frame_allocator.h"
|
||||||
@@ -6,43 +7,25 @@ using memory::frame_size;
|
|||||||
using memory::page_offset;
|
using memory::page_offset;
|
||||||
using frame_block_node = kutil::list_node<frame_block>;
|
using frame_block_node = kutil::list_node<frame_block>;
|
||||||
|
|
||||||
frame_allocator g_frame_allocator;
|
|
||||||
|
|
||||||
int
|
int
|
||||||
frame_block::compare(const frame_block *rhs) const
|
frame_block::compare(const frame_block &rhs) const
|
||||||
{
|
{
|
||||||
if (address < rhs->address)
|
if (address < rhs.address)
|
||||||
return -1;
|
return -1;
|
||||||
else if (address > rhs->address)
|
else if (address > rhs.address)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
frame_allocator::raw_alloc::raw_alloc(frame_allocator &fa) : m_fa(fa) {}
|
frame_allocator &
|
||||||
|
frame_allocator::get()
|
||||||
void *
|
|
||||||
frame_allocator::raw_alloc::allocate(size_t size)
|
|
||||||
{
|
{
|
||||||
kassert(size <= frame_size, "Raw allocator only allocates a single page");
|
extern frame_allocator &g_frame_allocator;
|
||||||
|
return g_frame_allocator;
|
||||||
uintptr_t addr = 0;
|
|
||||||
if (size <= frame_size)
|
|
||||||
m_fa.allocate(1, &addr);
|
|
||||||
return reinterpret_cast<void*>(addr + page_offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
frame_allocator::frame_allocator() {}
|
||||||
frame_allocator::raw_alloc::free(void *p)
|
|
||||||
{
|
|
||||||
m_fa.free(reinterpret_cast<uintptr_t>(p), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
frame_allocator::frame_allocator() :
|
|
||||||
m_raw_alloc(*this)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
frame_allocator::allocate(size_t count, uintptr_t *address)
|
frame_allocator::allocate(size_t count, uintptr_t *address)
|
||||||
@@ -69,6 +52,8 @@ inline uintptr_t end(frame_block *node) { return node->address + node->count * f
|
|||||||
void
|
void
|
||||||
frame_allocator::free(uintptr_t address, size_t count)
|
frame_allocator::free(uintptr_t address, size_t count)
|
||||||
{
|
{
|
||||||
|
kassert(address % frame_size == 0, "Trying to free a non page-aligned frame!");
|
||||||
|
|
||||||
frame_block_node *node =
|
frame_block_node *node =
|
||||||
reinterpret_cast<frame_block_node*>(address + page_offset);
|
reinterpret_cast<frame_block_node*>(address + page_offset);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "kutil/allocator.h"
|
|
||||||
#include "kutil/linked_list.h"
|
#include "kutil/linked_list.h"
|
||||||
|
|
||||||
struct frame_block;
|
struct frame_block;
|
||||||
@@ -30,23 +29,10 @@ public:
|
|||||||
/// \arg count The number of frames to be freed
|
/// \arg count The number of frames to be freed
|
||||||
void free(uintptr_t address, size_t count);
|
void free(uintptr_t address, size_t count);
|
||||||
|
|
||||||
/// Get a memory allocator that allocates raw pages
|
/// Get the global frame allocator
|
||||||
/// \returns The allocator ojbect
|
static frame_allocator & get();
|
||||||
kutil::allocator & raw_allocator() { return m_raw_alloc; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class raw_alloc :
|
|
||||||
public kutil::allocator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
raw_alloc(frame_allocator &fa);
|
|
||||||
virtual void * allocate(size_t size) override;
|
|
||||||
virtual void free(void *p) override;
|
|
||||||
private:
|
|
||||||
frame_allocator &m_fa;
|
|
||||||
};
|
|
||||||
|
|
||||||
raw_alloc m_raw_alloc;
|
|
||||||
frame_block_list m_free; ///< Free frames list
|
frame_block_list m_free; ///< Free frames list
|
||||||
|
|
||||||
frame_allocator(const frame_allocator &) = delete;
|
frame_allocator(const frame_allocator &) = delete;
|
||||||
@@ -63,8 +49,6 @@ struct frame_block
|
|||||||
/// Compare two blocks by address.
|
/// Compare two blocks by address.
|
||||||
/// \arg rhs The right-hand comparator
|
/// \arg rhs The right-hand comparator
|
||||||
/// \returns <0 if this is sorts earlier, >0 if this sorts later, 0 for equal
|
/// \returns <0 if this is sorts earlier, >0 if this sorts later, 0 for equal
|
||||||
int compare(const frame_block *rhs) const;
|
int compare(const frame_block &rhs) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
extern frame_allocator g_frame_allocator;
|
|
||||||
|
|||||||
119
src/kernel/hpet.cpp
Normal file
119
src/kernel/hpet.cpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include "kernel_memory.h"
|
||||||
|
#include "kutil/assert.h"
|
||||||
|
#include "device_manager.h"
|
||||||
|
#include "hpet.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
inline uint64_t volatile *capabilities(uint64_t volatile *base) { return base; }
|
||||||
|
inline uint64_t volatile *configuration(uint64_t volatile *base) { return base+2; }
|
||||||
|
inline uint64_t volatile *interrupt_status(uint64_t volatile *base) { return base+4; }
|
||||||
|
inline uint64_t volatile *counter_value(uint64_t volatile *base) { return base+30; }
|
||||||
|
|
||||||
|
inline uint64_t volatile *timer_base(uint64_t volatile *base, unsigned i) { return base + 0x20 + (4*i); }
|
||||||
|
inline uint64_t volatile *timer_config(uint64_t volatile *base, unsigned i) { return timer_base(base, i); }
|
||||||
|
inline uint64_t volatile *timer_comparator(uint64_t volatile *base, unsigned i) { return timer_base(base, i) + 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
hpet_irq_callback(void *hpet_ptr)
|
||||||
|
{
|
||||||
|
if (hpet_ptr)
|
||||||
|
reinterpret_cast<hpet*>(hpet_ptr)->callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hpet::hpet(uint8_t index, uint64_t *base) :
|
||||||
|
m_index(index),
|
||||||
|
m_base(base)
|
||||||
|
{
|
||||||
|
*configuration(m_base) = 0;
|
||||||
|
*counter_value(m_base) = 0;
|
||||||
|
|
||||||
|
uint64_t caps = *capabilities(base);
|
||||||
|
uint64_t config = *configuration(base);
|
||||||
|
m_timers = ((caps >> 8) & 0x1f) + 1;
|
||||||
|
m_period = (caps >> 32);
|
||||||
|
|
||||||
|
setup_timer_interrupts(0, 2, 1000, true);
|
||||||
|
// bool installed = device_manager::get()
|
||||||
|
// .install_irq(2, "HPET Timer", hpet_irq_callback, this);
|
||||||
|
// kassert(installed, "Installing HPET IRQ handler");
|
||||||
|
|
||||||
|
log::debug(logs::timer, "HPET %d capabilities:", index);
|
||||||
|
log::debug(logs::timer, " revision: %d", caps & 0xff);
|
||||||
|
log::debug(logs::timer, " timers: %d", m_timers);
|
||||||
|
log::debug(logs::timer, " bits: %d", ((caps >> 13) & 1) ? 64 : 32);
|
||||||
|
log::debug(logs::timer, " LRR capable: %d", ((caps >> 15) & 1));
|
||||||
|
log::debug(logs::timer, " period: %dns", m_period / 1000000);
|
||||||
|
log::debug(logs::timer, " global enabled: %d", config & 1);
|
||||||
|
log::debug(logs::timer, " LRR enable: %d", (config >> 1) & 1);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < m_timers; ++i) {
|
||||||
|
disable_timer(i);
|
||||||
|
uint64_t config = *timer_config(m_base, i);
|
||||||
|
|
||||||
|
log::debug(logs::timer, "HPET %d timer %d:", index, i);
|
||||||
|
log::debug(logs::timer, " int type: %d", (config >> 1) & 1);
|
||||||
|
log::debug(logs::timer, " int enable: %d", (config >> 2) & 1);
|
||||||
|
log::debug(logs::timer, " timer type: %d", (config >> 3) & 1);
|
||||||
|
log::debug(logs::timer, " periodic cap: %d", (config >> 4) & 1);
|
||||||
|
log::debug(logs::timer, " bits: %d", ((config >> 5) & 1) ? 64 : 32);
|
||||||
|
log::debug(logs::timer, " 32 mode: %d", (config >> 8) & 1);
|
||||||
|
log::debug(logs::timer, " int route: %d", (config >> 9) & 0x1f);
|
||||||
|
log::debug(logs::timer, " FSB enable: %d", (config >> 14) & 1);
|
||||||
|
log::debug(logs::timer, " FSB capable: %d", (config >> 15) & 1);
|
||||||
|
log::debug(logs::timer, " rotung cap: %08x", (config >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hpet::setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic)
|
||||||
|
{
|
||||||
|
constexpr uint64_t femto_per_us = 1000000000ull;
|
||||||
|
*timer_comparator(m_base, timer) =
|
||||||
|
*counter_value(m_base) +
|
||||||
|
(interval * femto_per_us) / m_period;
|
||||||
|
|
||||||
|
*timer_config(m_base, timer) = (irq << 9) | ((periodic ? 1 : 0) << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hpet::enable_timer(unsigned timer)
|
||||||
|
{
|
||||||
|
*timer_config(m_base, timer) = *timer_config(m_base, timer) | (1 << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hpet::disable_timer(unsigned timer)
|
||||||
|
{
|
||||||
|
*timer_config(m_base, timer) = *timer_config(m_base, timer) & ~(1ull << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hpet::callback()
|
||||||
|
{
|
||||||
|
log::debug(logs::timer, "HPET %d got irq", m_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hpet::enable()
|
||||||
|
{
|
||||||
|
log::debug(logs::timer, "HPET %d enabling", m_index);
|
||||||
|
*configuration(m_base) = (*configuration(m_base) & 0x3) | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
hpet::value() const
|
||||||
|
{
|
||||||
|
return *counter_value(m_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
hpet_clock_source(void *data)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<hpet*>(data)->value();
|
||||||
|
}
|
||||||
46
src/kernel/hpet.h
Normal file
46
src/kernel/hpet.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file hpet.h
|
||||||
|
/// Definitions for handling the HPET timer
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// Source function for the clock that takes a pointer to
|
||||||
|
/// an hpet object.
|
||||||
|
uint64_t hpet_clock_source(void *);
|
||||||
|
|
||||||
|
/// Represents a single HPET timer
|
||||||
|
class hpet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg index The index of the HPET out of all HPETs
|
||||||
|
/// \arg base The base address of the HPET for MMIO
|
||||||
|
hpet(uint8_t index, uint64_t *base);
|
||||||
|
|
||||||
|
/// Configure the timer and start it running.
|
||||||
|
void enable();
|
||||||
|
|
||||||
|
/// Get the timer rate in ticks per us
|
||||||
|
inline uint64_t rate() const { return 1000000000/m_period; }
|
||||||
|
|
||||||
|
/// Get the current timer value
|
||||||
|
uint64_t value() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend void hpet_irq_callback(void *);
|
||||||
|
friend uint64_t hpet_clock_source(void *);
|
||||||
|
|
||||||
|
void setup_timer_interrupts(unsigned timer, uint8_t irq, uint64_t interval, bool periodic);
|
||||||
|
void enable_timer(unsigned timer);
|
||||||
|
void disable_timer(unsigned timer);
|
||||||
|
|
||||||
|
void callback();
|
||||||
|
|
||||||
|
uint8_t m_timers;
|
||||||
|
uint8_t m_index;
|
||||||
|
|
||||||
|
uint64_t m_period;
|
||||||
|
volatile uint64_t *m_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -10,9 +10,12 @@
|
|||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "objects/process.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
|
#include "vm_space.h"
|
||||||
|
|
||||||
static const uint16_t PIC1 = 0x20;
|
static const uint16_t PIC1 = 0x20;
|
||||||
static const uint16_t PIC2 = 0xa0;
|
static const uint16_t PIC2 = 0xa0;
|
||||||
@@ -81,13 +84,6 @@ disable_legacy_pic()
|
|||||||
outb(PIC2+1, 0x02); io_wait();
|
outb(PIC2+1, 0x02); io_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
enable_serial_interrupts()
|
|
||||||
{
|
|
||||||
uint8_t ier = inb(COM1+1);
|
|
||||||
outb(COM1+1, ier | 0x1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
interrupts_init()
|
interrupts_init()
|
||||||
{
|
{
|
||||||
@@ -102,7 +98,6 @@ interrupts_init()
|
|||||||
#undef ISR
|
#undef ISR
|
||||||
|
|
||||||
disable_legacy_pic();
|
disable_legacy_pic();
|
||||||
enable_serial_interrupts();
|
|
||||||
|
|
||||||
log::info(logs::boot, "Interrupts enabled.");
|
log::info(logs::boot, "Interrupts enabled.");
|
||||||
}
|
}
|
||||||
@@ -188,8 +183,15 @@ isr_handler(cpu_state *regs)
|
|||||||
uintptr_t cr2 = 0;
|
uintptr_t cr2 = 0;
|
||||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
if ((regs->errorcode & 0x9) == 0 &&
|
bool user = cr2 < memory::kernel_offset;
|
||||||
page_manager::get()->fault_handler(cr2))
|
vm_space::fault_type ft =
|
||||||
|
static_cast<vm_space::fault_type>(regs->errorcode);
|
||||||
|
|
||||||
|
vm_space &space = user
|
||||||
|
? process::current().space()
|
||||||
|
: vm_space::kernel_space();
|
||||||
|
|
||||||
|
if (cr2 && space.handle_fault(cr2, ft))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
cons->set_color(11);
|
cons->set_color(11);
|
||||||
@@ -210,7 +212,7 @@ isr_handler(cpu_state *regs)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrTimer:
|
case isr::isrTimer:
|
||||||
scheduler::get().tick();
|
scheduler::get().schedule();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case isr::isrLINT0:
|
case isr::isrLINT0:
|
||||||
|
|||||||
@@ -2,21 +2,23 @@
|
|||||||
|
|
||||||
extern load_process_image
|
extern load_process_image
|
||||||
|
|
||||||
global ramdisk_process_loader
|
global preloaded_process_init
|
||||||
ramdisk_process_loader:
|
preloaded_process_init:
|
||||||
|
|
||||||
; create_process already pushed a cpu_state onto the stack for us, this
|
; create_process already pushed the arguments for load_process_image and
|
||||||
; acts both as the cpu_state parameter to load_process_image, and the
|
; the following iretq onto the stack for us
|
||||||
; saved state for the following iretq
|
|
||||||
|
|
||||||
pop rdi ; the address of the program image
|
pop rdi ; the physical address of the program image
|
||||||
pop rsi ; the size of the program image
|
pop rsi ; the virtual address of the program image
|
||||||
pop rdx ; the address of this process' process structure
|
pop rdx ; the size in bytes of the program image
|
||||||
|
pop rcx ; the address of this thread's TCB
|
||||||
|
|
||||||
call load_process_image
|
call load_process_image
|
||||||
|
|
||||||
push rax ; load_process_image returns the process entrypoint
|
; user rsp is now in rax, put it in the right place for iret
|
||||||
|
mov [rsp + 0x18], rax
|
||||||
|
|
||||||
|
; the entrypoint should already be on the stack
|
||||||
swapgs
|
swapgs
|
||||||
iretq
|
iretq
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
|
#include "kutil/no_construct.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
|
|
||||||
|
|
||||||
static uint8_t log_buffer[0x10000];
|
static uint8_t log_buffer[0x10000];
|
||||||
static log::logger g_logger(log_buffer, sizeof(log_buffer));
|
|
||||||
|
// The logger is initialized _before_ global constructors are called,
|
||||||
|
// so that we can start log output immediately. Keep its constructor
|
||||||
|
// from being called here so as to not overwrite the previous initialization.
|
||||||
|
static kutil::no_construct<log::logger> __g_logger_storage;
|
||||||
|
log::logger &g_logger = __g_logger_storage.value;
|
||||||
|
|
||||||
static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
|
static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
|
||||||
|
|
||||||
@@ -28,8 +33,8 @@ logger_task()
|
|||||||
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
||||||
auto *cons = console::get();
|
auto *cons = console::get();
|
||||||
|
|
||||||
//g_logger.set_immediate(nullptr);
|
|
||||||
log::info(logs::task, "Starting kernel logger task");
|
log::info(logs::task, "Starting kernel logger task");
|
||||||
|
g_logger.set_immediate(nullptr);
|
||||||
|
|
||||||
scheduler &s = scheduler::get();
|
scheduler &s = scheduler::get();
|
||||||
|
|
||||||
@@ -50,6 +55,10 @@ logger_task()
|
|||||||
|
|
||||||
void logger_init()
|
void logger_init()
|
||||||
{
|
{
|
||||||
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer));
|
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer), output_log);
|
||||||
g_logger.set_immediate(output_log);
|
}
|
||||||
|
|
||||||
|
void logger_clear_immediate()
|
||||||
|
{
|
||||||
|
g_logger.set_immediate(nullptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ namespace log = kutil::log;
|
|||||||
namespace logs = kutil::logs;
|
namespace logs = kutil::logs;
|
||||||
|
|
||||||
void logger_init();
|
void logger_init();
|
||||||
|
void logger_clear_immediate();
|
||||||
void logger_task();
|
void logger_task();
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
|
|
||||||
#include "j6/signals.h"
|
#include "j6/signals.h"
|
||||||
|
|
||||||
#include "initrd/initrd.h"
|
|
||||||
#include "kutil/assert.h"
|
#include "kutil/assert.h"
|
||||||
#include "kutil/heap_allocator.h"
|
|
||||||
#include "kutil/vm_space.h"
|
|
||||||
#include "apic.h"
|
#include "apic.h"
|
||||||
#include "block_device.h"
|
#include "block_device.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
@@ -18,42 +15,35 @@
|
|||||||
#include "kernel_args.h"
|
#include "kernel_args.h"
|
||||||
#include "kernel_memory.h"
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "objects/channel.h"
|
||||||
#include "objects/event.h"
|
#include "objects/event.h"
|
||||||
#include "objects/handle.h"
|
#include "objects/thread.h"
|
||||||
#include "page_manager.h"
|
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
#include "symbol_table.h"
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
|
|
||||||
|
#ifndef GIT_VERSION
|
||||||
|
#define GIT_VERSION
|
||||||
|
#endif
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void kernel_main(kernel::args::header *header);
|
void kernel_main(kernel::args::header *header);
|
||||||
void *__bss_start, *__bss_end;
|
void (*__ctors)(void);
|
||||||
|
void (*__ctors_end)(void);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void __kernel_assert(const char *, unsigned, const char *);
|
extern void __kernel_assert(const char *, unsigned, const char *);
|
||||||
|
|
||||||
extern kutil::heap_allocator g_kernel_heap;
|
/// Bootstrap the memory managers.
|
||||||
|
void setup_pat();
|
||||||
|
void memory_initialize_pre_ctors(kernel::args::header *kargs);
|
||||||
|
void memory_initialize_post_ctors(kernel::args::header *kargs);
|
||||||
|
|
||||||
using namespace kernel;
|
using namespace kernel;
|
||||||
|
|
||||||
class test_observer :
|
/// TODO: not this. this is awful.
|
||||||
public kobject::observer
|
args::framebuffer *fb = nullptr;
|
||||||
{
|
|
||||||
public:
|
|
||||||
test_observer(const char *name) : m_name(name) {}
|
|
||||||
|
|
||||||
virtual bool on_signals_changed(
|
|
||||||
kobject *obj,
|
|
||||||
j6_signal_t s,
|
|
||||||
j6_signal_t ds,
|
|
||||||
j6_status_t result)
|
|
||||||
{
|
|
||||||
log::info(logs::objs, " %s: Signals %016lx changed, object %p, result %016lx",
|
|
||||||
m_name, ds, obj, result);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *m_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
void
|
||||||
init_console()
|
init_console()
|
||||||
@@ -65,72 +55,111 @@ init_console()
|
|||||||
cons->puts("jsix OS ");
|
cons->puts("jsix OS ");
|
||||||
cons->set_color(0x08, 0x00);
|
cons->set_color(0x08, 0x00);
|
||||||
cons->puts(GIT_VERSION " booting...\n");
|
cons->puts(GIT_VERSION " booting...\n");
|
||||||
|
|
||||||
logger_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_constructors()
|
||||||
|
{
|
||||||
|
void (**p)(void) = &__ctors;
|
||||||
|
while (p < &__ctors_end) {
|
||||||
|
void (*ctor)(void) = *p++;
|
||||||
|
ctor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel *std_out = nullptr;
|
||||||
|
|
||||||
|
void
|
||||||
|
stdout_task()
|
||||||
|
{
|
||||||
|
uint8_t buffer[257];
|
||||||
|
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
||||||
|
auto *cons = console::get();
|
||||||
|
|
||||||
|
log::info(logs::task, "Starting kernel stdout task");
|
||||||
|
|
||||||
|
scheduler &s = scheduler::get();
|
||||||
|
thread *th = thread::from_tcb(s.current());
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
j6_signal_t current = std_out->signals();
|
||||||
|
if (!(current & j6_signal_channel_can_recv)) {
|
||||||
|
th->wait_on_signals(std_out, j6_signal_channel_can_recv);
|
||||||
|
s.schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t n = 256;
|
||||||
|
j6_status_t status = std_out->dequeue(&n, buffer);
|
||||||
|
if (status != j6_status_ok) {
|
||||||
|
log::warn(logs::task, "Kernel stdout error: %x", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[n] = 0;
|
||||||
|
const char *s = reinterpret_cast<const char *>(buffer);
|
||||||
|
|
||||||
|
while (n) {
|
||||||
|
size_t r = cons->puts(s);
|
||||||
|
n -= r + 1;
|
||||||
|
s += r + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
kernel_main(args::header *header)
|
kernel_main(args::header *header)
|
||||||
{
|
{
|
||||||
kutil::assert_set_callback(__kernel_assert);
|
kutil::assert_set_callback(__kernel_assert);
|
||||||
|
|
||||||
init_console();
|
init_console();
|
||||||
|
logger_init();
|
||||||
|
|
||||||
|
setup_pat();
|
||||||
|
|
||||||
|
bool has_video = false;
|
||||||
|
if (header->video.size > 0) {
|
||||||
|
has_video = true;
|
||||||
|
fb = memory::to_virtual<args::framebuffer>(reinterpret_cast<uintptr_t>(&header->video));
|
||||||
|
|
||||||
|
const args::framebuffer &video = header->video;
|
||||||
|
log::debug(logs::boot, "Framebuffer: %dx%d[%d] type %s @ %016llx",
|
||||||
|
video.horizontal, video.vertical, video.scanline, video.type, video.phys_addr);
|
||||||
|
logger_clear_immediate();
|
||||||
|
}
|
||||||
|
|
||||||
gdt_init();
|
gdt_init();
|
||||||
interrupts_init();
|
interrupts_init();
|
||||||
|
|
||||||
cpu_id cpu;
|
memory_initialize_pre_ctors(header);
|
||||||
cpu.validate();
|
run_constructors();
|
||||||
|
memory_initialize_post_ctors(header);
|
||||||
|
|
||||||
memory_initialize(header);
|
cpu_validate();
|
||||||
|
|
||||||
kutil::allocator &heap = g_kernel_heap;
|
for (size_t i = 0; i < header->num_modules; ++i) {
|
||||||
|
args::module &mod = header->modules[i];
|
||||||
|
switch (mod.type) {
|
||||||
|
case args::mod_type::symbol_table:
|
||||||
|
new symbol_table {mod.location, mod.size};
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
default:
|
||||||
if (header->frame_buffer && header->frame_buffer_length) {
|
break;
|
||||||
page_manager::get()->map_offset_pointer(
|
}
|
||||||
&header->frame_buffer,
|
|
||||||
header->frame_buffer_length);
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
log::debug(logs::boot, " jsix header is at: %016lx", header);
|
log::debug(logs::boot, " jsix header is at: %016lx", header);
|
||||||
log::debug(logs::boot, " Memory map is at: %016lx", header->mem_map);
|
log::debug(logs::boot, " Memory map is at: %016lx", header->mem_map);
|
||||||
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
|
||||||
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime_services);
|
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime_services);
|
||||||
|
|
||||||
// Load the module tagged as initrd
|
device_manager &devices = device_manager::get();
|
||||||
kutil::vector<initrd::disk> initrds(heap);
|
devices.parse_acpi(header->acpi_table);
|
||||||
for (unsigned i = 0; i < header->num_modules; ++i) {
|
|
||||||
args::module &mod = header->modules[i];
|
|
||||||
if (mod.type != args::mod_type::initrd)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
initrd::disk &ird = initrds.emplace(mod.location, heap);
|
|
||||||
log::info(logs::boot, "initrd loaded with %d files.", ird.files().count());
|
|
||||||
for (auto &f : ird.files())
|
|
||||||
log::info(logs::boot, " %s%s (%d bytes).", f.executable() ? "*" : "", f.name(), f.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
page_manager::get()->dump_pml4(nullptr, 0);
|
|
||||||
page_manager::get()->dump_blocks(true);
|
|
||||||
*/
|
|
||||||
|
|
||||||
device_manager *devices =
|
|
||||||
new (&device_manager::get()) device_manager(header->acpi_table, heap);
|
|
||||||
|
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
|
devices.init_drivers();
|
||||||
/*
|
devices.get_lapic()->calibrate_timer();
|
||||||
auto r = cpu.get(0x15);
|
|
||||||
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
|
|
||||||
|
|
||||||
uintptr_t cr4 = 0;
|
|
||||||
__asm__ __volatile__ ( "mov %%cr4, %0" : "=r" (cr4) );
|
|
||||||
log::info(logs::boot, "cr4: %016x", cr4);
|
|
||||||
*/
|
|
||||||
|
|
||||||
devices->init_drivers();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
block_device *disk = devices->get_block_device(0);
|
block_device *disk = devices->get_block_device(0);
|
||||||
@@ -156,34 +185,27 @@ kernel_main(args::header *header)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
devices->get_lapic()->calibrate_timer();
|
|
||||||
|
|
||||||
syscall_enable();
|
syscall_enable();
|
||||||
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic(), heap);
|
scheduler *sched = new scheduler(devices.get_lapic());
|
||||||
|
|
||||||
sched->create_kernel_task(-1, logger_task);
|
std_out = new channel;
|
||||||
|
|
||||||
for (auto &ird : initrds) {
|
// Skip program 0, which is the kernel itself
|
||||||
for (auto &f : ird.files()) {
|
for (size_t i = 1; i < header->num_programs; ++i) {
|
||||||
if (f.executable())
|
args::program &prog = header->programs[i];
|
||||||
sched->load_process(f.name(), f.data(), f.size());
|
thread *th = sched->load_process(prog.phys_addr, prog.virt_addr, prog.size, prog.entrypoint);
|
||||||
|
if (i == 2) {
|
||||||
|
//th->set_state(thread::state::constant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info(logs::objs, "Testing object system:");
|
if (!has_video)
|
||||||
|
sched->create_kernel_task(logger_task, scheduler::max_priority-1, true);
|
||||||
|
sched->create_kernel_task(stdout_task, scheduler::max_priority-1, true);
|
||||||
|
|
||||||
test_observer obs1("event");
|
const char stdout_message[] = "Hello on the fake stdout channel\n";
|
||||||
test_observer obs2("no handles");
|
size_t message_size = sizeof(stdout_message);
|
||||||
{
|
std_out->enqueue(&message_size, reinterpret_cast<const void*>(stdout_message));
|
||||||
event e;
|
|
||||||
|
|
||||||
e.register_signal_observer(&obs1, j6_signal_user0);
|
|
||||||
e.register_signal_observer(&obs2, j6_signal_no_handles);
|
|
||||||
|
|
||||||
e.assert_signal(j6_signal_user0);
|
|
||||||
|
|
||||||
handle h(1, 0, &e);
|
|
||||||
}
|
|
||||||
|
|
||||||
sched->start();
|
sched->start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "kernel_args.h"
|
#include "kernel_args.h"
|
||||||
|
|
||||||
#include "kutil/assert.h"
|
#include "kutil/assert.h"
|
||||||
#include "kutil/heap_allocator.h"
|
#include "kutil/heap_allocator.h"
|
||||||
#include "kutil/vm_space.h"
|
#include "kutil/no_construct.h"
|
||||||
|
|
||||||
#include "frame_allocator.h"
|
#include "frame_allocator.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "page_manager.h"
|
#include "msr.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/vm_area.h"
|
||||||
|
#include "vm_space.h"
|
||||||
|
|
||||||
using memory::frame_size;
|
using memory::frame_size;
|
||||||
using memory::heap_start;
|
using memory::heap_start;
|
||||||
using memory::kernel_max_heap;
|
using memory::kernel_max_heap;
|
||||||
using memory::kernel_offset;
|
using memory::kernel_offset;
|
||||||
|
using memory::heap_start;
|
||||||
using memory::page_offset;
|
using memory::page_offset;
|
||||||
using memory::pml4e_kernel;
|
using memory::pml4e_kernel;
|
||||||
using memory::pml4e_offset;
|
using memory::pml4e_offset;
|
||||||
@@ -23,20 +26,48 @@ using memory::table_entries;
|
|||||||
|
|
||||||
using namespace kernel;
|
using namespace kernel;
|
||||||
|
|
||||||
kutil::vm_space g_kernel_space;
|
|
||||||
kutil::heap_allocator g_kernel_heap;
|
// These objects are initialized _before_ global constructors are called,
|
||||||
|
// so we don't want them to have global constructors at all, lest they
|
||||||
|
// overwrite the previous initialization.
|
||||||
|
static kutil::no_construct<kutil::heap_allocator> __g_kernel_heap_storage;
|
||||||
|
kutil::heap_allocator &g_kernel_heap = __g_kernel_heap_storage.value;
|
||||||
|
|
||||||
|
static kutil::no_construct<frame_allocator> __g_frame_allocator_storage;
|
||||||
|
frame_allocator &g_frame_allocator = __g_frame_allocator_storage.value;
|
||||||
|
|
||||||
|
static kutil::no_construct<vm_area_open> __g_kernel_heap_area_storage;
|
||||||
|
vm_area_open &g_kernel_heap_area = __g_kernel_heap_area_storage.value;
|
||||||
|
|
||||||
|
vm_area_buffers g_kernel_stacks {
|
||||||
|
memory::kernel_max_stacks,
|
||||||
|
vm_space::kernel_space(),
|
||||||
|
vm_flags::write,
|
||||||
|
memory::kernel_stack_pages};
|
||||||
|
|
||||||
|
vm_area_buffers g_kernel_buffers {
|
||||||
|
memory::kernel_max_buffers,
|
||||||
|
vm_space::kernel_space(),
|
||||||
|
vm_flags::write,
|
||||||
|
memory::kernel_buffer_pages};
|
||||||
|
|
||||||
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
||||||
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
||||||
void operator delete (void *p) noexcept { return g_kernel_heap.free(p); }
|
void operator delete (void *p) noexcept { return g_kernel_heap.free(p); }
|
||||||
void operator delete [] (void *p) noexcept { return g_kernel_heap.free(p); }
|
void operator delete [] (void *p) noexcept { return g_kernel_heap.free(p); }
|
||||||
|
|
||||||
|
namespace kutil {
|
||||||
|
void * kalloc(size_t size) { return g_kernel_heap.allocate(size); }
|
||||||
|
void kfree(void *p) { return g_kernel_heap.free(p); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void walk_page_table(
|
void walk_page_table(
|
||||||
page_table *table,
|
page_table *table,
|
||||||
page_table::level level,
|
page_table::level level,
|
||||||
uintptr_t ¤t_start,
|
uintptr_t ¤t_start,
|
||||||
size_t ¤t_bytes,
|
size_t ¤t_bytes,
|
||||||
kutil::vm_space &kspace)
|
vm_area &karea)
|
||||||
{
|
{
|
||||||
constexpr size_t huge_page_size = (1ull<<30);
|
constexpr size_t huge_page_size = (1ull<<30);
|
||||||
constexpr size_t large_page_size = (1ull<<21);
|
constexpr size_t large_page_size = (1ull<<21);
|
||||||
@@ -44,7 +75,8 @@ void walk_page_table(
|
|||||||
for (unsigned i = 0; i < table_entries; ++i) {
|
for (unsigned i = 0; i < table_entries; ++i) {
|
||||||
page_table *next = table->get(i);
|
page_table *next = table->get(i);
|
||||||
if (!next) {
|
if (!next) {
|
||||||
kspace.commit(current_start, current_bytes);
|
if (current_bytes)
|
||||||
|
karea.commit(current_start, current_bytes);
|
||||||
current_start = 0;
|
current_start = 0;
|
||||||
current_bytes = 0;
|
current_bytes = 0;
|
||||||
continue;
|
continue;
|
||||||
@@ -67,46 +99,111 @@ void walk_page_table(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void
|
static void
|
||||||
memory_initialize(args::header *kargs)
|
log_mtrrs()
|
||||||
{
|
{
|
||||||
args::mem_entry *entries = kargs->mem_map;
|
uint64_t mtrrcap = rdmsr(msr::ia32_mtrrcap);
|
||||||
size_t entry_count = kargs->num_map_entries;
|
uint64_t mtrrdeftype = rdmsr(msr::ia32_mtrrdeftype);
|
||||||
page_table *kpml4 = reinterpret_cast<page_table*>(kargs->pml4);
|
unsigned vcap = mtrrcap & 0xff;
|
||||||
|
log::debug(logs::boot, "MTRRs: vcap=%d %s %s def=%02x %s %s",
|
||||||
|
vcap,
|
||||||
|
(mtrrcap & (1<< 8)) ? "fix" : "",
|
||||||
|
(mtrrcap & (1<<10)) ? "wc" : "",
|
||||||
|
mtrrdeftype & 0xff,
|
||||||
|
(mtrrdeftype & (1<<10)) ? "fe" : "",
|
||||||
|
(mtrrdeftype & (1<<11)) ? "enabled" : ""
|
||||||
|
);
|
||||||
|
|
||||||
new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap};
|
for (unsigned i = 0; i < vcap; ++i) {
|
||||||
|
uint64_t base = rdmsr(find_mtrr(msr::ia32_mtrrphysbase, i));
|
||||||
frame_allocator *fa = new (&g_frame_allocator) frame_allocator;
|
uint64_t mask = rdmsr(find_mtrr(msr::ia32_mtrrphysmask, i));
|
||||||
for (unsigned i = 0; i < entry_count; ++i) {
|
log::debug(logs::boot, " vcap[%2d] base:%016llx mask:%016llx type:%02x %s", i,
|
||||||
// TODO: use entry attributes
|
(base & ~0xfffull),
|
||||||
args::mem_entry &e = entries[i];
|
(mask & ~0xfffull),
|
||||||
if (e.type == args::mem_type::free)
|
(base & 0xff),
|
||||||
fa->free(e.start, e.pages);
|
(mask & (1<<11)) ? "valid" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the page manager
|
msr mtrr_fixed[] = {
|
||||||
page_manager *pm = new (&g_page_manager) page_manager(*fa, kpml4);
|
msr::ia32_mtrrfix64k_00000,
|
||||||
|
msr::ia32_mtrrfix16k_80000,
|
||||||
|
msr::ia32_mtrrfix16k_a0000,
|
||||||
|
msr::ia32_mtrrfix4k_c0000,
|
||||||
|
msr::ia32_mtrrfix4k_c8000,
|
||||||
|
msr::ia32_mtrrfix4k_d0000,
|
||||||
|
msr::ia32_mtrrfix4k_d8000,
|
||||||
|
msr::ia32_mtrrfix4k_e0000,
|
||||||
|
msr::ia32_mtrrfix4k_e8000,
|
||||||
|
msr::ia32_mtrrfix4k_f0000,
|
||||||
|
msr::ia32_mtrrfix4k_f8000,
|
||||||
|
};
|
||||||
|
|
||||||
new (&g_kernel_space) kutil::vm_space {
|
for (int i = 0; i < 11; ++i) {
|
||||||
kernel_offset,
|
uint64_t v = rdmsr(mtrr_fixed[i]);
|
||||||
(page_offset-kernel_offset),
|
log::debug(logs::boot, " fixed[%2d] %02x %02x %02x %02x %02x %02x %02x %02x", i,
|
||||||
g_kernel_heap};
|
((v << 0) & 0xff), ((v << 8) & 0xff), ((v << 16) & 0xff), ((v << 24) & 0xff),
|
||||||
|
((v << 32) & 0xff), ((v << 40) & 0xff), ((v << 48) & 0xff), ((v << 56) & 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||||
|
static const char *pat_names[] = {"UC ","WC ","XX ","XX ","WT ","WP ","WB ","UC-"};
|
||||||
|
log::debug(logs::boot, " PAT: 0:%s 1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s",
|
||||||
|
pat_names[(pat >> (0*8)) & 7], pat_names[(pat >> (1*8)) & 7],
|
||||||
|
pat_names[(pat >> (2*8)) & 7], pat_names[(pat >> (3*8)) & 7],
|
||||||
|
pat_names[(pat >> (4*8)) & 7], pat_names[(pat >> (5*8)) & 7],
|
||||||
|
pat_names[(pat >> (6*8)) & 7], pat_names[(pat >> (7*8)) & 7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setup_pat()
|
||||||
|
{
|
||||||
|
uint64_t pat = rdmsr(msr::ia32_pat);
|
||||||
|
pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC
|
||||||
|
wrmsr(msr::ia32_pat, pat);
|
||||||
|
log_mtrrs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
memory_initialize_pre_ctors(args::header *kargs)
|
||||||
|
{
|
||||||
|
new (&g_kernel_heap) kutil::heap_allocator {heap_start, kernel_max_heap};
|
||||||
|
new (&g_frame_allocator) frame_allocator;
|
||||||
|
|
||||||
|
args::mem_entry *entries = kargs->mem_map;
|
||||||
|
const size_t count = kargs->map_count;
|
||||||
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
|
// TODO: use entry attributes
|
||||||
|
// TODO: copy anything we need from "pending" memory and free it
|
||||||
|
args::mem_entry &e = entries[i];
|
||||||
|
if (e.type == args::mem_type::free)
|
||||||
|
g_frame_allocator.free(e.start, e.pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
page_table *kpml4 = reinterpret_cast<page_table*>(kargs->pml4);
|
||||||
|
process *kp = process::create_kernel_process(kpml4);
|
||||||
|
vm_space &vm = kp->space();
|
||||||
|
|
||||||
|
vm_area *heap = new (&g_kernel_heap_area)
|
||||||
|
vm_area_open(memory::kernel_max_heap, vm, vm_flags::write);
|
||||||
|
|
||||||
|
vm.add(memory::heap_start, heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
memory_initialize_post_ctors(args::header *kargs)
|
||||||
|
{
|
||||||
|
/*
|
||||||
uintptr_t current_start = 0;
|
uintptr_t current_start = 0;
|
||||||
size_t current_bytes = 0;
|
size_t current_bytes = 0;
|
||||||
|
|
||||||
// TODO: Should we exclude the top of this area? (eg, buffers, stacks, etc)
|
// TODO: Should we exclude the top of this area? (eg, buffers, stacks, etc)
|
||||||
|
page_table *kpml4 = reinterpret_cast<page_table*>(kargs->pml4);
|
||||||
for (unsigned i = pml4e_kernel; i < pml4e_offset; ++i) {
|
for (unsigned i = pml4e_kernel; i < pml4e_offset; ++i) {
|
||||||
page_table *pdp = kpml4->get(i);
|
page_table *pdp = kpml4->get(i);
|
||||||
|
kassert(pdp, "Bootloader did not create all kernelspace PDs");
|
||||||
if (!pdp) {
|
|
||||||
g_kernel_space.commit(current_start, current_bytes);
|
|
||||||
current_start = 0;
|
|
||||||
current_bytes = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
walk_page_table(
|
walk_page_table(
|
||||||
pdp, page_table::level::pdp,
|
pdp, page_table::level::pdp,
|
||||||
@@ -114,5 +211,15 @@ memory_initialize(args::header *kargs)
|
|||||||
g_kernel_space);
|
g_kernel_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
fa->free(reinterpret_cast<uintptr_t>(kargs->page_table_cache), kargs->num_free_tables);
|
if (current_bytes)
|
||||||
|
g_kernel_space.commit(current_start, current_bytes);
|
||||||
|
*/
|
||||||
|
vm_space &vm = vm_space::kernel_space();
|
||||||
|
vm.add(memory::stacks_start, &g_kernel_stacks);
|
||||||
|
vm.add(memory::buffers_start, &g_kernel_buffers);
|
||||||
|
|
||||||
|
g_frame_allocator.free(
|
||||||
|
reinterpret_cast<uintptr_t>(kargs->page_tables),
|
||||||
|
kargs->table_count);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
#include "msr.h"
|
#include "msr.h"
|
||||||
|
|
||||||
|
msr
|
||||||
|
find_mtrr(msr type, unsigned index)
|
||||||
|
{
|
||||||
|
return static_cast<msr>(static_cast<uint32_t>(type) + (2 * index));
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
rdmsr(msr addr)
|
rdmsr(msr addr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,27 @@
|
|||||||
|
|
||||||
enum class msr : uint32_t
|
enum class msr : uint32_t
|
||||||
{
|
{
|
||||||
|
ia32_mtrrcap = 0x000000fe,
|
||||||
|
ia32_mtrrdeftype = 0x000002ff,
|
||||||
|
|
||||||
|
ia32_mtrrphysbase = 0x00000200,
|
||||||
|
ia32_mtrrphysmask = 0x00000201,
|
||||||
|
|
||||||
|
ia32_mtrrfix64k_00000 = 0x00000250,
|
||||||
|
|
||||||
|
ia32_mtrrfix16k_80000 = 0x00000258,
|
||||||
|
ia32_mtrrfix16k_a0000 = 0x00000259,
|
||||||
|
|
||||||
|
ia32_mtrrfix4k_c0000 = 0x00000268,
|
||||||
|
ia32_mtrrfix4k_c8000 = 0x00000269,
|
||||||
|
ia32_mtrrfix4k_d0000 = 0x0000026A,
|
||||||
|
ia32_mtrrfix4k_d8000 = 0x0000026B,
|
||||||
|
ia32_mtrrfix4k_e0000 = 0x0000026C,
|
||||||
|
ia32_mtrrfix4k_e8000 = 0x0000026D,
|
||||||
|
ia32_mtrrfix4k_f0000 = 0x0000026E,
|
||||||
|
ia32_mtrrfix4k_f8000 = 0x0000026F,
|
||||||
|
|
||||||
|
ia32_pat = 0x00000277,
|
||||||
ia32_efer = 0xc0000080,
|
ia32_efer = 0xc0000080,
|
||||||
ia32_star = 0xc0000081,
|
ia32_star = 0xc0000081,
|
||||||
ia32_lstar = 0xc0000082,
|
ia32_lstar = 0xc0000082,
|
||||||
@@ -15,6 +36,9 @@ enum class msr : uint32_t
|
|||||||
ia32_kernel_gs_base = 0xc0000102
|
ia32_kernel_gs_base = 0xc0000102
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Find the msr for MTRR physical base or mask
|
||||||
|
msr find_mtrr(msr type, unsigned index);
|
||||||
|
|
||||||
/// Read the value of a MSR
|
/// Read the value of a MSR
|
||||||
/// \arg addr The MSR address
|
/// \arg addr The MSR address
|
||||||
/// \returns The current value of the MSR
|
/// \returns The current value of the MSR
|
||||||
|
|||||||
90
src/kernel/objects/channel.cpp
Normal file
90
src/kernel/objects/channel.cpp
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#include "kutil/assert.h"
|
||||||
|
|
||||||
|
#include "kernel_memory.h"
|
||||||
|
#include "objects/channel.h"
|
||||||
|
#include "objects/vm_area.h"
|
||||||
|
|
||||||
|
extern vm_area_buffers g_kernel_buffers;
|
||||||
|
|
||||||
|
constexpr size_t buffer_bytes = memory::kernel_buffer_pages * memory::frame_size;
|
||||||
|
|
||||||
|
channel::channel() :
|
||||||
|
m_len(0),
|
||||||
|
m_data(g_kernel_buffers.get_buffer()),
|
||||||
|
m_buffer(reinterpret_cast<uint8_t*>(m_data), buffer_bytes),
|
||||||
|
kobject(kobject::type::channel, j6_signal_channel_can_send)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
channel::~channel()
|
||||||
|
{
|
||||||
|
if (!closed()) close();
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
channel::enqueue(size_t *len, const void *data)
|
||||||
|
{
|
||||||
|
// TODO: Make this thread safe!
|
||||||
|
if (closed())
|
||||||
|
return j6_status_closed;
|
||||||
|
|
||||||
|
if (!len || !*len)
|
||||||
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
|
if (m_buffer.free_space() == 0)
|
||||||
|
return j6_err_not_ready;
|
||||||
|
|
||||||
|
void *buffer = nullptr;
|
||||||
|
size_t avail = m_buffer.reserve(*len, &buffer);
|
||||||
|
*len = *len > avail ? avail : *len;
|
||||||
|
|
||||||
|
kutil::memcpy(buffer, data, *len);
|
||||||
|
m_buffer.commit(*len);
|
||||||
|
|
||||||
|
assert_signal(j6_signal_channel_can_recv);
|
||||||
|
if (m_buffer.free_space() == 0)
|
||||||
|
deassert_signal(j6_signal_channel_can_send);
|
||||||
|
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
channel::dequeue(size_t *len, void *data)
|
||||||
|
{
|
||||||
|
// TODO: Make this thread safe!
|
||||||
|
if (closed())
|
||||||
|
return j6_status_closed;
|
||||||
|
|
||||||
|
if (!len || !*len)
|
||||||
|
return j6_err_invalid_arg;
|
||||||
|
|
||||||
|
if (m_buffer.size() == 0)
|
||||||
|
return j6_err_not_ready;
|
||||||
|
|
||||||
|
void *buffer = nullptr;
|
||||||
|
size_t avail = m_buffer.get_block(&buffer);
|
||||||
|
*len = *len > avail ? avail : *len;
|
||||||
|
|
||||||
|
kutil::memcpy(data, buffer, *len);
|
||||||
|
m_buffer.consume(*len);
|
||||||
|
|
||||||
|
assert_signal(j6_signal_channel_can_send);
|
||||||
|
if (m_buffer.size() == 0)
|
||||||
|
deassert_signal(j6_signal_channel_can_recv);
|
||||||
|
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
channel::close()
|
||||||
|
{
|
||||||
|
kobject::close();
|
||||||
|
g_kernel_buffers.return_buffer(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
channel::on_no_handles()
|
||||||
|
{
|
||||||
|
kobject::on_no_handles();
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
49
src/kernel/objects/channel.h
Normal file
49
src/kernel/objects/channel.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file channel.h
|
||||||
|
/// Definition of channel objects and related functions
|
||||||
|
|
||||||
|
#include "j6/signals.h"
|
||||||
|
#include "kutil/bip_buffer.h"
|
||||||
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
|
/// Channels are bi-directional means of sending messages
|
||||||
|
class channel :
|
||||||
|
public kobject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
channel();
|
||||||
|
virtual ~channel();
|
||||||
|
|
||||||
|
static constexpr kobject::type type = kobject::type::channel;
|
||||||
|
|
||||||
|
/// Check if the channel has space for a message to be sent
|
||||||
|
inline bool can_send() const { return check_signal(j6_signal_channel_can_send); }
|
||||||
|
|
||||||
|
/// Check if the channel has a message wiating already
|
||||||
|
inline bool can_receive() const { return check_signal(j6_signal_channel_can_recv); }
|
||||||
|
|
||||||
|
/// Put a message into the channel
|
||||||
|
/// \arg len [in] Bytes in data buffer [out] number of bytes written
|
||||||
|
/// \arg data Pointer to the message data
|
||||||
|
/// \returns j6_status_ok on success
|
||||||
|
j6_status_t enqueue(size_t *len, const void *data);
|
||||||
|
|
||||||
|
/// Get a message from the channel, copied into a provided buffer
|
||||||
|
/// \arg len On input, the size of the provided buffer. On output,
|
||||||
|
/// the size of the message copied into the buffer.
|
||||||
|
/// \arg data Pointer to the buffer
|
||||||
|
/// \returns j6_status_ok on success
|
||||||
|
j6_status_t dequeue(size_t *len, void *data);
|
||||||
|
|
||||||
|
/// Mark this channel as closed, all future calls to enqueue or
|
||||||
|
/// dequeue messages will fail with j6_status_closed.
|
||||||
|
virtual void close() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void on_no_handles() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_len;
|
||||||
|
uintptr_t m_data;
|
||||||
|
kutil::bip_buffer m_buffer;
|
||||||
|
};
|
||||||
132
src/kernel/objects/endpoint.cpp
Normal file
132
src/kernel/objects/endpoint.cpp
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#include "device_manager.h"
|
||||||
|
#include "objects/endpoint.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
|
#include "vm_space.h"
|
||||||
|
|
||||||
|
endpoint::endpoint() :
|
||||||
|
kobject {kobject::type::endpoint}
|
||||||
|
{}
|
||||||
|
|
||||||
|
endpoint::~endpoint()
|
||||||
|
{
|
||||||
|
if (!check_signal(j6_signal_closed))
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
endpoint::close()
|
||||||
|
{
|
||||||
|
kobject::close();
|
||||||
|
|
||||||
|
for (auto &data : m_blocked) {
|
||||||
|
if (data.th)
|
||||||
|
data.th->wake_on_result(this, j6_status_closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_manager::get().unbind_irqs(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
endpoint::send(j6_tag_t tag, size_t len, void *data)
|
||||||
|
{
|
||||||
|
thread_data sender = { &thread::current(), data };
|
||||||
|
sender.len = len;
|
||||||
|
sender.tag = tag;
|
||||||
|
|
||||||
|
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||||
|
assert_signal(j6_signal_endpoint_can_recv);
|
||||||
|
m_blocked.append(sender);
|
||||||
|
sender.th->wait_on_object(this);
|
||||||
|
|
||||||
|
// we woke up having already finished the send
|
||||||
|
// because it happened in the receiver
|
||||||
|
return sender.th->get_wait_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_data receiver = m_blocked.pop_front();
|
||||||
|
if (m_blocked.count() == 0)
|
||||||
|
deassert_signal(j6_signal_endpoint_can_send);
|
||||||
|
|
||||||
|
j6_status_t status = do_message_copy(sender, receiver);
|
||||||
|
|
||||||
|
receiver.th->wake_on_result(this, status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
endpoint::receive(j6_tag_t *tag, size_t *len, void *data)
|
||||||
|
{
|
||||||
|
thread_data receiver = { &thread::current(), data };
|
||||||
|
receiver.tag_p = tag;
|
||||||
|
receiver.len_p = len;
|
||||||
|
|
||||||
|
if (!check_signal(j6_signal_endpoint_can_recv)) {
|
||||||
|
assert_signal(j6_signal_endpoint_can_send);
|
||||||
|
m_blocked.append(receiver);
|
||||||
|
receiver.th->wait_on_object(this);
|
||||||
|
|
||||||
|
// we woke up having already finished the recv
|
||||||
|
// because it happened in the sender
|
||||||
|
return receiver.th->get_wait_result();
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_data sender = m_blocked.pop_front();
|
||||||
|
if (m_blocked.count() == 0)
|
||||||
|
deassert_signal(j6_signal_endpoint_can_recv);
|
||||||
|
|
||||||
|
// TODO: don't pop sender on some errors
|
||||||
|
j6_status_t status = do_message_copy(sender, receiver);
|
||||||
|
|
||||||
|
if (sender.th)
|
||||||
|
sender.th->wake_on_result(this, status);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
endpoint::signal_irq(unsigned irq)
|
||||||
|
{
|
||||||
|
j6_tag_t tag = j6_tag_from_irq(irq);
|
||||||
|
|
||||||
|
if (!check_signal(j6_signal_endpoint_can_send)) {
|
||||||
|
assert_signal(j6_signal_endpoint_can_recv);
|
||||||
|
|
||||||
|
for (auto &blocked : m_blocked)
|
||||||
|
if (blocked.tag == tag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
thread_data sender = { nullptr, nullptr };
|
||||||
|
sender.tag = tag;
|
||||||
|
m_blocked.append(sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_data receiver = m_blocked.pop_front();
|
||||||
|
if (m_blocked.count() == 0)
|
||||||
|
deassert_signal(j6_signal_endpoint_can_send);
|
||||||
|
|
||||||
|
*receiver.len_p = 0;
|
||||||
|
*receiver.tag_p = tag;
|
||||||
|
receiver.th->wake_on_result(this, j6_status_ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
endpoint::do_message_copy(const endpoint::thread_data &sender, endpoint::thread_data &receiver)
|
||||||
|
{
|
||||||
|
if (sender.len > *receiver.len_p)
|
||||||
|
return j6_err_insufficient;
|
||||||
|
|
||||||
|
if (sender.len) {
|
||||||
|
vm_space &source = sender.th->parent().space();
|
||||||
|
vm_space &dest = receiver.th->parent().space();
|
||||||
|
vm_space::copy(source, dest, sender.data, receiver.data, sender.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
*receiver.len_p = sender.len;
|
||||||
|
*receiver.tag_p = sender.tag;
|
||||||
|
|
||||||
|
// TODO: this will not work if non-contiguous pages are mapped!!
|
||||||
|
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
65
src/kernel/objects/endpoint.h
Normal file
65
src/kernel/objects/endpoint.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file endpoint.h
|
||||||
|
/// Definition of endpoint kobject types
|
||||||
|
|
||||||
|
#include "j6/signals.h"
|
||||||
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
|
/// Endpoints are objects that enable synchronous message-passing IPC
|
||||||
|
class endpoint :
|
||||||
|
public kobject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
endpoint();
|
||||||
|
virtual ~endpoint();
|
||||||
|
|
||||||
|
static constexpr kobject::type type = kobject::type::endpoint;
|
||||||
|
|
||||||
|
/// Close the endpoint, waking all waiting processes with an error
|
||||||
|
virtual void close() override;
|
||||||
|
|
||||||
|
/// Check if the endpoint has space for a message to be sent
|
||||||
|
inline bool can_send() const { return check_signal(j6_signal_endpoint_can_send); }
|
||||||
|
|
||||||
|
/// Check if the endpoint has a message wiating already
|
||||||
|
inline bool can_receive() const { return check_signal(j6_signal_endpoint_can_recv); }
|
||||||
|
|
||||||
|
/// Send a message to a thread waiting to receive on this endpoint. If no threads
|
||||||
|
/// are currently trying to receive, block the current thread.
|
||||||
|
/// \arg tag The application-specified message tag
|
||||||
|
/// \arg len The size in bytes of the message
|
||||||
|
/// \arg data The message data
|
||||||
|
/// \returns j6_status_ok on success
|
||||||
|
j6_status_t send(j6_tag_t tag, size_t len, void *data);
|
||||||
|
|
||||||
|
/// Receive a message from a thread waiting to send on this endpoint. If no threads
|
||||||
|
/// are currently trying to send, block the current thread.
|
||||||
|
/// \arg tag [in] The sender-specified message tag
|
||||||
|
/// \arg len [in] The size in bytes of the buffer [out] Number of bytes in the message
|
||||||
|
/// \arg data Buffer for copying message data into
|
||||||
|
/// \returns j6_status_ok on success
|
||||||
|
j6_status_t receive(j6_tag_t *tag, size_t *len, void *data);
|
||||||
|
|
||||||
|
/// Give the listener on the endpoint a message that a bound IRQ has been signalled
|
||||||
|
/// \arg irq The IRQ that caused this signal
|
||||||
|
void signal_irq(unsigned irq);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct thread_data
|
||||||
|
{
|
||||||
|
thread *th;
|
||||||
|
void *data;
|
||||||
|
union {
|
||||||
|
j6_tag_t *tag_p;
|
||||||
|
j6_tag_t tag;
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
size_t *len_p;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
j6_status_t do_message_copy(const thread_data &sender, thread_data &receiver);
|
||||||
|
|
||||||
|
kutil::vector<thread_data> m_blocked;
|
||||||
|
};
|
||||||
@@ -10,4 +10,6 @@ class event :
|
|||||||
public:
|
public:
|
||||||
event() :
|
event() :
|
||||||
kobject(type::event) {}
|
kobject(type::event) {}
|
||||||
|
|
||||||
|
static constexpr kobject::type type = kobject::type::event;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
#include "objects/handle.h"
|
|
||||||
|
|
||||||
handle::handle(handle&& other) :
|
|
||||||
m_owner(other.m_owner),
|
|
||||||
m_object(other.m_object),
|
|
||||||
m_rights(other.m_rights)
|
|
||||||
{
|
|
||||||
other.m_owner = 0;
|
|
||||||
other.m_object = nullptr;
|
|
||||||
other.m_rights = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle::handle(j6_koid_t owner, j6_rights_t rights, kobject *obj) :
|
|
||||||
m_owner(owner),
|
|
||||||
m_object(obj),
|
|
||||||
m_rights(rights)
|
|
||||||
{
|
|
||||||
if (m_object)
|
|
||||||
m_object->handle_retain();
|
|
||||||
}
|
|
||||||
|
|
||||||
handle::~handle()
|
|
||||||
{
|
|
||||||
if (m_object)
|
|
||||||
m_object->handle_release();
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
/// \file handle.h
|
|
||||||
/// Defines types for user handles to kernel objects
|
|
||||||
|
|
||||||
#include "j6/types.h"
|
|
||||||
#include "objects/kobject.h"
|
|
||||||
|
|
||||||
class handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Move constructor. Takes ownership of the object from other.
|
|
||||||
handle(handle&& other);
|
|
||||||
|
|
||||||
/// Constructor.
|
|
||||||
/// \arg owner koid of the process that has this handle
|
|
||||||
/// \arg rights access rights this handle has over the object
|
|
||||||
/// \arg obj the object held
|
|
||||||
handle(j6_koid_t owner, j6_rights_t rights, kobject *obj);
|
|
||||||
|
|
||||||
~handle();
|
|
||||||
|
|
||||||
handle() = delete;
|
|
||||||
handle(const handle &other) = delete;
|
|
||||||
handle & operator=(const handle& other) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
j6_koid_t m_owner;
|
|
||||||
kobject *m_object;
|
|
||||||
j6_rights_t m_rights;
|
|
||||||
};
|
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
#include "j6/errors.h"
|
#include "j6/errors.h"
|
||||||
#include "j6/signals.h"
|
#include "j6/signals.h"
|
||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
#include "kutil/heap_allocator.h"
|
|
||||||
#include "objects/kobject.h"
|
#include "objects/kobject.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
extern kutil::heap_allocator g_kernel_heap;
|
|
||||||
|
|
||||||
// TODO: per-cpu this?
|
// TODO: per-cpu this?
|
||||||
static j6_koid_t next_koid;
|
static j6_koid_t next_koids [static_cast<size_t>(kobject::type::max)] = { 0 };
|
||||||
|
|
||||||
kobject::kobject(type t, j6_signal_t signals) :
|
kobject::kobject(type t, j6_signal_t signals) :
|
||||||
m_koid(koid_generate(t)),
|
m_koid(koid_generate(t)),
|
||||||
m_signals(signals),
|
m_signals(signals),
|
||||||
m_observers(g_kernel_heap),
|
|
||||||
m_handle_count(0)
|
m_handle_count(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
kobject::~kobject()
|
kobject::~kobject()
|
||||||
{
|
{
|
||||||
notify_signal_observers(0, j6_status_destroyed);
|
for (auto *t : m_blocked_threads)
|
||||||
|
t->wake_on_result(this, j6_status_destroyed);
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_koid_t
|
j6_koid_t
|
||||||
kobject::koid_generate(type t)
|
kobject::koid_generate(type t)
|
||||||
{
|
{
|
||||||
return (static_cast<uint64_t>(t) << 48) | next_koid++;
|
kassert(t < type::max, "Object type out of bounds");
|
||||||
|
uint64_t type_int = static_cast<uint64_t>(t);
|
||||||
|
return (type_int << 48) | next_koids[type_int]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
kobject::type
|
kobject::type
|
||||||
@@ -37,58 +37,36 @@ void
|
|||||||
kobject::assert_signal(j6_signal_t s)
|
kobject::assert_signal(j6_signal_t s)
|
||||||
{
|
{
|
||||||
m_signals |= s;
|
m_signals |= s;
|
||||||
notify_signal_observers(s);
|
notify_signal_observers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::deassert_signal(j6_signal_t s)
|
kobject::deassert_signal(j6_signal_t s)
|
||||||
{
|
{
|
||||||
m_signals &= ~s;
|
m_signals &= ~s;
|
||||||
notify_signal_observers(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::register_signal_observer(observer *object, j6_signal_t s)
|
kobject::notify_signal_observers()
|
||||||
{
|
{
|
||||||
m_observers.emplace(object, s);
|
size_t i = 0;
|
||||||
}
|
bool readied = false;
|
||||||
|
while (i < m_blocked_threads.count()) {
|
||||||
|
thread *t = m_blocked_threads[i];
|
||||||
|
|
||||||
void
|
if (t->wake_on_signals(this, m_signals)) {
|
||||||
kobject::deregister_signal_observer(observer *object)
|
m_blocked_threads.remove_swap_at(i);
|
||||||
{
|
readied = true;
|
||||||
for (size_t i = 0; i < m_observers.count(); ++i) {
|
} else {
|
||||||
auto ® = m_observers[i];
|
++i;
|
||||||
if (reg.object != object) continue;
|
}
|
||||||
|
|
||||||
reg = m_observers[m_observers.count() - 1];
|
|
||||||
m_observers.remove();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
kobject::notify_signal_observers(j6_signal_t mask, j6_status_t result)
|
kobject::close()
|
||||||
{
|
{
|
||||||
for (auto ® : m_observers) {
|
assert_signal(j6_signal_closed);
|
||||||
if (mask == 0 || (reg.signals & mask) != 0) {
|
|
||||||
if (!reg.object->on_signals_changed(this, m_signals, mask, result))
|
|
||||||
reg.object = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compact the observer list
|
|
||||||
long last = m_observers.count() - 1;
|
|
||||||
while (m_observers[last].object == nullptr && last >= 0) last--;
|
|
||||||
|
|
||||||
for (long i = 0; i < long(m_observers.count()) && i < last; ++i) {
|
|
||||||
auto ® = m_observers[i];
|
|
||||||
if (reg.object != nullptr) continue;
|
|
||||||
reg = m_observers[last--];
|
|
||||||
|
|
||||||
while (m_observers[last].object == nullptr && last >= i) last--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_observers.set_size(last + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
/// Definition of base type for user-interactable kernel objects
|
/// Definition of base type for user-interactable kernel objects
|
||||||
|
|
||||||
#include "j6/errors.h"
|
#include "j6/errors.h"
|
||||||
|
#include "j6/signals.h"
|
||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
#include "kutil/vector.h"
|
#include "kutil/vector.h"
|
||||||
|
|
||||||
|
class thread;
|
||||||
|
|
||||||
/// Base type for all user-interactable kernel objects
|
/// Base type for all user-interactable kernel objects
|
||||||
class kobject
|
class kobject
|
||||||
{
|
{
|
||||||
@@ -14,16 +17,18 @@ public:
|
|||||||
enum class type : uint16_t
|
enum class type : uint16_t
|
||||||
{
|
{
|
||||||
none,
|
none,
|
||||||
|
system,
|
||||||
|
|
||||||
event,
|
event,
|
||||||
eventpair,
|
channel,
|
||||||
|
endpoint,
|
||||||
|
|
||||||
vms,
|
vma,
|
||||||
vmo,
|
|
||||||
|
|
||||||
job,
|
|
||||||
process,
|
process,
|
||||||
thread,
|
thread,
|
||||||
|
|
||||||
|
max
|
||||||
};
|
};
|
||||||
|
|
||||||
kobject(type t, j6_signal_t signals = 0ull);
|
kobject(type t, j6_signal_t signals = 0ull);
|
||||||
@@ -39,6 +44,12 @@ public:
|
|||||||
/// \returns The object type for the koid
|
/// \returns The object type for the koid
|
||||||
static type koid_type(j6_koid_t koid);
|
static type koid_type(j6_koid_t koid);
|
||||||
|
|
||||||
|
/// Get this object's type
|
||||||
|
inline type get_type() const { return koid_type(m_koid); }
|
||||||
|
|
||||||
|
/// Get this object's koid
|
||||||
|
inline j6_koid_t koid() const { return m_koid; }
|
||||||
|
|
||||||
/// Set the given signals active on this object
|
/// Set the given signals active on this object
|
||||||
/// \arg s The set of signals to assert
|
/// \arg s The set of signals to assert
|
||||||
void assert_signal(j6_signal_t s);
|
void assert_signal(j6_signal_t s);
|
||||||
@@ -47,30 +58,12 @@ public:
|
|||||||
/// \arg s The set of signals to deassert
|
/// \arg s The set of signals to deassert
|
||||||
void deassert_signal(j6_signal_t s);
|
void deassert_signal(j6_signal_t s);
|
||||||
|
|
||||||
class observer
|
/// Check if the given signals are set on this object
|
||||||
{
|
/// \arg s The set of signals to check
|
||||||
public:
|
inline bool check_signal(j6_signal_t s) const { return (m_signals & s) == s; }
|
||||||
/// Callback for when signals change.
|
|
||||||
/// \arg obj The object triggering the callback
|
|
||||||
/// \arg s The current state of obj's signals
|
|
||||||
/// \arg ds Which signals caused the callback
|
|
||||||
/// \arg result Status code for this notification
|
|
||||||
/// \returns True if this object wants to keep watching signals
|
|
||||||
virtual bool on_signals_changed(
|
|
||||||
kobject *obj,
|
|
||||||
j6_signal_t s,
|
|
||||||
j6_signal_t ds,
|
|
||||||
j6_status_t result) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Register a signal observer
|
/// Get the current object signal state
|
||||||
/// \arg object The observer
|
inline j6_signal_t signals() const { return m_signals; }
|
||||||
/// \arg s The signals the object wants notifiations for
|
|
||||||
void register_signal_observer(observer *object, j6_signal_t s);
|
|
||||||
|
|
||||||
/// Deegister a signal observer
|
|
||||||
/// \arg object The observer
|
|
||||||
void deregister_signal_observer(observer *object);
|
|
||||||
|
|
||||||
/// Increment the handle refcount
|
/// Increment the handle refcount
|
||||||
inline void handle_retain() { ++m_handle_count; }
|
inline void handle_retain() { ++m_handle_count; }
|
||||||
@@ -80,31 +73,36 @@ public:
|
|||||||
if (--m_handle_count == 0) on_no_handles();
|
if (--m_handle_count == 0) on_no_handles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the given thread to the list of threads waiting on this object.
|
||||||
|
inline void add_blocked_thread(thread *t) { m_blocked_threads.append(t); }
|
||||||
|
|
||||||
|
/// Remove the given thread from the list of threads waiting on this object.
|
||||||
|
inline void remove_blocked_thread(thread *t) { m_blocked_threads.remove_swap(t); }
|
||||||
|
|
||||||
|
/// Perform any cleanup actions necessary to mark this object closed
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
/// Check if this object has been closed
|
||||||
|
inline bool closed() const { return check_signal(j6_signal_closed); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
||||||
|
/// should either call the base version, or assert j6_signal_no_handles.
|
||||||
|
virtual void on_no_handles();
|
||||||
|
|
||||||
|
private:
|
||||||
kobject() = delete;
|
kobject() = delete;
|
||||||
kobject(const kobject &other) = delete;
|
kobject(const kobject &other) = delete;
|
||||||
kobject(const kobject &&other) = delete;
|
kobject(const kobject &&other) = delete;
|
||||||
|
|
||||||
/// Notifiy observers of this object
|
/// Notifiy observers of this object
|
||||||
/// \arg mask The signals that triggered this call. If 0, notify all observers.
|
|
||||||
/// \arg result The result to pass to the observers
|
/// \arg result The result to pass to the observers
|
||||||
void notify_signal_observers(j6_signal_t mask, j6_status_t result = j6_status_ok);
|
void notify_signal_observers();
|
||||||
|
|
||||||
/// Interface for subclasses to handle when all handles are closed. Subclasses
|
|
||||||
/// should either call the base version, or assert j6_signal_no_handles.
|
|
||||||
virtual void on_no_handles();
|
|
||||||
|
|
||||||
j6_koid_t m_koid;
|
j6_koid_t m_koid;
|
||||||
j6_signal_t m_signals;
|
j6_signal_t m_signals;
|
||||||
uint16_t m_handle_count;
|
uint16_t m_handle_count;
|
||||||
|
|
||||||
struct observer_registration
|
protected:
|
||||||
{
|
kutil::vector<thread*> m_blocked_threads;
|
||||||
observer_registration(observer* o = nullptr, j6_signal_t s = 0) :
|
|
||||||
object(o), signals(s) {}
|
|
||||||
|
|
||||||
observer *object;
|
|
||||||
j6_signal_t signals;
|
|
||||||
};
|
|
||||||
kutil::vector<observer_registration> m_observers;
|
|
||||||
};
|
};
|
||||||
|
|||||||
158
src/kernel/objects/process.cpp
Normal file
158
src/kernel/objects/process.cpp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#include "kutil/assert.h"
|
||||||
|
#include "kutil/no_construct.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
|
#include "objects/vm_area.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
|
||||||
|
// This object is initialized _before_ global constructors are called,
|
||||||
|
// so we don't want it to have a global constructor at all, lest it
|
||||||
|
// overwrite the previous initialization.
|
||||||
|
static kutil::no_construct<process> __g_kernel_process_storage;
|
||||||
|
process &g_kernel_process = __g_kernel_process_storage.value;
|
||||||
|
|
||||||
|
|
||||||
|
kutil::vector<process*> process::s_processes;
|
||||||
|
|
||||||
|
process::process() :
|
||||||
|
kobject {kobject::type::process},
|
||||||
|
m_next_handle {0},
|
||||||
|
m_state {state::running}
|
||||||
|
{
|
||||||
|
s_processes.append(this);
|
||||||
|
|
||||||
|
j6_handle_t self = add_handle(this);
|
||||||
|
kassert(self == self_handle(), "Process self-handle is not 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "kernel process"-only constructor
|
||||||
|
process::process(page_table *kpml4) :
|
||||||
|
kobject {kobject::type::process},
|
||||||
|
m_space {kpml4},
|
||||||
|
m_next_handle {self_handle()+1},
|
||||||
|
m_state {state::running}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
process::~process()
|
||||||
|
{
|
||||||
|
for (auto &it : m_handles)
|
||||||
|
if (it.val) it.val->handle_release();
|
||||||
|
s_processes.remove_swap(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
process & process::current() { return *bsp_cpu_data.p; }
|
||||||
|
process & process::kernel_process() { return g_kernel_process; }
|
||||||
|
|
||||||
|
process *
|
||||||
|
process::create_kernel_process(page_table *pml4)
|
||||||
|
{
|
||||||
|
return new (&g_kernel_process) process {pml4};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
process::exit(unsigned code)
|
||||||
|
{
|
||||||
|
// TODO: make this thread-safe
|
||||||
|
m_state = state::exited;
|
||||||
|
m_return_code = code;
|
||||||
|
close();
|
||||||
|
|
||||||
|
for (auto *thread : m_threads) {
|
||||||
|
thread->exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this == bsp_cpu_data.p)
|
||||||
|
scheduler::get().schedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
process::update()
|
||||||
|
{
|
||||||
|
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
uint32_t status = 0;
|
||||||
|
while (i < m_threads.count()) {
|
||||||
|
thread *th = m_threads[i];
|
||||||
|
if (th->has_state(thread::state::exited)) {
|
||||||
|
status = th->m_return_code;
|
||||||
|
m_threads.remove_swap_at(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_threads.count() == 0) {
|
||||||
|
// TODO: What really is the return code in this case?
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread *
|
||||||
|
process::create_thread(uint8_t priority, bool user)
|
||||||
|
{
|
||||||
|
if (priority == default_pri)
|
||||||
|
priority = scheduler::default_priority;
|
||||||
|
|
||||||
|
thread *th = new thread(*this, priority);
|
||||||
|
kassert(th, "Failed to create thread!");
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size);
|
||||||
|
|
||||||
|
vm_area *vma = new vm_area_open(stack_size, m_space,
|
||||||
|
vm_flags::zero|vm_flags::write);
|
||||||
|
m_space.add(stack_top - stack_size, vma);
|
||||||
|
|
||||||
|
th->tcb()->rsp3 = stack_top;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_threads.append(th);
|
||||||
|
scheduler::get().add_thread(th->tcb());
|
||||||
|
return th;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
process::thread_exited(thread *th)
|
||||||
|
{
|
||||||
|
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
||||||
|
uint32_t status = th->m_return_code;
|
||||||
|
m_threads.remove_swap(th);
|
||||||
|
remove_handle(th->self_handle());
|
||||||
|
delete th;
|
||||||
|
|
||||||
|
if (m_threads.count() == 0) {
|
||||||
|
exit(status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_handle_t
|
||||||
|
process::add_handle(kobject *obj)
|
||||||
|
{
|
||||||
|
if (!obj)
|
||||||
|
return j6_handle_invalid;
|
||||||
|
|
||||||
|
obj->handle_retain();
|
||||||
|
j6_handle_t handle = m_next_handle++;
|
||||||
|
m_handles.insert(handle, obj);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
process::remove_handle(j6_handle_t handle)
|
||||||
|
{
|
||||||
|
kobject *obj = m_handles.find(handle);
|
||||||
|
if (obj) obj->handle_release();
|
||||||
|
return m_handles.erase(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
kobject *
|
||||||
|
process::lookup_handle(j6_handle_t handle)
|
||||||
|
{
|
||||||
|
return m_handles.find(handle);
|
||||||
|
}
|
||||||
99
src/kernel/objects/process.h
Normal file
99
src/kernel/objects/process.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file process.h
|
||||||
|
/// Definition of process kobject types
|
||||||
|
|
||||||
|
#include "kutil/map.h"
|
||||||
|
#include "kutil/vector.h"
|
||||||
|
#include "objects/kobject.h"
|
||||||
|
#include "page_table.h"
|
||||||
|
#include "vm_space.h"
|
||||||
|
|
||||||
|
class process :
|
||||||
|
public kobject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Top of memory area where thread stacks are allocated
|
||||||
|
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
||||||
|
|
||||||
|
/// Size of userspace thread stacks
|
||||||
|
constexpr static size_t stack_size = 0x4000;
|
||||||
|
|
||||||
|
/// Value that represents default priority
|
||||||
|
constexpr static uint8_t default_pri = 0xff;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
process();
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
virtual ~process();
|
||||||
|
|
||||||
|
static constexpr kobject::type type = kobject::type::process;
|
||||||
|
|
||||||
|
/// Get the currently executing process.
|
||||||
|
static process & current();
|
||||||
|
|
||||||
|
/// Terminate this process.
|
||||||
|
/// \arg code The return code to exit with.
|
||||||
|
void exit(unsigned code);
|
||||||
|
|
||||||
|
/// Update internal bookkeeping about threads.
|
||||||
|
void update();
|
||||||
|
|
||||||
|
/// Get the process' virtual memory space
|
||||||
|
vm_space & space() { return m_space; }
|
||||||
|
|
||||||
|
/// Create a new thread in this process
|
||||||
|
/// \args priority The new thread's scheduling priority
|
||||||
|
/// \args user If true, create a userspace stack for this thread
|
||||||
|
/// \returns The newly created thread object
|
||||||
|
thread * create_thread(uint8_t priorty = default_pri, bool user = true);
|
||||||
|
|
||||||
|
/// Start tracking an object with a handle.
|
||||||
|
/// \args obj The object this handle refers to
|
||||||
|
/// \returns The new handle for this object
|
||||||
|
j6_handle_t add_handle(kobject *obj);
|
||||||
|
|
||||||
|
/// Stop tracking an object with a handle.
|
||||||
|
/// \args handle The handle that refers to the object
|
||||||
|
/// \returns True if the handle was removed
|
||||||
|
bool remove_handle(j6_handle_t handle);
|
||||||
|
|
||||||
|
/// Lookup an object for a handle
|
||||||
|
/// \args handle The handle to the object
|
||||||
|
/// \returns Pointer to the object, or null if not found
|
||||||
|
kobject * lookup_handle(j6_handle_t handle);
|
||||||
|
|
||||||
|
/// Inform the process of an exited thread
|
||||||
|
/// \args th The thread which has exited
|
||||||
|
/// \returns True if this thread ending has ended the process
|
||||||
|
bool thread_exited(thread *th);
|
||||||
|
|
||||||
|
/// Get the handle for this process to refer to itself
|
||||||
|
inline j6_handle_t self_handle() const { return 0; }
|
||||||
|
|
||||||
|
/// Get the process object that owns kernel threads and the
|
||||||
|
/// kernel address space
|
||||||
|
static process & kernel_process();
|
||||||
|
|
||||||
|
/// Create the special kernel process that owns kernel tasks
|
||||||
|
/// \arg pml4 The kernel-only pml4
|
||||||
|
/// \returns The kernel process object
|
||||||
|
static process * create_kernel_process(page_table *pml4);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This constructor is called by create_kernel_process
|
||||||
|
process(page_table *kpml4);
|
||||||
|
|
||||||
|
uint32_t m_return_code;
|
||||||
|
|
||||||
|
vm_space m_space;
|
||||||
|
|
||||||
|
kutil::vector<thread*> m_threads;
|
||||||
|
kutil::map<j6_handle_t, kobject*> m_handles;
|
||||||
|
j6_handle_t m_next_handle;
|
||||||
|
|
||||||
|
enum class state : uint8_t { running, exited };
|
||||||
|
state m_state;
|
||||||
|
|
||||||
|
static kutil::vector<process*> s_processes;
|
||||||
|
};
|
||||||
3
src/kernel/objects/system.cpp
Normal file
3
src/kernel/objects/system.cpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#include "objects/system.h"
|
||||||
|
|
||||||
|
system system::s_instance;
|
||||||
18
src/kernel/objects/system.h
Normal file
18
src/kernel/objects/system.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file system.h
|
||||||
|
/// Definition of kobject type representing the system
|
||||||
|
|
||||||
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
|
class system :
|
||||||
|
public kobject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr kobject::type type = kobject::type::event;
|
||||||
|
|
||||||
|
inline static system * get() { return &s_instance; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static system s_instance;
|
||||||
|
system() : kobject(type::system) {}
|
||||||
|
};
|
||||||
218
src/kernel/objects/thread.cpp
Normal file
218
src/kernel/objects/thread.cpp
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
#include "j6/signals.h"
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/vm_area.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
|
||||||
|
extern "C" void kernel_to_user_trampoline();
|
||||||
|
static constexpr j6_signal_t thread_default_signals = 0;
|
||||||
|
|
||||||
|
extern vm_area_buffers g_kernel_stacks;
|
||||||
|
|
||||||
|
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||||
|
kobject(kobject::type::thread, thread_default_signals),
|
||||||
|
m_parent(parent),
|
||||||
|
m_state(state::loading),
|
||||||
|
m_wait_type(wait_type::none),
|
||||||
|
m_wait_data(0),
|
||||||
|
m_wait_obj(0)
|
||||||
|
{
|
||||||
|
parent.space().initialize_tcb(m_tcb);
|
||||||
|
m_tcb.priority = pri;
|
||||||
|
|
||||||
|
if (!rsp0)
|
||||||
|
setup_kernel_stack();
|
||||||
|
else
|
||||||
|
m_tcb.rsp0 = rsp0;
|
||||||
|
|
||||||
|
m_self_handle = parent.add_handle(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::~thread()
|
||||||
|
{
|
||||||
|
g_kernel_stacks.return_buffer(m_tcb.kernel_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread *
|
||||||
|
thread::from_tcb(TCB *tcb)
|
||||||
|
{
|
||||||
|
static ptrdiff_t offset =
|
||||||
|
-1 * static_cast<ptrdiff_t>(offsetof(thread, m_tcb));
|
||||||
|
return reinterpret_cast<thread*>(kutil::offset_pointer(tcb, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
thread &
|
||||||
|
thread::current()
|
||||||
|
{
|
||||||
|
return *bsp_cpu_data.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void schedule_if_current(thread *t) { if (t == bsp_cpu_data.t) scheduler::get().schedule(); }
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::wait_on_signals(kobject *obj, j6_signal_t signals)
|
||||||
|
{
|
||||||
|
m_wait_type = wait_type::signal;
|
||||||
|
m_wait_data = signals;
|
||||||
|
clear_state(state::ready);
|
||||||
|
|
||||||
|
schedule_if_current(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::wait_on_time(uint64_t t)
|
||||||
|
{
|
||||||
|
m_wait_type = wait_type::time;
|
||||||
|
m_wait_data = t;
|
||||||
|
clear_state(state::ready);
|
||||||
|
|
||||||
|
schedule_if_current(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::wait_on_object(kobject *o)
|
||||||
|
{
|
||||||
|
m_wait_type = wait_type::object;
|
||||||
|
m_wait_data = reinterpret_cast<uint64_t>(o);
|
||||||
|
clear_state(state::ready);
|
||||||
|
|
||||||
|
schedule_if_current(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
thread::wake_on_signals(kobject *obj, j6_signal_t signals)
|
||||||
|
{
|
||||||
|
if (m_wait_type != wait_type::signal ||
|
||||||
|
(signals & m_wait_data) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_wait_type = wait_type::none;
|
||||||
|
m_wait_result = j6_status_ok;
|
||||||
|
m_wait_data = signals;
|
||||||
|
m_wait_obj = obj->koid();
|
||||||
|
set_state(state::ready);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
thread::wake_on_time(uint64_t now)
|
||||||
|
{
|
||||||
|
if (m_wait_type != wait_type::time ||
|
||||||
|
now < m_wait_data)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_wait_type = wait_type::none;
|
||||||
|
m_wait_result = j6_status_ok;
|
||||||
|
m_wait_data = now;
|
||||||
|
m_wait_obj = 0;
|
||||||
|
set_state(state::ready);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
thread::wake_on_object(kobject *o)
|
||||||
|
{
|
||||||
|
if (m_wait_type != wait_type::object ||
|
||||||
|
reinterpret_cast<uint64_t>(o) != m_wait_data)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_wait_type = wait_type::none;
|
||||||
|
m_wait_result = j6_status_ok;
|
||||||
|
m_wait_obj = o->koid();
|
||||||
|
set_state(state::ready);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::wake_on_result(kobject *obj, j6_status_t result)
|
||||||
|
{
|
||||||
|
m_wait_type = wait_type::none;
|
||||||
|
m_wait_result = result;
|
||||||
|
m_wait_data = 0;
|
||||||
|
m_wait_obj = obj->koid();
|
||||||
|
set_state(state::ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::exit(uint32_t code)
|
||||||
|
{
|
||||||
|
m_return_code = code;
|
||||||
|
set_state(state::exited);
|
||||||
|
clear_state(state::ready);
|
||||||
|
close();
|
||||||
|
|
||||||
|
schedule_if_current(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::add_thunk_kernel(uintptr_t rip)
|
||||||
|
{
|
||||||
|
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||||
|
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||||
|
|
||||||
|
stack[6] = rip; // return rip
|
||||||
|
stack[5] = m_tcb.rsp0; // rbp
|
||||||
|
stack[4] = 0xbbbbbbbb; // rbx
|
||||||
|
stack[3] = 0x12121212; // r12
|
||||||
|
stack[2] = 0x13131313; // r13
|
||||||
|
stack[1] = 0x14141414; // r14
|
||||||
|
stack[0] = 0x15151515; // r15
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::add_thunk_user(uintptr_t rip)
|
||||||
|
{
|
||||||
|
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
||||||
|
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||||
|
|
||||||
|
stack[7] = rip; // return rip in rcx
|
||||||
|
stack[6] = m_tcb.rsp3; // rbp
|
||||||
|
stack[5] = 0xbbbbbbbb; // rbx
|
||||||
|
stack[4] = 0x00000200; // r11 sets RFLAGS
|
||||||
|
stack[3] = 0x12121212; // r12
|
||||||
|
stack[2] = 0x13131313; // r13
|
||||||
|
stack[1] = 0x14141414; // r14
|
||||||
|
stack[0] = 0x15151515; // r15
|
||||||
|
|
||||||
|
static const uintptr_t trampoline =
|
||||||
|
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||||
|
add_thunk_kernel(trampoline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::setup_kernel_stack()
|
||||||
|
{
|
||||||
|
using memory::frame_size;
|
||||||
|
using memory::kernel_stack_pages;
|
||||||
|
static constexpr size_t stack_bytes = kernel_stack_pages * frame_size;
|
||||||
|
|
||||||
|
constexpr unsigned null_frame_entries = 2;
|
||||||
|
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||||
|
|
||||||
|
uintptr_t stack_addr = g_kernel_stacks.get_buffer();
|
||||||
|
uintptr_t stack_end = stack_addr + stack_bytes;
|
||||||
|
|
||||||
|
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_end - null_frame_size);
|
||||||
|
for (unsigned i = 0; i < null_frame_entries; ++i)
|
||||||
|
null_frame[i] = 0;
|
||||||
|
|
||||||
|
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
||||||
|
stack_addr, stack_bytes);
|
||||||
|
|
||||||
|
m_tcb.kernel_stack = stack_addr;
|
||||||
|
m_tcb.rsp0 = reinterpret_cast<uintptr_t>(null_frame);
|
||||||
|
m_tcb.rsp = m_tcb.rsp0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread *
|
||||||
|
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
|
||||||
|
{
|
||||||
|
thread *idle = new thread(kernel, pri, rsp0);
|
||||||
|
idle->set_state(state::constant);
|
||||||
|
idle->set_state(state::ready);
|
||||||
|
log::info(logs::task, "Created idle thread as koid %llx", idle->koid());
|
||||||
|
|
||||||
|
return idle;
|
||||||
|
}
|
||||||
183
src/kernel/objects/thread.h
Normal file
183
src/kernel/objects/thread.h
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file thread.h
|
||||||
|
/// Definition of thread kobject types
|
||||||
|
|
||||||
|
#include "kutil/linked_list.h"
|
||||||
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
|
struct page_table;
|
||||||
|
class process;
|
||||||
|
|
||||||
|
struct TCB
|
||||||
|
{
|
||||||
|
// Data used by assembly task control routines. If you change any of these,
|
||||||
|
// be sure to change the assembly definitions in 'tasking.inc'
|
||||||
|
uintptr_t rsp;
|
||||||
|
uintptr_t rsp0;
|
||||||
|
uintptr_t rsp3;
|
||||||
|
uintptr_t pml4;
|
||||||
|
|
||||||
|
uint8_t priority;
|
||||||
|
// note: 3 bytes padding
|
||||||
|
|
||||||
|
// TODO: move state into TCB?
|
||||||
|
|
||||||
|
uintptr_t kernel_stack;
|
||||||
|
|
||||||
|
uint32_t time_left;
|
||||||
|
uint64_t last_ran;
|
||||||
|
};
|
||||||
|
|
||||||
|
using tcb_list = kutil::linked_list<TCB>;
|
||||||
|
using tcb_node = tcb_list::item_type;
|
||||||
|
|
||||||
|
class thread :
|
||||||
|
public kobject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class wait_type : uint8_t { none, signal, time, object };
|
||||||
|
enum class state : uint8_t {
|
||||||
|
ready = 0x01,
|
||||||
|
loading = 0x02,
|
||||||
|
exited = 0x04,
|
||||||
|
constant = 0x80,
|
||||||
|
none = 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get the pointer to the thread object containing this TCB
|
||||||
|
static thread * from_tcb(TCB *tcb);
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
virtual ~thread();
|
||||||
|
|
||||||
|
static constexpr kobject::type type = kobject::type::thread;
|
||||||
|
|
||||||
|
/// Get the currently executing thread.
|
||||||
|
static thread & current();
|
||||||
|
|
||||||
|
/// Get the `ready` state of the thread.
|
||||||
|
/// \returns True if the thread is ready to execute.
|
||||||
|
inline bool ready() const { return has_state(state::ready); }
|
||||||
|
|
||||||
|
/// Get the `loading` state of the thread.
|
||||||
|
/// \returns True if the thread has not finished loading.
|
||||||
|
inline bool loading() const { return has_state(state::loading); }
|
||||||
|
|
||||||
|
/// Get the `constant` state of the thread.
|
||||||
|
/// \returns True if the thread has constant priority.
|
||||||
|
inline bool constant() const { return has_state(state::constant); }
|
||||||
|
|
||||||
|
/// Get the thread priority.
|
||||||
|
inline uint8_t priority() const { return m_tcb.priority; }
|
||||||
|
|
||||||
|
/// Set the thread priority.
|
||||||
|
/// \arg p The new thread priority
|
||||||
|
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||||
|
|
||||||
|
/// Block the thread, waiting on the given object's signals.
|
||||||
|
/// \arg obj Object to wait on
|
||||||
|
/// \arg signals Mask of signals to wait for
|
||||||
|
void wait_on_signals(kobject *obj, j6_signal_t signals);
|
||||||
|
|
||||||
|
/// Block the thread, waiting for a given clock value
|
||||||
|
/// \arg t Clock value to wait for
|
||||||
|
void wait_on_time(uint64_t t);
|
||||||
|
|
||||||
|
/// Block the thread, waiting on the given object
|
||||||
|
/// \arg o The ojbect that should wake this thread
|
||||||
|
void wait_on_object(kobject *o);
|
||||||
|
|
||||||
|
/// Wake the thread if it is waiting on signals.
|
||||||
|
/// \arg obj Object that changed signals
|
||||||
|
/// \arg signals Signal state of the object
|
||||||
|
/// \returns True if this action unblocked the thread
|
||||||
|
bool wake_on_signals(kobject *obj, j6_signal_t signals);
|
||||||
|
|
||||||
|
/// Wake the thread if it is waiting on the clock.
|
||||||
|
/// \arg now Current clock value
|
||||||
|
/// \returns True if this action unblocked the thread
|
||||||
|
bool wake_on_time(uint64_t now);
|
||||||
|
|
||||||
|
/// Wake the thread if it is waiting on the given object.
|
||||||
|
/// \arg o Object trying to wake the thread
|
||||||
|
/// \returns True if this action unblocked the thread
|
||||||
|
bool wake_on_object(kobject *o);
|
||||||
|
|
||||||
|
/// Wake the thread with a given result code.
|
||||||
|
/// \arg obj Object that changed signals
|
||||||
|
/// \arg result Result code to return to the thread
|
||||||
|
void wake_on_result(kobject *obj, j6_status_t result);
|
||||||
|
|
||||||
|
/// Get the result status code from the last blocking operation
|
||||||
|
j6_status_t get_wait_result() const { return m_wait_result; }
|
||||||
|
|
||||||
|
/// Get the current blocking opreation's wait data
|
||||||
|
uint64_t get_wait_data() const { return m_wait_data; }
|
||||||
|
|
||||||
|
inline bool has_state(state s) const {
|
||||||
|
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set_state(state s) {
|
||||||
|
m_state = static_cast<state>(static_cast<uint8_t>(m_state) | static_cast<uint8_t>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear_state(state s) {
|
||||||
|
m_state = static_cast<state>(static_cast<uint8_t>(m_state) & ~static_cast<uint8_t>(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline tcb_node * tcb() { return &m_tcb; }
|
||||||
|
inline process & parent() { return m_parent; }
|
||||||
|
|
||||||
|
/// Terminate this thread.
|
||||||
|
/// \arg code The return code to exit with.
|
||||||
|
void exit(unsigned code);
|
||||||
|
|
||||||
|
/// Add a stack header that returns to the given address in kernel space.
|
||||||
|
/// \arg rip The address to return to, must be kernel space
|
||||||
|
void add_thunk_kernel(uintptr_t rip);
|
||||||
|
|
||||||
|
/// Add a stack header that returns to the given address in user space.
|
||||||
|
/// \arg rip The address to return to, must be user space
|
||||||
|
void add_thunk_user(uintptr_t rip);
|
||||||
|
|
||||||
|
/// Get the handle representing this thread to its process
|
||||||
|
j6_handle_t self_handle() const { return m_self_handle; }
|
||||||
|
|
||||||
|
/// Create the kernel idle thread
|
||||||
|
/// \arg kernel The process object that owns kernel tasks
|
||||||
|
/// \arg pri The idle thread priority value
|
||||||
|
/// \arg rsp The existing stack for the idle thread
|
||||||
|
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
thread() = delete;
|
||||||
|
thread(const thread &other) = delete;
|
||||||
|
thread(const thread &&other) = delete;
|
||||||
|
friend class process;
|
||||||
|
|
||||||
|
/// Constructor. Used when a kernel stack already exists.
|
||||||
|
/// \arg parent The process which owns this thread
|
||||||
|
/// \arg pri Initial priority level of this thread
|
||||||
|
/// \arg rsp0 The existing kernel stack rsp, 0 for none
|
||||||
|
thread(process &parent, uint8_t pri, uintptr_t rsp0 = 0);
|
||||||
|
|
||||||
|
/// Set up a new empty kernel stack for this thread.
|
||||||
|
void setup_kernel_stack();
|
||||||
|
|
||||||
|
tcb_node m_tcb;
|
||||||
|
|
||||||
|
process &m_parent;
|
||||||
|
|
||||||
|
state m_state;
|
||||||
|
wait_type m_wait_type;
|
||||||
|
// There should be 1 byte of padding here
|
||||||
|
|
||||||
|
uint32_t m_return_code;
|
||||||
|
|
||||||
|
uint64_t m_wait_data;
|
||||||
|
j6_status_t m_wait_result;
|
||||||
|
j6_koid_t m_wait_obj;
|
||||||
|
|
||||||
|
j6_handle_t m_self_handle;
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user