4 Commits
d .. dev
Author SHA1 Message Date
josh 4a39341a83 Turn off local APIC timer 2022-12-14 23:56:11 -05:00
josh 42ab6f396d Processing multiple RTC interrupts 2022-12-14 23:51:35 -05:00
josh 58d685a15b Program I/O APIC; got one RTC interrupt 2022-12-14 23:43:26 -05:00
josh d49f9e32cd dev checkpoint 2022-12-14 19:40:22 -05:00
41 changed files with 617 additions and 3329 deletions

No files matched your search

-1
View File
@@ -1,4 +1,3 @@
.rscons* .rscons*
/x86_64-elf-gcc/ /x86_64-elf-gcc/
/build/ /build/
/qemu/
+2 -12
View File
@@ -17,8 +17,6 @@ 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:
@@ -69,13 +67,5 @@ 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 System, change the Chipset to ICH9. 15. Under Storage, add a hard disk and browse to select the HOS.vmdk file.
16. Under Storage, add a hard disk and browse to select the HOS.vmdk file. 16. To run HOS, either start the VM through VirtualBox or with "./rscons run-vb".
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.
+14 -61
View File
@@ -11,13 +11,12 @@ configure do
check_c_compiler check_c_compiler
check_program "mformat", on_fail: "Install the mtools package" check_program "mformat", on_fail: "Install the mtools package"
check_program "parted" check_program "parted"
check_program "pkg-config"
check_cfg package: "freetype2", on_fail: "Install libfreetype-dev", use: "freetype" check_cfg package: "freetype2", on_fail: "Install libfreetype-dev", use: "freetype"
sh %w[git submodule update --init] sh %w[git submodule update --init]
end end
# Kernel default font size. # Kernel default font size.
KFONT_SIZE = 16 KFONT_SIZE = 18
# One kilobyte. # One kilobyte.
KB = 1024 KB = 1024
@@ -27,7 +26,7 @@ MB = 1024 * 1024
class HulkBinObj < Builder class HulkBinObj < Builder
def run(options) def run(options)
@cache.mkdir_p(File.dirname(@target)) FileUtils.mkdir_p(File.dirname(@target))
File.write(@target, <<EOF) File.write(@target, <<EOF)
.section ".rodata" .section ".rodata"
.balign 4096 .balign 4096
@@ -114,8 +113,10 @@ hulk_env = env "hulk", use: %w[ldc2 x86_64-elf-gcc] do |env|
-avx512bw -avx512bw
-avx512cd -avx512cd
-avx512dq -avx512dq
-avx512er
-avx512f -avx512f
-avx512ifma -avx512ifma
-avx512pf
-avx512vbmi -avx512vbmi
-avx512vbmi2 -avx512vbmi2
-avx512vl -avx512vl
@@ -131,11 +132,11 @@ hulk_env = env "hulk", use: %w[ldc2 x86_64-elf-gcc] do |env|
-sse4a -sse4a
-ssse3 -ssse3
] ]
env["DFLAGS"] += %W[-g -mtriple=x86_64-unknown-elf -mattr=#{cpu_attrs.join(",")} --betterC -release -O3 --wi --enable-cross-module-inlining -code-model=kernel --disable-red-zone] env["DFLAGS"] += %W[-mtriple=x86_64-unknown-elf -mattr=#{cpu_attrs.join(",")} --betterC -release -O3 --wi --enable-cross-module-inlining -code-model=large]
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[-g -nostdlib -Tsrc/hulk/hulk.ld -Wl,--gc-sections -Wl,-Map,${_TARGET}.map -Wl,--no-warn-rwx-segments] env["LDFLAGS"] += %w[-nostdlib -Tsrc/hulk/hulk.ld -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"
@@ -144,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.elf.size", "^/hulk.elf") env.Size("^/hulk.size", "^/hulk.elf")
env.Disassemble("^/hulk.elf.txt", "^/hulk.elf") env.Disassemble("^/hulk.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:")
@@ -161,19 +162,15 @@ 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")
env.depends("^/hulk_bin.o", hulk_env.expand("^/hulk.bin")) env.depends("^/hulk_bin.o", hulk_env.expand("^/hulk.bin"))
env["sources"] << "^/hulk_bin.o" env["sources"] << "^/hulk_bin.o"
env["DFLAGS"] += %w[-g -mtriple=x86_64-unknown-windows-coff --betterC -release -O3 --wi --enable-cross-module-inlining --disable-red-zone] env["DFLAGS"] += %w[-mtriple=x86_64-unknown-windows-coff --betterC -release -O3 --wi --enable-cross-module-inlining]
env["D_IMPORT_PATH"] += %w[src uefi-d/source] env["D_IMPORT_PATH"] += %w[src uefi-d/source]
env["LD"] = "x86_64-w64-mingw32-gcc" env["LD"] = "x86_64-w64-mingw32-gcc"
env["LDFLAGS"] += %w[-g -nostdlib -Wl,-dll -shared -Wl,--subsystem,10 -e efi_main -Wl,-Map,${_TARGET}.map] env["LDFLAGS"] += %w[-nostdlib -Wl,-dll -shared -Wl,--subsystem,10 -e efi_main -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-w64-mingw32-objdump" env["OBJDUMP"] = "x86_64-w64-mingw32-objdump"
env.Program("^/HOS.EFI", "${sources}") env.Program("^/HOS.EFI", "${sources}")
@@ -186,60 +183,16 @@ 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
FileUtils.rm_rf("qemu") Dir.mktmpdir do |tmpdir|
FileUtils.mkdir_p("qemu")
img = hello_env.expand("^/HOS.img") img = hello_env.expand("^/HOS.img")
FileUtils.cp(img, "qemu") FileUtils.cp(img, tmpdir)
ovmf = "OVMF.fd" sh %W[qemu-system-x86_64 -cpu max -bios OVMF.fd -drive file=#{tmpdir}/HOS.img,format=raw -device qemu-xhci -device usb-tablet]
if File.exist?("/usr/share/edk2/x64/OVMF.fd")
ovmf = "/usr/share/edk2/x64/OVMF.fd"
elsif File.exist?("/usr/share/edk2/x64/OVMF.4m.fd")
ovmf = "/usr/share/edk2/x64/OVMF.4m.fd"
end end
sh %W[
qemu-system-x86_64
-machine q35
-m 128M
-cpu max
-smp cpus=4
-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
path = File.expand_path(hello_env.expand("^/HOS.img")) sh %W[VBoxManage internalcommands createrawvmdk -filename HOS.vmdk -rawdisk #{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.
+15 -77
View File
@@ -1,6 +1,5 @@
#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
@@ -87,43 +86,35 @@ static void generate_bytes(FILE * file, const uint8_t * bytes, int count)
} }
} }
static bool generate(const char * d_file_name) static void 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, " /** Glyph bitmap width. */\n"); fprintf(fh, " uint width;\n");
fprintf(fh, " ubyte width;\n"); fprintf(fh, " uint height;\n");
fprintf(fh, " /** Glyph bitmap height. */\n"); fprintf(fh, " int top;\n");
fprintf(fh, " ubyte height;\n"); fprintf(fh, " int left;\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, " /** Font line height. */\n"); fprintf(fh, " uint line_height;\n");
fprintf(fh, " ubyte line_height;\n"); fprintf(fh, " uint advance;\n");
fprintf(fh, " /** Font advance. */\n"); fprintf(fh, " int baseline_offset;\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, "/** Kernel font data. */\n"); fprintf(fh, "__gshared const FontInfo kfont = {\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 /* width */,\n", char_infos[i].width); fprintf(fh, " %du,\n", char_infos[i].width);
fprintf(fh, " %du /* height */,\n", char_infos[i].height); fprintf(fh, " %du,\n", char_infos[i].height);
fprintf(fh, " %du /* top */,\n", char_infos[i].top); fprintf(fh, " %d,\n", char_infos[i].top);
fprintf(fh, " %du /* left */,\n", char_infos[i].left); fprintf(fh, " %d,\n", char_infos[i].left);
if (char_infos[i].width > 0) if (char_infos[i].width > 0)
{ {
fprintf(fh, " [\n"); fprintf(fh, " [\n");
@@ -139,7 +130,6 @@ static bool 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[])
@@ -177,59 +167,7 @@ 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;
if ((line_height < 1) || (line_height > 0xFF)) generate(out_file);
{
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;
} }
+115 -22
View File
@@ -6,62 +6,155 @@ 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
{ {
/** /**
* Write a character to the console. * Format a hexadecimal value to a string.
* *
* @param ch Character to write. * @param s String buffer.
* @param v Value to format.
* @param ptr If true, always output all 16 digits, and separate the upper and
* lower halves with an underscore.
*/ */
public static void write(ubyte ch) private static size_t format_hex(CHAR16 * s, ulong v, bool ptr)
{ {
if (ch == '\n') string hex_chars = "0123456789ABCDEF";
bool print = ptr;
size_t si = 0u;
for (size_t i = 0u; i < 16u; i++)
{ {
write('\r'); if (ptr && (i == 8u))
{
s[si++] = '_';
} }
CHAR16[2] s16 = [ch, 0]; ulong n = (v >> (60u - 4u * i)) & 0xFu;
st.ConOut.OutputString(st.ConOut, &s16[0]); if (n != 0u)
{
print = true;
}
if (print || (i == 15u))
{
s[si++] = hex_chars[n];
}
}
return si;
} }
/** /**
* Write a formatted string to the console. * 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 s Format string.
* @param args Variable arguments structure. * @param args Variable arguments structure.
*/ */
public static void writef(string s, va_list args) public static void write(string s, va_list args)
{ {
hulk.writef.writefv(function void(ubyte ch) { __gshared static CHAR16[256] s16;
Console.write(ch); size_t i = 0u;
}, s, args); 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]);
} }
/** /**
* Write a formatted string to the console. * Write a string to the console.
* *
* @param s Format string. * @param s Format string.
*/ */
public static extern (C) void writef(string s, ...) public static extern (C) void write(string s, ...)
{ {
va_list args; va_list args;
va_start(args, s); va_start(args, s);
writef(s, args); write(s, args);
va_end(args); va_end(args);
} }
/** /**
* Write a formatted string and newline to the console. * Write a string to the console.
* *
* @param s Format string. * @param s Format string.
*/ */
public static extern (C) void writefln(string s, ...) public static extern (C) void writeln(string s, ...)
{ {
va_list args; va_list args;
va_start(args, s); va_start(args, s);
writef(s, args); write(s, args);
writef("\n", args); write("\r\n", args);
va_end(args); va_end(args);
} }
@@ -70,7 +163,7 @@ struct Console
*/ */
public static void wait_key() public static void wait_key()
{ {
writefln("Press any key..."); writeln("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)
+14 -31
View File
@@ -11,7 +11,6 @@ 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. */
@@ -94,7 +93,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.writefln("LocateHandle: error %x", status); console.writeln("LocateHandle: error %x", status);
return false; return false;
} }
EFI_HANDLE gop_handle = handles[0]; EFI_HANDLE gop_handle = handles[0];
@@ -103,12 +102,12 @@ private bool set_graphics_mode()
&gop_guid, &gop_interface); &gop_guid, &gop_interface);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
{ {
Console.writefln("HandleProtocol: error %x", status); console.writeln("HandleProtocol: error %x", status);
return false; return false;
} }
if (gop_interface == null) if (gop_interface == null)
{ {
Console.writefln("null interface from HandleProtocol"); console.writeln("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;
@@ -130,7 +129,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.writefln("SetMode: Error %x\n", status); console.writeln("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;
@@ -175,7 +174,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.writefln("GetMemoryMap: Error %x", status); console.writeln("GetMemoryMap: Error %x", status);
for (;;) {} for (;;) {}
} }
size_t n_entries = memory_map_size / descriptor_size; size_t n_entries = memory_map_size / descriptor_size;
@@ -188,7 +187,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.writefln("Memory map too large"); console.writeln("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];
@@ -206,7 +205,6 @@ 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;
@@ -219,9 +217,6 @@ 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) &&
@@ -231,9 +226,6 @@ 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) &&
@@ -243,15 +235,8 @@ 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))
@@ -419,8 +404,6 @@ private bool map_hulk(PageTable * pt_base)
{ {
return false; return false;
} }
/* Zero BSS region. */
memset64(cast(void *)bootinfo().bss_phys, 0u, hulk_bss_size() / 8);
/* Map HULK stack. */ /* Map HULK stack. */
virt = hulk_virt_stack_top() - hulk_stack_size(); virt = hulk_virt_stack_top() - hulk_stack_size();
if (!map4kregion(virt, bootinfo().stack_phys, hulk_stack_size(), pt_base)) if (!map4kregion(virt, bootinfo().stack_phys, hulk_stack_size(), pt_base))
@@ -528,21 +511,21 @@ extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
{ {
.st = st; .st = st;
Console.clear(); console.clear();
Console.writefln("Welcome to HELLO, HOS EFI Lightweight LOader, v0.1.0"); console.writeln("Welcome to HELLO, HOS EFI Lightweight LOader, v0.1.0");
Console.writefln("Firmware vendor: '%S', version: 0x%x", st.FirmwareVendor, st.FirmwareVendor); console.writeln("Firmware vendor: '%S', version: 0x%x", st.FirmwareVendor, st.FirmwareVendor);
if (!find_acpi_xsdt()) if (!find_acpi_xsdt())
{ {
Console.writefln("Error: Could not locate ACPI XSDT"); console.writeln("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;
} }
@@ -561,8 +544,8 @@ extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
{ {
break; break;
} }
Console.writefln("ExitBootServices: Error %x", status); console.writeln("ExitBootServices: Error %x", status);
Console.wait_key(); console.wait_key();
return EFI_SUCCESS; return EFI_SUCCESS;
} }
+53 -77
View File
@@ -6,121 +6,97 @@ module hulk.acpi;
import hulk.hurl; import hulk.hurl;
import hulk.klog; import hulk.klog;
import hulk.memory; import hulk.memory;
import hulk.hurl.a1;
struct Acpi struct acpi
{ {
/** enum uint APIC_SIGNATURE = 0x43495041u;
* List of ACPI tables. enum uint XSDT_SIGNATURE = 0x54445358u;
*/
private static __gshared Header *[] tables;
enum uint XSDT_SIGNATURE = signature("XSDT"); static struct XsdtHeader
/**
* ACPI table header structure.
*/
public static struct Header
{ {
uint signature; uint signature;
uint length; uint length;
ubyte revision; ubyte revision;
ubyte checksum; ubyte checksum;
char[6] oemid; char[6] oemid;
char[8] oemtableid; ulong oemtableid;
uint oem_revision; uint oem_revision;
uint creator_id; uint creator_id;
uint creator_revision; uint creator_revision;
} }
/** static struct Xsdt
* XSDT table.
*/
static struct XSDT
{ {
Header header; XsdtHeader header;
/* Table pointers are not ulong-aligned! They begin at offset 36. */ uint _pad;
align(4) ulong[1] tables; ulong[0] tables;
} }
static assert(XSDT.tables.offsetof == 36);
/** static struct MadtHeader
* Initialize ACPI. {
*/ uint signature;
uint length;
ubyte revision;
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 length;
}
public static __gshared ulong apic_address;
public static void initialize(ulong acpi_xsdt_phys) public static void initialize(ulong acpi_xsdt_phys)
{ {
Klog.writefln("\a3Initializing ACPI");
/* Map the XSDT header. */ /* Map the XSDT header. */
map_table(acpi_xsdt_phys, PAGE_SIZE); hurl.identity_map_range(acpi_xsdt_phys, Xsdt.sizeof, 0u);
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.fatal_error("XSDT signature invalid"); klog.writef("XSDT signature invalid\n");
return; return;
} }
/* Map the entire XSDT. */ /* Map the entire XSDT. */
if (xsdt.header.length > PAGE_SIZE) hurl.identity_map_range(acpi_xsdt_phys, xsdt.header.length, 0u);
{
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;
Header ** table_pointers = cast(Header **)A1.allocate(n_entries * (Header *).sizeof);
tables = table_pointers[0..n_entries];
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];
map_table(address, PAGE_SIZE); hurl.identity_map_range(address, 4u, 0u);
Header * header = cast(Header *)address; uint signature = *cast(const(uint) *)address;
uint length = header.length; if (signature == APIC_SIGNATURE)
if (length > PAGE_SIZE)
{ {
map_table(address, length); parse_apic_table(address);
} }
tables[i] = header;
uint signature = header.signature;
Klog.writefln("Found ACPI table '%c%c%c%c' at %p",
signature & 0xFFu,
(signature >> 8u) & 0xFFu,
(signature >> 16u) & 0xFFu,
(signature >> 24u) & 0xFFu,
address);
} }
} }
/** private static parse_apic_table(ulong address)
* Get pointer to ACPI table by name.
*
* @param name
* Table name.
*/
public static Header * get_table(string name)
{ {
uint signature = signature(name); const(MadtHeader) * madt_header = cast(const(MadtHeader) *)address;
foreach (table; tables) 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)
{ {
if (table.signature == signature) madt_entry = cast(const(MadtEntry) *)(cast(size_t)madt_entry + madt_entry.length);
if (madt_entry.type == 5u)
{ {
return table; /* 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);
} }
} }
Klog.fatal_error("Could not find requested ACPI table");
return null;
}
/**
* Map an ACPI table.
*/
private static void map_table(ulong address, ulong length)
{
Hurl.identity_map_range(address, length, PT_WRITABLE | PT_DISABLE_CACHE | PT_NO_EXECUTE);
}
/**
* Convert table signature from string to 32-bit unsigned integer.
*/
private static uint signature(string s)
{
return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
} }
} }
+19 -297
View File
@@ -3,255 +3,15 @@
*/ */
module hulk.apic; module hulk.apic;
import hulk.klog;
import hulk.hurl; import hulk.hurl;
import hulk.acpi; import hulk.acpi;
import hulk.him; import hulk.idt;
import hulk.rtc;
import hulk.pit;
import hulk.klog;
import hulk.memory;
struct Apic struct apic
{ {
private enum DEBUG = false;
private enum DEFAULT_IO_APIC_ADDRESS = 0xFEC0_0000u;
private enum uint PERIODIC_MODE = 0x2_0000u; private enum uint PERIODIC_MODE = 0x2_0000u;
private enum ulong IRQ_PIT = 0u;
private enum ulong IRQ_RTC = 8u;
/**
* MADT ACPI table structure.
*/
static struct MADT
{
/**
* MADT table entry header.
*/
static struct Entry
{
enum PROCESSOR_LOCAL_APIC = 0;
enum IO_APIC = 1;
enum IO_APIC_INTERRUPT_SOURCE_OVERRIDE = 2;
enum IO_APIC_NMI_SOURCE = 3;
enum LOCAL_APIC_NMI = 4;
enum LOCAL_APIC_ADDRESS_OVERRIDE = 5;
enum PROCESSOR_LOCAL_X2APIC = 9;
ubyte type;
ubyte length;
}
/**
* Processor Local APIC.
*/
static struct Entry0
{
Entry header;
ubyte acpi_processor_id;
ubyte apic_id;
uint flags;
}
static assert(Entry0.sizeof == 8);
/**
* I/O APIC.
*/
static struct Entry1
{
Entry header;
ubyte io_apic_id;
ubyte _reserved;
uint io_apic_address;
uint global_system_interrupt_base;
}
static assert(Entry1.sizeof == 12);
/**
* IO/APIC Interrupt Source Override.
*/
static struct Entry2
{
Entry header;
ubyte bus_source;
ubyte irq_source;
uint global_system_interrupt;
ushort flags;
ushort _reserved;
}
static assert(Entry2.sizeof == 12);
/**
* IO/APIC Non-maskable interrupt source.
*/
static struct Entry3
{
Entry header;
ubyte nmi_source;
ubyte _reserved;
ushort flags;
ubyte[4] global_system_interrupt;
}
static assert(Entry3.sizeof == 10);
/**
* Local APIC Non-maskable interrupts.
*/
static struct Entry4
{
Entry header;
ubyte acpi_processor_id;
ubyte[2] flags;
ubyte lint_nr;
}
static assert(Entry4.sizeof == 6);
/**
* Local APIC Address Override.
*/
static struct Entry5
{
Entry header;
ushort _reserved;
ubyte[8] local_apic_address;
}
static assert(Entry5.sizeof == 12);
/**
* Processor Local x2APIC.
*/
static struct Entry9
{
Entry header;
ushort _reserved;
uint local_x2apic_id;
uint flags;
uint acpi_id;
}
static assert(Entry9.sizeof == 16);
Acpi.Header header;
uint local_apic_address;
uint flags;
static assert(MADT.sizeof == 0x2C);
/**
* Scan the MADT table.
*/
public void scan()
{
apic_registers = cast(ApicRegisters *)local_apic_address;
const(void) * end = cast(const(void) *)(&this) + header.length;
const(Entry) * entry = cast(const(Entry) *)(cast(ulong)&this + MADT.sizeof);
bool first_io_apic = true;
size_t n_cpus = 0u;
while (entry < end)
{
if (DEBUG)
{
Klog.writefln("MADT entry type %u, length %u", entry.type, entry.length);
}
switch (entry.type)
{
case Entry.PROCESSOR_LOCAL_APIC:
Entry0 * e = cast(Entry0 *)entry;
n_cpus++;
if (DEBUG)
{
Klog.writefln(" ACPI processor ID: %u, APIC ID: %u, Flags: 0x%x",
e.acpi_processor_id, e.apic_id, e.flags);
}
break;
case Entry.IO_APIC:
Entry1 * e = cast(Entry1 *)entry;
if (first_io_apic)
{
io_apic_registers = cast(IoApicRegisters *)e.io_apic_address;
first_io_apic = false;
}
if (DEBUG)
{
Klog.writefln(" I/O APIC ID: %u, I/O APIC Address: 0x%x, GSIB: %u",
e.io_apic_id, e.io_apic_address, e.global_system_interrupt_base);
}
break;
case Entry.IO_APIC_INTERRUPT_SOURCE_OVERRIDE:
Entry2 * e = cast(Entry2 *)entry;
if (e.global_system_interrupt < INT_APIC_COUNT)
{
irq_redirects[e.irq_source] = cast(ubyte)e.global_system_interrupt;
}
else
{
Klog.writefln("Warning: unexpected interrupt source override global system interrupt value: %u", e.global_system_interrupt);
}
if (DEBUG)
{
Klog.writefln(" Bus %u IRQ %u GSI %u Flags: 0x%x",
e.bus_source, e.irq_source, e.global_system_interrupt, e.flags);
}
break;
case Entry.IO_APIC_NMI_SOURCE:
Entry3 * e = cast(Entry3 *)entry;
if (DEBUG)
{
uint global_system_interrupt;
memcpy(&global_system_interrupt, &e.global_system_interrupt, 4u);
Klog.writefln(" NMI Source: %u, Flags: 0x%x, GSI: %u",
e.nmi_source, e.flags, global_system_interrupt);
}
break;
case Entry.LOCAL_APIC_NMI:
Entry4 * e = cast(Entry4 *)entry;
if (DEBUG)
{
ushort flags;
memcpy(&flags, &e.flags, 2u);
Klog.writefln(" ACPI Processor ID: %u, Flags: 0x%x, LINT#: %u",
e.acpi_processor_id, flags, e.lint_nr);
}
break;
case Entry.LOCAL_APIC_ADDRESS_OVERRIDE:
Entry5 * e = cast(Entry5 *)entry;
if (DEBUG)
{
memcpy(&apic_registers, &e.local_apic_address, 8u);
Klog.writefln(" Local APIC Address: 0x%x", apic_registers);
}
break;
case Entry.PROCESSOR_LOCAL_X2APIC:
Entry9 * e = cast(Entry9 *)entry;
if (DEBUG)
{
Klog.writefln(" x2APIC ID: %u, Flags: 0x%x, ACPI ID: %u",
e.local_x2apic_id, e.flags, e.acpi_id);
}
break;
default:
break;
}
/* Move to next entry. */
entry = cast(const(Entry) *)(cast(size_t)entry + entry.length);
}
if (n_cpus > 0u)
{
Klog.writefln("%u CPU(s) found", n_cpus);
}
}
}
/**
* APIC register structure.
*/
static struct ApicRegister static struct ApicRegister
{ {
public uint value; public uint value;
@@ -260,9 +20,6 @@ struct Apic
private ubyte[12] _padding; private ubyte[12] _padding;
} }
/**
* APIC registers.
*/
static struct ApicRegisters static struct ApicRegisters
{ {
ApicRegister[2] _reserved0; ApicRegister[2] _reserved0;
@@ -295,88 +52,53 @@ struct Apic
ApicRegister divide_configuration; ApicRegister divide_configuration;
} }
/**
* I/O APIC registers.
*/
static struct IoApicRegisters static struct IoApicRegisters
{ {
ApicRegister address; ApicRegister address;
ApicRegister data; ApicRegister data;
} }
alias ISRHandler = void function();
private static __gshared ApicRegisters * apic_registers; private static __gshared ApicRegisters * apic_registers;
private static __gshared IoApicRegisters * io_apic_registers; private static __gshared IoApicRegisters * io_apic_registers;
private static __gshared ISRHandler[INT_APIC_COUNT] irq_handlers;
private static __gshared ubyte[INT_APIC_COUNT] irq_redirects;
/**
* Initialize APIC.
*/
public static void initialize() public static void initialize()
{ {
Klog.writefln("\a3Initializing APIC"); apic_registers = cast(ApicRegisters *)acpi.apic_address;
io_apic_registers = cast(IoApicRegisters *)DEFAULT_IO_APIC_ADDRESS; io_apic_registers = cast(IoApicRegisters *)0xFEC0_0000u;
for (ubyte i = 0u; i < INT_APIC_COUNT; i++) hurl.map(cast(ulong)apic_registers, cast(ulong)apic_registers,
{
irq_redirects[i] = i;
}
MADT * madt = cast(MADT *)Acpi.get_table("APIC");
madt.scan();
Klog.writefln("APIC found at %p", apic_registers);
Klog.writefln("I/O APIC found at %p", io_apic_registers);
Hurl.map(cast(ulong)apic_registers, cast(ulong)apic_registers,
PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE); PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE);
Hurl.map(cast(ulong)io_apic_registers, cast(ulong)io_apic_registers, 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 = 0x100u | INT_APIC_SPURIOUS; apic_registers.spurious_interrupt_vector.value = 0x1FFu;
apic_registers.lvt_timer.value = INT_APIC_TIMER; // apic_registers.lvt_timer.value = idt.INT_LAPIC_TIMER | PERIODIC_MODE;
apic_registers.lvt_lint[0].value = INT_APIC_LINT0; apic_registers.lvt_timer.value = idt.INT_LAPIC_TIMER;
apic_registers.lvt_lint[1].value = INT_APIC_LINT1; apic_registers.lvt_lint[0].value = idt.INT_LAPIC_LINT0;
/* Enable PIT interrupt. */ apic_registers.lvt_lint[1].value = idt.INT_LAPIC_LINT1;
configure_io_apic_irq(IRQ_PIT, &Pit.isr); // apic_registers.divide_configuration.value = 3u;
/* Enable RTC interrupt. */ configure_io_apic_irq(8u, 0x48u);
configure_io_apic_irq(IRQ_RTC, &Rtc.isr);
} }
/** private static void configure_io_apic_irq(size_t io_apic_irq, size_t interrupt_id)
* Configure routing for an I/O APIC IRQ.
*/
private static void configure_io_apic_irq(size_t orig_irq, ISRHandler isr_handler)
{ {
size_t io_apic_irq = irq_redirects[orig_irq]; ulong entry = interrupt_id;
ulong entry = INT_APIC_BASE + io_apic_irq;
io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u); io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u);
io_apic_registers.data.value = entry & 0xFFFF_FFFFu; io_apic_registers.data.value = entry & 0xFFFF_FFFFu;
io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u + 1u); io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u + 1u);
io_apic_registers.data.value = entry >> 32u; io_apic_registers.data.value = entry >> 32u;
irq_handlers[io_apic_irq] = isr_handler;
} }
/**
* Signal APIC end of interrupt.
*/
public static void eoi() public static void eoi()
{ {
apic_registers.eoi.value = 0u; apic_registers.eoi.value = 0u;
} }
/**
* APIC interrupt handler.
*/
public static void isr(ulong vector) public static void isr(ulong vector)
{ {
ulong io_apic_irq = vector - INT_APIC_BASE; klog.writefln("APIC ISR 0x%x", vector);
if ((io_apic_irq < INT_APIC_COUNT) && (irq_handlers[io_apic_irq] != null))
{
irq_handlers[io_apic_irq]();
}
else
{
Klog.writefln("Unhandled APIC ISR %u", vector);
}
eoi(); eoi();
} }
} }
+21 -195
View File
@@ -5,55 +5,28 @@ 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
{ {
/** Border color. */ /** Console width in text columns. */
private enum BORDER_COLOR = 0xFF8000u;
/** Heading color. */
private enum HEADING_COLOR = 0x0066FFu;
/** Console page width in text columns. */
private static __gshared size_t m_width; private static __gshared size_t m_width;
/** Console page height in text rows. */ /** Console height in text rows. */
private static __gshared size_t m_height; private static __gshared size_t m_height;
/** Current console page. */ /** Current console cursor X position. */
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 page cursor Y position. */ /** Current console cursor Y position. */
private static __gshared size_t m_y; private static __gshared size_t m_y;
/** Number of delayed newline characters. */
private static __gshared size_t m_newlines;
/** Active console escape code. */
private static __gshared char m_escape_code;
/** Flag to indicate the previous character was an escape character. */
private static __gshared bool m_escape;
/** /**
* Initialize the console. * Initialize the console.
*/ */
public static void initialize() public static void initialize()
{ {
size_t fb_console_height = Fb.height - Kfont.line_height - 1; m_width = fb.width / kfont.advance;
m_width = (Fb.width - 1) / 2 / Kfont.advance; m_height = fb.height / kfont.line_height;
m_height = fb_console_height / Kfont.line_height;
} }
/** /**
@@ -61,100 +34,18 @@ 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();
} }
/** /**
* Write a character to the console (buffer newlines). * Write a character to the console.
* *
* @param ch Character to write. * @param ch Character to write.
*/ */
public static void write(char ch) public static void write(char ch)
{ {
if (ch == '\n')
{
render_heading_end();
m_escape_code = 0u;
m_newlines++;
}
else
{
dowrite(ch);
}
}
private static void render_heading_start()
{
if ('0' <= m_escape_code && m_escape_code <= '9' && m_x < (m_width - 1))
{
size_t heading_level = m_escape_code - ('0' - 1);
size_t nx = m_x + heading_level;
if (nx >= m_width)
{
nx = m_width - 1;
}
size_t hx = fb_x(m_page, m_x);
size_t hy = fb_y(m_y) + Kfont.line_height / 2 - 1;
size_t hwidth = (nx - m_x - 1) * Kfont.advance + Kfont.advance / 2 - 1;
Fb.rect(hx, hy, hwidth, 2, HEADING_COLOR);
Fb.rect(hx + hwidth, fb_y(m_y) + Kfont.line_height / 8,
2, Kfont.line_height * 3 / 4, HEADING_COLOR);
m_x = nx;
}
}
private static void render_heading_end()
{
if ('0' <= m_escape_code && m_escape_code <= '9' && m_x < (m_width - 1))
{
size_t heading_level = m_escape_code - ('0' - 1);
size_t nx = m_x + heading_level;
if (nx >= m_width)
{
nx = m_width - 1;
}
size_t hwidth = (nx - m_x - 1) * Kfont.advance + Kfont.advance / 2 - 1;
size_t hx = fb_x(m_page, nx) - hwidth;
size_t hy = fb_y(m_y) + Kfont.line_height / 2 - 1;
Fb.rect(hx, hy, hwidth, 2, HEADING_COLOR);
Fb.rect(hx - 2, fb_y(m_y) + Kfont.line_height / 8,
2, Kfont.line_height * 3 / 4, HEADING_COLOR);
m_x = nx;
}
}
/**
* Write a character to the console (no newline buffering).
*
* @param ch Character to write.
*/
private static void dowrite(char ch)
{
if (m_newlines != 0u)
{
size_t newlines = m_newlines;
m_newlines = 0u;
for (size_t i = 0u; i < newlines; i++)
{
dowrite('\n');
}
}
if (m_escape)
{
m_escape_code = ch;
render_heading_start();
m_escape = false;
return;
}
if (ch == '\a')
{
m_escape = true;
return;
}
if (ch == '\n') if (ch == '\n')
{ {
m_x = 0u; m_x = 0u;
@@ -162,7 +53,7 @@ struct Console
} }
else else
{ {
render_char(fb_x(m_page, m_x), fb_y(m_y), ch); render_char(m_x, m_y, ch);
m_x++; m_x++;
if (m_x == m_width) if (m_x == m_width)
{ {
@@ -171,42 +62,14 @@ struct Console
} }
} }
if (m_y == m_height) if (m_y == m_height)
{
if (m_page == 0u)
{
m_page = 1u;
m_x = 0u;
m_y = 0u;
}
else
{ {
m_y--; m_y--;
shift_rows(); fb.copy_rows_up(fb_y(m_height - 1u),
(m_height - 1u) * kfont.line_height,
kfont.line_height);
fb.rect(0u, fb_y(m_height - 1u), fb_x(m_width), kfont.line_height, 0u);
} }
} }
}
/**
* 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);
}
/** /**
* Render a character. * Render a character.
@@ -217,60 +80,23 @@ 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)
{ {
if (ch > 127) 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);
ch = 0u;
}
const(CharInfo) * ci = &Kfont.chars[ch];
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 page X position. * Get the framebuffer X coordinate corresponding to the console X position.
*/ */
private static size_t fb_x(size_t page, size_t x) private static size_t fb_x(size_t x)
{ {
return x * Kfont.advance + page * (m_width * Kfont.advance + 1); return x * kfont.advance;
} }
/** /**
* Get the framebuffer Y coordinate corresponding to the console page Y position. * Get the framebuffer Y coordinate corresponding to the console Y position.
*/ */
private static size_t fb_y(size_t y) private static size_t fb_y(size_t y)
{ {
return (y + 1) * Kfont.line_height + 1; return fb.height - ((y + 1u) * kfont.line_height);
}
/**
* 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 -17
View File
@@ -57,7 +57,6 @@ enum ulong XCR0_PKRU = 0x100u;
/** @} */ /** @} */
/** MSR register numbers. @{ */ /** MSR register numbers. @{ */
enum uint MSR_PAT = 0x277u;
enum uint MSR_EFER = 0xC000_0080u; enum uint MSR_EFER = 0xC000_0080u;
/** @} */ /** @} */
@@ -186,14 +185,6 @@ void write_cr4(ulong v)
__asm("mov $0, %cr4", "r", v); __asm("mov $0, %cr4", "r", v);
} }
ulong read_rflags()
{
return __asm!ulong(`
pushf
mov (%rsp), %rax
add $$8, %rsp`, "={rax}");
}
ulong rdmsr(uint msr) ulong rdmsr(uint msr)
{ {
return __asm!ulong(` return __asm!ulong(`
@@ -230,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);
} }
uint in32(ushort ioaddr) ulong in32(ushort ioaddr)
{ {
return __asm!uint("inl %dx, %eax", "={eax},{dx}", ioaddr); return __asm!ulong("inl %dx, %eax", "={eax},{dx}", ioaddr);
} }
void out8(ushort ioaddr, ubyte v) void out8(ushort ioaddr, ubyte v)
@@ -245,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, uint v) void out32(ushort ioaddr, ulong v)
{ {
__asm("outl %eax, %dx", "{dx},{eax}", ioaddr, v); __asm("outl %eax, %dx", "{dx},{eax}", ioaddr, v);
} }
@@ -254,8 +245,3 @@ void cpuid1(uint * ebx, uint * ecx, uint * edx)
{ {
__asm("cpuid", "=*{ebx},=*{ecx},=*{edx},{eax}", ebx, ecx, edx, 1u); __asm("cpuid", "=*{ebx},=*{ecx},=*{edx},{eax}", ebx, ecx, edx, 1u);
} }
void swint(ubyte swint_id)()
{
mixin(`__asm("int $$`, cast(int)cast(uint)swint_id, `", "");`);
}
+89 -39
View File
@@ -9,11 +9,9 @@ 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;
@@ -82,8 +80,8 @@ struct Fb
/** /**
* Draw a solid rectangle on the framebuffer. * Draw a solid rectangle on the framebuffer.
* *
* @param x X coordinate of left of rectangle. * @param x X coordinate of left side of rectangle.
* @param y Y coordinate of top of rectangle. * @param y Y coordinate of bottom side 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.
@@ -102,7 +100,7 @@ struct Fb
/** /**
* Draw a horizontal line. * Draw a horizontal line.
* *
* @param x X coordinate of left of line. * @param x X coordinate of left side 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.
@@ -118,7 +116,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 top of line. * @param y Y coordinate of bottom of line.
* @param height Height of line. * @param height Height of line.
* @param color Color of line. * @param color Color of line.
*/ */
@@ -129,17 +127,81 @@ 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 of target location. * @param x X coordinate of left side of target location.
* @param y Y coordinate of top of target location. * @param y Y coordinate of bottom side 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.
@@ -147,6 +209,7 @@ 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++)
{ {
@@ -159,7 +222,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--;
} }
} }
@@ -168,8 +231,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 of target location. * @param x X coordinate of left side of target location.
* @param y Y coordinate of top of target location. * @param y Y coordinate of bottom side 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.
@@ -183,14 +246,14 @@ struct Fb
/** /**
* Copy framebuffer rows up in the framebuffer. * Copy framebuffer rows up in the framebuffer.
* *
* @param y Y coordinate of top of region to copy up. * @param y Y coordinate of bottom side 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 - offset); size_t dest_buffer_index = buffer_index(0u, y + height + offset - 1u);
size_t src_buffer_index = buffer_index(0u, y); size_t src_buffer_index = buffer_index(0u, y + height - 1u);
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));
@@ -200,31 +263,18 @@ struct Fb
} }
/** /**
* Copy a rectangle in the framebuffer. * Scale a color value by an alpha amount.
* *
* @param sx X coordinate of left of source rectangle. * @param color Color value.
* @param sy Y coordinate of top of source rectangle. * @param alpha Alpha amount.
* @param width Width of rectangle to copy. *
* @param height Height of rectangle to copy. * @return Scaled color value.
* @param dx X coordinate of left of destination rectangle.
* @param dy Y coordinate of top of destination rectangle.
*/ */
static void copy_rect(size_t sx, size_t sy, size_t width, size_t height, private static uint scale_color(uint color, ubyte alpha)
size_t dx, size_t dy)
{ {
size_t dest_buffer_index = buffer_index(dx, dy); return ((((color & 0xFFu) * alpha) >> 8u) & 0xFFu) |
size_t src_buffer_index = buffer_index(sx, sy); ((((color & 0xFF00u) * alpha) >> 8u) & 0xFF00u) |
for (size_t y = 0u; y < height; y++) ((((color & 0xFF0000u) * alpha) >> 8u) & 0xFF0000u);
{
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;
}
} }
/** /**
@@ -237,6 +287,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 y * m_stride + x; return (m_height - y - 1u) * 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
{ {
-329
View File
@@ -1,329 +0,0 @@
/**
* HULK Interrupt Manager.
*/
module hulk.him;
import hulk.gdt;
import ldc.llvmasm;
import hulk.fb;
import hulk.console;
import hulk.klog;
import hulk.apic;
import hulk.cpu;
import hulk.kfont;
import hulk.thread;
/** Number if Interrupt Service Routines. */
private enum N_ISRS = 256;
/** Interrupt stack frame indices. */
public static enum
{
ISF_RBP,
ISF_R15,
ISF_R14,
ISF_R13,
ISF_R12,
ISF_R11,
ISF_R10,
ISF_R9,
ISF_R8,
ISF_RDI,
ISF_RSI,
ISF_RDX,
ISF_RCX,
ISF_RBX,
ISF_RAX,
ISF_ERROR_CODE,
ISF_RIP,
ISF_CS,
ISF_RFLAGS,
ISF_RSP,
ISF_SS,
ISF_COUNT,
}
private static __gshared string[ISF_COUNT] ISF_NAMES = [
"RBP",
"R15",
"R14",
"R13",
"R12",
"R11",
"R10",
"R9",
"R8",
"RDI",
"RSI",
"RDX",
"RCX",
"RBX",
"RAX",
"Error Code",
"RIP",
"CS",
"RFLAGS",
"RSP",
"SS",
];
/** Interrupt IDs. @{ */
public enum ulong INT_PAGE_FAULT = 0x0Eu;
/* The I/O APIC is configured to map IRQ 0 to interrupt 64 (0x40). */
public enum ulong INT_APIC_BASE = 0x40u; /* IRQ 0 */
public enum ulong INT_APIC_COUNT = 24u;
public enum ulong INT_APIC_TIMER = 0x70u;
public enum ulong INT_APIC_LINT0 = 0x71u;
public enum ulong INT_APIC_LINT1 = 0x72u;
public enum ulong INT_KERNEL_SWINT = 0x80u;
public enum ulong INT_APIC_SPURIOUS = 0xFFu;
/** @} */
static foreach(isr; 0 .. N_ISRS)
{
mixin(`private extern(C) void isr_`, isr, `();`);
}
private string isrs_list(int index = 0)()
{
static if (index < 256)
{
return "&isr_" ~ index.stringof ~ ",\n" ~ isrs_list!(index + 1)();
}
else
{
return "";
}
}
mixin(`private static __gshared extern(C) void function()[N_ISRS] isrs = [`,
isrs_list(),
`];`);
/** Interrupt suspend level (0 when interrupts are enabled). */
private __gshared size_t interrupt_suspend_level;
/** Interrupt nest level (0 when not in an interrupt handler). */
private __gshared size_t interrupt_level;
/**
* Suspend interrupts.
*
* This function disables interrupts. Calls can be nested and interrupts will
* be enabled only when the outermost pair of
* suspend_interrupts()/resume_interrupts() is complete.
*/
void suspend_interrupts()
{
if (interrupt_level == 0)
{
cli();
interrupt_suspend_level++;
}
}
/**
* Resume interrupts.
*
* Calls can be nested and interrupts will be enabled only when the outermost
* pair of suspend_interrupts()/resume_interrupts() is complete.
*/
void resume_interrupts()
{
if (interrupt_level == 0)
{
interrupt_suspend_level--;
if (interrupt_suspend_level == 0)
{
sti();
}
}
}
struct Him
{
/**
* IDT descriptor entry type.
*/
struct descriptor_t
{
ulong[2] descriptor;
this(T)(size_t index, ulong dpl, bool trap, T offset)
{
descriptor[0] =
0x0000_8E_00_0000_0000u |
((cast(ulong)offset & 0xFFFF_0000u) << 32u) |
(dpl << 45u) |
((trap ? 1uL : 0uL) << 40u) |
(Gdt.SELECTOR_KERNEL_CODE << 16u) |
(cast(ulong)offset & 0xFFFFu);
descriptor[1] = cast(ulong)offset >> 32u;
}
}
/**
* IDTR register format.
*/
struct idtr_t
{
ushort limit;
align(2) ulong offset;
}
static assert(idtr_t.sizeof == 10u);
private static __gshared idtr_t idtr;
private static __gshared align(0x10) descriptor_t[N_ISRS] idt;
static assert(idt.sizeof == (N_ISRS * 16u));
public static void initialize()
{
set_isrs();
idtr.limit = idt.sizeof - 1u;
idtr.offset = cast(ulong)&idt;
lidt(&idtr);
}
private static void lidt(idtr_t * idtr)
{
__asm("lidt $0", "*m", idtr);
}
private static void set_isrs()
{
static bool isr_has_error_code(size_t isr)
{
return (isr == 8) ||
((10 <= isr) && (isr <= 14)) ||
(isr == 17) ||
(isr == 21) ||
(isr == 29) ||
(isr == 30);
}
static bool isr_is_trap(size_t isr)
{
return (isr < 32u) && (isr != 2u);
}
__asm("jmp 1f", "");
static foreach(isr; 0 .. N_ISRS)
{
mixin("__asm(`isr_", isr, ":
" ~ (isr_has_error_code(isr) ? "" : "pushq $$0") ~ "
pushq %rax
mov $$", isr, ", %rax
jmp isr_common`, ``);");
}
__asm(`isr_common:
pushq %rbx
pushq %rcx
pushq %rdx
pushq %rsi
pushq %rdi
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
pushq %rbp
mov %rsp, %rbp
mov %rsp, %rsi
and $$0xFFFFFFFFFFFFFFF0, %rsp
mov %rsp, %rdx
mov %rax, %rdi
movabs $$isr, %rax
callq *%rax
mov %rax, %rsp
popq %rbp
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rdi
popq %rsi
popq %rdx
popq %rcx
popq %rbx
popq %rax
add $$8, %rsp
iretq
1:`, ``);
for (size_t isr = 0; isr < N_ISRS; isr++)
{
idt[isr] = descriptor_t(isr, 0u, isr_is_trap(isr), isrs[isr]);
}
}
}
/**
* Interrupt service routine.
*
* This function returns the stack pointer of the thread to execute when
* returning from this function.
*/
public extern(C) ulong * isr(ulong vector, ulong * stack_frame, ulong * rsp)
{
interrupt_level++;
if (interrupt_level == 1)
{
Thread.current_thread.stack_pointer = stack_frame;
}
if ((vector >= INT_APIC_BASE) && (vector < (INT_APIC_BASE + INT_APIC_COUNT)))
{
Apic.isr(vector);
}
else
{
switch (vector)
{
case INT_APIC_TIMER:
case INT_APIC_LINT0:
case INT_APIC_LINT1:
case INT_APIC_SPURIOUS:
Apic.isr(vector);
break;
case INT_KERNEL_SWINT:
Thread.handle_swint(stack_frame);
break;
default:
Fb.rect(0, 0, Fb.width, Kfont.line_height, 0xCC0000u);
Klog.writefln("\n **** Unhandled ISR %u ****", vector);
for (size_t i = 0u; i < ISF_COUNT; i++)
{
Klog.writef(ISF_NAMES[i]);
Klog.writefln(" = 0x%x", stack_frame[i]);
}
Klog.writefln("CR2 = 0x%p", read_cr2());
__asm("cli", "");
for (;;)
{
__asm("hlt", "");
}
}
}
Thread.current_thread = Thread.current_thread.list_next;
interrupt_level--;
if (interrupt_level == 0)
{
return Thread.current_thread.stack_pointer;
}
else
{
return rsp;
}
}
+4 -153
View File
@@ -7,38 +7,21 @@
*/ */
module hulk.hippo; module hulk.hippo;
import hulk.hurl; struct hippo
import hulk.klog;
struct Hippo
{ {
/** /**
* Linked list node entry for a single page. * Linked list node entry for a physical page.
*/ */
private static struct PhysicalPage private static struct PhysicalPage
{ {
PhysicalPage * next; PhysicalPage * next;
} }
/**
* Linked list node entry for a region.
*/
private static struct Region
{
ulong size;
Region * next;
}
/** /**
* Linked list of free physical pages. * Linked list of free physical pages.
*/ */
private static __gshared PhysicalPage * free_pages; private static __gshared PhysicalPage * free_pages;
/**
* Linked list of free regions.
*/
private static __gshared Region * regions;
/** /**
* Number of free physical pages. * Number of free physical pages.
*/ */
@@ -65,154 +48,22 @@ struct Hippo
public static void * allocate_page() public static void * allocate_page()
{ {
PhysicalPage * pp; PhysicalPage * pp;
if (free_pages !is null) if (free_pages != null)
{ {
pp = free_pages; pp = free_pages;
free_pages = free_pages.next; free_pages = free_pages.next;
m_n_free_pages--; m_n_free_pages--;
} }
else if (regions !is null)
{
pp = cast(PhysicalPage *)(cast(ulong)regions + regions.size - PAGE_SIZE);
regions.size -= PAGE_SIZE;
if (regions.size == 0)
{
regions = regions.next;
}
m_n_free_pages--;
}
return cast(void *)pp; return cast(void *)pp;
} }
/**
* Free a physical memory region.
*/
public static void free_region(T)(T start, ulong size)
{
if (size == PAGE_SIZE)
{
free_page(start);
return;
}
if (regions is null)
{
/* The free regions list is empty. Append this new region. */
regions = cast(Region *)start;
regions.size = size;
regions.next = null;
}
else if (size < regions.size)
{
/* The new region is smaller than the first one in the list.
* Add the new region to the beginning of the list. */
Region * new_region = cast(Region *)start;
new_region.size = size;
new_region.next = regions;
regions = new_region;
}
else
{
/* Find the last free region that is smaller than this one. */
Region * region = regions;
while (region.next !is null && region.next.size < size)
{
region = region.next;
}
/* Add the new region after this entry. */
Region * new_region = cast(Region *)start;
new_region.size = size;
new_region.next = region.next;
region.next = new_region;
}
m_n_free_pages += size >> 12;
}
/**
* Allocate a region with the given size and alignment.
*
* @param size Region size.
* @param alignment Region alignment.
*/
public static void * allocate_aligned_region(ulong size, ulong alignment)
{
Region ** regionp = &regions;
for (;;)
{
Region * region = *regionp;
if (region is null)
{
break;
}
/* The size of the region must be large enough */
if (region.size >= size)
{
/* The alignment contstraint must be satisfied. */
if ((cast(ulong)region & (alignment - 1)) == 0)
{
/* The region start address happens to already be aligned. */
if (region.size > size)
{
/* Region found is larger that needed. */
Region * new_region = cast(Region *)(cast(ulong)region + size);
new_region.size = region.size - size;
new_region.next = region.next;
*regionp = new_region;
return region;
}
else
{
/* Region happens to be the size requested. */
*regionp = region.next;
return region;
}
}
else
{
/* This region is large enough but not aligned. See if an
* aligned subregion can be found at the end. */
ulong start = (cast(ulong)region + region.size - size) & ~(alignment - 1);
if (start > cast(ulong)region)
{
/* The aligned subregion does fit in this region. */
region.size = (start - cast(ulong)region);
ulong region_end = cast(ulong)region + region.size;
if (region_end > (start + size))
{
/* There is another region following the aligned
* subregion that we must reclaim. */
free_region(start + size, region_end - (start + size));
}
return cast(void *)start;
}
}
}
regionp = &region.next;
}
return null;
}
/** /**
* Get the number of free pages. * Get the number of free pages.
* *
* @return The number of free pages. * @return The number of free pages.
*/ */
public static @property size_t n_free_pages() public @property size_t n_free_pages() const
{ {
return m_n_free_pages; return m_n_free_pages;
} }
public static void dump_regions()
{
Region * region = regions;
while (region !is null)
{
Klog.writefln("HIPPO free region: %p %uKB", region, region.size >> 10);
region = region.next;
}
}
} }
+21 -49
View File
@@ -14,19 +14,14 @@ import hulk.hurl;
import hulk.hippo; import hulk.hippo;
import hulk.pci; import hulk.pci;
import hulk.gdt; import hulk.gdt;
import hulk.him; import hulk.idt;
import hulk.cpu; import hulk.cpu;
import ldc.llvmasm; 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.rtc;
import hulk.serial;
import hulk.usb;
import hulk.pit; import hulk.pit;
import hulk.time;
import hulk.test;
import hulk.thread;
extern extern(C) __gshared ubyte _hulk_bss_size; extern extern(C) __gshared ubyte _hulk_bss_size;
@@ -34,10 +29,10 @@ extern extern(C) __gshared ubyte _hulk_bss_size;
private __gshared HulkHeader hulk_header = { private __gshared HulkHeader hulk_header = {
&_hulk_bss_size, /* hulk_bss_size */ &_hulk_bss_size, /* hulk_bss_size */
&hulk_start, /* entry */ &hulk_start, /* entry */
Thread.STACK_SIZE, /* stack_size */ 16u * 1024u, /* stack_size */
Hurl.HULK_BASE, /* virt_base */ HULK_VIRTUAL_BASE_ADDRESS, /* virt_base */
Hurl.HULK_STACK_TOP, /* virt_stack_top */ HULK_VIRTUAL_STACK_TOP_ADDRESS, /* virt_stack_top */
Hurl.HULK_FRAMEBUFFER, /* virt_fb_buffer */ HULK_VIRTUAL_FRAMEBUFFER_ADDRESS, /* virt_fb_buffer */
}; };
private void initialize_cpu() private void initialize_cpu()
@@ -60,55 +55,32 @@ private void initialize_cpu()
*/ */
void hulk_start() void hulk_start()
{ {
suspend_interrupts(); cli();
initialize_cpu(); initialize_cpu();
Serial.initialize(); gdt.initialize();
Gdt.initialize(); idt.initialize();
Him.initialize(); fb.initialize(cast(uint *)HULK_VIRTUAL_FRAMEBUFFER_ADDRESS,
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(); console.clear();
Klog.initialize(); klog.initialize();
Klog.writefln("\a5Welcome to HULK, the HOS UltraLight Kernel!"); klog.writefln("Welcome to HULK, the HOS UltraLight Kernel!");
Hurl.initialize(&hulk_header); hurl.initialize(&hulk_header);
Pic.initialize(); pci.initialize();
Acpi.initialize(hulk_header.bootinfo.acpi_xsdt_phys); pic.initialize();
Apic.initialize(); acpi.initialize(hulk_header.bootinfo.acpi_xsdt_phys);
Rtc.initialize(); apic.initialize();
Pit.initialize(); rtc.initialize();
Pci.initialize(); pit.initialize();
Thread.initialize(); sti();
resume_interrupts();
Usb.initialize();
/* Run kernel tests. */
Test.run();
Klog.writefln("\a5HULK Initialization Complete!");
// Thread.start(&th);
// Time.msleep(1);
// Time.msleep(500);
// Klog.writefln("Main thread!");
/* Idle loop. */
for (;;) for (;;)
{ {
hlt(); hlt();
} }
} }
void th()
{
for (;;)
{
Klog.writefln("Hello from thread.");
Time.msleep(1000);
}
}
+2 -6
View File
@@ -1,12 +1,12 @@
SECTIONS SECTIONS
{ {
. = 0xFFFFFFFF80000000; . = 0xFFFF800000000000;
_hulk_mem_start = .; _hulk_mem_start = .;
_hulk_header_start = .; _hulk_header_start = .;
.hulk_header : .hulk_header :
{ {
KEEP(*(.hulk_header)) *(.hulk_header)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -16,7 +16,6 @@ SECTIONS
.text : .text :
{ {
*(.text) *(.text)
*(.text.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -26,7 +25,6 @@ SECTIONS
.rodata : .rodata :
{ {
*(.rodata) *(.rodata)
*(.rodata.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -37,7 +35,6 @@ SECTIONS
.data : .data :
{ {
*(.data) *(.data)
*(.data.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
@@ -49,7 +46,6 @@ SECTIONS
{ {
*(COMMON) *(COMMON)
*(.bss) *(.bss)
*(.bss.*)
} }
. = ALIGN(4K); . = ALIGN(4K);
+47 -110
View File
@@ -3,7 +3,7 @@
* *
* HURL provides virtual memory management for HULK. * HURL provides virtual memory management for HULK.
*/ */
module hulk.hurl.hurl; module hulk.hurl;
public import hulk.pagetable; public import hulk.pagetable;
import hulk.cpu; import hulk.cpu;
@@ -13,22 +13,18 @@ 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;
struct Hurl /** HULK virtual base address. */
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_FFFF_8000_0000u;
/** HULK stack top address. */
enum ulong HULK_STACK_TOP = 0xFFFF_FFFF_9000_0000u;
/** HULK framebuffer address. */
enum ulong HULK_FRAMEBUFFER = 0xFFFF_FFFF_9000_0000u;
/** A1 allocator range base address. */
enum ulong A1_BASE = 0xFFFF_FFFF_A000_0000u;
/** /**
* Pointer to the base page table. * Pointer to the base page table.
*/ */
@@ -49,7 +45,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_BASE; ulong virt_address = HULK_VIRTUAL_BASE_ADDRESS;
/* Map HULK header region. */ /* Map HULK header region. */
map_range(virt_address, map_range(virt_address,
phys_address, phys_address,
@@ -83,15 +79,15 @@ 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_STACK_TOP - header.stack_size, map_range(HULK_VIRTUAL_STACK_TOP_ADDRESS - 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_FRAMEBUFFER, map_range(HULK_VIRTUAL_FRAMEBUFFER_ADDRESS,
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_PAT | PT_WRITABLE | PT_NO_EXECUTE); PT_WRITABLE | PT_NO_EXECUTE);
/* Map framebuffer buffer1. */ /* Map framebuffer buffer1. */
map_range(cast(ulong)header.bootinfo.fb_buffer1_phys, map_range(cast(ulong)header.bootinfo.fb_buffer1_phys,
cast(ulong)header.bootinfo.fb_buffer1_phys, cast(ulong)header.bootinfo.fb_buffer1_phys,
@@ -125,7 +121,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;
} }
@@ -156,15 +152,14 @@ 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 = round_up_power_2(header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u, PAGE_SIZE); const(size_t) fb_size = (header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u + PAGE_SIZE - 1u) & ~(PAGE_SIZE - 1u);
ulong[2][2] reserved_regions = [ 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],
]; ];
for (size_t ri = 0u; ri < reserved_regions.length; ri++) for (size_t ri = 0u; ri < reserved.length; ri++)
{ {
Klog.writefln("Reserved region %p %uKB", reserved_regions[ri][0], reserved_regions[ri][1] >> 10); reserved[ri][1] += reserved[ri][0];
reserved_regions[ri][1] += reserved_regions[ri][0];
} }
for (size_t bii = 0u; bii < header.bootinfo.memory_map_count; bii++) for (size_t bii = 0u; bii < header.bootinfo.memory_map_count; bii++)
{ {
@@ -172,20 +167,31 @@ struct Hurl
(header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Conventional)) (header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Conventional))
{ {
ulong phys = header.bootinfo.memory_map[bii].base; ulong phys = header.bootinfo.memory_map[bii].base;
ulong size = header.bootinfo.memory_map[bii].size; ulong phys_end = phys + header.bootinfo.memory_map[bii].size;
ulong phys_end = phys + size;
if (phys_end > physical_address_limit) if (phys_end > physical_address_limit)
{ {
physical_address_limit = phys_end; physical_address_limit = phys_end;
} }
usable_memory += header.bootinfo.memory_map[bii].size; usable_memory += header.bootinfo.memory_map[bii].size;
add_region(phys, size, reserved_regions); while (phys < phys_end)
{
bool is_reserved = false;
for (size_t ri = 0u; ri < reserved.length; ri++)
{
if ((reserved[ri][0] <= phys) && (phys < reserved[ri][1]))
{
is_reserved = true;
break;
}
}
if (!is_reserved)
{
hippo.free_page(phys);
}
phys += PAGE_SIZE;
}
} }
} }
/* Set PAT4 to indicate Write Combining so we can set the PAT bit in a
* page table entry to use Write Combining for that page. */
wrmsr(MSR_PAT, (rdmsr(MSR_PAT) & 0xFFFF_FF00_FFFF_FFFFu) | 0x0000_0001_0000_0000u);
/* /*
* Now that we have available physical pages to allocate from, we can * Now that we have available physical pages to allocate from, we can
@@ -207,65 +213,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);
Klog.writefln("Free pages: %u", Hippo.n_free_pages);
// Hippo.dump_regions();
} }
/** public static void map(T, U)(T virtual, U physical, ulong flags)
* Add a region of available physical memory.
*
* @param start Start address of region.
* @param size Size of region.
* @param reserved_regions List of reserved regions to exclude.
*/
private static void add_region(ulong start, ulong size, ulong[2][] reserved_regions)
{
ulong end = start + size;
bool split;
/* Skip empty regions. */
if (size == 0)
{
return;
}
for (size_t rri = 0; rri < reserved_regions.length; rri++)
{
/* Skip this region if it is entirely within a reserved region. */
if (reserved_regions[rri][0] <= start && reserved_regions[rri][1] >= end)
{
return;
}
/* Check if the region is partly reserved but the beginning is free. */
if (reserved_regions[rri][0] > start && reserved_regions[rri][0] < end)
{
split = true;
add_region(start, reserved_regions[rri][0] - start, reserved_regions);
}
/* Check if the region is partly reserved but the end is free. */
if (reserved_regions[rri][1] > start && reserved_regions[rri][1] < end)
{
split = true;
add_region(reserved_regions[rri][1], end - reserved_regions[rri][1], reserved_regions);
}
if (split)
{
return;
}
}
/* If a region makes it here, it did not overlap with any reserved
* region, so the entire region can be used. */
// Klog.writefln("HURL: freeing region %p %uKB", start, size >> 10);
Hippo.free_region(start, size);
}
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++)
@@ -292,12 +244,7 @@ struct Hurl
} }
} }
public static void map(T, U)(T virtual, U physical, ulong flags) public static void map_range(size_t virtual, size_t physical, size_t length, 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)
@@ -308,33 +255,23 @@ struct Hurl
} }
} }
public static void map_range(T, U)(T virtual, U physical, size_t length, ulong flags) public static void identity_map_range(size_t address, 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 & 0xFFFF_FFFF_FFFF_F000u; page < end; page += PAGE_SIZE) for (size_t page = address & ~0xFFFu; 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();
@@ -352,7 +289,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;
} }
-67
View File
@@ -1,67 +0,0 @@
/**
* A1 memory allocator.
*/
module hulk.hurl.a1;
import hulk.hurl;
import hulk.hippo;
import hulk.util;
import hulk.pagetable;
import hulk.memory;
/**
* 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;
}
memset64(cast(void *)address, 0, size / 8);
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);
}
}
-3
View File
@@ -1,3 +0,0 @@
module hulk.hurl;
public import hulk.hurl.hurl;
+128
View File
@@ -0,0 +1,128 @@
/**
* IDT (Interrupt Descriptor Table) functionality.
*/
module hulk.idt;
import hulk.gdt;
import ldc.llvmasm;
import hulk.fb;
import hulk.console;
import hulk.klog;
import hulk.apic;
import hulk.rtc;
struct idt
{
/* The I/O APIC is configured to map IRQ 0 to interrupt 64 (0x40). */
public static enum ulong INT_RTC = 0x48u; /* IRQ 8 */
public static enum ulong INT_LAPIC_TIMER = 0x70u;
public static enum ulong INT_LAPIC_LINT0 = 0x71u;
public static enum ulong INT_LAPIC_LINT1 = 0x72u;
struct idtr_t
{
ushort limit;
align(2) ulong offset;
}
static assert(idtr_t.sizeof == 10u);
alias descriptor_t = ulong[2];
static __gshared align(0x10) descriptor_t[256] idt;
static assert(idt.sizeof == (256u * 16u));
static __gshared idtr_t idtr;
public static void initialize()
{
set_isrs();
idtr.limit = idt.sizeof - 1u;
idtr.offset = cast(ulong)&idt;
lidt(&idtr);
}
private static void lidt(idtr_t * idtr)
{
__asm("lidt $0", "*m", idtr);
}
private static bool isr_has_error_code(size_t isr)
{
return (isr == 8) ||
((10 <= isr) && (isr <= 14)) ||
(isr == 17) ||
(isr == 21) ||
(isr == 29) ||
(isr == 30);
}
private static bool isr_is_trap(size_t isr)
{
return (isr < 32u) && (isr != 2u);
}
private static void set_isrs()
{
ulong isr_address;
static foreach(isr; 0 .. 256)
{
mixin("isr_address = __asm!ulong(`
movabs $$1f, %rax
jmp 2f
1:
pushq %rdi
pushq %rsi
pushq %rax
mov $$", isr, ", %rdi
" ~ (isr_has_error_code(isr) ? "mov 0x18(%rsp), %rsi" : "") ~ "
movabs $$isr, %rax
callq *%rax
popq %rax
popq %rsi
popq %rdi
" ~ (isr_has_error_code(isr) ? "add $$8, %rsp" : "") ~ "
iretq
2:`, `={rax}`);");
mixin("set_descriptor(", isr, ", 0u, " ~
(isr_is_trap(isr) ? "true" : "false") ~ ", isr_address);");
}
}
private static void set_descriptor(size_t index, ulong dpl, bool trap, ulong offset)
{
idt[index][0] =
0x0000_8E_00_0000_0000u |
((offset & 0xFFFF_0000u) << 32u) |
(dpl << 45u) |
((trap ? 1uL : 0uL) << 40u) |
(gdt.SELECTOR_KERNEL_CODE << 16u) |
(offset & 0xFFFFu);
idt[index][1] = offset >> 32u;
}
}
public extern(C) void isr(ulong vector, ulong arg)
{
switch (vector)
{
case idt.INT_RTC:
rtc.isr();
break;
case idt.INT_LAPIC_TIMER:
case idt.INT_LAPIC_LINT0:
case idt.INT_LAPIC_LINT1:
apic.isr(vector);
break;
default:
console.clear();
fb.clear(0xFF8000u);
klog.writefln("ISR %u, 0x%x", vector, arg);
__asm("cli", "");
for (;;)
{
__asm("hlt", "");
}
}
}
+5 -19
View File
@@ -6,9 +6,8 @@ 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.
@@ -40,9 +39,9 @@ struct Klog
*/ */
public static void writef(string s, va_list args) public static void writef(string s, va_list args)
{ {
hulk.writef.writefv(function void(ubyte ch) { hulk.writef.writef(s, args, function void(ubyte ch) {
Console.write(ch); console.write(ch);
}, s, args); });
} }
/** /**
@@ -59,7 +58,7 @@ struct Klog
} }
/** /**
* Write a formatted string and newline to the kernel log. * Write a formatted string and newline to the console.
* *
* @param s Format string. * @param s Format string.
*/ */
@@ -71,17 +70,4 @@ 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 (;;)
{
}
}
} }
-100
View File
@@ -1,100 +0,0 @@
/**
* Linked list functionality.
*/
module hulk.list;
/**
* Linked list mixin.
*/
mixin template List(T)
{
/**
* Next item in linked list.
*/
T * list_next;
/**
* Previous item in linked list.
*/
T * list_prev;
/**
* Initialize linked list fields.
*/
public void list_init()
{
list_next = &this;
list_prev = &this;
}
/**
* Add this item after another in a list.
*/
public void list_insert_after(T * item)
{
list_next = item.list_next;
list_prev = item;
item.list_next = &this;
list_next.list_prev = &this;
}
/**
* Add this item before another in a list.
*/
public void list_insert_before(T * item)
{
list_next = item;
list_prev = item.list_prev;
item.list_prev = &this;
list_prev.list_next = &this;
}
/**
* Remove this item from the list.
*/
public void list_remove()
{
list_next.list_prev = list_prev;
list_prev.list_next = list_next;
list_init();
}
/**
* Get the number of items in this list.
*/
public @property size_t list_count() const
{
size_t count = 1;
const(T) * item = &this;
while (item.list_next != &this)
{
count++;
item = item.list_next;
}
return count;
}
/**
* Allow foreach iteration through this linked list.
*/
public int opApply(scope int delegate(T *) dg)
{
T * item = &this;
T * last = item.list_prev;
for (;;)
{
T * next = item.list_next;
int result = dg(item);
if (result != 0)
{
return result;
}
if (item == last)
{
break;
}
item = next;
}
return 0;
}
}
+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);
+1 -10
View File
@@ -11,8 +11,7 @@ enum ulong PT_WRITE_THROUGH = 0x8u;
enum ulong PT_DISABLE_CACHE = 0x10u; enum ulong PT_DISABLE_CACHE = 0x10u;
enum ulong PT_ACCESSED = 0x20u; enum ulong PT_ACCESSED = 0x20u;
enum ulong PT_DIRTY = 0x40u; enum ulong PT_DIRTY = 0x40u;
enum ulong PT_PAT = 0x80u; /* Page table entry bit. */ enum ulong PT_HUGE_PAGE = 0x80u;
enum ulong PT_HUGE_PAGE = 0x80u; /* Page directory entry bit. */
enum ulong PT_GLOBAL = 0x100u; enum ulong PT_GLOBAL = 0x100u;
enum ulong PT_NO_EXECUTE = 0x8000_0000_0000_0000u; enum ulong PT_NO_EXECUTE = 0x8000_0000_0000_0000u;
/** @} */ /** @} */
@@ -184,11 +183,3 @@ 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));
}
+21 -425
View File
@@ -1,452 +1,48 @@
/**
* PCI (Peripheral Component Interconnect) functionality.
*/
module hulk.pci; 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;
import hulk.list;
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;
/** IO address for PCI configuration data. */ /* IO address for PCI configuration data. */
enum IO_ADDR_CONFIG_DATA = 0xCFCu; enum IO_ADDR_CONFIG_DATA = 0xCFCu;
/** Maximum number of devices on a bus. */ private static ulong read_config_register(ulong bus_id, ulong device_id, ulong register_id)
enum MAX_DEVICES_PER_BUS = 32;
/** Maximum number of functions on a devices. */
enum MAX_FUNCTIONS_PER_DEVICE = 8;
/** XHCI controller device type. */
enum XHCI_CONTROLLER = Type(0x0Cu, 0x03u, 0x30u);
/**
* MCFG ACPI table structure.
*/
static struct MCFG
{ {
/* PCI Segment Group Memory Area Descriptor. */ ulong addr = 0x8000_0000u | (bus_id << 16u) | (device_id << 11u) | (register_id << 2u);
static struct SGMADesc out32(IO_ADDR_CONFIG_ADDRESS, addr);
{
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);
Acpi.Header header;
uint[2] _reserved;
SGMADesc[0] sgma_descs;
size_t n_sgma_descs()
{
return (header.length - MCFG.sgma_descs.offsetof) / MCFG.SGMADesc.sizeof;
}
}
/**
* PCI device address (bus number, device number, function number).
*/
struct Address
{
uint address;
alias address this;
this(ubyte bus_nr, ubyte device_nr, ubyte function_nr)
{
this.address = (bus_nr << 16u) | (device_nr << 8u) | function_nr;
}
/**
* Bus number (0-255).
*/
public @property ubyte bus_nr() const
{
return cast(ubyte)(address >> 16u);
}
/**
* Device number (0-31).
*/
public @property ubyte device_nr() const
{
return cast(ubyte)(address >> 8u);
}
/**
* Function number (0-7).
*/
public @property ubyte function_nr() const
{
return cast(ubyte)address;
}
}
/**
* PCI device type (class ID, subclass ID, interface ID).
*/
struct Type
{
uint type;
alias type this;
this(ubyte class_id, ubyte subclass_id, ubyte interface_id)
{
this.type = (class_id << 16u) | (subclass_id << 8u) | interface_id;
}
/**
* Class ID.
*/
public @property ubyte class_id() const
{
return cast(ubyte)(type >> 16u);
}
/**
* Subclass ID.
*/
public @property ubyte subclass_id() const
{
return cast(ubyte)(type >> 8u);
}
/**
* Interface ID.
*/
public @property ubyte interface_id() const
{
return cast(ubyte)type;
}
}
/**
* PCI device object.
*/
static struct Device
{
Configuration * config;
Address address;
Type type;
bool multifunction;
Range[6] memory_ranges;
mixin List!Device;
void initialize(Address address, Configuration * config)
{
list_init();
this.config = config;
this.address = address;
type = Type(config.class_id, config.subclass_id, config.interface_id);
Klog.writefln("Found PCI device %04x:%04x (%02x:%02x:%02x) at %02u:%02u.%u",
config.vendor_id, config.device_id,
type.class_id, type.subclass_id, type.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();
}
}
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, PT_WRITABLE | flags);
memory_ranges[range_index].address = mm_region_address;
memory_ranges[range_index].length = length;
range_index++;
if (is_64bit)
{
bar_idx++;
}
}
}
}
/**
* Device list.
*/
public static __gshared Device * devices;
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 write_config_register(Address address, uint register_id, uint value) private static void scan(ulong bus_id, ulong device_id)
{ {
uint cfg_addr = 0x8000_0000u | ulong reg0 = read_config_register(bus_id, device_id, 0u);
(address.bus_nr << 16u) | if (reg0 != 0xFFFFFFFFu)
(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
{ {
static struct HeaderType0 ulong reg2 = read_config_register(bus_id, device_id, 2u);
{ klog.writefln("Found PCI device %04x:%04x (%02x:%02x:%02x) at %02u:%02u",
ushort vendor_id; (reg0 & 0xFFFFu), (reg0 >> 16u),
ushort device_id; (reg2 >> 24u) & 0xFFu,
ushort command; (reg2 >> 16u) & 0xFFu,
ushort status; (reg2 >> 8u) & 0xFFu,
ubyte revision_id; bus_id, device_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();
if (devices == null)
{
devices = device;
}
else
{
device.list_insert_before(devices);
}
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("\a3Initializing PCI"); klog.writefln("Scanning PCI devices...");
MCFG * mcfg = cast(MCFG *)Acpi.get_table("MCFG"); for (ulong bus_id = 0u; bus_id < 256u; bus_id++)
size_t n_sgma_descs = mcfg.n_sgma_descs;
for (size_t i = 0u; i < n_sgma_descs; i++)
{ {
MCFG.SGMADesc * sgma_desc = &mcfg.sgma_descs[i]; for (ulong device_id = 0u; device_id < 32u; device_id++)
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++)
{ {
for (ubyte device_nr = 0u; device_nr < MAX_DEVICES_PER_BUS; device_nr++) scan(bus_id, device_id);
{
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.");
} }
} }
+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()
{ {
+3 -6
View File
@@ -5,14 +5,12 @@ module hulk.pit;
import hulk.cpu; import hulk.cpu;
import hulk.klog; import hulk.klog;
import hulk.console;
import hulk.time;
struct Pit struct pit
{ {
/** PIT input frequency in 1/1000 Hz. */ /** PIT input frequency in 1/1000 Hz. */
private enum ulong PIT_FREQUENCY_1000HZ = 1_193_181_667u; private enum ulong PIT_FREQUENCY_1000HZ = 1_193_181_667u;
private enum ulong TARGET_INTERRUPT_HZ = 1000u; private enum ulong TARGET_INTERRUPT_HZ = 100u;
private enum ulong TARGET_INTERRUPT_1000HZ = TARGET_INTERRUPT_HZ * 1000u; 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; private enum ulong PIT_DIVIDER = (PIT_FREQUENCY_1000HZ + TARGET_INTERRUPT_1000HZ / 2u) / TARGET_INTERRUPT_1000HZ;
@@ -32,7 +30,6 @@ struct Pit
public static void initialize() public static void initialize()
{ {
Klog.writefln("\a3Initializing PIT");
out8(PORT_MC, MC_CH_0 | MC_LO_HI | MC_RATE_GENERATOR | MC_BINARY); 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 & 0xFFu);
out8(PORT_CH_0, PIT_DIVIDER >> 8u); out8(PORT_CH_0, PIT_DIVIDER >> 8u);
@@ -40,6 +37,6 @@ struct Pit
public static void isr() public static void isr()
{ {
Time.ms_isr(); klog.writefln("PIT ISR");
} }
} }
-27
View File
@@ -1,27 +0,0 @@
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;
}
}
+17 -109
View File
@@ -5,146 +5,54 @@ module hulk.rtc;
import hulk.cpu; import hulk.cpu;
import hulk.klog; import hulk.klog;
import hulk.console; import hulk.apic;
struct Rtc struct rtc
{ {
private enum ubyte PORT_SELECT = 0x70u; private enum ubyte PORT_SELECT = 0x70u;
private enum ubyte PORT_DATA = 0x71u; private enum ubyte PORT_DATA = 0x71u;
private enum ubyte DISABLE_NMI = 0x80u; private enum ubyte DISABLE_NMI = 0x80u;
private enum ubyte REG_RTC_SECONDS = 0x0u; private enum ubyte SR_A = 0xAu;
private enum ubyte REG_RTC_MINUTES = 0x2u; private enum ubyte SR_B = 0xBu;
private enum ubyte REG_RTC_HOURS = 0x4u; private enum ubyte SR_C = 0xCu;
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 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() public static void initialize()
{ {
Klog.writefln("\a3Initializing RTC");
/* Enable IRQ 8 to receive RTC interrupts. */ /* Enable IRQ 8 to receive RTC interrupts. */
ubyte sr_b = read_register(REG_SR_B, true); out8(PORT_SELECT, DISABLE_NMI | SR_B);
write_register(REG_SR_B, sr_b | SR_B_ENABLE_IRQ, true); ubyte sr_b = in8(PORT_DATA);
out8(PORT_SELECT, DISABLE_NMI | SR_B);
out8(PORT_DATA, sr_b | SR_B_ENABLE_IRQ);
/* Record RTC mode flags from status register B. */ out8(PORT_SELECT, DISABLE_NMI | SR_A);
rtc_24hour_mode = (sr_b & SR_B_24HOUR) != 0u; klog.writefln("SR_A = 0x%x", in8(PORT_DATA));
rtc_binary_mode = (sr_b & SR_B_BINARY) != 0u;
/* Send EOI to enable more RTC interrupts and re-enable NMIs. */ /* Send EOI to enable more RTC interrupts and re-enable NMIs. */
eoi(); eoi();
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);
}
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() private static void eoi()
{ {
/* Read from status register C to clear the interrupt. */ /* Read from status register C to clear the interrupt. */
read_register(REG_SR_C); out8(PORT_SELECT, SR_C);
in8(PORT_DATA);
} }
public static void isr() public static void isr()
{ {
static __gshared ulong count; static __gshared ulong count;
static __gshared ulong seconds;
count++; count++;
if ((count % 1024) == 0u) if ((count % 1024) == 0u)
{ {
Console.update_header(); seconds++;
klog.writefln("Seconds: %u", seconds);
} }
eoi(); eoi();
} apic.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
@@ -1,74 +0,0 @@
/**
* 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);
}
}
-139
View File
@@ -1,139 +0,0 @@
/**
* Kernel tests.
*/
module hulk.test;
import hulk.klog;
import hulk.list;
import hulk.time;
import hulk.hurl.a1;
import core.volatile;
struct Test
{
/**
* Run tests.
*/
public static void run()
{
Klog.writefln("\a3Running kernel tests");
test_pit();
test_list();
Klog.writefln("\a3Kernel tests complete");
}
private static void test_pit()
{
Klog.writefln("Testing PIT...");
/* Check that PIT millisecond interrupt is firing. */
size_t uptime = Time.uptime();
while (Time.uptime() <= uptime)
{
}
}
private static void test_list()
{
Klog.writefln("Testing list...");
struct S
{
size_t v;
mixin List!S;
void initialize()
{
list_init();
}
}
S * s1 = A1.allocate!S();
s1.initialize();
s1.v = 33;
assert_eq(1, s1.list_count);
S * s2 = A1.allocate!S();
s2.initialize();
s2.v = 42;
s2.list_insert_after(s1);
S * s3 = A1.allocate!S();
s3.initialize();
s3.v = 55;
s3.list_insert_before(s1);
size_t count;
assert_eq(3, s1.list_count);
assert_eq(3, s2.list_count);
assert_eq(3, s3.list_count);
foreach (entry; *s1)
{
switch (count)
{
case 0:
assert_eq(33, entry.v);
break;
case 1:
assert_eq(42, entry.v);
break;
case 2:
assert_eq(55, entry.v);
break;
case 3:
assert_eq(0, 1);
break;
default:
break;
}
count++;
}
foreach (entry; *s3)
{
if (entry.v == 33)
{
entry.list_remove();
}
}
assert_eq(2, s3.list_count);
count = 0;
foreach (entry; *s3)
{
switch (count)
{
case 0:
assert_eq(55, entry.v);
break;
case 1:
assert_eq(42, entry.v);
break;
case 2:
assert_eq(0, 1);
break;
default:
break;
}
count++;
}
foreach (entry; *s2)
{
if (entry.v == 42)
{
entry.list_remove();
}
}
assert_eq(1, s2.list_count);
assert_eq(1, s3.list_count);
}
private static void assert_eq(T)(T first, T second, size_t line_number = __LINE__)
{
if (first != second)
{
Klog.fatal_error("Assertion failed! %x != %x (line %u)", first, second, line_number);
}
}
private static void assert_neq(T)(T first, T second, size_t line_number = __LINE__)
{
if (first == second)
{
Klog.fatal_error("Assertion failed! %x == %x (line %u)", first, second, line_number);
}
}
}
-143
View File
@@ -1,143 +0,0 @@
/**
* Kernel thread functionality.
*/
module hulk.thread;
import hulk.hurl.a1;
import hulk.him;
import hulk.gdt;
import hulk.memory;
import hulk.list;
import hulk.cpu;
import hulk.hurl;
import ldc.llvmasm;
struct Thread
{
/* TODO: currently kernel thread stack size is fixed. */
enum STACK_SIZE = 16 * 1024;
enum ulong INT_TCTRL_YIELD = 0;
/** Interrupt stack template. */
static immutable __gshared ulong[ISF_COUNT] interrupt_stack_template = [
0x16161616_16161616u, /* RBP */
0x15151515_15151515u, /* R15 */
0x14141414_14141414u, /* R14 */
0x13131313_13131313u, /* R13 */
0x12121212_12121212u, /* R12 */
0x11111111_11111111u, /* R11 */
0x10101010_10101010u, /* R10 */
0x09090909_09090909u, /* R9 */
0x08080808_08080808u, /* R8 */
0x06060606_06060606u, /* RDI */
0x05050505_05050505u, /* RSI */
0x04040404_04040404u, /* RDX */
0x03030303_03030303u, /* RCX */
0x02020202_02020202u, /* RBX */
0x01010101_01010101u, /* RAX */
0u, /* Error Code */
0u, /* RIP */
Gdt.SELECTOR_KERNEL_CODE, /* CS */
0u, /* RFLAGS */
0u, /* RSP */
Gdt.SELECTOR_KERNEL_DATA, /* SS */
];
/** List of all runnable kernel threads. */
private static __gshared Thread * runnable_threads;
/** Currently executing kernel thread. */
static __gshared Thread * current_thread;
/** Current kernel thread stack pointer. */
ulong * stack_pointer;
static assert(stack_pointer.offsetof == 0);
/** Base address of kernel thread stack. */
void * stack_addr;
mixin List!Thread;
/**
* Initialize a kernel thread.
*/
public void initialize(void function() fn)
{
list_init();
suspend_interrupts();
list_insert_after(current_thread);
resume_interrupts();
stack_addr = A1.allocate(STACK_SIZE);
ulong * stack_top = cast(ulong *)(stack_addr + STACK_SIZE);
stack_pointer = stack_top - ISF_COUNT;
memcpy64(stack_pointer, &interrupt_stack_template[0], ISF_COUNT);
stack_pointer[ISF_RIP] = cast(ulong)fn;
stack_pointer[ISF_RFLAGS] = read_rflags();
stack_pointer[ISF_RSP] = cast(ulong)stack_top;
}
/**
* Initialize first kernel thread.
*/
public void initialize_first_thread()
{
list_init();
runnable_threads = &this;
stack_addr = cast(void *)(Hurl.HULK_STACK_TOP - STACK_SIZE);
}
/**
* Initialize kernel threading.
*/
public static void initialize()
{
Thread * thread = A1.allocate!Thread();
thread.initialize_first_thread();
current_thread = thread;
}
/**
* Create and start a kernel thread.
*
* TODO: fn must not return.
* TODO: threads are always runnable.
* TODO: threads cannot be destroyed.
*/
public static Thread * start(void function() fn)
{
Thread * thread = A1.allocate!Thread();
thread.initialize(fn);
swint(INT_TCTRL_YIELD);
return thread;
}
/**
* Trigger a thread management software interrupt.
*/
public static void swint(T1)(T1 v1)
{
mixin(`__asm("int $$`, cast(int)INT_KERNEL_SWINT, `", "{rax}", v1);`);
}
/**
* Handle a thread management software interrupt.
*
* This function is called in interrupt context.
*
* @param stack_frame
* Pointer to the stack frame.
*/
public static void handle_swint(ulong * stack_frame)
{
switch (stack_frame[ISF_RAX])
{
case INT_TCTRL_YIELD:
Thread.current_thread = Thread.current_thread.list_next;
break;
default:
break;
}
}
}
-42
View File
@@ -1,42 +0,0 @@
/**
* Time functionality.
*/
module hulk.time;
import hulk.volatile;
struct Time
{
/** System uptime (ms). */
private static __gshared Volatile!ulong s_uptime;
/**
* Millisecond ISR.
*/
public static void ms_isr()
{
s_uptime++;
}
/**
* Get system uptime (ms).
*/
public static @property ulong uptime()
{
return s_uptime;
}
/**
* Sleep for the given amount of time (ms).
*
* @param count
* Number of milliseconds to sleep for.
*/
public static void msleep(ulong count)
{
ulong wait_for = uptime() + count + 1;
while (uptime() < wait_for)
{
}
}
}
-3
View File
@@ -1,3 +0,0 @@
module hulk.usb;
public import hulk.usb.usb;
-24
View File
@@ -1,24 +0,0 @@
/**
* USB (Universal Serial Bus) functionality.
*/
module hulk.usb.usb;
import hulk.klog;
import hulk.usb.xhci;
import hulk.pci;
struct Usb
{
public static void initialize()
{
Klog.writefln("\a3Initializing USB");
foreach (pci_device; *Pci.devices)
{
if (pci_device.type == Pci.XHCI_CONTROLLER)
{
XHCI.build(pci_device);
}
}
}
}
-535
View File
@@ -1,535 +0,0 @@
/**
* 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;
import hulk.hurl.a1;
import hulk.hippo;
import hulk.klog;
import hulk.time;
import hulk.volatile;
struct XHCI
{
/**
* Located at PCI base address.
*
* All are read-only.
*/
struct CapabilityRegisters
{
/**
* Offset from beginning of capability registers to beginning of
* operational registers.
*/
Volatile!ubyte capability_length;
ubyte _reserved01;
/**
* Interface version number supported (psuedo-BSD).
*/
Volatile!ushort hci_version;
/**
* HCSPARAMS1.
* 7:0 number of device slots (MaxSlots)
* 18:8 number of interrupters (MaxIntrs)
* 23:19 reserved
* 31:24 number of ports (MaxPorts)
*/
Volatile!uint hcs_params1;
@property ubyte max_slots()
{
return this.hcs_params1 & 0xFFu;
}
/**
* 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)
*/
Volatile!uint hcs_params2;
@property size_t max_scratchpad_buffers()
{
uint v = this.hcs_params2;
return (v >> 27u) | ((v >> (21 - 5)) & 0x3E0u);
}
/**
* 7:0 U1 device exit latency
* 15:8 reserved
* 31:16 U2 device exit latency
*/
Volatile!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
*/
Volatile!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
*/
Volatile!uint doorbell_offset;
/**
* 4:0 reserved
* 31:5 runtime register space offset
*/
Volatile!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
*/
Volatile!uint hcc_params2;
/**
* 11:0 reserved
* 31:12 VTIO register space offset (xHCI v1.2+)
*/
Volatile!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
*/
Volatile!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
*/
Volatile!uint usb_status;
/**
* 15:0 Page Size (actual page size is 2^(x+12))
* 31:16 reserved
*/
Volatile!uint page_size;
uint[2] _reserved01;
/**
* 15:0 Notification Enable
* 31:16 reserved
*/
Volatile!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
*/
Volatile!ulong cr_ctrl;
uint[4] _reserved02;
/**
* 5:0 reserved
* 63:6 Device Context Base Address Array Pointer
*/
Volatile!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
*/
Volatile!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
*/
Volatile!uint portsc;
/**
* Port Power Management Status and Control
*/
Volatile!uint portpmsc;
/**
* Port Link Info
*/
Volatile!uint portli;
/**
* Port Hardware LPM Control (xHCI v1.1+)
*/
Volatile!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.
*/
Volatile!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
*/
Volatile!uint imr;
/**
* Interrupter Moderation.
* 15:0 Interrupt Moderation Interval
* 31:16 Interrupt Moderation Counter
*/
Volatile!uint im;
/**
* Event Ring Segment Table Size.
* 15:0 Event Ring Segment Table Size
* 31:16 reserved
*/
Volatile!uint ersts;
uint reserved;
/**
* Event Ring Segment Table Base Address.
* 5:0 reserved
* 63:6 Event Ring Segment Table Base Address
*/
Volatile!ulong erstba;
/**
* Event Ring Dequeue Pointer.
* 2:0 Dequeue ERST Segment Index
* 3 Event Handler Busy
* 63:4 Event Ring Dequeue Pointer
*/
Volatile!ulong erdp;
}
/**
* Located at offset doorbell_offset from PCI base address.
*
* The number of these is given by HCSPARAMS1.MaxSlots + 1 (for the command
* ring).
*/
struct DoorbellRegister
{
Volatile!ubyte target;
ubyte _reserved01;
Volatile!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();
if (!reset())
{
Klog.writefln("XHCI controller failed to initialize");
return;
}
/* Allocate memory for various controller data structures. */
ubyte * page = cast(ubyte *)Hippo.allocate_page();
/* Device Context Index: 2K */
void ** device_context_index = cast(void **)page;
/* Input Control Context: 64 bytes */
void * input_control_context = &page[2048];
/* Slot Context: 64 bytes */
void * slot_context = &page[2048 + 64];
/* Endpoint Context: 64 bytes */
void * endpoint_context = &page[2048 + 64 + 64];
/* Stream Context: 16 bytes */
void * stream_context = &page[2048 + 64 + 64 + 64];
/* Stream Array (Linear): 1M */
void * stream_array_linear = Hippo.allocate_aligned_region(1024 * 1024, PAGE_SIZE);
/* Stream Array (Pri/Sec): 4K */
void * stream_array_pri_sec = Hippo.allocate_page();
/* Transfer Ring Segments: 64K */
void * transfer_ring_segments = Hippo.allocate_aligned_region(64 * 1024, 64 * 1024);
/* Command Ring Segments: 64K */
void * command_ring_segments = Hippo.allocate_aligned_region(64 * 1024, 64 * 1024);
/* Event Ring Segments: 64K */
void * event_ring_segments = Hippo.allocate_aligned_region(64 * 1024, 64 * 1024);
/* Event Ring Segment Table: 512K */
void * event_ring_segment_table = Hippo.allocate_aligned_region(512 * 1024, PAGE_SIZE);
/* Scratchpad Buffer Array: 248 bytes */
void ** scratchpad_buffer_array = cast(void **)Hippo.allocate_aligned_region(2 * PAGE_SIZE, PAGE_SIZE);
size_t n_scratchpad_buffers = m_capability_registers.max_scratchpad_buffers;
for (size_t i = 0u; i < n_scratchpad_buffers; i++)
{
void * scratchpad_buffer = Hippo.allocate_page();
scratchpad_buffer_array[i] = scratchpad_buffer;
}
device_context_index[0] = scratchpad_buffer_array;
size_t max_slots = m_capability_registers.max_slots;
for (size_t i = 0u; i < max_slots; i++)
{
void * addr;
if ((i & 1) == 0)
{
page = cast(ubyte *)Hippo.allocate_page();
addr = page;
}
else
{
addr = &page[2048];
}
device_context_index[1 + i] = addr;
}
m_operational_registers.dcbaap = cast(ulong)device_context_index;
/* TODO: write cr_ctrl. */
/+ TODO:
m_operational_registers.config = m_capability_registers.hcs_params1 & 0xFu;
m_operational_registers.dn_ctrl = 1u << 1u;
+/
/* TODO: set up interrupt register set. */
Klog.writefln("XHCI controller initialization successful");
}
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;
}
}
}
private bool reset()
{
/* Clear run/stop bit. */
m_operational_registers.usb_command &= ~0x1u;
/* Wait for HCHalted bit to be set. */
enum halt_timeout = 40u;
for (size_t halt_timeout_counter = 0u;;)
{
Time.msleep(1u);
if ((m_operational_registers.usb_status & 1u) != 0u)
{
break;
}
halt_timeout_counter++;
if (halt_timeout_counter >= halt_timeout)
{
Klog.writefln("XHCI controller failed to halt");
return false;
}
}
/* Set HC reset bit. */
m_operational_registers.usb_command |= (1u << 1u);
/* Wait for controller to be ready. */
enum reset_timeout = 100u;
for (size_t reset_timeout_counter = 0u;;)
{
Time.msleep(1u);
if (((m_operational_registers.usb_command & (1u << 1u)) == 0u) &&
((m_operational_registers.usb_status & (1u << 11u)) == 0u))
{
break;
}
reset_timeout_counter++;
if (reset_timeout_counter >= reset_timeout)
{
Klog.writefln("XHCI controller failed to reset");
return false;
}
}
return true;
}
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
@@ -1,17 +0,0 @@
/**
* 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
@@ -1,3 +0,0 @@
module hulk.ver;
enum VERSION = "0.18.0";
-35
View File
@@ -1,35 +0,0 @@
module hulk.volatile;
import core.volatile;
struct Volatile(T)
{
private T value;
public @property T read()
{
return volatileLoad(&value);
}
alias read this;
public void opAssign(T value)
{
volatileStore(&this.value, value);
}
public T opUnary(string s)()
{
T v = read();
mixin(s ~ "v;");
volatileStore(&this.value, v);
return v;
}
public T opOpAssign(string s)(T rhs)
{
T v = read();
mixin("v " ~ s ~ "= rhs;");
volatileStore(&this.value, v);
return v;
}
}
+15 -55
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 writefv(ch_out_fn ch_out, string s, va_list args) size_t writef(string s, va_list args, ch_out_fn ch_out)
{ {
size_t length_written; size_t length_written;
bool escape = false; bool escape = false;
@@ -26,33 +26,18 @@ size_t writefv(ch_out_fn ch_out, string s, va_list args)
{ {
if (escape) if (escape)
{ {
if (c == 'c') if (c == 'x')
{
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, true, true, pad, width, ch_out); length_written += write_hex(v, false, 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, false, pad, width, ch_out); length_written += write_hex(v, true, pad, width, ch_out);
escape = false; escape = false;
} }
else if (c == 'u') else if (c == 'u')
@@ -90,57 +75,32 @@ size_t writefv(ch_out_fn ch_out, string s, va_list args)
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, bool pointer, char pad, size_t width, ch_out_fn ch_out) private size_t write_hex(ulong v, bool upper, 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[19] buf; char[16] buf;
size_t digit; size_t i;
size_t buf_idx;
do do
{ {
ulong n = v & 0xFu; ulong n = v & 0xFu;
buf[buf_idx] = upper ? hex_chars_upper[n] : hex_chars_lower[n]; buf[i] = upper ? hex_chars_upper[n] : hex_chars_lower[n];
v >>= 4u; v >>= 4u;
buf_idx++; i++;
digit++;
if (pointer && ((digit % 4) == 0) && (v != 0u))
{
buf[buf_idx] = '_';
buf_idx++;
}
} while (v != 0u); } while (v != 0u);
size_t length_written = buf_idx; size_t length_written = i;
while (width > buf_idx) while (width > i)
{ {
ch_out(pad); ch_out(pad);
width--; width--;
@@ -148,9 +108,9 @@ private size_t write_hex(ulong v, bool upper, bool pointer, char pad, size_t wid
} }
do do
{ {
buf_idx--; i--;
ch_out(buf[buf_idx]); ch_out(buf[i]);
} while (buf_idx != 0u); } while (i != 0u);
return length_written; return length_written;
} }
+4 -13
View File
@@ -1,8 +1,8 @@
project_name "x86_64-elf-gcc" project_name "x86_64-elf-gcc"
binutils_version = "2.41" binutils_version = "2.35"
binutils_checksum = "ae9a5789e23459e59606e6714723f2d3ffc31c03174191ef0d015bdf06007450" binutils_checksum = "1b11659fb49e20e18db460d44485f09442c8c56d5df165de9461eb09c8302f85"
gcc_version = "13.2.0" gcc_version = "10.2.0"
gcc_checksum = "e275e76442a6067341a27f04c5c6b83d8613144004c0413528863dc6b5c743da" gcc_checksum = "b8dd4368bb9c7f0b98188317ee0254dd8cc99d1e3a18d0ff146c855fe16c1d8c"
install_path = File.expand_path("x86_64-elf-gcc") install_path = File.expand_path("x86_64-elf-gcc")
target = "x86_64-elf" target = "x86_64-elf"
path_prepend "#{install_path}/bin" path_prepend "#{install_path}/bin"
@@ -21,30 +21,23 @@ 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
@@ -56,7 +49,6 @@ 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
@@ -70,7 +62,6 @@ 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"