84 Commits
Author SHA1 Message Date
josh f7dde241ad Split console into two pages 2023-10-21 20:15:11 -04:00
josh 8638840cd8 Add console header line 2023-10-21 17:08:45 -04:00
josh 4273c703e9 writef: split into writef() and writefv() 2023-10-21 16:45:41 -04:00
josh b2597fb5da Add hulk.ver module 2023-10-18 19:33:43 -04:00
josh 291ba2bc1c Remove xHCI TODO 2023-10-13 14:17:34 -04:00
josh 71bc3543a1 fontgen: show more error info for invalid character metrics 2023-09-26 12:00:22 -04:00
josh a87d24d417 Reduce kernel console font size slightly 2023-09-26 09:59:22 -04:00
josh 6fcc6da022 Remove APIC debug outputs 2023-09-24 11:03:21 -04:00
josh dc07fd0255 Document xHCI runtime registers and interrupter registers 2023-09-23 19:48:14 -04:00
josh eb0a4b2a4d Document xHCI port registers 2023-09-23 19:28:35 -04:00
josh 0ae3c6c96d Document xHCI operational registers 2023-09-23 11:59:20 -04:00
josh 4814b51daf Dump xHCI extended capabilities 2023-09-19 11:07:18 -04:00
josh 14bb3adf37 Document xHCI capability registers 2023-09-19 11:07:02 -04:00
josh cda45b205d Remove legacy PCI bus scanning 2023-09-17 16:13:37 -04:00
josh cfd42550fc Add struct definitions for all PCI header types 2023-09-17 15:22:58 -04:00
josh 673ce8f097 Scan PCI devices via MCFG table 2023-09-17 14:49:02 -04:00
josh ba0befa21b Check that needed ACPI tables are found 2023-09-17 13:40:26 -04:00
josh fa4de31aa4 Store pertinent ACPI table pointers 2023-09-17 11:22:58 -04:00
josh 3412d9ece9 Add Klog.fatal_error() 2023-09-17 11:15:02 -04:00
josh e8978c73f2 Move PCI initialization after ACPI/APIC/RTC 2023-09-17 11:00:48 -04:00
josh f79b0098c5 Map ACPI tables as disable cache + no execute 2023-09-17 09:12:20 -04:00
josh 10d7e7f0c6 Move parsing of ACPI tables into table structures
Move mapping ACPI table memory into common location
2023-09-17 09:08:01 -04:00
josh e088f942d1 Use Acpi.Header in ACPI table structures 2023-09-17 08:53:34 -04:00
josh 6514af8e94 Begin parsing MCFG ACPI table 2023-09-17 08:41:08 -04:00
josh 602a7525d2 Add %p format specifier for printing pointers 2023-09-16 17:09:55 -04:00
josh 617b861524 Size to hulk.elf.size 2023-09-15 13:38:25 -04:00
josh 2f968de7ed Disassemble to hulk.elf.txt 2023-09-15 13:37:48 -04:00
josh 68e85999fb Drop unused sections when linking HULK 2023-09-15 13:37:00 -04:00
josh 6b1445935c Update mk-vmdk task to have a file template since VBoxManage createmedium command was failing 2023-09-15 11:04:36 -04:00
josh 665d8aef80 Update qemu command to change machine to q35
This enables PCIe
2023-09-15 11:04:12 -04:00
josh af5ad73cf7 Update instructions for VirtualBox to change machine chipset
This enables PCIe
2023-09-15 11:03:39 -04:00
josh 4c67faaad4 Rework Kfont to store glyph top as offset from top of text row 2023-09-13 13:48:11 -04:00
josh 4fcea6a283 Reverse framebuffer vertical coordinates to start with 0 at top 2023-09-13 11:57:47 -04:00
josh c958b9ffcc Remove alpha-blending framebuffer functions 2023-09-13 10:57:45 -04:00
josh 3d911ab191 Adjust console text height by font baseline offset
This fixes having leftover pixels on screen bottom when scrolling the
console up.
2023-09-13 10:52:24 -04:00
josh d04c5444c7 fontgen: put character number before character data 2023-09-13 08:54:09 -04:00
josh 209658ee09 Use cache to mkdir_p for HulkBinObj builder 2023-09-10 09:48:07 -04:00
josh f3f45445b3 Determine XHCI controller addresses 2023-09-07 14:04:30 -04:00
josh 459d5c36aa Set alignment of XSDT table pointers to 4 instead of 8 2023-09-07 12:57:26 -04:00
josh 22c9891c4f Add page_address() 2023-09-07 12:51:41 -04:00
josh 2a832bfb8b Print progress messages for cross compiler toolchain installation 2023-09-06 22:58:18 -04:00
josh 496445e533 Fix linker script section placement 2023-09-06 16:40:17 -04:00
josh 156642293f Debug output HULK memory range end addresses 2023-09-04 21:20:10 -04:00
josh 4e46b98efc Capture QEMU serial port output 2023-09-04 21:13:35 -04:00
josh ce3e2c55df Store QEMU files in local qemu directory 2023-09-04 21:12:36 -04:00
josh cb24d00e5f Document running in QEMU in README 2023-09-04 21:04:30 -04:00
josh 4500f3cbca Write HELLO memory map debug data to serial port 2023-09-04 20:59:38 -04:00
josh 0781962e9f Rework HELLO console module to use hulk.writef 2023-09-04 16:17:47 -04:00
josh e25d28ebc7 Add serial module 2023-09-04 14:56:12 -04:00
josh 93a4065dea Add path to OVMF.fd for Arch 2023-09-04 14:07:05 -04:00
josh 5b017fb810 Add some xHCI register structures 2023-06-11 17:22:50 -04:00
josh 37814b4f86 Start on XHCI struct 2023-06-10 22:55:08 -04:00
josh 238659e94b Store PCI device mapped memory ranges 2023-06-10 22:22:09 -04:00
josh c16aa0e9c3 Add hulk.range 2023-06-10 22:21:55 -04:00
josh 2935903bae Map PCI device memory regions 2023-06-10 20:12:46 -04:00
josh 5179b5881b Read PCI device header type 2023-06-10 12:01:06 -04:00
josh a0c62937ff Remove Pci.init_device() for now 2023-06-10 11:28:51 -04:00
josh 657287c160 Move Pci.scan() recursion into scan() itself 2023-06-10 11:20:50 -04:00
josh d600e089e5 Add Pci.Device class 2023-06-10 11:16:27 -04:00
josh dd07a08388 Fix A1 allocator to map the correct pages 2023-06-10 11:06:41 -04:00
josh 63fbbc9051 Use ulong for round_up_power_2() 2023-06-10 10:59:46 -04:00
josh 3e32802862 Add A1.allocate() template to allocate struct/class instances 2023-06-10 10:30:56 -04:00
josh 40ff805f7d Add Pci.Address struct 2023-06-10 09:35:52 -04:00
josh 9ac69c9003 Add Pci.write_config_register() 2023-06-10 09:19:58 -04:00
josh 7aa28da1ef Allow pointers or integers for Hurl.*map_range() 2023-06-09 23:38:52 -04:00
josh 222a1efea4 Fix address calculation in Hurl.identity_map_range() 2023-06-09 23:27:20 -04:00
josh 7a4edf38f0 Scan for multi-function PCI devices 2023-06-08 20:28:32 -04:00
josh dda1b6379f Fix in32()/out32() to use uint instead of ulong 2023-06-08 16:07:44 -04:00
josh 98134957e7 Print APIC table signatures found 2023-06-08 13:21:43 -04:00
josh 22463e729b writef: add %c format specifier support 2023-06-08 13:21:24 -04:00
josh 8de86dd647 Add Pci.init_device() skeleton 2023-06-07 16:49:54 -04:00
josh f6fde01b06 Fix build error -- import hulk.util 2023-04-05 01:08:55 -04:00
josh ea6f485bd9 Add hulk.util and round_up_power_2() 2023-02-24 21:15:33 -05:00
josh 2ab388f2e9 Remove seconds counter printout 2023-02-24 20:58:11 -05:00
josh cc891f6d4d Assign PCI header fields to named variables 2023-02-24 20:53:46 -05:00
josh f5e5c40c12 Reduce Hurl.map() template overhead 2023-01-02 20:46:12 -05:00
josh 7b09c9beed Add A1 memory allocator 2023-01-01 22:02:48 -05:00
josh 103205bff2 Move address constants into Hurl struct 2023-01-01 21:04:29 -05:00
josh 25ee1775f0 Capitalize namespacing structs
This helps to avoid name clashes when breaking up modules into packages.
2023-01-01 17:34:41 -05:00
josh a423cdf1b1 Fix RTC clock reading in 24-hour mode 2022-12-19 11:32:08 -05:00
josh 7c8744d1e0 Read RTC date and time 2022-12-18 22:29:12 -05:00
josh 4081a220ef Route I/O APIC interrupts, enable RTC tick interrupt 2022-12-16 00:32:49 -05:00
josh 9adc248225 Initialize rtc module 2022-12-15 20:41:44 -05:00
josh 676fd17bf5 Add pit and rtc modules 2022-12-15 20:41:15 -05:00
33 changed files with 1805 additions and 431 deletions

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
.rscons* .rscons*
/x86_64-elf-gcc/ /x86_64-elf-gcc/
/build/ /build/
/qemu/
+12 -2
View File
@@ -17,6 +17,8 @@ the build command.
# Running HOS # Running HOS
HOS can run on a physical machine, in VirtualBox, or in QEMU.
## Physical Machine ## Physical Machine
To install HOS to an EFI partition, follow these steps: To install HOS to an EFI partition, follow these steps:
@@ -67,5 +69,13 @@ To create a VM in VirtualBox:
12. Move the HOS.vmdk file generated to the HOS VirtualBox folder. 12. Move the HOS.vmdk file generated to the HOS VirtualBox folder.
13. In VirtualBox Manager, open HOS VM settings. 13. In VirtualBox Manager, open HOS VM settings.
14. Under System, check the Enable EFI checkbox. 14. Under System, check the Enable EFI checkbox.
15. Under Storage, add a hard disk and browse to select the HOS.vmdk file. 15. Under System, change the Chipset to ICH9.
16. To run HOS, either start the VM through VirtualBox or with "./rscons run-vb". 16. Under Storage, add a hard disk and browse to select the HOS.vmdk file.
17. To run HOS, either start the VM through VirtualBox or with "./rscons run-vb".
## QEMU
Make sure QEMU is installed (possibly the `qemu-desktop` package). Then:
1. Build HOS ("./rscons").
2. Run `./rscons run` to run HOS in QEMU.
+53 -9
View File
@@ -16,7 +16,7 @@ configure do
end end
# Kernel default font size. # Kernel default font size.
KFONT_SIZE = 18 KFONT_SIZE = 16
# One kilobyte. # One kilobyte.
KB = 1024 KB = 1024
@@ -26,7 +26,7 @@ MB = 1024 * 1024
class HulkBinObj < Builder class HulkBinObj < Builder
def run(options) def run(options)
FileUtils.mkdir_p(File.dirname(@target)) @cache.mkdir_p(File.dirname(@target))
File.write(@target, <<EOF) File.write(@target, <<EOF)
.section ".rodata" .section ".rodata"
.balign 4096 .balign 4096
@@ -136,7 +136,7 @@ hulk_env = env "hulk", use: %w[ldc2 x86_64-elf-gcc] do |env|
env["D_IMPORT_PATH"] += %w[src] env["D_IMPORT_PATH"] += %w[src]
env["D_IMPORT_PATH"] << env.expand("^/src") env["D_IMPORT_PATH"] << env.expand("^/src")
env["LD"] = "x86_64-elf-gcc" env["LD"] = "x86_64-elf-gcc"
env["LDFLAGS"] += %w[-nostdlib -Tsrc/hulk/hulk.ld -Wl,-Map,${_TARGET}.map] env["LDFLAGS"] += %w[-nostdlib -Tsrc/hulk/hulk.ld -Wl,--gc-sections -Wl,-Map,${_TARGET}.map]
env["LDCMD"] = %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}] env["LDCMD"] = %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}]
env["OBJDUMP"] = "x86_64-elf-objdump" env["OBJDUMP"] = "x86_64-elf-objdump"
env["OBJCOPY"] = "x86_64-elf-objcopy" env["OBJCOPY"] = "x86_64-elf-objcopy"
@@ -145,8 +145,8 @@ hulk_env = env "hulk", use: %w[ldc2 x86_64-elf-gcc] do |env|
env.CheckThreadLocal(:hulk_map_check, "^/hulk.elf.map") env.CheckThreadLocal(:hulk_map_check, "^/hulk.elf.map")
env.depends("^/hulk.elf", "src/hulk/hulk.ld") env.depends("^/hulk.elf", "src/hulk/hulk.ld")
env["SIZE"] = "x86_64-elf-size" env["SIZE"] = "x86_64-elf-size"
env.Size("^/hulk.size", "^/hulk.elf") env.Size("^/hulk.elf.size", "^/hulk.elf")
env.Disassemble("^/hulk.txt", "^/hulk.elf") env.Disassemble("^/hulk.elf.txt", "^/hulk.elf")
env.Command("^/hulk.bin", "^/hulk.elf", env.Command("^/hulk.bin", "^/hulk.elf",
"CMD" => %W[${OBJCOPY} -O binary ${_SOURCES} ${_TARGET}], "CMD" => %W[${OBJCOPY} -O binary ${_SOURCES} ${_TARGET}],
"CMD_DESC" => "Convert ELF to binary:") "CMD_DESC" => "Convert ELF to binary:")
@@ -162,6 +162,10 @@ hello_env = env "hello", use: %w[ldc2 x86_64-w64-mingw32-gcc] do |env|
env.add_builder(CheckThreadLocal) env.add_builder(CheckThreadLocal)
env.add_builder(HulkBinObj) env.add_builder(HulkBinObj)
env["sources"] = glob("src/hello/**/*.d") env["sources"] = glob("src/hello/**/*.d")
env["sources"] += %w[
src/hulk/serial.d
src/hulk/writef.d
]
env["sources"] += glob("uefi-d/source/**/*.d") env["sources"] += glob("uefi-d/source/**/*.d")
env.HulkBinObj("^/hulk_bin.S", hulk_env.expand("^/hulk.bin")) env.HulkBinObj("^/hulk_bin.S", hulk_env.expand("^/hulk.bin"))
env.Object("^/hulk_bin.o", "^/hulk_bin.S") env.Object("^/hulk_bin.o", "^/hulk_bin.S")
@@ -183,16 +187,56 @@ hello_env = env "hello", use: %w[ldc2 x86_64-w64-mingw32-gcc] do |env|
end end
task "run", desc: "Run HOS in QEMU" do task "run", desc: "Run HOS in QEMU" do
Dir.mktmpdir do |tmpdir| FileUtils.rm_rf("qemu")
FileUtils.mkdir_p("qemu")
img = hello_env.expand("^/HOS.img") img = hello_env.expand("^/HOS.img")
FileUtils.cp(img, tmpdir) FileUtils.cp(img, "qemu")
sh %W[qemu-system-x86_64 -cpu max -bios OVMF.fd -drive file=#{tmpdir}/HOS.img,format=raw -device qemu-xhci -device usb-tablet] ovmf = "OVMF.fd"
if File.exist?("/usr/share/edk2/x64/OVMF.fd")
ovmf = "/usr/share/edk2/x64/OVMF.fd"
end end
sh %W[
qemu-system-x86_64
-machine q35
-cpu max
-serial file:qemu/serial.out
-bios #{ovmf}
-drive file=qemu/HOS.img,format=raw
-device qemu-xhci
-device usb-tablet]
end end
# See README.md for how to set up VirtualBox for HOS. # See README.md for how to set up VirtualBox for HOS.
task "mk-vmdk", desc: "Create VirtualBox VMDK virtual drive for HOS" do task "mk-vmdk", desc: "Create VirtualBox VMDK virtual drive for HOS" do
sh %W[VBoxManage internalcommands createrawvmdk -filename HOS.vmdk -rawdisk #{File.expand_path(hello_env.expand("^/HOS.img"))}] path = File.expand_path(hello_env.expand("^/HOS.img"))
sectors = File.stat(path).size / 512
File.binwrite("HOS.vmdk", <<EOF)
# Disk DescriptorFile
version=1
CID=1d1c3615
parentCID=ffffffff
createType="fullDevice"
# Extent description
RW #{sectors} FLAT "#{path}" 0
# The disk Data Base
#DDB
ddb.virtualHWVersion = "4"
ddb.adapterType="ide"
ddb.geometry.cylinders="8"
ddb.geometry.heads="16"
ddb.geometry.sectors="63"
ddb.uuid.image="03783b5a-7587-4d8b-ad04-9d70f70a94c2"
ddb.uuid.parent="00000000-0000-0000-0000-000000000000"
ddb.uuid.modification="00000000-0000-0000-0000-000000000000"
ddb.uuid.parentmodification="00000000-0000-0000-0000-000000000000"
ddb.geometry.biosCylinders="8"
ddb.geometry.biosHeads="16"
ddb.geometry.biosSectors="63"
EOF
#sh %W[VBoxManage createmedium disk --filename=HOS.vmdk --variant=RawDisk --format=VMDK --property RawDrive=#{File.expand_path(hello_env.expand("^/HOS.img"))}]
end end
# See README.md for how to set up VirtualBox for HOS. # See README.md for how to set up VirtualBox for HOS.
+77 -15
View File
@@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <ft2build.h> #include <ft2build.h>
#include <stdio.h> #include <stdio.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
@@ -86,35 +87,43 @@ static void generate_bytes(FILE * file, const uint8_t * bytes, int count)
} }
} }
static void generate(const char * d_file_name) static bool generate(const char * d_file_name)
{ {
FILE * fh = fopen(d_file_name, "wb"); FILE * fh = fopen(d_file_name, "wb");
fprintf(fh, "module hulk.kfont;\n"); fprintf(fh, "module hulk.kfont;\n");
fprintf(fh, "struct CharInfo {\n"); fprintf(fh, "struct CharInfo {\n");
fprintf(fh, " uint width;\n"); fprintf(fh, " /** Glyph bitmap width. */\n");
fprintf(fh, " uint height;\n"); fprintf(fh, " ubyte width;\n");
fprintf(fh, " int top;\n"); fprintf(fh, " /** Glyph bitmap height. */\n");
fprintf(fh, " int left;\n"); fprintf(fh, " ubyte height;\n");
fprintf(fh, " /** Glyph bitmap top offset (descent from top of text row). */\n");
fprintf(fh, " ubyte top;\n");
fprintf(fh, " /** Glyph bitmap left offset. */\n");
fprintf(fh, " ubyte left;\n");
fprintf(fh, " /** Glyph bitmap. */\n");
fprintf(fh, " const ubyte[] bitmap;\n"); fprintf(fh, " const ubyte[] bitmap;\n");
fprintf(fh, "};\n"); fprintf(fh, "};\n");
fprintf(fh, "struct FontInfo {\n"); fprintf(fh, "struct FontInfo {\n");
fprintf(fh, " uint line_height;\n"); fprintf(fh, " /** Font line height. */\n");
fprintf(fh, " uint advance;\n"); fprintf(fh, " ubyte line_height;\n");
fprintf(fh, " int baseline_offset;\n"); fprintf(fh, " /** Font advance. */\n");
fprintf(fh, " ubyte advance;\n");
fprintf(fh, " /** Characters. */\n");
fprintf(fh, " const CharInfo[] chars;\n"); fprintf(fh, " const CharInfo[] chars;\n");
fprintf(fh, "};\n"); fprintf(fh, "};\n");
fprintf(fh, "__gshared const FontInfo kfont = {\n"); fprintf(fh, "/** Kernel font data. */\n");
fprintf(fh, "__gshared const FontInfo Kfont = {\n");
fprintf(fh, " %du,\n", line_height); fprintf(fh, " %du,\n", line_height);
fprintf(fh, " %du,\n", max_advance); fprintf(fh, " %du,\n", max_advance);
fprintf(fh, " %d,\n", baseline_offset);
fprintf(fh, " [\n"); fprintf(fh, " [\n");
for (int i = 0; i < N_CHARS; i++) for (int i = 0; i < N_CHARS; i++)
{ {
fprintf(fh, " /* %d */", i);
fprintf(fh, " CharInfo(\n"); fprintf(fh, " CharInfo(\n");
fprintf(fh, " %du,\n", char_infos[i].width); fprintf(fh, " %du /* width */,\n", char_infos[i].width);
fprintf(fh, " %du,\n", char_infos[i].height); fprintf(fh, " %du /* height */,\n", char_infos[i].height);
fprintf(fh, " %d,\n", char_infos[i].top); fprintf(fh, " %du /* top */,\n", char_infos[i].top);
fprintf(fh, " %d,\n", char_infos[i].left); fprintf(fh, " %du /* left */,\n", char_infos[i].left);
if (char_infos[i].width > 0) if (char_infos[i].width > 0)
{ {
fprintf(fh, " [\n"); fprintf(fh, " [\n");
@@ -130,6 +139,7 @@ static void generate(const char * d_file_name)
fprintf(fh, " ],\n"); fprintf(fh, " ],\n");
fprintf(fh, "};\n"); fprintf(fh, "};\n");
fclose(fh); fclose(fh);
return true;
} }
int main(int argc, char * argv[]) int main(int argc, char * argv[])
@@ -167,7 +177,59 @@ int main(int argc, char * argv[])
line_height = round_up_26_6(face->size->metrics.height); line_height = round_up_26_6(face->size->metrics.height);
baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom; baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom;
generate(out_file); if ((line_height < 1) || (line_height > 0xFF))
{
fprintf(stderr, "Error: invalid line_height %d\n", line_height);
return false;
}
if ((max_advance < 1) || (max_advance > 0xFF))
{
fprintf(stderr, "Error: invalid max_advance %d\n", max_advance);
return false;
}
for (int i = 0; i < N_CHARS; i++)
{
if ((char_infos[i].width < 0) || (char_infos[i].width > max_advance))
{
fprintf(stderr, "Error: invalid character %d width: %d (max advance %d)\n", i, char_infos[i].width, max_advance);
return false;
}
if ((char_infos[i].height < 0) || (char_infos[i].height > line_height))
{
fprintf(stderr, "Error: invalid character %d height: %d (line height %d)\n", i, char_infos[i].height, line_height);
return false;
}
if ((char_infos[i].left < 0) || (char_infos[i].left >= max_advance))
{
fprintf(stderr, "Error: invalid character %d left: %d (max advance %d)\n", i, char_infos[i].left, max_advance);
return false;
}
/* Adjust character top to be index downward from top of font box. */
char_infos[i].top = line_height - baseline_offset - char_infos[i].top;
if ((char_infos[i].top < 0) || (char_infos[i].top >= line_height))
{
fprintf(stderr, "Error: invalid character %d top: %d (line height %d)\n", i, char_infos[i].top, line_height);
return false;
}
if ((char_infos[i].left + char_infos[i].width) > max_advance)
{
fprintf(stderr, "Error: character %d left (%d) + width (%d) > advance (%d)\n",
i, char_infos[i].left, char_infos[i].width, max_advance);
return false;
}
if ((char_infos[i].top + char_infos[i].height) > line_height)
{
fprintf(stderr, "Error: character %d top (%d) + height (%d) > line height (%d)\n",
i, char_infos[i].top, char_infos[i].height, line_height);
return false;
}
}
if (!generate(out_file))
{
return 1;
}
return 0; return 0;
} }
+29 -122
View File
@@ -6,155 +6,62 @@ module hello.console;
import uefi; import uefi;
import hello.hello; import hello.hello;
import core.stdc.stdarg; import core.stdc.stdarg;
import hulk.writef;
struct console struct Console
{ {
/** /**
* Format a hexadecimal value to a string. * Write a character to the console.
* *
* @param s String buffer. * @param ch Character to write.
* @param v Value to format.
* @param ptr If true, always output all 16 digits, and separate the upper and
* lower halves with an underscore.
*/ */
private static size_t format_hex(CHAR16 * s, ulong v, bool ptr) public static void write(ubyte ch)
{ {
string hex_chars = "0123456789ABCDEF"; if (ch == '\n')
bool print = ptr;
size_t si = 0u;
for (size_t i = 0u; i < 16u; i++)
{ {
if (ptr && (i == 8u)) write('\r');
{
s[si++] = '_';
} }
ulong n = (v >> (60u - 4u * i)) & 0xFu; CHAR16[2] s16 = [ch, 0];
if (n != 0u)
{
print = true;
}
if (print || (i == 15u))
{
s[si++] = hex_chars[n];
}
}
return si;
}
/**
* Format a decimal value to a string.
*
* @param s String buffer.
* @param v Value to format.
*/
private static size_t format_dec(CHAR16 * s, ulong v)
{
string dec_chars = "0123456789";
bool print;
char[20] buf;
size_t buf_i;
while ((buf_i == 0u) || (v != 0u))
{
ulong n = v % 10u;
buf[buf_i++] = dec_chars[n];
v /= 10u;
}
for (size_t si = 0u; si < buf_i; si++)
{
s[si] = buf[buf_i - si - 1u];
}
return buf_i;
}
/**
* Write a string to the console.
*
* @param s Format string.
* @param args Variable arguments structure.
*/
public static void write(string s, va_list args)
{
__gshared static CHAR16[256] s16;
size_t i = 0u;
bool escape = false;
foreach (char c; s)
{
if (escape)
{
if (c == '%')
{
s16[i++] = '%';
}
else if (c == 'x')
{
ulong v;
va_arg(args, v);
i += format_hex(&s16[i], v, false);
}
else if (c == 'p')
{
ulong v;
va_arg(args, v);
i += format_hex(&s16[i], v, true);
}
else if (c == 'S')
{
const(CHAR16) * s2;
va_arg(args, s2);
for (size_t s2_i = 0u; s2[s2_i] != 0; s2_i++)
{
s16[i++] = s2[s2_i];
}
}
else if (c == 'u')
{
ulong v;
va_arg(args, v);
i += format_dec(&s16[i], v);
}
else
{
s16[i++] = c;
}
escape = false;
}
else if (c == '%')
{
escape = true;
}
else
{
s16[i++] = c;
}
}
s16[i++] = 0u;
st.ConOut.OutputString(st.ConOut, &s16[0]); st.ConOut.OutputString(st.ConOut, &s16[0]);
} }
/** /**
* Write a string to the console. * Write a formatted string to the console.
*
* @param s Format string.
* @param args Variable arguments structure.
*/
public static void writef(string s, va_list args)
{
hulk.writef.writefv(function void(ubyte ch) {
Console.write(ch);
}, s, args);
}
/**
* Write a formatted string to the console.
* *
* @param s Format string. * @param s Format string.
*/ */
public static extern (C) void write(string s, ...) public static extern (C) void writef(string s, ...)
{ {
va_list args; va_list args;
va_start(args, s); va_start(args, s);
write(s, args); writef(s, args);
va_end(args); va_end(args);
} }
/** /**
* Write a string to the console. * Write a formatted string and newline to the console.
* *
* @param s Format string. * @param s Format string.
*/ */
public static extern (C) void writeln(string s, ...) public static extern (C) void writefln(string s, ...)
{ {
va_list args; va_list args;
va_start(args, s); va_start(args, s);
write(s, args); writef(s, args);
write("\r\n", args); writef("\n", args);
va_end(args); va_end(args);
} }
@@ -163,7 +70,7 @@ struct console
*/ */
public static void wait_key() public static void wait_key()
{ {
writeln("Press any key..."); writefln("Press any key...");
st.ConIn.Reset(st.ConIn, FALSE); st.ConIn.Reset(st.ConIn, FALSE);
EFI_INPUT_KEY key; EFI_INPUT_KEY key;
while (st.ConIn.ReadKeyStroke(st.ConIn, &key) == EFI_NOT_READY) while (st.ConIn.ReadKeyStroke(st.ConIn, &key) == EFI_NOT_READY)
+29 -14
View File
@@ -11,6 +11,7 @@ import hulk.pagetable;
import hulk.cpu; import hulk.cpu;
import hulk.memory; import hulk.memory;
import ldc.llvmasm; import ldc.llvmasm;
import hulk.serial;
__gshared EFI_SYSTEM_TABLE * st; __gshared EFI_SYSTEM_TABLE * st;
/** HULK binary start address, 4KB-aligned. */ /** HULK binary start address, 4KB-aligned. */
@@ -93,7 +94,7 @@ private bool set_graphics_mode()
&gop_guid, null, &buffer_size, handles); &gop_guid, null, &buffer_size, handles);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
{ {
console.writeln("LocateHandle: error %x", status); Console.writefln("LocateHandle: error %x", status);
return false; return false;
} }
EFI_HANDLE gop_handle = handles[0]; EFI_HANDLE gop_handle = handles[0];
@@ -102,12 +103,12 @@ private bool set_graphics_mode()
&gop_guid, &gop_interface); &gop_guid, &gop_interface);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
{ {
console.writeln("HandleProtocol: error %x", status); Console.writefln("HandleProtocol: error %x", status);
return false; return false;
} }
if (gop_interface == null) if (gop_interface == null)
{ {
console.writeln("null interface from HandleProtocol"); Console.writefln("null interface from HandleProtocol");
return false; return false;
} }
EFI_GRAPHICS_OUTPUT_PROTOCOL * gop = cast(EFI_GRAPHICS_OUTPUT_PROTOCOL *)gop_interface; EFI_GRAPHICS_OUTPUT_PROTOCOL * gop = cast(EFI_GRAPHICS_OUTPUT_PROTOCOL *)gop_interface;
@@ -129,7 +130,7 @@ private bool set_graphics_mode()
} }
if ((status = gop.SetMode(gop, best_mode_number)) != EFI_SUCCESS) if ((status = gop.SetMode(gop, best_mode_number)) != EFI_SUCCESS)
{ {
console.writeln("SetMode: Error %x\n", status); Console.writefln("SetMode: Error %x\n", status);
return false; return false;
} }
bootinfo().fb.buffer = cast(uint *)gop.Mode.FrameBufferBase; bootinfo().fb.buffer = cast(uint *)gop.Mode.FrameBufferBase;
@@ -174,7 +175,7 @@ private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_k
&descriptor_version); &descriptor_version);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
{ {
console.writeln("GetMemoryMap: Error %x", status); Console.writefln("GetMemoryMap: Error %x", status);
for (;;) {} for (;;) {}
} }
size_t n_entries = memory_map_size / descriptor_size; size_t n_entries = memory_map_size / descriptor_size;
@@ -187,7 +188,7 @@ private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_k
{ {
if (count > bootinfo().memory_map.length) if (count > bootinfo().memory_map.length)
{ {
console.writeln("Memory map too large"); Console.writefln("Memory map too large");
for (;;) {} for (;;) {}
} }
EFI_MEMORY_DESCRIPTOR * descriptor = cast(EFI_MEMORY_DESCRIPTOR *)&scratch_base[count * descriptor_size]; EFI_MEMORY_DESCRIPTOR * descriptor = cast(EFI_MEMORY_DESCRIPTOR *)&scratch_base[count * descriptor_size];
@@ -205,6 +206,7 @@ private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_k
} }
if (descriptor.Type >= efi_to_hulk_memory_map_type.length) if (descriptor.Type >= efi_to_hulk_memory_map_type.length)
{ {
Serial.writefln("Unexpected descriptor type %u", descriptor.Type);
continue; continue;
} }
bootinfo().memory_map[count].base = descriptor.PhysicalStart; bootinfo().memory_map[count].base = descriptor.PhysicalStart;
@@ -217,6 +219,9 @@ private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_k
bootinfo().bss_phys = bootinfo().memory_map[count].base; bootinfo().bss_phys = bootinfo().memory_map[count].base;
bootinfo().memory_map[count].base += hulk_bss_size(); bootinfo().memory_map[count].base += hulk_bss_size();
bootinfo().memory_map[count].size -= hulk_bss_size(); bootinfo().memory_map[count].size -= hulk_bss_size();
Serial.writefln("Locating HULK BSS at %x:%x",
bootinfo().bss_phys,
bootinfo().bss_phys + hulk_bss_size() - 1u);
found_bss = true; found_bss = true;
} }
if ((!found_stack) && if ((!found_stack) &&
@@ -226,6 +231,9 @@ private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_k
bootinfo().stack_phys = bootinfo().memory_map[count].base; bootinfo().stack_phys = bootinfo().memory_map[count].base;
bootinfo().memory_map[count].base += hulk_stack_size(); bootinfo().memory_map[count].base += hulk_stack_size();
bootinfo().memory_map[count].size -= hulk_stack_size(); bootinfo().memory_map[count].size -= hulk_stack_size();
Serial.writefln("Locating HULK stack at %x:%x",
bootinfo().stack_phys,
bootinfo().stack_phys + hulk_stack_size() - 1u);
found_stack = true; found_stack = true;
} }
if ((!found_fb_buffer1) && if ((!found_fb_buffer1) &&
@@ -235,8 +243,15 @@ private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_k
bootinfo().fb_buffer1_phys = bootinfo().memory_map[count].base; bootinfo().fb_buffer1_phys = bootinfo().memory_map[count].base;
bootinfo().memory_map[count].base += fb_size; bootinfo().memory_map[count].base += fb_size;
bootinfo().memory_map[count].size -= fb_size; bootinfo().memory_map[count].size -= fb_size;
Serial.writefln("Locating HULK FB1 at %x:%x",
bootinfo().fb_buffer1_phys,
bootinfo().fb_buffer1_phys + fb_size - 1u);
found_fb_buffer1 = true; found_fb_buffer1 = true;
} }
Serial.writefln("Memory range %x:%x (%u)",
bootinfo().memory_map[count].base,
bootinfo().memory_map[count].base + bootinfo().memory_map[count].size - 1u,
bootinfo().memory_map[count].type);
} }
bootinfo().memory_map_count = count; bootinfo().memory_map_count = count;
if ((!found_bss) || (!found_stack) || (!found_fb_buffer1)) if ((!found_bss) || (!found_stack) || (!found_fb_buffer1))
@@ -511,21 +526,21 @@ extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
{ {
.st = st; .st = st;
console.clear(); Console.clear();
console.writeln("Welcome to HELLO, HOS EFI Lightweight LOader, v0.1.0"); Console.writefln("Welcome to HELLO, HOS EFI Lightweight LOader, v0.1.0");
console.writeln("Firmware vendor: '%S', version: 0x%x", st.FirmwareVendor, st.FirmwareVendor); Console.writefln("Firmware vendor: '%S', version: 0x%x", st.FirmwareVendor, st.FirmwareVendor);
if (!find_acpi_xsdt()) if (!find_acpi_xsdt())
{ {
console.writeln("Error: Could not locate ACPI XSDT"); Console.writefln("Error: Could not locate ACPI XSDT");
console.wait_key(); Console.wait_key();
return EFI_SUCCESS; return EFI_SUCCESS;
} }
if (!set_graphics_mode()) if (!set_graphics_mode())
{ {
console.wait_key(); Console.wait_key();
return EFI_SUCCESS; return EFI_SUCCESS;
} }
@@ -544,8 +559,8 @@ extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
{ {
break; break;
} }
console.writeln("ExitBootServices: Error %x", status); Console.writefln("ExitBootServices: Error %x", status);
console.wait_key(); Console.wait_key();
return EFI_SUCCESS; return EFI_SUCCESS;
} }
+110 -48
View File
@@ -7,12 +7,18 @@ import hulk.hurl;
import hulk.klog; import hulk.klog;
import hulk.memory; import hulk.memory;
struct acpi struct Acpi
{ {
enum uint APIC_SIGNATURE = 0x43495041u; private static uint signature(string s)
enum uint XSDT_SIGNATURE = 0x54445358u; {
return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
}
static struct XsdtHeader enum uint APIC_SIGNATURE = signature("APIC");
enum uint MCFG_SIGNATURE = signature("MCFG");
enum uint XSDT_SIGNATURE = signature("XSDT");
static align(4) struct Header
{ {
uint signature; uint signature;
uint length; uint length;
@@ -25,78 +31,134 @@ struct acpi
uint creator_revision; uint creator_revision;
} }
static struct Xsdt static struct XSDT
{ {
XsdtHeader header; Header header;
uint _pad; /* Table pointers are not ulong-aligned! They begin at offset 36. */
ulong[0] tables; align(4) ulong[1] tables;
} }
static struct MadtHeader static struct MADT
{ {
uint signature; static struct Entry
uint length; {
ubyte revision; enum LOCAL_APIC_ADDRESS_OVERRIDE = 5;
ubyte checksum;
char[6] oemid;
ulong oemtableid;
uint oem_revision;
uint creator_id;
uint creator_revision;
uint local_apic_address;
uint flags;
}
static struct MadtEntry
{
ubyte type; ubyte type;
ubyte length; ubyte length;
} }
public static __gshared ulong apic_address; Header header;
uint local_apic_address;
uint flags;
void initialize()
{
apic_address = local_apic_address;
const(void) * end = cast(const(void) *)(&this) + header.length;
const(Entry) * entry = cast(const(Entry) *)(cast(ulong)&this + MADT.sizeof);
while (entry < end)
{
entry = cast(const(Entry) *)(cast(size_t)entry + entry.length);
if (entry.type == Entry.LOCAL_APIC_ADDRESS_OVERRIDE)
{
/* Found a 64-bit Local APIC Address Override entry. */
memcpy(cast(void *)&apic_address, cast(const(void) *)entry + 4u, apic_address.sizeof);
}
}
}
}
static struct MCFG
{
/* PCI Segment Group Memory Area Descriptor. */
static struct SGMADesc
{
uint[2] base_address_a;
ushort pci_segment_group_number;
ubyte start_pci_bus_number;
ubyte end_pci_bus_number;
uint _reserved;
public @property ulong base_address() const
{
return base_address_a[0] | (cast(ulong)base_address_a[1] << 32);
}
}
static assert(SGMADesc.sizeof == 16);
static assert(SGMADesc.alignof == 4);
Header header;
uint[2] _reserved;
SGMADesc[0] sgma_descs;
size_t n_sgma_descs()
{
return (header.length - MCFG.sgma_descs.offsetof) / MCFG.SGMADesc.sizeof;
}
}
public __gshared ulong apic_address;
public __gshared MADT * madt;
public __gshared MCFG * mcfg;
public static void initialize(ulong acpi_xsdt_phys) public static void initialize(ulong acpi_xsdt_phys)
{ {
/* Map the XSDT header. */ /* Map the XSDT header. */
hurl.identity_map_range(acpi_xsdt_phys, Xsdt.sizeof, 0u); map_table(acpi_xsdt_phys, PAGE_SIZE);
const(Xsdt) * xsdt = cast(const(Xsdt) *)acpi_xsdt_phys; const(XSDT) * xsdt = cast(const(XSDT) *)acpi_xsdt_phys;
if (xsdt.header.signature != XSDT_SIGNATURE) if (xsdt.header.signature != XSDT_SIGNATURE)
{ {
klog.writef("XSDT signature invalid\n"); Klog.fatal_error("XSDT signature invalid");
return; return;
} }
/* Map the entire XSDT. */ /* Map the entire XSDT. */
hurl.identity_map_range(acpi_xsdt_phys, xsdt.header.length, 0u); if (xsdt.header.length > PAGE_SIZE)
{
map_table(acpi_xsdt_phys, xsdt.header.length);
}
size_t n_entries = (xsdt.header.length - xsdt.header.sizeof) / xsdt.tables[0].sizeof; size_t n_entries = (xsdt.header.length - xsdt.header.sizeof) / xsdt.tables[0].sizeof;
for (size_t i = 0u; i < n_entries; i++) for (size_t i = 0u; i < n_entries; i++)
{ {
ulong address = xsdt.tables[i]; ulong address = xsdt.tables[i];
hurl.identity_map_range(address, 4u, 0u); map_table(address, PAGE_SIZE);
uint signature = *cast(const(uint) *)address; const(Header) * header = cast(const(Header) *)address;
uint length = header.length;
if (length > PAGE_SIZE)
{
map_table(address, length);
}
uint signature = header.signature;
Klog.writefln("Found ACPI table %08x (%c%c%c%c)",
signature,
signature & 0xFFu,
(signature >> 8u) & 0xFFu,
(signature >> 16u) & 0xFFu,
(signature >> 24u) & 0xFFu);
if (signature == APIC_SIGNATURE) if (signature == APIC_SIGNATURE)
{ {
parse_apic_table(address); madt = cast(MADT *)address;
madt.initialize();
} }
else if (signature == MCFG_SIGNATURE)
{
mcfg = cast(MCFG *)address;
}
}
if (madt == null)
{
Klog.fatal_error("MADT table not found");
}
if (mcfg == null)
{
Klog.fatal_error("MCFG table not found");
} }
} }
private static parse_apic_table(ulong address) private static void map_table(ulong address, ulong length)
{ {
const(MadtHeader) * madt_header = cast(const(MadtHeader) *)address; Hurl.identity_map_range(address, length, PT_WRITABLE | PT_DISABLE_CACHE | PT_NO_EXECUTE);
hurl.identity_map_range(address, madt_header.length, 0u);
apic_address = madt_header.local_apic_address;
klog.writefln("Found 32-bit APIC address: 0x%x", apic_address);
const(void) * madt_end = cast(const(void) *)(address + madt_header.length);
const(MadtEntry) * madt_entry = cast(const(MadtEntry) *)(address + 0x2Cu);
while (madt_entry < madt_end)
{
madt_entry = cast(const(MadtEntry) *)(cast(size_t)madt_entry + madt_entry.length);
if (madt_entry.type == 5u)
{
/* Found a 64-bit Local APIC Address Override entry. */
memcpy(cast(void *)&apic_address, cast(const(void) *)madt_entry + 4u, 8u);
klog.writefln("Found 64-bit APIC address: 0x%x", apic_address);
}
}
} }
} }
+41 -12
View File
@@ -3,15 +3,17 @@
*/ */
module hulk.apic; module hulk.apic;
import hulk.klog;
import hulk.hurl; import hulk.hurl;
import hulk.acpi; import hulk.acpi;
import hulk.idt; import hulk.idt;
import hulk.rtc;
struct apic struct Apic
{ {
private enum uint PERIODIC_MODE = 0x2_0000u; private enum uint PERIODIC_MODE = 0x2_0000u;
private enum ulong IRQ_RTC = 8u;
static struct ApicRegister static struct ApicRegister
{ {
public uint value; public uint value;
@@ -52,31 +54,58 @@ struct apic
ApicRegister divide_configuration; ApicRegister divide_configuration;
} }
static struct IoApicRegisters
{
ApicRegister address;
ApicRegister data;
}
private static __gshared ApicRegisters * apic_registers; private static __gshared ApicRegisters * apic_registers;
private static __gshared IoApicRegisters * io_apic_registers;
public static void initialize() public static void initialize()
{ {
apic_registers = cast(ApicRegisters *)acpi.apic_address; apic_registers = cast(ApicRegisters *)Acpi.apic_address;
hurl.map(cast(ulong)apic_registers, cast(ulong)apic_registers, io_apic_registers = cast(IoApicRegisters *)0xFEC0_0000u;
Hurl.map(cast(ulong)apic_registers, cast(ulong)apic_registers,
PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE);
Hurl.map(cast(ulong)io_apic_registers, cast(ulong)io_apic_registers,
PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE); PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE);
klog.writefln("LAPIC ID: 0x%08x", apic_registers.lapic_id.value);
klog.writefln("LAPIC version: 0x%08x", apic_registers.lapic_version.value);
/* Enable local APIC to receive interrupts and set spurious interrupt /* Enable local APIC to receive interrupts and set spurious interrupt
* vector to 0xFF. */ * vector to 0xFF. */
apic_registers.spurious_interrupt_vector.value = 0x1FFu; apic_registers.spurious_interrupt_vector.value = 0x100u | Idt.INT_APIC_SPURIOUS;
apic_registers.lvt_timer.value = idt.EXCEPTION_LAPIC_TIMER | PERIODIC_MODE; apic_registers.lvt_timer.value = Idt.INT_APIC_TIMER;
apic_registers.lvt_lint[0].value = idt.EXCEPTION_LAPIC_LINT0; apic_registers.lvt_lint[0].value = Idt.INT_APIC_LINT0;
apic_registers.lvt_lint[1].value = idt.EXCEPTION_LAPIC_LINT1; apic_registers.lvt_lint[1].value = Idt.INT_APIC_LINT1;
apic_registers.divide_configuration.value = 3u; /* Enable RTC interrupt. */
configure_io_apic_irq(IRQ_RTC, Idt.INT_APIC_BASE + IRQ_RTC);
} }
private static void eoi() private static void configure_io_apic_irq(size_t io_apic_irq, size_t interrupt_id)
{
ulong entry = interrupt_id;
io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u);
io_apic_registers.data.value = entry & 0xFFFF_FFFFu;
io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u + 1u);
io_apic_registers.data.value = entry >> 32u;
}
public static void eoi()
{ {
apic_registers.eoi.value = 0u; apic_registers.eoi.value = 0u;
} }
public static void isr(ulong vector) public static void isr(ulong vector)
{ {
switch (vector)
{
case Idt.INT_APIC_BASE + IRQ_RTC:
Rtc.isr();
break;
default:
break;
}
eoi(); eoi();
} }
} }
+99 -21
View File
@@ -5,19 +5,33 @@ module hulk.console;
import hulk.fb; import hulk.fb;
import hulk.kfont; import hulk.kfont;
import hulk.ver;
import hulk.rtc;
import hulk.writef;
struct console /**
* Represent the HULK console.
*
* Coordinate (0,0) is the top-left console position.
*/
struct Console
{ {
/** Console width in text columns. */ /** Border color. */
private enum BORDER_COLOR = 0xFF8000u;
/** Console page width in text columns. */
private static __gshared size_t m_width; private static __gshared size_t m_width;
/** Console height in text rows. */ /** Console page height in text rows. */
private static __gshared size_t m_height; private static __gshared size_t m_height;
/** Current console cursor X position. */ /** Current console page. */
private static __gshared size_t m_page;
/** Current page cursor X position. */
private static __gshared size_t m_x; private static __gshared size_t m_x;
/** Current console cursor Y position. */ /** Current page cursor Y position. */
private static __gshared size_t m_y; private static __gshared size_t m_y;
/** /**
@@ -25,8 +39,9 @@ struct console
*/ */
public static void initialize() public static void initialize()
{ {
m_width = fb.width / kfont.advance; size_t fb_console_height = Fb.height - Kfont.line_height - 1;
m_height = fb.height / kfont.line_height; m_width = (Fb.width - 1) / 2 / Kfont.advance;
m_height = fb_console_height / Kfont.line_height;
} }
/** /**
@@ -34,9 +49,11 @@ struct console
*/ */
public static void clear() public static void clear()
{ {
fb.clear(); Fb.clear();
m_page = 0u;
m_x = 0u; m_x = 0u;
m_y = 0u; m_y = 0u;
draw();
} }
/** /**
@@ -53,7 +70,7 @@ struct console
} }
else else
{ {
render_char(m_x, m_y, ch); render_char(fb_x(m_page, m_x), fb_y(m_y), ch);
m_x++; m_x++;
if (m_x == m_width) if (m_x == m_width)
{ {
@@ -63,12 +80,40 @@ struct console
} }
if (m_y == m_height) if (m_y == m_height)
{ {
m_y--; if (m_page == 0u)
fb.copy_rows_up(fb_y(m_height - 1u), {
(m_height - 1u) * kfont.line_height, m_page = 1u;
kfont.line_height); m_x = 0u;
fb.rect(0u, fb_y(m_height - 1u), fb_x(m_width), kfont.line_height, 0u); m_y = 0u;
} }
else
{
m_y--;
shift_rows();
}
}
}
/**
* Shift console rows.
*/
private static void shift_rows()
{
/* Shift up page 0 */
Fb.copy_rect(fb_x(0u, 0u), fb_y(1u),
m_width * Kfont.advance, (m_height - 1u) * Kfont.line_height,
fb_x(0u, 0u), fb_y(0u));
/* Copy page 1 first row to page 0 last row. */
Fb.copy_rect(fb_x(1u, 0u), fb_y(0u),
m_width * Kfont.advance, Kfont.line_height,
fb_x(0u, 0u), fb_y(m_height - 1u));
/* Shift up page 1 */
Fb.copy_rect(fb_x(1u, 0u), fb_y(1u),
m_width * Kfont.advance, (m_height - 1u) * Kfont.line_height,
fb_x(1u, 0u), fb_y(0u));
/* Erase page 1 last row. */
Fb.rect(fb_x(1u, 0u), fb_y(m_height - 1u),
m_width * Kfont.advance, Kfont.line_height, 0u);
} }
/** /**
@@ -80,23 +125,56 @@ struct console
*/ */
private static void render_char(size_t x, size_t y, char ch) private static void render_char(size_t x, size_t y, char ch)
{ {
const(CharInfo) * ci = &kfont.chars[ch]; const(CharInfo) * ci = &Kfont.chars[ch];
fb.blit_alpha_bitmap(fb_x(x) + ci.left, fb_y(y) + ci.top - ci.height, ci.bitmap, ci.width, ci.height); Fb.blit_alpha_bitmap(x + ci.left, y + ci.top, ci.bitmap, ci.width, ci.height);
} }
/** /**
* Get the framebuffer X coordinate corresponding to the console X position. * Get the framebuffer X coordinate corresponding to the console page X position.
*/ */
private static size_t fb_x(size_t x) private static size_t fb_x(size_t page, size_t x)
{ {
return x * kfont.advance; return x * Kfont.advance + page * (m_width * Kfont.advance + 1);
} }
/** /**
* Get the framebuffer Y coordinate corresponding to the console Y position. * Get the framebuffer Y coordinate corresponding to the console page Y position.
*/ */
private static size_t fb_y(size_t y) private static size_t fb_y(size_t y)
{ {
return fb.height - ((y + 1u) * kfont.line_height); return (y + 1) * Kfont.line_height + 1;
}
/**
* Draw console.
*/
private static draw()
{
static __gshared string header_text = "Welcome to HOS v" ~ VERSION ~ "!";
Fb.rect(0u, Kfont.line_height, Fb.width, 1, BORDER_COLOR);
Fb.rect(m_width * Kfont.advance, Kfont.line_height, 1, Fb.height - Kfont.line_height, BORDER_COLOR);
size_t x = 0;
foreach (c; header_text)
{
render_char(x, 0, c);
x += Kfont.advance;
}
update_header();
}
/**
* Update console header.
*/
public static update_header()
{
__gshared uint x;
x = Fb.width - 8 * Kfont.advance;
Fb.rect(x - 1, 0, 1, Kfont.line_height, BORDER_COLOR);
Fb.rect(x, 0, Fb.width - x, Kfont.line_height, 0);
Rtc.time rtc_time = Rtc.read_rtc_time();
writef(function(ubyte ch) {
render_char(x, 0, ch);
x += Kfont.advance;
}, "%02u:%02u:%02u", rtc_time.hour, rtc_time.minute, rtc_time.second);
} }
} }
+3 -3
View File
@@ -221,9 +221,9 @@ ushort in16(ushort ioaddr)
return __asm!ushort("inw %dx, %ax", "={ax},{dx}", ioaddr); return __asm!ushort("inw %dx, %ax", "={ax},{dx}", ioaddr);
} }
ulong in32(ushort ioaddr) uint in32(ushort ioaddr)
{ {
return __asm!ulong("inl %dx, %eax", "={eax},{dx}", ioaddr); return __asm!uint("inl %dx, %eax", "={eax},{dx}", ioaddr);
} }
void out8(ushort ioaddr, ubyte v) void out8(ushort ioaddr, ubyte v)
@@ -236,7 +236,7 @@ void out16(ushort ioaddr, ushort v)
__asm("outw %ax, %dx", "{dx},{ax}", ioaddr, v); __asm("outw %ax, %dx", "{dx},{ax}", ioaddr, v);
} }
void out32(ushort ioaddr, ulong v) void out32(ushort ioaddr, uint v)
{ {
__asm("outl %eax, %dx", "{dx},{eax}", ioaddr, v); __asm("outl %eax, %dx", "{dx},{eax}", ioaddr, v);
} }
+39 -89
View File
@@ -9,9 +9,11 @@ import hulk.kfont;
/** /**
* Represent a graphical frame buffer. * Represent a graphical frame buffer.
* *
* Coordinate (0,0) is the top-left corner of the framebuffer.
*
* TODO: Handle other pixel formats. Currently only BGRx is supported. * TODO: Handle other pixel formats. Currently only BGRx is supported.
*/ */
struct fb struct Fb
{ {
/** Device frame buffer base address. */ /** Device frame buffer base address. */
private __gshared static uint * m_device_buffer; private __gshared static uint * m_device_buffer;
@@ -80,8 +82,8 @@ struct fb
/** /**
* Draw a solid rectangle on the framebuffer. * Draw a solid rectangle on the framebuffer.
* *
* @param x X coordinate of left side of rectangle. * @param x X coordinate of left of rectangle.
* @param y Y coordinate of bottom side of rectangle. * @param y Y coordinate of top of rectangle.
* @param width Width of rectangle. * @param width Width of rectangle.
* @param height Height of rectangle. * @param height Height of rectangle.
* @param color Color of rectangle. * @param color Color of rectangle.
@@ -100,7 +102,7 @@ struct fb
/** /**
* Draw a horizontal line. * Draw a horizontal line.
* *
* @param x X coordinate of left side of line. * @param x X coordinate of left of line.
* @param y Y coordinate of line. * @param y Y coordinate of line.
* @param width Width of line. * @param width Width of line.
* @param color Color of line. * @param color Color of line.
@@ -116,7 +118,7 @@ struct fb
* Draw a vertical line. * Draw a vertical line.
* *
* @param x X coordinate of line. * @param x X coordinate of line.
* @param y Y coordinate of bottom of line. * @param y Y coordinate of top of line.
* @param height Height of line. * @param height Height of line.
* @param color Color of line. * @param color Color of line.
*/ */
@@ -127,81 +129,17 @@ struct fb
{ {
m_buffer1[buffer_index] = color; m_buffer1[buffer_index] = color;
m_device_buffer[buffer_index] = color; m_device_buffer[buffer_index] = color;
buffer_index -= m_stride; buffer_index += m_stride;
} }
} }
/**
* Draw a character.
*
* @param x X coordinate of left side of character.
* @param y Y coordinate of bottom side of character.
* @param ch Character to draw.
* @param color Color of character.
*/
static void character(int x, int y, char ch, uint color)
{
const(CharInfo) * ci = &kfont.chars[ch];
blend_alpha_bitmap(x + ci.left, y + kfont.baseline_offset + ci.top - ci.height, ci.bitmap, ci.width, ci.height, color);
}
/**
* Blend an 8-bit alpha bitmap to the framebuffer.
*
* @param x X coordinate of left side of target location.
* @param y Y coordinate of bottom side of target location.
* @param alpha_bitmap 8-bit alpha-channel bitmap.
* @param width Bitmap width.
* @param height Bitmap height.
* @param color Color to blend with alpha value.
*/
static void blend_alpha_bitmap(size_t x, size_t y, const(ubyte) * alpha_bitmap,
size_t width, size_t height, uint color)
{
y += height - 1u;
size_t bitmap_index;
for (size_t iy = 0u; iy < height; iy++)
{
size_t row_buffer_index = buffer_index(x, y);
for (size_t ix = 0u; ix < width; ix++)
{
size_t buffer_index = row_buffer_index + ix;
ubyte alpha = alpha_bitmap[bitmap_index];
uint current_color = m_buffer1[buffer_index];
uint in_color_scaled = scale_color(color, alpha);
uint old_color_scaled = scale_color(current_color, 255u - alpha);
uint new_color = old_color_scaled + in_color_scaled;
m_buffer1[buffer_index] = new_color;
bitmap_index++;
}
memcpy32(&m_device_buffer[row_buffer_index], &m_buffer1[row_buffer_index], width);
y--;
}
}
/**
* Blend an 8-bit alpha bitmap to the framebuffer.
*
* @param x X coordinate of left side of target location.
* @param y Y coordinate of bottom side of target location.
* @param alpha_bitmap 8-bit alpha-channel bitmap.
* @param width Bitmap width.
* @param height Bitmap height.
* @param color Color to blend with alpha value.
*/
static void blend_alpha_bitmap(size_t x, size_t y, const(ubyte)[] alpha_bitmap,
size_t width, size_t height, uint color)
{
blend_alpha_bitmap(x, y, alpha_bitmap.ptr, width, height, color);
}
/** /**
* Blit an 8-bit alpha bitmap to the framebuffer. * Blit an 8-bit alpha bitmap to the framebuffer.
* The foreground will be white (based on the alpha value), and the * The foreground will be white (based on the alpha value), and the
* background black. * background black.
* *
* @param x X coordinate of left side of target location. * @param x X coordinate of left of target location.
* @param y Y coordinate of bottom side of target location. * @param y Y coordinate of top of target location.
* @param alpha_bitmap 8-bit alpha-channel bitmap. * @param alpha_bitmap 8-bit alpha-channel bitmap.
* @param width Bitmap width. * @param width Bitmap width.
* @param height Bitmap height. * @param height Bitmap height.
@@ -209,7 +147,6 @@ struct fb
static void blit_alpha_bitmap(size_t x, size_t y, const(ubyte) * alpha_bitmap, static void blit_alpha_bitmap(size_t x, size_t y, const(ubyte) * alpha_bitmap,
size_t width, size_t height) size_t width, size_t height)
{ {
y += height - 1u;
size_t bitmap_index; size_t bitmap_index;
for (size_t iy = 0u; iy < height; iy++) for (size_t iy = 0u; iy < height; iy++)
{ {
@@ -222,7 +159,7 @@ struct fb
bitmap_index++; bitmap_index++;
} }
memcpy32(&m_device_buffer[row_buffer_index], &m_buffer1[row_buffer_index], width); memcpy32(&m_device_buffer[row_buffer_index], &m_buffer1[row_buffer_index], width);
y--; y++;
} }
} }
@@ -231,8 +168,8 @@ struct fb
* The foreground will be white (based on the alpha value), and the * The foreground will be white (based on the alpha value), and the
* background black. * background black.
* *
* @param x X coordinate of left side of target location. * @param x X coordinate of left of target location.
* @param y Y coordinate of bottom side of target location. * @param y Y coordinate of top of target location.
* @param alpha_bitmap 8-bit alpha-channel bitmap. * @param alpha_bitmap 8-bit alpha-channel bitmap.
* @param width Bitmap width. * @param width Bitmap width.
* @param height Bitmap height. * @param height Bitmap height.
@@ -246,14 +183,14 @@ struct fb
/** /**
* Copy framebuffer rows up in the framebuffer. * Copy framebuffer rows up in the framebuffer.
* *
* @param y Y coordinate of bottom side of region to copy up. * @param y Y coordinate of top of region to copy up.
* @param height Height of region to copy up. * @param height Height of region to copy up.
* @param offset Number of rows to copy region up by. * @param offset Number of rows to copy region up by.
*/ */
static void copy_rows_up(size_t y, size_t height, size_t offset) static void copy_rows_up(size_t y, size_t height, size_t offset)
{ {
size_t dest_buffer_index = buffer_index(0u, y + height + offset - 1u); size_t dest_buffer_index = buffer_index(0u, y - offset);
size_t src_buffer_index = buffer_index(0u, y + height - 1u); size_t src_buffer_index = buffer_index(0u, y);
memcpy32(&m_buffer1[dest_buffer_index], memcpy32(&m_buffer1[dest_buffer_index],
&m_buffer1[src_buffer_index], &m_buffer1[src_buffer_index],
(m_stride * height)); (m_stride * height));
@@ -263,18 +200,31 @@ struct fb
} }
/** /**
* Scale a color value by an alpha amount. * Copy a rectangle in the framebuffer.
* *
* @param color Color value. * @param sx X coordinate of left of source rectangle.
* @param alpha Alpha amount. * @param sy Y coordinate of top of source rectangle.
* * @param width Width of rectangle to copy.
* @return Scaled color value. * @param height Height of rectangle to copy.
* @param dx X coordinate of left of destination rectangle.
* @param dy Y coordinate of top of destination rectangle.
*/ */
private static uint scale_color(uint color, ubyte alpha) static void copy_rect(size_t sx, size_t sy, size_t width, size_t height,
size_t dx, size_t dy)
{ {
return ((((color & 0xFFu) * alpha) >> 8u) & 0xFFu) | size_t dest_buffer_index = buffer_index(dx, dy);
((((color & 0xFF00u) * alpha) >> 8u) & 0xFF00u) | size_t src_buffer_index = buffer_index(sx, sy);
((((color & 0xFF0000u) * alpha) >> 8u) & 0xFF0000u); for (size_t y = 0u; y < height; y++)
{
memcpy32(&m_buffer1[dest_buffer_index],
&m_buffer1[src_buffer_index],
width);
memcpy32(&m_device_buffer[dest_buffer_index],
&m_buffer1[dest_buffer_index],
width);
dest_buffer_index += m_stride;
src_buffer_index += m_stride;
}
} }
/** /**
@@ -287,6 +237,6 @@ struct fb
*/ */
private static size_t buffer_index(size_t x, size_t y) private static size_t buffer_index(size_t x, size_t y)
{ {
return (m_height - y - 1u) * m_stride + x; return y * m_stride + x;
} }
} }
+1 -1
View File
@@ -5,7 +5,7 @@ module hulk.gdt;
import ldc.llvmasm; import ldc.llvmasm;
struct gdt struct Gdt
{ {
struct gdtr_t struct gdtr_t
{ {
+1 -1
View File
@@ -7,7 +7,7 @@
*/ */
module hulk.hippo; module hulk.hippo;
struct hippo struct Hippo
{ {
/** /**
* Linked list node entry for a physical page. * Linked list node entry for a physical page.
+24 -15
View File
@@ -20,6 +20,8 @@ import ldc.llvmasm;
import hulk.pic; import hulk.pic;
import hulk.acpi; import hulk.acpi;
import hulk.apic; import hulk.apic;
import hulk.rtc;
import hulk.serial;
extern extern(C) __gshared ubyte _hulk_bss_size; extern extern(C) __gshared ubyte _hulk_bss_size;
@@ -28,9 +30,9 @@ private __gshared HulkHeader hulk_header = {
&_hulk_bss_size, /* hulk_bss_size */ &_hulk_bss_size, /* hulk_bss_size */
&hulk_start, /* entry */ &hulk_start, /* entry */
16u * 1024u, /* stack_size */ 16u * 1024u, /* stack_size */
HULK_VIRTUAL_BASE_ADDRESS, /* virt_base */ Hurl.HULK_BASE, /* virt_base */
HULK_VIRTUAL_STACK_TOP_ADDRESS, /* virt_stack_top */ Hurl.HULK_STACK_TOP, /* virt_stack_top */
HULK_VIRTUAL_FRAMEBUFFER_ADDRESS, /* virt_fb_buffer */ Hurl.HULK_FRAMEBUFFER, /* virt_fb_buffer */
}; };
private void initialize_cpu() private void initialize_cpu()
@@ -55,24 +57,31 @@ void hulk_start()
{ {
cli(); cli();
initialize_cpu(); initialize_cpu();
gdt.initialize(); Serial.initialize();
idt.initialize(); Gdt.initialize();
fb.initialize(cast(uint *)HULK_VIRTUAL_FRAMEBUFFER_ADDRESS, Idt.initialize();
Fb.initialize(cast(uint *)Hurl.HULK_FRAMEBUFFER,
cast(uint *)hulk_header.bootinfo.fb_buffer1_phys, cast(uint *)hulk_header.bootinfo.fb_buffer1_phys,
hulk_header.bootinfo.fb.width, hulk_header.bootinfo.fb.width,
hulk_header.bootinfo.fb.height, hulk_header.bootinfo.fb.height,
hulk_header.bootinfo.fb.stride); hulk_header.bootinfo.fb.stride);
console.initialize(); Console.initialize();
console.clear(); Rtc.initialize();
klog.initialize(); Console.clear();
Klog.initialize();
klog.writefln("Welcome to HULK, the HOS UltraLight Kernel!"); Klog.writefln("Welcome to HULK, the HOS UltraLight Kernel!");
hurl.initialize(&hulk_header); Hurl.initialize(&hulk_header);
pci.initialize(); Pic.initialize();
pic.initialize(); Acpi.initialize(hulk_header.bootinfo.acpi_xsdt_phys);
acpi.initialize(hulk_header.bootinfo.acpi_xsdt_phys); Apic.initialize();
apic.initialize(); /* Read the current system time. */
Rtc.time t = Rtc.read_rtc_time();
Klog.writefln("System time is 20%02u-%02u-%02u %02u:%02u:%02u",
t.year, t.month, t.day, t.hour, t.minute, t.second);
Pci.initialize();
sti(); sti();
for (;;) for (;;)
+5 -1
View File
@@ -6,7 +6,7 @@ SECTIONS
.hulk_header : .hulk_header :
{ {
*(.hulk_header) KEEP(*(.hulk_header))
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -16,6 +16,7 @@ SECTIONS
.text : .text :
{ {
*(.text) *(.text)
*(.text.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -25,6 +26,7 @@ SECTIONS
.rodata : .rodata :
{ {
*(.rodata) *(.rodata)
*(.rodata.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -35,6 +37,7 @@ SECTIONS
.data : .data :
{ {
*(.data) *(.data)
*(.data.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -46,6 +49,7 @@ SECTIONS
{ {
*(COMMON) *(COMMON)
*(.bss) *(.bss)
*(.bss.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
+64
View File
@@ -0,0 +1,64 @@
/**
* A1 memory allocator.
*/
module hulk.hurl.a1;
import hulk.hurl;
import hulk.hippo;
import hulk.util;
import hulk.pagetable;
/**
* The A1 memory allocator is a one-shot memory allocator for kernel memory
* that is allocated but never freed.
*/
struct A1
{
/**
* Number of bytes allocated in the A1 region so far.
*/
private static __gshared size_t allocated;
/**
* Allocate memory.
*
* @param size
* Size of memory to allocate.
*
* @return Address of allocated memory. This address will always be aligned
* to a multiple of 16 bytes.
*/
public static void * allocate(size_t size)
{
/* Round size up to a multiple of 16. */
size = round_up_power_2(size, 16u);
ulong address = Hurl.A1_BASE + allocated;
ulong current_limit = round_up_power_2(address, PAGE_SIZE);
allocated += size;
ulong desired_limit = round_up_power_2(Hurl.A1_BASE + allocated, PAGE_SIZE);
while (desired_limit > current_limit)
{
void * page = Hippo.allocate_page();
Hurl.map(current_limit, page, PT_WRITABLE);
current_limit += PAGE_SIZE;
}
return cast(void *)address;
}
/**
* Allocate memory to store an instance of a given type.
*
* @return Address of allocated memory. This address will always be aligned
* to a multiple of 16 bytes.
*/
public static T * allocate(T)()
{
return cast(T *)allocate(T.sizeof);
}
}
+45 -26
View File
@@ -3,7 +3,7 @@
* *
* HURL provides virtual memory management for HULK. * HURL provides virtual memory management for HULK.
*/ */
module hulk.hurl; module hulk.hurl.hurl;
public import hulk.pagetable; public import hulk.pagetable;
import hulk.cpu; import hulk.cpu;
@@ -13,18 +13,22 @@ import hulk.klog;
import hulk.bootinfo; import hulk.bootinfo;
import hulk.header; import hulk.header;
import hulk.linker_addresses; import hulk.linker_addresses;
import hulk.util;
/** HULK virtual base address. */ struct Hurl
enum ulong HULK_VIRTUAL_BASE_ADDRESS = 0xFFFF_8000_0000_0000u;
/** HULK virtual stack top address. */
enum ulong HULK_VIRTUAL_STACK_TOP_ADDRESS = 0xFFFF_A000_0000_0000u;
/** HULK virtual framebuffer address. */
enum ulong HULK_VIRTUAL_FRAMEBUFFER_ADDRESS = 0xFFFF_A000_0000_0000u;
struct hurl
{ {
/** HULK base address. */
enum ulong HULK_BASE = 0xFFFF_8000_0000_0000u;
/** HULK stack top address. */
enum ulong HULK_STACK_TOP = 0xFFFF_8001_0000_0000u;
/** HULK framebuffer address. */
enum ulong HULK_FRAMEBUFFER = 0xFFFF_8001_0000_0000u;
/** A1 allocator range base address. */
enum ulong A1_BASE = 0xFFFF_8002_0000_0000u;
/** /**
* Pointer to the base page table. * Pointer to the base page table.
*/ */
@@ -45,7 +49,7 @@ struct hurl
physical_address_limit, physical_address_limit,
PT_WRITABLE | PT_NO_EXECUTE); PT_WRITABLE | PT_NO_EXECUTE);
ulong phys_address = header.bootinfo.hulk_phys; ulong phys_address = header.bootinfo.hulk_phys;
ulong virt_address = HULK_VIRTUAL_BASE_ADDRESS; ulong virt_address = HULK_BASE;
/* Map HULK header region. */ /* Map HULK header region. */
map_range(virt_address, map_range(virt_address,
phys_address, phys_address,
@@ -79,12 +83,12 @@ struct hurl
LinkerAddresses.hulk_bss_size, LinkerAddresses.hulk_bss_size,
PT_WRITABLE | PT_NO_EXECUTE); PT_WRITABLE | PT_NO_EXECUTE);
/* Map HULK stack. */ /* Map HULK stack. */
map_range(HULK_VIRTUAL_STACK_TOP_ADDRESS - header.stack_size, map_range(HULK_STACK_TOP - header.stack_size,
header.bootinfo.stack_phys, header.bootinfo.stack_phys,
header.stack_size, header.stack_size,
PT_WRITABLE | PT_NO_EXECUTE); PT_WRITABLE | PT_NO_EXECUTE);
/* Map device framebuffer. */ /* Map device framebuffer. */
map_range(HULK_VIRTUAL_FRAMEBUFFER_ADDRESS, map_range(HULK_FRAMEBUFFER,
cast(ulong)header.bootinfo.fb.buffer, cast(ulong)header.bootinfo.fb.buffer,
header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u, header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u,
PT_WRITABLE | PT_NO_EXECUTE); PT_WRITABLE | PT_NO_EXECUTE);
@@ -121,7 +125,7 @@ struct hurl
reclaimed_pages += reclaim_bootloader_page_table_pages(pte.follow(), level + 1u); reclaimed_pages += reclaim_bootloader_page_table_pages(pte.follow(), level + 1u);
} }
} }
hippo.free_page(pt); Hippo.free_page(pt);
reclaimed_pages++; reclaimed_pages++;
return reclaimed_pages; return reclaimed_pages;
} }
@@ -152,7 +156,7 @@ struct hurl
*/ */
size_t usable_memory; size_t usable_memory;
size_t physical_address_limit; size_t physical_address_limit;
const(size_t) fb_size = (header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u + PAGE_SIZE - 1u) & ~(PAGE_SIZE - 1u); const(size_t) fb_size = round_up_power_2(header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u, PAGE_SIZE);
ulong[2][2] reserved = [ ulong[2][2] reserved = [
[header.bootinfo.hulk_phys, LinkerAddresses.hulk_binary_size], [header.bootinfo.hulk_phys, LinkerAddresses.hulk_binary_size],
[cast(ulong)header.bootinfo.fb.buffer, fb_size], [cast(ulong)header.bootinfo.fb.buffer, fb_size],
@@ -186,7 +190,7 @@ struct hurl
} }
if (!is_reserved) if (!is_reserved)
{ {
hippo.free_page(phys); Hippo.free_page(phys);
} }
phys += PAGE_SIZE; phys += PAGE_SIZE;
} }
@@ -213,11 +217,11 @@ struct hurl
usable_memory += LinkerAddresses.hulk_bss_size; usable_memory += LinkerAddresses.hulk_bss_size;
usable_memory += header.stack_size; usable_memory += header.stack_size;
usable_memory += fb_size; usable_memory += fb_size;
klog.writefln("Usable memory: %uKB", usable_memory >> 10u); Klog.writefln("Usable memory: %uKB", usable_memory >> 10u);
klog.writefln("Kernel size: %uKB", (LinkerAddresses.hulk_binary_size + LinkerAddresses.hulk_bss_size + header.stack_size) >> 10u); Klog.writefln("Kernel size: %uKB", (LinkerAddresses.hulk_binary_size + LinkerAddresses.hulk_bss_size + header.stack_size) >> 10u);
} }
public static void map(T, U)(T virtual, U physical, ulong flags) public static void map(ulong virtual, ulong physical, ulong flags)
{ {
PageTable * pt = m_pt_base; PageTable * pt = m_pt_base;
for (size_t level = 0; level < 4u; level++) for (size_t level = 0; level < 4u; level++)
@@ -244,7 +248,12 @@ struct hurl
} }
} }
public static void map_range(size_t virtual, size_t physical, size_t length, ulong flags) public static void map(T, U)(T virtual, U physical, ulong flags)
{
map(cast(ulong)virtual, cast(ulong)physical, flags);
}
public static void map_range(ulong virtual, ulong physical, size_t length, ulong flags)
{ {
size_t end = virtual + length; size_t end = virtual + length;
while (virtual < end) while (virtual < end)
@@ -255,23 +264,33 @@ struct hurl
} }
} }
public static void identity_map_range(size_t address, size_t length, ulong flags) public static void map_range(T, U)(T virtual, U physical, size_t length, ulong flags)
{
map_range(cast(ulong)virtual, cast(ulong)physical, length, flags);
}
public static void identity_map_range(ulong address, size_t length, ulong flags)
{ {
size_t end = address + length; size_t end = address + length;
for (size_t page = address & ~0xFFFu; page < end; page += PAGE_SIZE) for (size_t page = address & 0xFFFF_FFFF_FFFF_F000u; page < end; page += PAGE_SIZE)
{ {
map(page, page, flags); map(page, page, flags);
} }
} }
public static void identity_map_range(T)(T address, size_t length, ulong flags)
{
identity_map_range(cast(ulong)address, length, flags);
}
public static void debug_lookup(void * address) public static void debug_lookup(void * address)
{ {
klog.writefln("Debugging page table lookup of 0x%x", address); Klog.writefln("Debugging page table lookup of 0x%x", address);
PageTable * pt = m_pt_base; PageTable * pt = m_pt_base;
for (size_t level = 0; level < 4u; level++) for (size_t level = 0; level < 4u; level++)
{ {
PageTableEntry pte = *pt.entry(address, level); PageTableEntry pte = *pt.entry(address, level);
klog.writefln("Level %u, index %u, entry = 0x%x", level, pt.index(address, level), pte); Klog.writefln("Level %u, index %u, entry = 0x%x", level, pt.index(address, level), pte);
if (pte.present) if (pte.present)
{ {
pt = pte.follow(); pt = pte.follow();
@@ -289,7 +308,7 @@ struct hurl
private static PageTable * allocate_pt() private static PageTable * allocate_pt()
{ {
PageTable * pt = cast(PageTable *)hippo.allocate_page(); PageTable * pt = cast(PageTable *)Hippo.allocate_page();
memset64(pt, 0u, PAGE_SIZE / 8u); memset64(pt, 0u, PAGE_SIZE / 8u);
return pt; return pt;
} }
+3
View File
@@ -0,0 +1,3 @@
module hulk.hurl;
public import hulk.hurl.hurl;
+26 -10
View File
@@ -10,11 +10,16 @@ import hulk.console;
import hulk.klog; import hulk.klog;
import hulk.apic; import hulk.apic;
struct idt struct Idt
{ {
public static enum ulong EXCEPTION_LAPIC_TIMER = 0x70u; /* The I/O APIC is configured to map IRQ 0 to interrupt 64 (0x40). */
public static enum ulong EXCEPTION_LAPIC_LINT0 = 0x71u; public static enum ulong INT_APIC_BASE = 0x40u; /* IRQ 0 */
public static enum ulong EXCEPTION_LAPIC_LINT1 = 0x72u; public static enum ulong INT_APIC_COUNT = 24u;
public static enum ulong INT_APIC_TIMER = 0x70u;
public static enum ulong INT_APIC_LINT0 = 0x71u;
public static enum ulong INT_APIC_LINT1 = 0x72u;
public static enum ulong INT_APIC_SPURIOUS = 0xFFu;
struct idtr_t struct idtr_t
{ {
@@ -91,7 +96,7 @@ struct idt
((offset & 0xFFFF_0000u) << 32u) | ((offset & 0xFFFF_0000u) << 32u) |
(dpl << 45u) | (dpl << 45u) |
((trap ? 1uL : 0uL) << 40u) | ((trap ? 1uL : 0uL) << 40u) |
(gdt.SELECTOR_KERNEL_CODE << 16u) | (Gdt.SELECTOR_KERNEL_CODE << 16u) |
(offset & 0xFFFFu); (offset & 0xFFFFu);
idt[index][1] = offset >> 32u; idt[index][1] = offset >> 32u;
} }
@@ -99,15 +104,25 @@ struct idt
public extern(C) void isr(ulong vector, ulong arg) public extern(C) void isr(ulong vector, ulong arg)
{ {
if ((idt.EXCEPTION_LAPIC_TIMER <= vector) && (vector <= idt.EXCEPTION_LAPIC_LINT1)) if ((vector >= Idt.INT_APIC_BASE) && (vector < (Idt.INT_APIC_BASE + Idt.INT_APIC_COUNT)))
{ {
apic.isr(vector); Apic.isr(vector);
} }
else else
{ {
console.clear(); switch (vector)
fb.clear(0xFF8000u); {
klog.writefln("ISR %u, 0x%x", vector, arg); case Idt.INT_APIC_TIMER:
case Idt.INT_APIC_LINT0:
case Idt.INT_APIC_LINT1:
case Idt.INT_APIC_SPURIOUS:
Apic.isr(vector);
break;
default:
Console.clear();
Fb.clear(0xFF8000u);
Klog.writefln("ISR %u, 0x%x", vector, arg);
__asm("cli", ""); __asm("cli", "");
for (;;) for (;;)
{ {
@@ -115,3 +130,4 @@ public extern(C) void isr(ulong vector, ulong arg)
} }
} }
} }
}
+19 -5
View File
@@ -6,8 +6,9 @@ module hulk.klog;
import core.stdc.stdarg; import core.stdc.stdarg;
import hulk.console; import hulk.console;
static import hulk.writef; static import hulk.writef;
import hulk.cpu;
struct klog struct Klog
{ {
/** /**
* Kernel buffer size log. * Kernel buffer size log.
@@ -39,9 +40,9 @@ struct klog
*/ */
public static void writef(string s, va_list args) public static void writef(string s, va_list args)
{ {
hulk.writef.writef(s, args, function void(ubyte ch) { hulk.writef.writefv(function void(ubyte ch) {
console.write(ch); Console.write(ch);
}); }, s, args);
} }
/** /**
@@ -58,7 +59,7 @@ struct klog
} }
/** /**
* Write a formatted string and newline to the console. * Write a formatted string and newline to the kernel log.
* *
* @param s Format string. * @param s Format string.
*/ */
@@ -70,4 +71,17 @@ struct klog
writef("\n", args); writef("\n", args);
va_end(args); va_end(args);
} }
/**
* Print a fatal kernel error and loop forever.
*/
public static void fatal_error(T...)(T args)
{
writef("FATAL ERROR: ");
writefln(args);
cli();
for (;;)
{
}
}
} }
+2 -2
View File
@@ -31,7 +31,7 @@ enum ulong MTRRPHYSBASE_TYPE_MASK = 0xFFu;
enum ulong MTRRPHYSMASK_PHYSMASK_MASK = 0x000F_FFFF_FFFF_F000u; enum ulong MTRRPHYSMASK_PHYSMASK_MASK = 0x000F_FFFF_FFFF_F000u;
enum ulong MTRRPHYSMASK_VALID = 0x800u; enum ulong MTRRPHYSMASK_VALID = 0x800u;
struct mtrr struct Mtrr
{ {
public static void initialize() public static void initialize()
{ {
@@ -41,7 +41,7 @@ struct mtrr
cpuid1(&ebx, &ecx, &edx); cpuid1(&ebx, &ecx, &edx);
if ((edx & CPUID_1_EDX_MTRR) == 0u) if ((edx & CPUID_1_EDX_MTRR) == 0u)
{ {
klog.writefln("CPU does not support MTRR"); Klog.writefln("CPU does not support MTRR");
return; return;
} }
const(ulong) mtrrcap = rdmsr(MSR_MTRRCAP); const(ulong) mtrrcap = rdmsr(MSR_MTRRCAP);
+8
View File
@@ -183,3 +183,11 @@ struct PageTable
} }
} }
static assert(PageTable.sizeof == 4096u); static assert(PageTable.sizeof == 4096u);
/**
* Get the base address of the page containing the given address.
*/
T page_address(T)(T address)
{
return cast(T)(cast(ulong)address & ~(PAGE_SIZE - 1u));
}
+343 -19
View File
@@ -2,8 +2,13 @@ module hulk.pci;
import hulk.cpu; import hulk.cpu;
import hulk.klog; import hulk.klog;
import hulk.hurl;
import hulk.hurl.a1;
import hulk.range;
import hulk.usb.xhci;
import hulk.acpi;
struct pci struct Pci
{ {
/* IO address for PCI configuration address. */ /* IO address for PCI configuration address. */
enum IO_ADDR_CONFIG_ADDRESS = 0xCF8u; enum IO_ADDR_CONFIG_ADDRESS = 0xCF8u;
@@ -11,38 +16,357 @@ struct pci
/* IO address for PCI configuration data. */ /* IO address for PCI configuration data. */
enum IO_ADDR_CONFIG_DATA = 0xCFCu; enum IO_ADDR_CONFIG_DATA = 0xCFCu;
private static ulong read_config_register(ulong bus_id, ulong device_id, ulong register_id) enum MAX_DEVICES_PER_BUS = 32;
enum MAX_FUNCTIONS_PER_DEVICE = 8;
struct Address
{ {
ulong addr = 0x8000_0000u | (bus_id << 16u) | (device_id << 11u) | (register_id << 2u); /** Bus number (0-255). */
out32(IO_ADDR_CONFIG_ADDRESS, addr); ubyte bus_nr;
/** Device number (0-31). */
ubyte device_nr;
/** Function number (0-7). */
ubyte function_nr;
}
static struct Device
{
Configuration * config;
Address address;
bool multifunction;
Range[6] memory_ranges;
void initialize(Address address, Configuration * config)
{
this.config = config;
this.address = address;
Klog.writefln("Found PCI device %04x:%04x (%02x:%02x:%02x) at %02u:%02u.%u",
config.vendor_id, config.device_id,
config.class_id, config.subclass_id, config.interface_id,
address.bus_nr, address.device_nr, address.function_nr);
multifunction = (address.function_nr == 0u) && ((config.header_type & 0x80u) != 0u);
ubyte header_type = config.header_type & 0x7Fu;
if (header_type == 0u)
{
map_memory_regions();
}
spawn_device_instance();
}
private void map_memory_regions()
{
size_t range_index;
uint[2] r;
uint[2] s;
for (uint bar_idx = 0u; bar_idx < config.header0.base_addresses.length; bar_idx++)
{
/* Read the BAR. */
r[0] = config.header0.base_addresses[bar_idx];
/* Skip zeroed BARs. */
if (r[0] == 0u)
{
continue;
}
/* Skip I/O BARs. */
if ((r[0] & 1u) != 0u)
{
continue;
}
bool is_64bit;
uint bar_type = (r[0] >> 1u) & 0x3u;
if (bar_type == 0u)
{
/* 32-bit region. */
r[1] = 0u;
}
else if ((bar_type == 2u) && (bar_idx < 5u))
{
/* 64-bit region. */
is_64bit = true;
r[1] = config.header0.base_addresses[bar_idx + 1];
}
else
{
continue;
}
/* Determine the region length. */
config.header0.base_addresses[bar_idx] = 0xFFFF_FFFFu;
s[0] = config.header0.base_addresses[bar_idx];
config.header0.base_addresses[bar_idx] = r[0];
if (is_64bit)
{
config.header0.base_addresses[bar_idx + 1] = 0xFFFF_FFFFu;
s[1] = config.header0.base_addresses[bar_idx + 1];
config.header0.base_addresses[bar_idx + 1] = r[1];
}
else
{
s[1] = 0xFFFF_FFFFu;
}
ulong mm_region_address = (cast(ulong)r[0] & 0xFFFF_FFF0u) | (cast(ulong)r[1] << 32u);
ulong length = ~((cast(ulong)s[0] & 0xFFFF_FFF0u) | (cast(ulong)s[1] << 32u)) + 1u;
ulong flags = (r[0] & 0x8) != 0u ? PT_WRITE_THROUGH : 0u;
Hurl.identity_map_range(mm_region_address, length, flags);
memory_ranges[range_index].address = mm_region_address;
memory_ranges[range_index].length = length;
range_index++;
if (is_64bit)
{
bar_idx++;
}
}
}
private void spawn_device_instance()
{
switch (config.class_id)
{
case SerialBusController.ID:
switch (config.subclass_id)
{
case SerialBusController.USBController.ID:
switch (config.interface_id)
{
case SerialBusController.USBController.XHCIController.ID:
XHCI.build(&this);
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
}
}
struct SerialBusController
{
enum ID = 0x0Cu;
struct USBController
{
enum ID = 0x03u;
struct XHCIController
{
enum ID = 0x30u;
}
}
}
private static uint read_config_register(Address address, uint register_id)
{
uint cfg_addr = 0x8000_0000u |
(address.bus_nr << 16u) |
(address.device_nr << 11u) |
(address.function_nr << 8u) |
(register_id << 2u);
out32(IO_ADDR_CONFIG_ADDRESS, cfg_addr);
return in32(IO_ADDR_CONFIG_DATA); return in32(IO_ADDR_CONFIG_DATA);
} }
private static void scan(ulong bus_id, ulong device_id) private static void write_config_register(Address address, uint register_id, uint value)
{ {
ulong reg0 = read_config_register(bus_id, device_id, 0u); uint cfg_addr = 0x8000_0000u |
if (reg0 != 0xFFFFFFFFu) (address.bus_nr << 16u) |
(address.device_nr << 11u) |
(address.function_nr << 8u) |
(register_id << 2u);
out32(IO_ADDR_CONFIG_ADDRESS, cfg_addr);
out32(IO_ADDR_CONFIG_DATA, value);
}
static struct Configuration
{ {
ulong reg2 = read_config_register(bus_id, device_id, 2u); static struct HeaderType0
klog.writefln("Found PCI device %04x:%04x (%02x:%02x:%02x) at %02u:%02u", {
(reg0 & 0xFFFFu), (reg0 >> 16u), ushort vendor_id;
(reg2 >> 24u) & 0xFFu, ushort device_id;
(reg2 >> 16u) & 0xFFu, ushort command;
(reg2 >> 8u) & 0xFFu, ushort status;
bus_id, device_id); ubyte revision_id;
ubyte interface_id;
ubyte subclass_id;
ubyte class_id;
ubyte cache_line_size;
ubyte latency_timer;
ubyte header_type;
ubyte bist;
uint[6] base_addresses;
uint cardbus_cis_pointer;
ushort subsystem_vendor_id;
ushort subsystem_id;
uint expansion_rom_base_address;
ubyte capabilities_pointer;
ubyte[7] _reserved;
ubyte interrupt_line;
ubyte interrupt_pin;
ubyte min_grant;
ubyte max_latency;
}
/**
* PCI configuration format for header type 1 (PCI-to-PCI bridge).
*/
static struct HeaderType1
{
ushort vendor_id;
ushort device_id;
ushort command;
ushort status;
ubyte revision_id;
ubyte interface_id;
ubyte subclass_id;
ubyte class_id;
ubyte cache_line_size;
ubyte latency_timer;
ubyte header_type;
ubyte bist;
uint[2] base_addresses;
ubyte primary_bus_nr;
ubyte secondary_bus_nr;
ubyte subordinate_bus_nr;
ubyte secondary_latency_timer;
ubyte io_base;
ubyte io_limit;
ushort secondary_status;
ushort memory_base;
ushort memory_limit;
ushort prefetchable_memory_base;
ushort prefetchable_memory_limit;
uint prefetchable_base_high;
uint prefetchable_limit_high;
ushort io_base_high;
ushort io_limit_high;
ubyte capability_pointer;
ubyte[3] _reserved;
uint expansion_rom_base_address;
ubyte interrupt_line;
ubyte interrupt_pin;
ushort bridge_control;
}
/**
* PCI configuration format for header type 2 (PCI-to-CardBus bridge).
*/
static struct HeaderType2
{
ushort vendor_id;
ushort device_id;
ushort command;
ushort status;
ubyte revision_id;
ubyte interface_id;
ubyte subclass_id;
ubyte class_id;
ubyte cache_line_size;
ubyte latency_timer;
ubyte header_type;
ubyte bist;
uint cardbus_base_address;
ubyte capability_list_offset;
ubyte _reserved;
ushort secondary_status;
ubyte pci_bus_nr;
ubyte cardbus_bus_nr;
ubyte subordinate_bus_nr;
ubyte cardbus_latency_timer;
uint base_address_0;
uint memory_limit_0;
uint base_address_1;
uint memory_limit_1;
uint io_base_address_0;
uint io_limit_0;
uint io_base_address_1;
uint io_limit_1;
ubyte interrupt_line;
ubyte interrupt_pin;
ushort bridge_control;
ushort subsystem_device_id;
ushort subsystem_vendor_id;
uint legacy_base_address;
}
union
{
struct
{
ushort vendor_id;
ushort device_id;
ushort command;
ushort status;
ubyte revision_id;
ubyte interface_id;
ubyte subclass_id;
ubyte class_id;
ubyte cache_line_size;
ubyte latency_timer;
ubyte header_type;
ubyte bist;
}
HeaderType0 header0;
HeaderType1 header1;
HeaderType2 header2;
}
}
private static void scan(ubyte bus_nr, ubyte device_nr, ubyte function_nr, ulong config_address)
{
Configuration * config = cast(Configuration *)config_address;
if (config.vendor_id != 0xFFFFu)
{
Device * device = A1.allocate!Device();
device.initialize(Address(bus_nr, device_nr, function_nr), config);
if (device.multifunction)
{
for (function_nr = 1u; function_nr < MAX_FUNCTIONS_PER_DEVICE; function_nr++)
{
config_address += PAGE_SIZE;
scan(bus_nr, device_nr, function_nr, config_address);
}
}
} }
} }
public static void initialize() public static void initialize()
{ {
klog.writefln("Scanning PCI devices..."); Klog.writefln("Scanning PCI devices...");
for (ulong bus_id = 0u; bus_id < 256u; bus_id++) size_t n_sgma_descs = Acpi.mcfg.n_sgma_descs;
for (size_t i = 0u; i < n_sgma_descs; i++)
{ {
for (ulong device_id = 0u; device_id < 32u; device_id++) Acpi.MCFG.SGMADesc * sgma_desc = &Acpi.mcfg.sgma_descs[i];
ulong base_address = sgma_desc.base_address;
uint start_pci_bus = sgma_desc.start_pci_bus_number;
uint end_pci_bus = sgma_desc.end_pci_bus_number;
ulong n_buses = (end_pci_bus - start_pci_bus) + 1u;
ulong map_length = PAGE_SIZE * MAX_FUNCTIONS_PER_DEVICE * MAX_DEVICES_PER_BUS * n_buses;
Hurl.identity_map_range(base_address, map_length, PT_WRITABLE | PT_DISABLE_CACHE | PT_NO_EXECUTE);
for (uint bus_nr = start_pci_bus; bus_nr <= end_pci_bus; bus_nr++)
{ {
scan(bus_id, device_id); for (ubyte device_nr = 0u; device_nr < MAX_DEVICES_PER_BUS; device_nr++)
{
ulong device_address = base_address + ((bus_nr - start_pci_bus) * (MAX_DEVICES_PER_BUS * MAX_FUNCTIONS_PER_DEVICE) + device_nr * MAX_FUNCTIONS_PER_DEVICE) * PAGE_SIZE;
scan(cast(ubyte)bus_nr, device_nr, 0u, device_address);
} }
} }
klog.writefln("PCI scan complete."); }
Klog.writefln("PCI scan complete.");
} }
} }
+1 -1
View File
@@ -5,7 +5,7 @@ module hulk.pic;
import hulk.cpu; import hulk.cpu;
struct pic struct Pic
{ {
public static void initialize() public static void initialize()
{ {
+42
View File
@@ -0,0 +1,42 @@
/**
* PIT (Programmable Interval Timer) functionality.
*/
module hulk.pit;
import hulk.cpu;
import hulk.klog;
struct Pit
{
/** PIT input frequency in 1/1000 Hz. */
private enum ulong PIT_FREQUENCY_1000HZ = 1_193_181_667u;
private enum ulong TARGET_INTERRUPT_HZ = 100u;
private enum ulong TARGET_INTERRUPT_1000HZ = TARGET_INTERRUPT_HZ * 1000u;
private enum ulong PIT_DIVIDER = (PIT_FREQUENCY_1000HZ + TARGET_INTERRUPT_1000HZ / 2u) / TARGET_INTERRUPT_1000HZ;
/** PIT channel 0 I/O port. */
private enum ubyte PORT_CH_0 = 0x40u;
/** PIT channel 1 I/O port. */
private enum ubyte PORT_CH_1 = 0x41u;
/** PIT channel 2 I/O port. */
private enum ubyte PORT_CH_2 = 0x42u;
/** PIT mode/command register I/O port. */
private enum ubyte PORT_MC = 0x43u;
private enum ubyte MC_CH_0 = 0x00u;
private enum ubyte MC_LO_HI = 0x30u;
private enum ubyte MC_RATE_GENERATOR = 0x04u;
private enum ubyte MC_BINARY = 0x00u;
public static void initialize()
{
out8(PORT_MC, MC_CH_0 | MC_LO_HI | MC_RATE_GENERATOR | MC_BINARY);
out8(PORT_CH_0, PIT_DIVIDER & 0xFFu);
out8(PORT_CH_0, PIT_DIVIDER >> 8u);
}
public static void isr()
{
Klog.writefln("PIT ISR");
}
}
+27
View File
@@ -0,0 +1,27 @@
module hulk.range;
/**
* Representation of a memory range.
*/
struct Range
{
/** Range base address. */
void * _address;
/** Range length. */
size_t length;
/** Set the range address. */
@property void * address(T)(T a)
{
static assert(T.sizeof == size_t.sizeof);
_address = cast(void *)a;
return _address;
}
/** Get the range address. */
@property void * address()
{
return _address;
}
}
+146
View File
@@ -0,0 +1,146 @@
/**
* RTC (Real-Time Clock) functionality.
*/
module hulk.rtc;
import hulk.cpu;
import hulk.klog;
import hulk.console;
struct Rtc
{
private enum ubyte PORT_SELECT = 0x70u;
private enum ubyte PORT_DATA = 0x71u;
private enum ubyte DISABLE_NMI = 0x80u;
private enum ubyte REG_RTC_SECONDS = 0x0u;
private enum ubyte REG_RTC_MINUTES = 0x2u;
private enum ubyte REG_RTC_HOURS = 0x4u;
private enum ubyte REG_RTC_DAY = 0x7u;
private enum ubyte REG_RTC_MONTH = 0x8u;
private enum ubyte REG_RTC_YEAR = 0x9u;
private enum ubyte REG_SR_A = 0xAu;
private enum ubyte REG_SR_B = 0xBu;
private enum ubyte REG_SR_C = 0xCu;
private enum ubyte SR_A_RTC_UPDATE_IN_PROGRESS = 0x80u;
private enum ubyte SR_B_24HOUR = 0x02u;
private enum ubyte SR_B_BINARY = 0x04u;
private enum ubyte SR_B_ENABLE_IRQ = 0x40u;
private static __gshared bool rtc_24hour_mode;
private static __gshared bool rtc_binary_mode;
static struct time
{
public ubyte year;
public ubyte month;
public ubyte day;
public ubyte hour;
public ubyte minute;
public ubyte second;
}
public static void initialize()
{
/* Enable IRQ 8 to receive RTC interrupts. */
ubyte sr_b = read_register(REG_SR_B, true);
write_register(REG_SR_B, sr_b | SR_B_ENABLE_IRQ, true);
/* Record RTC mode flags from status register B. */
rtc_24hour_mode = (sr_b & SR_B_24HOUR) != 0u;
rtc_binary_mode = (sr_b & SR_B_BINARY) != 0u;
/* Send EOI to enable more RTC interrupts and re-enable NMIs. */
eoi();
}
private static ubyte read_register(ubyte register, bool disable_nmi = false)
{
out8(PORT_SELECT, register | (disable_nmi ? DISABLE_NMI : 0u));
return in8(PORT_DATA);
}
private static void write_register(ubyte register, ubyte value, bool disable_nmi = false)
{
out8(PORT_SELECT, register | (disable_nmi ? DISABLE_NMI : 0u));
out8(PORT_DATA, value);
}
private static void eoi()
{
/* Read from status register C to clear the interrupt. */
read_register(REG_SR_C);
}
public static void isr()
{
static __gshared ulong count;
static __gshared ulong seconds;
count++;
if ((count % 1024) == 0u)
{
seconds++;
Console.update_header();
}
eoi();
}
public static time read_rtc_time()
{
time[2] times;
size_t reads;
do
{
times[reads & 1] = read_rtc_time_unsafe();
reads++;
} while ((reads < 2u) || (times[0] != times[1]));
return times[0];
}
private static time read_rtc_time_unsafe()
{
time t;
while ((read_register(REG_SR_A) & SR_A_RTC_UPDATE_IN_PROGRESS) != 0u)
{
}
t.year = read_rtc_time_register(REG_RTC_YEAR);
t.month = read_rtc_time_register(REG_RTC_MONTH);
t.day = read_rtc_time_register(REG_RTC_DAY);
t.minute = read_rtc_time_register(REG_RTC_MINUTES);
t.second = read_rtc_time_register(REG_RTC_SECONDS);
/* Hour field is treated differently because it might have 'PM' flag
* in highest bit if we're in 24-hour mode. */
ubyte hour = read_register(REG_RTC_HOURS);
ubyte pm = hour >> 7u;
hour &= 0x7Fu;
if (!rtc_binary_mode)
{
/* Hour is in BCD format; convert to binary. */
hour = bcd_to_binary(hour);
}
if (!rtc_24hour_mode)
{
/* Hour is in 12-hour format; convert to 24-hour format. */
hour = cast(ubyte)((hour % 12u) + pm * 12u);
}
t.hour = hour;
return t;
}
private static ubyte read_rtc_time_register(ubyte register)
{
ubyte value = read_register(register);
if (!rtc_binary_mode)
{
value = bcd_to_binary(value);
}
return value;
}
private static ubyte bcd_to_binary(ubyte bcd)
{
return (bcd & 0xFu) + ((bcd & 0xF0u) >> 4u) * 10u;
}
}
+74
View File
@@ -0,0 +1,74 @@
/**
* Serial port access.
*/
module hulk.serial;
import hulk.cpu;
import core.stdc.stdarg;
static import hulk.writef;
struct Serial
{
enum BASE_PORT = 0x3F8;
/**
* Initialize the serial module.
*/
static void initialize()
{
}
/**
* Write a character to the serial port.
*
* @param ch Character to write.
*/
static void write(ubyte ch)
{
if (ch == '\n')
{
out8(BASE_PORT, '\r');
}
out8(BASE_PORT, ch);
}
/**
* Write a formatted string to the serial port.
*
* @param s Format string.
* @param args Variable arguments structure.
*/
public static void writef(string s, va_list args)
{
hulk.writef.writefv(function void(ubyte ch) {
Serial.write(ch);
}, s, args);
}
/**
* Write a formatted string to the serial port.
*
* @param s Format string.
*/
public static extern (C) void writef(string s, ...)
{
va_list args;
va_start(args, s);
writef(s, args);
va_end(args);
}
/**
* Write a formatted string and newline to the serial port.
*
* @param s Format string.
*/
public static extern (C) void writefln(string s, ...)
{
va_list args;
va_start(args, s);
writef(s, args);
writef("\n", args);
va_end(args);
}
}
+397
View File
@@ -0,0 +1,397 @@
/**
* eXtensible Host Controller Interface for Universal Serial Bus
*
* Documentation:
* - https://wiki.osdev.org/EXtensible_Host_Controller_Interface
* - https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
*/
module hulk.usb.xhci;
import hulk.pci;
import hulk.hurl.a1;
import hulk.klog;
struct XHCI
{
/**
* Located at PCI base address.
*
* All are read-only.
*/
struct CapabilityRegisters
{
/**
* Offset from beginning of capability registers to beginning of
* operational registers.
*/
ubyte capability_length;
ubyte _reserved01;
/**
* Interface version number supported (psuedo-BSD).
*/
ushort hci_version;
/**
* 7:0 number of device slots (MaxSlots)
* 18:8 number of interrupters (MaxIntrs)
* 23:19 reserved
* 31:24 number of ports (MaxPorts)
*/
uint hcs_params1;
/**
* 3:0 isochronous scheduling threshold (IST)
* 7:4 event ring segment table max (ERST Max)
* 20:8 reserved
* 25:21 max scratchpad buffers (high 5 bits; xHCI v1.1+)
* 26 scratchpad restore (SPR)
* 31:27 max scratchpad buffers (low 5 bits)
*/
uint hcs_params2;
/**
* 7:0 U1 device exit latency
* 15:8 reserved
* 31:16 U2 device exit latency
*/
uint hcs_params3;
/**
* 0 64-bit addressing capability (AC64)
* 1 BW negotiation capability (BNC)
* 2 context size (CSZ)
* 3 port power control (PPC)
* 4 port indicators (PIND)
* 5 light HC reset capability (LHRC)
* 6 latency tolerance messaging capability (LTC)
* 7 no secondary SID support (NSS)
* 8 parse all event data (PAE; xHCI v1.1+)
* 9 stopped - short packet capability (SPC; xHCI v1.1+)
* 10 stopped EDTLA capability (SEC; xHCI v1.1+)
* 11 continuous frame ID capability (CFC; xHCI v1.1+)
* 15:12 maximum primary stream array size (MaxPSASize)
* 31:16 xHCI extended capabilities pointer (xECP)
* offset in 32-bit words from base of capability registers to base
* of capabilities list
*/
uint hcc_params1;
/**
* 1:0 reserved
* 31:2 doorbell array offset in 32-bit words
* offset in 32-bit words from base of capability registers to the
* doorbell array
*/
uint doorbell_offset;
/**
* 4:0 reserved
* 31:5 runtime register space offset
*/
uint rts_offset;
/**
* 0 U3 entry capability (U3C)
* 1 ConfigEP command max exit latency too large (CMC)
* 2 force save context capability (FSC)
* 3 compliance transition capability (CTC)
* 4 large ESIT payload capability (LEC)
* 5 configuration information capability (CIC)
* 6 extended TBC capability (ETC)
* 7 extended TBC TRB status capability (ETC_TSC)
* 8 get/set extended property capability (GSC; xHCI v1.1+)
* 9 virtualization based trusted I/O capability (VTC; xHCI v1.2+)
* 31:10 reserved
*/
uint hcc_params2;
/**
* 11:0 reserved
* 31:12 VTIO register space offset (xHCI v1.2+)
*/
uint vtios_offset;
}
static assert(CapabilityRegisters.sizeof == 0x24u);
/**
* Located at offset capability_length from PCI base address.
*/
struct OperationalRegisters
{
/**
* 0 run/stop (RS)
* 1 host controller reset (HCRST)
* 2 interrupter enable (INTE)
* 3 host system error enable (HSEE)
* 6:4 reserved
* 7 light host controller reset (LHCRST)
* 8 controller save state (CSS)
* 9 controller restore state (CRS)
* 10 enable wrap event (EWE)
* 11 enable U3 MFINDEX stop (UE3S)
* 12 stopped short packet enable (SPE; xHCI v1.1+)
* 13 CEM enable (CME; xHCI v1.1+)
* 14 extended TBC enable (ETE; xHCI v1.2+)
* 15 extended TBC TRB status enable (TSC_EN; xHCI v1.2+)
* 16 VTIO enable (VTIOE; xHCI v1.2+)
* 31:17 reserved
*/
uint usb_command;
/**
* 0 HCHalted
* 1 reserved
* 2 Host System Error (HSE)
* 3 Event Interrupt (EINT)
* 4 Port Change Detected (PCD)
* 7:5 reserved
* 8 Save State Status (SSS)
* 9 Restore State Status (RSS)
* 10 Save/Restore Error (SRE)
* 11 Controller Not Ready (CNR)
* 12 Host Controller Error (HCE)
* 31:13 reserved
*/
uint usb_status;
/**
* 15:0 Page Size (actual page size is 2^(x+12))
* 31:16 reserved
*/
uint page_size;
uint[2] _reserved01;
/**
* 15:0 Notification Enable
* 31:16 reserved
*/
uint dn_ctrl;
/**
* 0 Ring Cycle State (RCS)
* 1 Command Stop (CS)
* 2 Command Abort (CA)
* 3 Command Ring Running (CRR)
* 5:4 reserved
* 63:6 Command Ring Pointer
*/
ulong cr_ctrl;
uint[4] _reserved02;
/**
* 5:0 reserved
* 63:6 Device Context Base Address Array Pointer
*/
ulong dcbaap;
/**
* 7:0 Max Device Slots Enabled (MaxSlotsEn)
* 8 U3 Entry Enable (U3E; xHCI v1.1+)
* 9 Configuration Information Enable (CIE; xHCI v1.1+)
* 31:10 reserved
*/
uint config;
uint _reserved03;
}
static assert(OperationalRegisters.sizeof == 0x40u);
/**
* Located at offset 0x400 from start of Operational Registers.
*
* There is one set of these registers for each port of the root hub.
*/
struct PortRegisters
{
/**
* Port Status and Control
*
* 0 Current Connect Status
* 1 Port Enabled/Disabled
* 2 reserved
* 3 Over-current active
* 4 Port Reset
* 8:5 Port Link State
* 9 Port Power
* 13:10 Port Speed
* 15:14 Port Indicator Control
* 16 Port Link State Write Strobe
* 17 Connect Status Change
* 18 Port Enable/Disable Change
* 19 Warm Port Reset Change
* 20 Over-current Change
* 21 Port Reset Change
* 22 Port Link State Change
* 23 Port Config Error Change
* 24 Cold Attach Status
* 25 Wake on Connect Enable
* 26 Wake on Disconnect Enable
* 27 Wake on Over-current Enable
* 29:28 reserved
* 30 Device Removable
* 31 Warm Port Reset
*/
uint portsc;
/**
* Port Power Management Status and Control
*/
uint portpmsc;
/**
* Port Link Info
*/
uint portli;
/**
* Port Hardware LPM Control (xHCI v1.1+)
*/
uint porthlpmc;
}
static assert(PortRegisters.sizeof == 0x10u);
/**
* Located at offset rts_offset from PCI base address.
*/
struct RuntimeRegisters
{
/**
* Microframe index. Incremented by controller each microframe.
*/
uint mfindex;
uint[7] _reserved01;
/**
* Interrupter register sets.
*/
InterrupterRegister[0] ir;
}
/**
* Interrupter register set.
*
* A copy of this register set is present for each interrupter.
*/
struct InterrupterRegister
{
/**
* Interrupter Management Register.
* 0 Interrupt Pending (IP)
* 1 Interrupt Enable
* 31:2 reserved
*/
uint imr;
/**
* Interrupter Moderation.
* 15:0 Interrupt Moderation Interval
* 31:16 Interrupt Moderation Counter
*/
uint im;
/**
* Event Ring Segment Table Size.
* 15:0 Event Ring Segment Table Size
* 31:16 reserved
*/
uint ersts;
uint reserved;
/**
* Event Ring Segment Table Base Address.
* 5:0 reserved
* 63:6 Event Ring Segment Table Base Address
*/
ulong erstba;
/**
* Event Ring Dequeue Pointer.
* 2:0 Dequeue ERST Segment Index
* 3 Event Handler Busy
* 63:4 Event Ring Dequeue Pointer
*/
ulong erdp;
}
struct DoorbellRegister
{
ubyte target;
ubyte _reserved01;
ushort task_id;
}
static assert(DoorbellRegister.sizeof == 4u);
private CapabilityRegisters * m_capability_registers;
private OperationalRegisters * m_operational_registers;
private PortRegisters * m_port_registers;
private RuntimeRegisters * m_runtime_registers;
private DoorbellRegister * m_doorbell_registers;
void initialize(Pci.Device * pci_device)
{
void * base_address = pci_device.memory_ranges[0].address;
Klog.writefln("Found XHCI controller at address %p", base_address);
m_capability_registers = cast(CapabilityRegisters *)base_address;
m_operational_registers = cast(OperationalRegisters *)(base_address + m_capability_registers.capability_length);
m_port_registers = cast(PortRegisters *)(cast(void *)m_operational_registers + 0x400);
m_runtime_registers = cast(RuntimeRegisters *)(base_address + m_capability_registers.rts_offset);
m_doorbell_registers = cast(DoorbellRegister *)(base_address + m_capability_registers.doorbell_offset);
dump_extended_capabilities();
}
private void dump_extended_capabilities()
{
size_t ext_cap_off = (m_capability_registers.hcc_params1 >> 16u);
if (ext_cap_off != 0u)
{
Klog.writefln("Extended capabilities:");
uint * extended_capabilities = cast(uint *)m_capability_registers + ext_cap_off;
for (;;)
{
uint ec0 = extended_capabilities[0];
size_t next_offset = (ec0 >> 8u) & 0xFFu;
size_t len = next_offset;
if (len < 1u)
{
len = 1u;
}
ubyte type = ec0 & 0xFFu;
if (type == 2u)
{
len = 4u;
}
for (size_t i = 0u; i < len; i++)
{
uint ecv = extended_capabilities[i];
Klog.writef(" %02X %02X %02X %02X",
ecv & 0xFFu,
(ecv >> 8u) & 0xFFu,
(ecv >> 16u) & 0xFFu,
(ecv >> 24u) & 0xFFu);
}
Klog.writef("\n");
if (next_offset == 0u)
{
break;
}
extended_capabilities += next_offset;
}
}
}
static void build(Pci.Device * pci_device)
{
if (pci_device.memory_ranges[0].address != null)
{
XHCI * xhci = A1.allocate!XHCI();
xhci.initialize(pci_device);
}
}
}
+17
View File
@@ -0,0 +1,17 @@
/**
* HULK utility functions.
*/
module hulk.util;
/**
* Round a value up to the next power of 2.
*
* @param v Value to round up.
* @param p Power of 2 to round up to.
*
* @return Rounded up value.
*/
ulong round_up_power_2(ulong v, ulong p)
{
return (v + p - 1u) & ~(p - 1u);
}
+3
View File
@@ -0,0 +1,3 @@
module hulk.ver;
enum VERSION = "0.18.0";
+55 -15
View File
@@ -10,13 +10,13 @@ alias ch_out_fn = void function(ubyte);
/** /**
* Format a string and write characters to the given output function. * Format a string and write characters to the given output function.
* *
* @param ch_out Character output function.
* @param s Format string. * @param s Format string.
* @param args Variable arguments structure. * @param args Variable arguments structure.
* @param ch_out Character output function.
* *
* @return Number of characters written. * @return Number of characters written.
*/ */
size_t writef(string s, va_list args, ch_out_fn ch_out) size_t writefv(ch_out_fn ch_out, string s, va_list args)
{ {
size_t length_written; size_t length_written;
bool escape = false; bool escape = false;
@@ -26,18 +26,33 @@ size_t writef(string s, va_list args, ch_out_fn ch_out)
{ {
if (escape) if (escape)
{ {
if (c == 'x') if (c == 'c')
{
ulong ch;
va_arg(args, ch);
ch_out(cast(ubyte)ch);
length_written++;
escape = false;
}
else if (c == 'p')
{ {
ulong v; ulong v;
va_arg(args, v); va_arg(args, v);
length_written += write_hex(v, false, pad, width, ch_out); length_written += write_hex(v, true, true, pad, width, ch_out);
escape = false;
}
else if (c == 'x')
{
ulong v;
va_arg(args, v);
length_written += write_hex(v, false, false, pad, width, ch_out);
escape = false; escape = false;
} }
else if (c == 'X') else if (c == 'X')
{ {
ulong v; ulong v;
va_arg(args, v); va_arg(args, v);
length_written += write_hex(v, true, pad, width, ch_out); length_written += write_hex(v, true, false, pad, width, ch_out);
escape = false; escape = false;
} }
else if (c == 'u') else if (c == 'u')
@@ -75,32 +90,57 @@ size_t writef(string s, va_list args, ch_out_fn ch_out)
return length_written; return length_written;
} }
/**
* Format a string and write characters to the given output function.
*
* @param ch_out Character output function.
* @param s Format string.
*
* @return Number of characters written.
*/
extern(C) size_t writef(ch_out_fn ch_out, string s, ...)
{
va_list args;
va_start(args, s);
size_t rv = writefv(ch_out, s, args);
va_end(args);
return rv;
}
/** /**
* Format a value in hexadecimal to the given output function. * Format a value in hexadecimal to the given output function.
* *
* @param v Value to format. * @param v Value to format.
* @param upper Whether to use uppercase letters. * @param upper Whether to use uppercase letters.
* @param pointer Whether to format as a pointer.
* @param pad Pad character to use. * @param pad Pad character to use.
* @param width Field width. * @param width Field width.
* @param ch_out Character output function. * @param ch_out Character output function.
* *
* @return Number of characters written. * @return Number of characters written.
*/ */
private size_t write_hex(ulong v, bool upper, char pad, size_t width, ch_out_fn ch_out) private size_t write_hex(ulong v, bool upper, bool pointer, char pad, size_t width, ch_out_fn ch_out)
{ {
static __gshared const(char)[16] hex_chars_lower = "0123456789abcdef"; static __gshared const(char)[16] hex_chars_lower = "0123456789abcdef";
static __gshared const(char)[16] hex_chars_upper = "0123456789ABCDEF"; static __gshared const(char)[16] hex_chars_upper = "0123456789ABCDEF";
char[16] buf; char[19] buf;
size_t i; size_t digit;
size_t buf_idx;
do do
{ {
ulong n = v & 0xFu; ulong n = v & 0xFu;
buf[i] = upper ? hex_chars_upper[n] : hex_chars_lower[n]; buf[buf_idx] = upper ? hex_chars_upper[n] : hex_chars_lower[n];
v >>= 4u; v >>= 4u;
i++; buf_idx++;
digit++;
if (pointer && ((digit % 4) == 0) && (v != 0u))
{
buf[buf_idx] = '_';
buf_idx++;
}
} while (v != 0u); } while (v != 0u);
size_t length_written = i; size_t length_written = buf_idx;
while (width > i) while (width > buf_idx)
{ {
ch_out(pad); ch_out(pad);
width--; width--;
@@ -108,9 +148,9 @@ private size_t write_hex(ulong v, bool upper, char pad, size_t width, ch_out_fn
} }
do do
{ {
i--; buf_idx--;
ch_out(buf[i]); ch_out(buf[buf_idx]);
} while (i != 0u); } while (buf_idx != 0u);
return length_written; return length_written;
} }
+9
View File
@@ -21,23 +21,30 @@ end
default do default do
unless Dir.exist?(install_path) unless Dir.exist?(install_path)
puts "Building cross compiler toolchain..."
# Download archives. # Download archives.
puts "Downloading binutils..."
download "https://ftp.gnu.org/gnu/binutils/binutils-#{binutils_version}.tar.xz", download "https://ftp.gnu.org/gnu/binutils/binutils-#{binutils_version}.tar.xz",
"#{build_dir}/binutils-#{binutils_version}.tar.xz", "#{build_dir}/binutils-#{binutils_version}.tar.xz",
sha256sum: binutils_checksum sha256sum: binutils_checksum
puts "Downloading gcc..."
download "https://ftp.gnu.org/gnu/gcc/gcc-#{gcc_version}/gcc-#{gcc_version}.tar.xz", download "https://ftp.gnu.org/gnu/gcc/gcc-#{gcc_version}/gcc-#{gcc_version}.tar.xz",
"#{build_dir}/gcc-#{gcc_version}.tar.xz", "#{build_dir}/gcc-#{gcc_version}.tar.xz",
sha256sum: gcc_checksum sha256sum: gcc_checksum
# Extract archives. # Extract archives.
puts "Extracting binutils..."
sh "tar", "xJf", "binutils-#{binutils_version}.tar.xz", sh "tar", "xJf", "binutils-#{binutils_version}.tar.xz",
chdir: build_dir chdir: build_dir
puts "Extracting gcc..."
sh "tar", "xJf", "gcc-#{gcc_version}.tar.xz", sh "tar", "xJf", "gcc-#{gcc_version}.tar.xz",
chdir: build_dir chdir: build_dir
# Build binutils. # Build binutils.
puts "Building binutils..."
rm_rf "#{build_dir}/build-binutils" rm_rf "#{build_dir}/build-binutils"
mkdir_p "#{build_dir}/build-binutils" mkdir_p "#{build_dir}/build-binutils"
cd "#{build_dir}/build-binutils" do cd "#{build_dir}/build-binutils" do
@@ -49,6 +56,7 @@ default do
end end
# Build gcc. # Build gcc.
puts "Building gcc..."
rm_rf "#{build_dir}/build-gcc" rm_rf "#{build_dir}/build-gcc"
mkdir_p "#{build_dir}/build-gcc" mkdir_p "#{build_dir}/build-gcc"
cd "#{build_dir}/build-gcc" do cd "#{build_dir}/build-gcc" do
@@ -62,6 +70,7 @@ default do
end end
# Remove archives and build directories if everything succeeded. # Remove archives and build directories if everything succeeded.
puts "Cleaning up..."
rm_f "#{build_dir}/binutils-#{binutils_version}.tar.xz" rm_f "#{build_dir}/binutils-#{binutils_version}.tar.xz"
rm_rf "#{build_dir}/binutils-#{binutils_version}" rm_rf "#{build_dir}/binutils-#{binutils_version}"
rm_rf "#{build_dir}/build-binutils" rm_rf "#{build_dir}/build-binutils"