Compare commits

..

No commits in common. "d" and "master" have entirely different histories.
d ... master

71 changed files with 1516 additions and 6379 deletions

3
.gitignore vendored
View File

@ -1,4 +1,3 @@
.rscons*
/x86_64-elf-gcc/
/i686-elf-gcc/
/build/
/qemu/

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "uefi-d"]
path = uefi-d
url = git://github.com/kubasz/uefi-d

View File

@ -1,81 +0,0 @@
# HOS System Requirements
HOS requires:
* A 64-bit CPU.
* A UEFI system.
* 128MB of RAM.
# Building HOS
From the top level of the repository, run:
./rscons
If you get errors about missing packages, install the packages and then re-run
the build command.
# Running HOS
HOS can run on a physical machine, in VirtualBox, or in QEMU.
## Physical Machine
To install HOS to an EFI partition, follow these steps:
1. Copy build/hello/HOS.EFI to EFI partition EFI/HOS.EFI. For example, from Linux:
sudo cp build/hello/HOS.EFI /boot/efi/EFI/HOS.EFI
2. Register EFI loader for HOS (one time only) with command:
efibootmgr -c -d /dev/nvme1n1 -p 2 -l '\EFI\HOS.EFI' -L HOS
(Update the -d device parameter and -p partition parameter as appropriate)
3. Set boot order to desired boot order. Exact order will be system dependent. Example:
efibootmgr -o 2,0,6,5
At this point you should be able to boot HOS from your EFI boot selection menu.
If you use GRUB, you can optionally create a GRUB boot entry to load HOS.EFI
with an entry like this:
menuentry 'HOS' {
insmod part_gpt
insmod chain
set root='(hd0,gpt1)'
chainloader /EFI/HOS.EFI
}
Just update the root setting for the correction location to your system's EFI
partition.
## VirtualBox
To create a VM in VirtualBox:
1. Build HOS ("./rscons").
2. In VirtualBox, select Machine -> New.
3. Set name to HOS.
4. Set Type to Other.
5. Set Version to Other/Unknown (64-bit).
6. Click Next.
7. Set memory size to 512 MB.
8. Click Next.
9. Select Do not add a virtual hard disk.
10. Click Create.
11. From the HOS repository, create a VirtualBox VMDK image with "./rscons mk-vmdk".
12. Move the HOS.vmdk file generated to the HOS VirtualBox folder.
13. In VirtualBox Manager, open HOS VM settings.
14. Under System, check the Enable EFI checkbox.
15. Under System, change the Chipset to ICH9.
16. Under Storage, add a hard disk and browse to select the HOS.vmdk file.
17. To run HOS, either start the VM through VirtualBox or with "./rscons run-vb".
## QEMU
Make sure QEMU is installed (possibly the `qemu-desktop` package). Then:
1. Build HOS ("./rscons").
2. Run `./rscons run` to run HOS in QEMU.

View File

@ -1,87 +1,104 @@
require "tmpdir"
project_name "HOS"
path_prepend "x86_64-elf-gcc/bin"
path_prepend "i686-elf-gcc/bin"
configure do
rscons "x86_64-elf-gcc.rb", "-b", "#{build_dir}/x86_64-elf-gcc"
check_d_compiler "ldc2", use: "ldc2"
check_c_compiler "x86_64-w64-mingw32-gcc", use: "x86_64-w64-mingw32-gcc"
check_c_compiler "x86_64-elf-gcc", use: "x86_64-elf-gcc"
check_c_compiler
rscons "i686-elf-gcc.rb", "-b", "#{build_dir}/i686-elf-gcc"
check_c_compiler "i686-elf-gcc"
check_program "genext2fs"
check_program "grub-mkstandalone"
check_program "mformat", on_fail: "Install the mtools package"
check_program "parted"
check_program "pkg-config"
check_program "xorriso"
check_cfg package: "freetype2", on_fail: "Install libfreetype-dev", use: "freetype"
sh %w[git submodule update --init]
end
# Kernel default font size.
KFONT_SIZE = 16
require "tmpdir"
# One kilobyte.
KB = 1024
# EFI (w/ GRUB) partition size (MiB)
EFI_PART_SIZE = 8
# HOS partition size (MiB)
HOS_PART_SIZE = 4
# Kernel default font size
KFONT_SIZE = 15
# One megabyte.
MB = 1024 * 1024
class HulkBinObj < Builder
class BiosImage < Builder
def run(options)
@cache.mkdir_p(File.dirname(@target))
File.write(@target, <<EOF)
.section ".rodata"
.balign 4096
.global _hulk_bin_start
_hulk_bin_start:
.incbin "#{@sources.first}"
.balign 4096
.global _hulk_bin_end
_hulk_bin_end:
unless @cache.up_to_date?(@target, nil, @sources, @env)
print_run_message("Generating BIOS boot image #{@target}", nil)
Dir.mktmpdir do |tmpdir|
# Create iso directory.
FileUtils.mkdir_p("#{tmpdir}/iso/boot/grub")
File.open("#{tmpdir}/iso/boot/grub/grub.cfg", "wb") do |fh|
fh.write(<<EOF)
set default="0"
set timeout=1
menuentry "HOS" {
insmod multiboot2
multiboot2 /hos.elf
}
EOF
unless @cache.up_to_date?(@target, nil, @sources, @env)
print_run_message("Creating HULK binary object <target>#{@target}<reset>", nil)
end
@sources.each do |source|
FileUtils.cp(source, "#{tmpdir}/iso")
end
# Build bootable GRUB image.
system(*%W[grub-mkrescue -o #{@target} #{tmpdir}/iso], err: "#{@env.build_root}/grub-mkrescue.log")
end
@cache.register_build(@target, nil, @sources, @env)
end
true
end
end
# Create a GPT disk image with an EFI partition containing the EFI image.
class Image < Builder
class EfiImage < Builder
def run(options)
unless @cache.up_to_date?(@target, nil, @sources, @env)
print_run_message("Creating disk image <target>#{@target}<reset>", nil)
efi_image_size = File.stat(@sources.first).size
efi_image_size_mb = (efi_image_size + MB - 1) / MB
partition_size_mb = efi_image_size_mb + 1
empty_mb = "\0".b * MB
File.binwrite(@target, empty_mb * partition_size_mb)
system(*%W[mformat -i #{@target} ::])
system(*%W[mmd -i #{@target} ::/EFI])
system(*%W[mmd -i #{@target} ::/EFI/BOOT])
system(*%W[mcopy -i #{@target} #{@sources.first} ::/EFI/BOOT/BOOTX64.EFI])
partition_contents = File.binread(@target)
disk_image = empty_mb + partition_contents + empty_mb
File.binwrite(@target, disk_image)
system(*%W[parted --script #{@target} mklabel gpt mkpart HOS fat32 1MiB #{partition_size_mb + 1}MiB])
print_run_message("Generating EFI boot image #{@target}", nil)
Dir.mktmpdir do |tmpdir|
# Build a standalone GRUB.
File.open("#{tmpdir}/grub.cfg", "wb") do |fh|
fh.write(<<EOF)
insmod part_gpt
configfile (hd0,gpt2)/grub.cfg
EOF
end
system(*%W[grub-mkstandalone -O x86_64-efi -o #{tmpdir}/BOOTX64.EFI boot/grub/grub.cfg=#{tmpdir}/grub.cfg])
# Create EFI partition.
system(*%W[dd if=/dev/zero of=#{tmpdir}/efi.part bs=1M count=#{EFI_PART_SIZE}], err: "/dev/null")
system(*%W[mformat -i #{tmpdir}/efi.part ::])
system(*%W[mmd -i #{tmpdir}/efi.part ::/EFI])
system(*%W[mmd -i #{tmpdir}/efi.part ::/EFI/BOOT])
system(*%W[mcopy -i #{tmpdir}/efi.part #{tmpdir}/BOOTX64.EFI ::/EFI/BOOT])
# Create ext2 HOS partition.
FileUtils.mkdir_p("#{tmpdir}/ext2")
@sources.each do |source|
FileUtils.cp(source, "#{tmpdir}/ext2")
end
File.open("#{tmpdir}/ext2/grub.cfg", "wb") do |fh|
fh.write(<<EOF)
set default="0"
set timeout=1
menuentry "HOS" {
insmod part_gpt
insmod multiboot2
set root=(hd0,gpt2)
multiboot2 /hos.elf
}
EOF
end
system(*%W[genext2fs -b #{HOS_PART_SIZE * 1024} -d #{tmpdir}/ext2 #{tmpdir}/ext2.part])
# Create full disk image.
system(*%W[dd if=/dev/zero of=#{@target} bs=1M count=#{EFI_PART_SIZE + HOS_PART_SIZE + 2}], err: "/dev/null")
system(*%W[parted -s #{@target} mklabel gpt])
system(*%W[parted -s #{@target} mkpart efi 1MiB #{EFI_PART_SIZE + 1}MiB])
system(*%W[parted -s #{@target} mkpart hos #{EFI_PART_SIZE + 1}MiB #{EFI_PART_SIZE + HOS_PART_SIZE + 1}MiB])
system(*%W[dd if=#{tmpdir}/efi.part of=#{@target} bs=1M seek=1 conv=notrunc], err: "/dev/null")
system(*%W[dd if=#{tmpdir}/ext2.part of=#{@target} bs=1M seek=#{1 + EFI_PART_SIZE} conv=notrunc], err: "/dev/null")
end
@cache.register_build(@target, nil, @sources, @env)
end
true
end
end
class CheckThreadLocal < Builder
def run(options)
map_file = File.binread(@sources.first)
if map_file =~ /\.(tdata|tbss)\b/
$stderr.puts "Error: found thread-local data in #{@sources.first}"
false
else
true
end
end
end
class FontGen < Builder
def run(options)
if @command
@ -95,154 +112,55 @@ class FontGen < Builder
end
end
class Size < Builder
def run(options)
if @command
finalize_command
else
@vars["_SOURCES"] = @sources
@vars["_TARGET"] = @target
command = @env.build_command(%w[${SIZE} ${_SOURCES}], @vars)
standard_command("Size <target>#{@target}<reset>", command, stdout: @target)
end
end
end
# FontGen Environment
fontgen_env = env "fontgen", use: "freetype" do |env|
env.Program("^/fontgen", glob("src/fontgen/**/*.c"))
env["CC"] = "gcc"
env.Program("^/fontgen.bin", glob("fontgen/**/*.c"))
end
hulk_env = env "hulk", use: %w[ldc2 x86_64-elf-gcc] do |env|
# Kernel Environment
kernel_env = env "kernel" do |env|
env.add_builder(EfiImage)
env.add_builder(BiosImage)
env.add_builder(FontGen)
env.add_builder(CheckThreadLocal)
env.FontGen("^/src/hulk/kfont.d", "font/Hack-Regular.ttf",
"fontgen" => fontgen_env.expand("^/fontgen"))
env["sources"] = glob("src/hulk/**/*.d")
env["sources"] << "^/src/hulk/kfont.d"
cpu_attrs = %w[
-avx
-avx2
-avx512bf16
-avx512bitalg
-avx512bw
-avx512cd
-avx512dq
-avx512f
-avx512ifma
-avx512vbmi
-avx512vbmi2
-avx512vl
-avx512vnni
-avx512vp2intersect
-avx512vpopcntdq
-sse
-sse-unaligned-mem
-sse2
-sse3
-sse4.1
-sse4.2
-sse4a
-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["D_IMPORT_PATH"] += %w[src]
env["D_IMPORT_PATH"] << env.expand("^/src")
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["LDCMD"] = %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}]
env["OBJDUMP"] = "x86_64-elf-objdump"
env["OBJCOPY"] = "x86_64-elf-objcopy"
env.Program("^/hulk.elf", "${sources}")
env.produces("^/hulk.elf", "^/hulk.elf.map")
env.CheckThreadLocal(:hulk_map_check, "^/hulk.elf.map")
env.depends("^/hulk.elf", "src/hulk/hulk.ld")
env["SIZE"] = "x86_64-elf-size"
env.Size("^/hulk.elf.size", "^/hulk.elf")
env.Disassemble("^/hulk.elf.txt", "^/hulk.elf")
env.Command("^/hulk.bin", "^/hulk.elf",
"CMD" => %W[${OBJCOPY} -O binary ${_SOURCES} ${_TARGET}],
"CMD_DESC" => "Convert ELF to binary:")
env.add_build_hook do |builder|
if builder.target.end_with?(".o")
env.Disassemble("#{builder.target}.txt", builder.target)
end
end
end
hello_env = env "hello", use: %w[ldc2 x86_64-w64-mingw32-gcc] do |env|
env.add_builder(Image)
env.add_builder(CheckThreadLocal)
env.add_builder(HulkBinObj)
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.HulkBinObj("^/hulk_bin.S", hulk_env.expand("^/hulk.bin"))
env.Object("^/hulk_bin.o", "^/hulk_bin.S")
env.depends("^/hulk_bin.o", hulk_env.expand("^/hulk.bin"))
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["D_IMPORT_PATH"] += %w[src uefi-d/source]
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["LDCMD"] = %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}]
env["OBJDUMP"] = "x86_64-w64-mingw32-objdump"
env.Program("^/HOS.EFI", "${sources}")
env.produces("^/HOS.EFI", "^/HOS.EFI.map")
env.CheckThreadLocal(:hos_map_check, "^/HOS.EFI.map")
env["SIZE"] = "x86_64-w64-mingw32-size"
env.Size("^/HOS.size", "^/HOS.EFI")
env.Disassemble("^/HOS.txt", "^/HOS.EFI")
env.Image("^/HOS.img", "^/HOS.EFI")
env.add_builder(Size)
env["OBJDUMP"] = "i686-elf-objdump"
env["SIZE"] = "i686-elf-size"
env["CCFLAGS"] += %w[-ffreestanding -Wall -O2]
env["LDFLAGS"] += %w[-ffreestanding -nostdlib -T src/link.ld]
env["LDFLAGS"] += %W[-Wl,-Map,${_TARGET}.map]
env["LIBS"] += %w[gcc]
env.FontGen("^/kfont/kfont.c", "font/Hack-Regular.ttf",
"fontgen" => fontgen_env.expand("^/fontgen.bin"))
env.barrier
env["CPPPATH"] += ["#{env.build_root}/kfont"]
env.Program("^/hos.elf", glob("src/**/*.{S,c}") + ["^/kfont/kfont.c"])
env.depends("#{env.build_root}/hos.elf", "src/link.ld")
env.Disassemble("^/hos.elf.txt", "^/hos.elf")
env.Size("^/hos.elf.size", "^/hos.elf")
env.EfiImage("^/hos-efi.img", %w[^/hos.elf])
env.BiosImage("^/hos.img", %w[^/hos.elf])
end
task "run", desc: "Run HOS in QEMU" do
FileUtils.rm_rf("qemu")
FileUtils.mkdir_p("qemu")
img = hello_env.expand("^/HOS.img")
FileUtils.cp(img, "qemu")
ovmf = "OVMF.fd"
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
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]
img = kernel_env.expand("^/hos.img")
sh %W[qemu-system-x86_64 -hda #{img}]
end
# See README.md for how to set up VirtualBox for HOS.
task "mk-vmdk", desc: "Create VirtualBox VMDK virtual drive for HOS" do
path = File.expand_path(hello_env.expand("^/HOS.img"))
sectors = File.stat(path).size / 512
File.binwrite("HOS.vmdk", <<EOF)
# Disk DescriptorFile
version=1
CID=1d1c3615
parentCID=ffffffff
createType="fullDevice"
# Extent description
RW #{sectors} FLAT "#{path}" 0
# The disk Data Base
#DDB
ddb.virtualHWVersion = "4"
ddb.adapterType="ide"
ddb.geometry.cylinders="8"
ddb.geometry.heads="16"
ddb.geometry.sectors="63"
ddb.uuid.image="03783b5a-7587-4d8b-ad04-9d70f70a94c2"
ddb.uuid.parent="00000000-0000-0000-0000-000000000000"
ddb.uuid.modification="00000000-0000-0000-0000-000000000000"
ddb.uuid.parentmodification="00000000-0000-0000-0000-000000000000"
ddb.geometry.biosCylinders="8"
ddb.geometry.biosHeads="16"
ddb.geometry.biosSectors="63"
EOF
#sh %W[VBoxManage createmedium disk --filename=HOS.vmdk --variant=RawDisk --format=VMDK --property RawDrive=#{File.expand_path(hello_env.expand("^/HOS.img"))}]
end
# See README.md for how to set up VirtualBox for HOS.
task "run-vb", desc: "Run HOS in VirtualBox" do
sh %W[VBoxManage startvm HOS]
task "run-efi", desc: "Run HOS EFI in QEMU" do
img = kernel_env.expand("^/hos-efi.img")
sh %W[qemu-system-x86_64 -bios OVMF.fd -hda #{img}]
end

224
fontgen/fontgen.c Normal file
View File

@ -0,0 +1,224 @@
#include <stdlib.h>
#include <stdint.h>
#include <ft2build.h>
#include <stdio.h>
#include FT_FREETYPE_H
#include <string.h>
#include <strings.h>
#include <ctype.h>
#define N_CHARS 128
#define round_up_26_6(val) (((val) + 63) >> 6u)
int max_advance;
int max_top = -9999;
int min_bottom = 9999;
int line_height;
int baseline_offset;
typedef struct {
int width;
int height;
int top;
int left;
uint8_t * bitmap;
} char_info_t;
static char_info_t char_infos[N_CHARS];
static void load_char(FT_Face face, int char_code)
{
if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0)
{
return;
}
int advance = round_up_26_6(face->glyph->advance.x);
if (advance > max_advance)
{
max_advance = advance;
}
if ((face->glyph->bitmap.width == 0) ||
(face->glyph->bitmap.rows == 0))
{
return;
}
char_infos[char_code].width = face->glyph->bitmap.width;
char_infos[char_code].height = face->glyph->bitmap.rows;
char_infos[char_code].top = face->glyph->bitmap_top;
if (char_infos[char_code].top > max_top)
{
max_top = char_infos[char_code].top;
}
int bottom = char_infos[char_code].top - char_infos[char_code].height;
if (bottom < min_bottom)
{
min_bottom = bottom;
}
char_infos[char_code].left = face->glyph->bitmap_left;
char_infos[char_code].bitmap = malloc(char_infos[char_code].width * char_infos[char_code].height);
memcpy(char_infos[char_code].bitmap,
face->glyph->bitmap.buffer,
char_infos[char_code].width * char_infos[char_code].height);
}
static const char * bare_header_name(const char * h_file_name)
{
const char * p = rindex(h_file_name, '/');
if (p == NULL)
{
p = h_file_name;
}
else
{
p++;
}
return p;
}
static char * include_guard_name(const char * h_file_name)
{
const char * p = bare_header_name(h_file_name);
char * guard_name = malloc(strlen(p) + 1);
strcpy(guard_name, p);
char * m = guard_name;
while (*m != '\0')
{
if ('a' <= *m && *m <= 'z')
{
*m = toupper(*m);
}
else if (('0' <= *m && *m <= '9') || ('A' <= *m && *m <= 'Z'))
{
/* no change */
}
else
{
*m = '_';
}
m++;
}
return guard_name;
}
static void generate_bytes(FILE * file, const uint8_t * bytes, int count)
{
for (int i = 0; i < count; i++)
{
if (i % 8 == 0)
{
fprintf(file, " ");
}
fprintf(file, "0x%02xu,", bytes[i]);
if ((i + 1) % 8 == 0)
{
fprintf(file, "\n");
}
else if (i < (count - 1))
{
fprintf(file, " ");
}
}
if (count % 8 != 0)
{
fprintf(file, "\n");
}
}
static void generate(const char * c_file_name)
{
char * h_file_name = malloc(strlen(c_file_name) + 1);
strcpy(h_file_name, c_file_name);
h_file_name[strlen(h_file_name) - 1] = 'h';
char * guard = include_guard_name(h_file_name);
FILE * h_file = fopen(h_file_name, "wb");
fprintf(h_file, "#ifndef %s\n", guard);
fprintf(h_file, "#define %s\n\n", guard);
fprintf(h_file, "#include <stdint.h>\n");
fprintf(h_file, "typedef struct {\n int width;\n int height;\n int top;\n int left;\n const uint8_t * bitmap;\n} fontgen_char_info_t;\n");
fprintf(h_file, "typedef struct {\n int line_height;\n int advance;\n int baseline_offset;\n const fontgen_char_info_t ** char_infos;\n} fontgen_font_t;\n");
fprintf(h_file, "extern const fontgen_font_t kfont;\n");
fprintf(h_file, "#endif\n");
fclose(h_file);
FILE * c_file = fopen(c_file_name, "wb");
fprintf(c_file, "#include \"%s\"\n", bare_header_name(h_file_name));
fprintf(c_file, "#include <stddef.h>\n");
for (int i = 0; i < N_CHARS; i++)
{
if (char_infos[i].width > 0)
{
fprintf(c_file, "static const uint8_t char_bitmap_%d[] = {\n", i);
generate_bytes(c_file, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height);
fprintf(c_file, "};\n");
}
fprintf(c_file, "static const fontgen_char_info_t char_%d = {\n", i);
fprintf(c_file, " %d,\n", char_infos[i].width);
fprintf(c_file, " %d,\n", char_infos[i].height);
fprintf(c_file, " %d,\n", char_infos[i].top);
fprintf(c_file, " %d,\n", char_infos[i].left);
if (char_infos[i].width > 0)
{
fprintf(c_file, " char_bitmap_%d,\n", i);
}
else
{
fprintf(c_file, " NULL,\n");
}
fprintf(c_file, "};\n\n");
}
fprintf(c_file, "const fontgen_char_info_t * char_infos[] = {\n");
for (int i = 0; i < N_CHARS; i++)
{
fprintf(c_file, " &char_%d,\n", i);
}
fprintf(c_file, "};\n");
fprintf(c_file, "const fontgen_font_t kfont = {\n");
fprintf(c_file, " %d,\n", line_height);
fprintf(c_file, " %d,\n", max_advance);
fprintf(c_file, " %d,\n", baseline_offset);
fprintf(c_file, " char_infos,\n");
fprintf(c_file, "};\n");
fclose(c_file);
}
int main(int argc, char * argv[])
{
/* Expect: font file, size, out file */
if (argc != 4)
{
fprintf(stderr, "Incorrect arguments\n");
return 1;
}
const char * font_file = argv[1];
int size = atoi(argv[2]);
const char * out_file = argv[3];
FT_Library ft_library;
if (FT_Init_FreeType(&ft_library) != 0)
{
fprintf(stderr, "Could not initialize freetype\n");
return 2;
}
FT_Face face;
if (FT_New_Face(ft_library, font_file, 0, &face) != 0)
{
fprintf(stderr, "Could not open %s\n", font_file);
return 3;
}
FT_Set_Pixel_Sizes(face, 0, size);
for (int i = 0; i < N_CHARS; i++)
{
load_char(face, i);
}
line_height = round_up_26_6(face->size->metrics.height);
baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom;
generate(out_file);
return 0;
}

View File

@ -1,10 +1,9 @@
project_name "x86_64-elf-gcc"
binutils_version = "2.41"
binutils_checksum = "ae9a5789e23459e59606e6714723f2d3ffc31c03174191ef0d015bdf06007450"
gcc_version = "13.2.0"
gcc_checksum = "e275e76442a6067341a27f04c5c6b83d8613144004c0413528863dc6b5c743da"
install_path = File.expand_path("x86_64-elf-gcc")
target = "x86_64-elf"
binutils_version = "2.35"
binutils_checksum = "1b11659fb49e20e18db460d44485f09442c8c56d5df165de9461eb09c8302f85"
gcc_version = "10.2.0"
gcc_checksum = "b8dd4368bb9c7f0b98188317ee0254dd8cc99d1e3a18d0ff146c855fe16c1d8c"
install_path = File.expand_path("i686-elf-gcc")
target = "i686-elf"
path_prepend "#{install_path}/bin"
configure do
@ -21,30 +20,23 @@ end
default do
unless Dir.exist?(install_path)
puts "Building cross compiler toolchain..."
# Download archives.
puts "Downloading binutils..."
download "https://ftp.gnu.org/gnu/binutils/binutils-#{binutils_version}.tar.xz",
"#{build_dir}/binutils-#{binutils_version}.tar.xz",
sha256sum: binutils_checksum
puts "Downloading gcc..."
download "https://ftp.gnu.org/gnu/gcc/gcc-#{gcc_version}/gcc-#{gcc_version}.tar.xz",
"#{build_dir}/gcc-#{gcc_version}.tar.xz",
sha256sum: gcc_checksum
# Extract archives.
puts "Extracting binutils..."
sh "tar", "xJf", "binutils-#{binutils_version}.tar.xz",
chdir: build_dir
puts "Extracting gcc..."
sh "tar", "xJf", "gcc-#{gcc_version}.tar.xz",
chdir: build_dir
# Build binutils.
puts "Building binutils..."
rm_rf "#{build_dir}/build-binutils"
mkdir_p "#{build_dir}/build-binutils"
cd "#{build_dir}/build-binutils" do
@ -56,13 +48,12 @@ default do
end
# Build gcc.
puts "Building gcc..."
rm_rf "#{build_dir}/build-gcc"
mkdir_p "#{build_dir}/build-gcc"
cd "#{build_dir}/build-gcc" do
sh %W[../gcc-#{gcc_version}/configure
--target=#{target} --prefix=#{install_path} --disable-nls
--enable-languages=c --without-headers]
--enable-languages=c,c++ --without-headers]
sh "make all-gcc"
sh "make all-target-libgcc"
sh "make install-gcc"
@ -70,7 +61,6 @@ default do
end
# Remove archives and build directories if everything succeeded.
puts "Cleaning up..."
rm_f "#{build_dir}/binutils-#{binutils_version}.tar.xz"
rm_rf "#{build_dir}/binutils-#{binutils_version}"
rm_rf "#{build_dir}/build-binutils"

6
rscons

File diff suppressed because one or more lines are too long

18
src/boot.S Normal file
View File

@ -0,0 +1,18 @@
.global hos_start
.type hos_start, @function
hos_start:
/* Set stack pointer. */
mov $_stack_end, %esp
/* Jump to C. */
push $0
push $0
push $0
push %ebx
call hos_main
cli
1: hlt
jmp 1b
.size hos_start, . - hos_start

116
src/fb.c Normal file
View File

@ -0,0 +1,116 @@
#include "fb.h"
#include <stddef.h>
#include "mem.h"
static struct {
uint32_t * addr;
uint32_t width;
uint32_t height;
uint32_t pitch;
} fb;
static inline uint32_t build_pixel(uint8_t r, uint8_t g, uint8_t b)
{
return (r << 16u) | (g << 8u) | b;
}
static inline void fb_set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b)
{
fb.addr[fb.pitch * y + x] = build_pixel(r, g, b);
}
void fb_clear(void)
{
memset32(fb.addr, 0u, fb.pitch * fb.height);
}
void fb_init(uint32_t * addr, uint32_t width, uint32_t height, uint32_t pitch)
{
fb.addr = addr;
fb.width = width;
fb.height = height;
fb.pitch = pitch / 4u;
fb_clear();
}
uint32_t * fb_addr(void)
{
return fb.addr;
}
bool fb_ready(void)
{
return fb.addr != NULL;
}
uint32_t fb_width(void)
{
return fb.width;
}
uint32_t fb_height(void)
{
return fb.height;
}
void fb_blend_alpha8(const uint8_t * bitmap, int width, int height, int pitch, int x, int y, uint8_t r, uint8_t g, uint8_t b)
{
if (((x + width) <= 0) || (x >= (int)fb.width) || ((y + height) <= 0) || (y >= (int)fb.height))
{
return;
}
if (x < 0)
{
width += x;
bitmap += (-x);
x = 0;
}
if (y < 0)
{
height += y;
bitmap += ((-y) * pitch);
y = 0;
}
if ((x + width) > (int)fb.width)
{
width = (int)fb.width - x;
}
if ((y + height) > (int)fb.height)
{
height = (int)fb.height - y;
}
uint32_t * target = &fb.addr[fb.pitch * y + x];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
uint32_t alpha = bitmap[col];
uint32_t current_pixel = target[col];
uint8_t cr = (current_pixel >> 16u) & 0xFFu;
uint8_t cg = (current_pixel >> 8u) & 0xFFu;
uint8_t cb = current_pixel & 0xFFu;
uint8_t pr = alpha * r / 255u;
uint8_t pg = alpha * g / 255u;
uint8_t pb = alpha * b / 255u;
uint32_t current_alpha = 255u - alpha;
uint32_t pixel = build_pixel(
pr + current_alpha * cr / 255u,
pg + current_alpha * cg / 255u,
pb + current_alpha * cb / 255u);
target[col] = pixel;
}
bitmap += pitch;
target += fb.pitch;
}
}
void fb_fill(int x, int y, int width, int height, uint8_t r, uint8_t g, uint8_t b)
{
uint32_t * target = &fb.addr[fb.pitch * y + x];
uint32_t pixel = build_pixel(r, g, b);
for (int row = 0; row < height; row++)
{
memset32(target, pixel, width);
target += fb.pitch;
}
}

16
src/fb.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef FB_H
#define FB_H
#include <stdint.h>
#include <stdbool.h>
void fb_init(uint32_t * addr, uint32_t width, uint32_t height, uint32_t pitch);
bool fb_ready(void);
uint32_t * fb_addr(void);
uint32_t fb_width(void);
uint32_t fb_height(void);
void fb_blend_alpha8(const uint8_t * bitmap, int width, int height, int pitch, int x, int y, uint8_t r, uint8_t g, uint8_t b);
void fb_fill(int x, int y, int width, int height, uint8_t r, uint8_t g, uint8_t b);
void fb_clear(void);
#endif

11
src/fb_text.c Normal file
View File

@ -0,0 +1,11 @@
#include "fb_text.h"
#include "kfont.h"
#include "fb.h"
void fb_text_render_char(int c, int x, int y, uint8_t r, uint8_t g, uint8_t b)
{
const fontgen_char_info_t * char_info = kfont.char_infos[c];
y += kfont.line_height - kfont.baseline_offset - char_info->top;
x += char_info->left;
fb_blend_alpha8(char_info->bitmap, char_info->width, char_info->height, char_info->width, x, y, r, g, b);
}

8
src/fb_text.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef FB_TEXT_H
#define FB_TEXT_H
#include <stdint.h>
void fb_text_render_char(int c, int x, int y, uint8_t r, uint8_t g, uint8_t b);
#endif

View File

@ -1,235 +0,0 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <ft2build.h>
#include <stdio.h>
#include FT_FREETYPE_H
#include <string.h>
#include <strings.h>
#include <ctype.h>
#define N_CHARS 128
#define round_up_26_6(val) (((val) + 63) >> 6u)
int max_advance;
int max_top = -9999;
int min_bottom = 9999;
int line_height;
int baseline_offset;
typedef struct {
int width;
int height;
int top;
int left;
uint8_t * bitmap;
} char_info_t;
static char_info_t char_infos[N_CHARS];
static void load_char(FT_Face face, int char_code)
{
if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0)
{
return;
}
int advance = round_up_26_6(face->glyph->advance.x);
if (advance > max_advance)
{
max_advance = advance;
}
if ((face->glyph->bitmap.width == 0) ||
(face->glyph->bitmap.rows == 0))
{
return;
}
char_infos[char_code].width = face->glyph->bitmap.width;
char_infos[char_code].height = face->glyph->bitmap.rows;
char_infos[char_code].top = face->glyph->bitmap_top;
if (char_infos[char_code].top > max_top)
{
max_top = char_infos[char_code].top;
}
int bottom = char_infos[char_code].top - char_infos[char_code].height;
if (bottom < min_bottom)
{
min_bottom = bottom;
}
char_infos[char_code].left = face->glyph->bitmap_left;
char_infos[char_code].bitmap = malloc(char_infos[char_code].width * char_infos[char_code].height);
memcpy(char_infos[char_code].bitmap,
face->glyph->bitmap.buffer,
char_infos[char_code].width * char_infos[char_code].height);
}
static void generate_bytes(FILE * file, const uint8_t * bytes, int count)
{
for (int i = 0; i < count; i++)
{
if (i % 8 == 0)
{
fprintf(file, " ");
}
fprintf(file, "0x%02xu,", bytes[i]);
if ((i + 1) % 8 == 0)
{
fprintf(file, "\n");
}
else if (i < (count - 1))
{
fprintf(file, " ");
}
}
if (count % 8 != 0)
{
fprintf(file, "\n");
}
}
static bool generate(const char * d_file_name)
{
FILE * fh = fopen(d_file_name, "wb");
fprintf(fh, "module hulk.kfont;\n");
fprintf(fh, "struct CharInfo {\n");
fprintf(fh, " /** Glyph bitmap width. */\n");
fprintf(fh, " ubyte width;\n");
fprintf(fh, " /** Glyph bitmap height. */\n");
fprintf(fh, " ubyte height;\n");
fprintf(fh, " /** Glyph bitmap top offset (descent from top of text row). */\n");
fprintf(fh, " ubyte top;\n");
fprintf(fh, " /** Glyph bitmap left offset. */\n");
fprintf(fh, " ubyte left;\n");
fprintf(fh, " /** Glyph bitmap. */\n");
fprintf(fh, " const ubyte[] bitmap;\n");
fprintf(fh, "};\n");
fprintf(fh, "struct FontInfo {\n");
fprintf(fh, " /** Font line height. */\n");
fprintf(fh, " ubyte line_height;\n");
fprintf(fh, " /** Font advance. */\n");
fprintf(fh, " ubyte advance;\n");
fprintf(fh, " /** Characters. */\n");
fprintf(fh, " const CharInfo[] chars;\n");
fprintf(fh, "};\n");
fprintf(fh, "/** Kernel font data. */\n");
fprintf(fh, "__gshared const FontInfo Kfont = {\n");
fprintf(fh, " %du,\n", line_height);
fprintf(fh, " %du,\n", max_advance);
fprintf(fh, " [\n");
for (int i = 0; i < N_CHARS; i++)
{
fprintf(fh, " /* %d */", i);
fprintf(fh, " CharInfo(\n");
fprintf(fh, " %du /* width */,\n", char_infos[i].width);
fprintf(fh, " %du /* height */,\n", char_infos[i].height);
fprintf(fh, " %du /* top */,\n", char_infos[i].top);
fprintf(fh, " %du /* left */,\n", char_infos[i].left);
if (char_infos[i].width > 0)
{
fprintf(fh, " [\n");
generate_bytes(fh, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height);
fprintf(fh, " ]\n");
}
else
{
fprintf(fh, " []\n");
}
fprintf(fh, " ),\n");
}
fprintf(fh, " ],\n");
fprintf(fh, "};\n");
fclose(fh);
return true;
}
int main(int argc, char * argv[])
{
/* Expect: font file, size, out file */
if (argc != 4)
{
fprintf(stderr, "Incorrect arguments\n");
return 1;
}
const char * font_file = argv[1];
int size = atoi(argv[2]);
const char * out_file = argv[3];
FT_Library ft_library;
if (FT_Init_FreeType(&ft_library) != 0)
{
fprintf(stderr, "Could not initialize freetype\n");
return 2;
}
FT_Face face;
if (FT_New_Face(ft_library, font_file, 0, &face) != 0)
{
fprintf(stderr, "Could not open %s\n", font_file);
return 3;
}
FT_Set_Pixel_Sizes(face, 0, size);
for (int i = 0; i < N_CHARS; i++)
{
load_char(face, i);
}
line_height = round_up_26_6(face->size->metrics.height);
baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom;
if ((line_height < 1) || (line_height > 0xFF))
{
fprintf(stderr, "Error: invalid line_height %d\n", line_height);
return false;
}
if ((max_advance < 1) || (max_advance > 0xFF))
{
fprintf(stderr, "Error: invalid max_advance %d\n", max_advance);
return false;
}
for (int i = 0; i < N_CHARS; i++)
{
if ((char_infos[i].width < 0) || (char_infos[i].width > max_advance))
{
fprintf(stderr, "Error: invalid character %d width: %d (max advance %d)\n", i, char_infos[i].width, max_advance);
return false;
}
if ((char_infos[i].height < 0) || (char_infos[i].height > line_height))
{
fprintf(stderr, "Error: invalid character %d height: %d (line height %d)\n", i, char_infos[i].height, line_height);
return false;
}
if ((char_infos[i].left < 0) || (char_infos[i].left >= max_advance))
{
fprintf(stderr, "Error: invalid character %d left: %d (max advance %d)\n", i, char_infos[i].left, max_advance);
return false;
}
/* Adjust character top to be index downward from top of font box. */
char_infos[i].top = line_height - baseline_offset - char_infos[i].top;
if ((char_infos[i].top < 0) || (char_infos[i].top >= line_height))
{
fprintf(stderr, "Error: invalid character %d top: %d (line height %d)\n", i, char_infos[i].top, line_height);
return false;
}
if ((char_infos[i].left + char_infos[i].width) > max_advance)
{
fprintf(stderr, "Error: character %d left (%d) + width (%d) > advance (%d)\n",
i, char_infos[i].left, char_infos[i].width, max_advance);
return false;
}
if ((char_infos[i].top + char_infos[i].height) > line_height)
{
fprintf(stderr, "Error: character %d top (%d) + height (%d) > line height (%d)\n",
i, char_infos[i].top, char_infos[i].height, line_height);
return false;
}
}
if (!generate(out_file))
{
return 1;
}
return 0;
}

16
src/gdt.S Normal file
View File

@ -0,0 +1,16 @@
.global gdt_set
.extern gdtr
.type gdt_set, @function
gdt_set:
lgdt gdtr
jmp $0x8, $gdt_set_reload
gdt_set_reload:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
ret
.size gdt_set, . - gdt_set

20
src/gdt.c Normal file
View File

@ -0,0 +1,20 @@
#include "gdt.h"
static const gdt_entry_t gdt_entries[] = {
/* Null descriptor */
0u,
/* Code segment for kernel */
gdt_build_entry(0u, 0xFFFFFu, 1u, 0u, 1u, 1u, 0u, 1u, 0u, 1u, 1u, 0u),
/* Data segment for kernel */
gdt_build_entry(0u, 0xFFFFFu, 1u, 0u, 1u, 0u, 0u, 1u, 0u, 1u, 1u, 0u),
};
gdtr_t gdtr;
void gdt_init(void)
{
gdtr.size = sizeof(gdt_entries);
gdtr.offset_lower = (uintptr_t)gdt_entries & 0xFFFFu;
gdtr.offset_upper = (uintptr_t)gdt_entries >> 16u;
gdt_set();
}

34
src/gdt.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
typedef struct {
uint16_t size;
uint16_t offset_lower;
uint16_t offset_upper;
uint16_t _reserved;
} gdtr_t;
typedef uint64_t gdt_entry_t;
#define gdt_build_entry(base, limit, pr, privl, s, ex, dc, rw, ac, gr, sz, l) \
(gdt_entry_t)( \
(((gdt_entry_t)base << 32) & 0xFF00000000000000ull) | /* Base 0:15 */ \
(((gdt_entry_t)gr & 0x1u) << 55) | /* Granularity (0 = bytes, 1 = blocks) */ \
(((gdt_entry_t)sz & 0x1u) << 54) | /* Size (0 = 16-bit, 1 = 32-bit) */ \
(((gdt_entry_t)l & 0x1u) << 53) | /* L flag (x86_64 code) */ \
(((gdt_entry_t)limit << 32) & 0x000F000000000000ull) | /* Limit 16:19 */ \
(((gdt_entry_t)pr & 0x1u) << 47) | /* Present flag */ \
(((gdt_entry_t)privl & 0x3u) << 45) | /* Privilege (ring level) */ \
(((gdt_entry_t)s & 0x1u) << 44) | /* Type (0 = system, 1 = code/data) */ \
(((gdt_entry_t)ex & 0x1u) << 43) | /* Executable flag */ \
(((gdt_entry_t)dc & 0x1u) << 42) | /* Direction/Conforming */ \
(((gdt_entry_t)rw & 0x1u) << 41) | /* Readable/Writable */ \
(((gdt_entry_t)ac & 0x1u) << 40) | /* Accessed flag */ \
(((gdt_entry_t)base << 16) & 0x000000FFFFFF0000ull) | /* Base 0:23 */ \
((gdt_entry_t)limit & 0x000000000000FFFFull)) /* Limit 0:15 */
void gdt_init(void);
void gdt_set(void);
#endif

View File

@ -1,88 +0,0 @@
/**
* HELLO console support.
*/
module hello.console;
import uefi;
import hello.hello;
import core.stdc.stdarg;
import hulk.writef;
struct Console
{
/**
* Write a character to the console.
*
* @param ch Character to write.
*/
public static void write(ubyte ch)
{
if (ch == '\n')
{
write('\r');
}
CHAR16[2] s16 = [ch, 0];
st.ConOut.OutputString(st.ConOut, &s16[0]);
}
/**
* Write a formatted string to the console.
*
* @param s Format string.
* @param args Variable arguments structure.
*/
public static void writef(string s, va_list args)
{
hulk.writef.writefv(function void(ubyte ch) {
Console.write(ch);
}, s, args);
}
/**
* Write a formatted string to the console.
*
* @param s Format string.
*/
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 console.
*
* @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);
}
/**
* Write a message to press any key and wait for a key to be pressed.
*/
public static void wait_key()
{
writefln("Press any key...");
st.ConIn.Reset(st.ConIn, FALSE);
EFI_INPUT_KEY key;
while (st.ConIn.ReadKeyStroke(st.ConIn, &key) == EFI_NOT_READY)
{
}
}
/**
* Clear the console.
*/
public static void clear()
{
st.ConOut.ClearScreen(st.ConOut);
}
}

View File

@ -1,579 +0,0 @@
/**
* HELLO, the HOS EFI Lightweight LOader.
*/
module hello.hello;
import uefi;
import hello.console;
import hulk.bootinfo;
import hulk.header;
import hulk.pagetable;
import hulk.cpu;
import hulk.memory;
import ldc.llvmasm;
import hulk.serial;
__gshared EFI_SYSTEM_TABLE * st;
/** HULK binary start address, 4KB-aligned. */
extern extern(C) __gshared ubyte _hulk_bin_start;
/** HULK binary end address, 4KB-aligned. */
extern extern(C) __gshared ubyte _hulk_bin_end;
private align(4096) __gshared ubyte[1024 * 1024] scratch_buffer;
/** Index to the memory map region being used to allocate page tables. */
private __gshared size_t memory_map_page_table_alloc_index;
private HulkHeader * hulk_header()
{
return cast(HulkHeader *)&_hulk_bin_start;
}
private BootInfo * bootinfo()
{
return &hulk_header().bootinfo;
}
private ulong hulk_bin_phys()
{
return cast(ulong)&_hulk_bin_start;
}
private ulong hulk_bin_size()
{
return cast(ulong)&_hulk_bin_end - cast(ulong)&_hulk_bin_start;
}
private ulong hulk_bss_size()
{
return cast(ulong)hulk_header().hulk_bss_size;
}
private ulong hulk_stack_size()
{
return hulk_header().stack_size;
}
private ulong hulk_virt_base_address()
{
return hulk_header().virt_base;
}
private ulong hulk_virt_stack_top()
{
return hulk_header().virt_stack_top;
}
private ulong hulk_virt_framebuffer_address()
{
return hulk_header().virt_fb_buffer;
}
/**
* Detect if we're running in QEMU.
*/
private bool in_qemu()
{
ulong * firmware_vendor = cast(ulong *) st.FirmwareVendor;
ulong fv1 = firmware_vendor[0];
return fv1 ==
((cast(ulong)'E') |
(cast(ulong)'D' << 16) |
(cast(ulong)'K' << 32) |
(cast(ulong)' ' << 48));
}
/**
* Set a graphics mode.
*/
private bool set_graphics_mode()
{
uint max_horizontal_resolution = in_qemu() ? 1920u : 0xFFFFFFFFu;
UINTN buffer_size = scratch_buffer.sizeof;
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
EFI_HANDLE * handles = cast(EFI_HANDLE *)&scratch_buffer[0];
EFI_STATUS status = st.BootServices.LocateHandle(ByProtocol,
&gop_guid, null, &buffer_size, handles);
if (status != EFI_SUCCESS)
{
Console.writefln("LocateHandle: error %x", status);
return false;
}
EFI_HANDLE gop_handle = handles[0];
EFI_HANDLE gop_interface;
status = st.BootServices.HandleProtocol(gop_handle,
&gop_guid, &gop_interface);
if (status != EFI_SUCCESS)
{
Console.writefln("HandleProtocol: error %x", status);
return false;
}
if (gop_interface == null)
{
Console.writefln("null interface from HandleProtocol");
return false;
}
EFI_GRAPHICS_OUTPUT_PROTOCOL * gop = cast(EFI_GRAPHICS_OUTPUT_PROTOCOL *)gop_interface;
UINTN info_size;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info;
uint best_width;
uint best_mode_number;
for (uint mode_number = 0u;
(status = gop.QueryMode(gop, mode_number, &info_size, &info)) == EFI_SUCCESS;
mode_number++)
{
if ((info.HorizontalResolution > best_width) &&
(info.HorizontalResolution <= max_horizontal_resolution))
{
best_width = info.HorizontalResolution;
best_mode_number = mode_number;
}
st.BootServices.FreePool(info);
}
if ((status = gop.SetMode(gop, best_mode_number)) != EFI_SUCCESS)
{
Console.writefln("SetMode: Error %x\n", status);
return false;
}
bootinfo().fb.buffer = cast(uint *)gop.Mode.FrameBufferBase;
bootinfo().fb.width = gop.Mode.Info.HorizontalResolution;
bootinfo().fb.height = gop.Mode.Info.VerticalResolution;
bootinfo().fb.stride = gop.Mode.Info.PixelsPerScanLine;
bootinfo().fb.format = gop.Mode.Info.PixelFormat;
return true;
}
/**
* Walk the EFI memory map and translate it to the HULK bootinfo format.
*/
private void get_memory_map(ulong * physical_address_limit, UINTN * memory_map_key)
{
immutable static ubyte[] efi_to_hulk_memory_map_type = [
BootInfo.MemoryRegion.Type.Reserved, // EfiReservedMemoryType
BootInfo.MemoryRegion.Type.Bootloader, // EfiLoaderCode
BootInfo.MemoryRegion.Type.Bootloader, // EfiLoaderData
BootInfo.MemoryRegion.Type.Bootloader, // EfiBootServicesCode
BootInfo.MemoryRegion.Type.Bootloader, // EfiBootServicesData
BootInfo.MemoryRegion.Type.Reserved, // EfiRuntimeServicesCode
BootInfo.MemoryRegion.Type.Reserved, // EfiRuntimeServicesData
BootInfo.MemoryRegion.Type.Conventional, // EfiConventionalMemory
BootInfo.MemoryRegion.Type.Unusable, // EfiUnusableMemory
BootInfo.MemoryRegion.Type.ACPIReclaim, // EfiACPIReclaimMemory
BootInfo.MemoryRegion.Type.ACPINVS, // EfiACPIMemoryNVS
BootInfo.MemoryRegion.Type.MemoryMappedIO, // EfiMemoryMappedIO
BootInfo.MemoryRegion.Type.MemoryMappedIOPortSpace, // EfiMemoryMappedIOPortSpace
BootInfo.MemoryRegion.Type.PalCode, // EfiPalCode
];
*physical_address_limit = 0u;
UINTN memory_map_size = scratch_buffer.sizeof;
UINTN descriptor_size;
UINT32 descriptor_version;
ubyte * scratch_base = cast(ubyte *)&scratch_buffer[0];
UINTN status = st.BootServices.GetMemoryMap(
&memory_map_size,
cast(EFI_MEMORY_DESCRIPTOR *)scratch_base,
memory_map_key,
&descriptor_size,
&descriptor_version);
if (status != EFI_SUCCESS)
{
Console.writefln("GetMemoryMap: Error %x", status);
for (;;) {}
}
size_t n_entries = memory_map_size / descriptor_size;
size_t count;
bool found_bss;
bool found_stack;
bool found_fb_buffer1;
const(size_t) fb_size = (bootinfo().fb.stride * bootinfo().fb.height * 4u + PAGE_SIZE - 1u) & ~(PAGE_SIZE - 1u);
for (count = 0u; count < n_entries; count++)
{
if (count > bootinfo().memory_map.length)
{
Console.writefln("Memory map too large");
for (;;) {}
}
EFI_MEMORY_DESCRIPTOR * descriptor = cast(EFI_MEMORY_DESCRIPTOR *)&scratch_base[count * descriptor_size];
ulong end_address = descriptor.PhysicalStart + descriptor.NumberOfPages * 4096u;
if ((end_address > *physical_address_limit) &&
((descriptor.Type == EfiLoaderCode) ||
(descriptor.Type == EfiLoaderData) ||
(descriptor.Type == EfiBootServicesCode) ||
(descriptor.Type == EfiBootServicesData) ||
(descriptor.Type == EfiRuntimeServicesCode) ||
(descriptor.Type == EfiRuntimeServicesData) ||
(descriptor.Type == EfiConventionalMemory)))
{
*physical_address_limit = end_address;
}
if (descriptor.Type >= efi_to_hulk_memory_map_type.length)
{
Serial.writefln("Unexpected descriptor type %u", descriptor.Type);
continue;
}
bootinfo().memory_map[count].base = descriptor.PhysicalStart;
bootinfo().memory_map[count].size = descriptor.NumberOfPages * 4096u;
bootinfo().memory_map[count].type = efi_to_hulk_memory_map_type[descriptor.Type];
if ((!found_bss) &&
(descriptor.Type == EfiConventionalMemory) &&
(bootinfo().memory_map[count].size >= hulk_bss_size()))
{
bootinfo().bss_phys = bootinfo().memory_map[count].base;
bootinfo().memory_map[count].base += 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;
}
if ((!found_stack) &&
(descriptor.Type == EfiConventionalMemory) &&
(bootinfo().memory_map[count].size >= hulk_stack_size()))
{
bootinfo().stack_phys = bootinfo().memory_map[count].base;
bootinfo().memory_map[count].base += 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;
}
if ((!found_fb_buffer1) &&
(descriptor.Type == EfiConventionalMemory) &&
(bootinfo().memory_map[count].size >= fb_size))
{
bootinfo().fb_buffer1_phys = bootinfo().memory_map[count].base;
bootinfo().memory_map[count].base += 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;
}
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;
if ((!found_bss) || (!found_stack) || (!found_fb_buffer1))
{
for (;;) {}
}
}
private void * alloc_pt_page()
{
BootInfo.MemoryRegion * regions = &bootinfo().memory_map[0];
for (;;)
{
BootInfo.MemoryRegion * region = &bootinfo().memory_map[memory_map_page_table_alloc_index];
if ((region.type == BootInfo.MemoryRegion.Type.Conventional) && (region.size >= PAGE_SIZE))
{
void * addr = cast(void *)region.base;
region.base += PAGE_SIZE;
region.size -= PAGE_SIZE;
return addr;
}
memory_map_page_table_alloc_index++;
if (memory_map_page_table_alloc_index >= bootinfo().memory_map_count)
{
return null;
}
}
}
/**
* Allocate a new page table.
*/
private PageTable * new_page_table()
{
PageTable * pt = cast(PageTable *)alloc_pt_page();
if (pt == null)
{
return null;
}
memset64(pt, 0u, 512);
return pt;
}
/**
* Map a virtual address to a physical address using 4KB pages.
*
* @param source_page Source page address.
* @param dest_page Destination page address.
* @param pt_base Page table base address.
*/
private bool map4k(ulong source_page, ulong dest_page, PageTable * pt_base)
{
PageTable * pt = pt_base;
for (size_t level = 0; level < 4u; level++)
{
PageTableEntry * ppte = pt.entry(source_page, level);
PageTableEntry pte = *ppte;
if (pte.present)
{
pt = pte.follow();
}
else
{
PageTable * next_pt;
ulong addr;
if (level < 3u)
{
next_pt = new_page_table();
if (next_pt == null)
{
return false;
}
addr = cast(ulong)next_pt;
}
else
{
addr = dest_page;
}
*ppte = PageTableEntry(addr, PT_WRITABLE | PT_PRESENT);
pt = next_pt;
}
}
return true;
}
/**
* Map a virtual region to a physical region using 4KB pages.
*
* @param source Source address.
* @param dest Destination address.
* @param size Region size.
* @param pt_base Page table base address.
*/
private bool map4kregion(ulong source, ulong dest, size_t size, PageTable * pt_base)
{
ulong end = source + size;
while (source < end)
{
if (!map4k(source, dest, pt_base))
{
return false;
}
source += 4096u;
dest += 4096u;
}
return true;
}
/**
* Map a virtual address to a physical address using 2MB pages.
*
* @param source_page Source page address.
* @param dest_page Destination page address.
* @param pt_base Page table base address.
*/
private bool map2m(ulong source_page, ulong dest_page, PageTable * pt_base)
{
PageTable * pt = pt_base;
for (size_t level = 0; level < 3u; level++)
{
PageTableEntry * ppte = pt.entry(source_page, level);
PageTableEntry pte = *ppte;
if (pte.present)
{
pt = pte.follow();
}
else
{
PageTable * next_pt;
if (level < 2u)
{
next_pt = new_page_table();
if (next_pt == null)
{
return false;
}
*ppte = PageTableEntry(next_pt, PT_WRITABLE | PT_PRESENT);
}
else
{
*ppte = PageTableEntry(dest_page, PT_HUGE_PAGE | PT_WRITABLE | PT_PRESENT);
}
pt = next_pt;
}
}
return true;
}
/**
* Map HULK virtual addresses to physical kernel location.
*
* @param pt_base Page table base address.
*/
private bool map_hulk(PageTable * pt_base)
{
/* Map HULK bin region. */
ulong virt = hulk_virt_base_address();
if (!map4kregion(virt, hulk_bin_phys(), hulk_bin_size(), pt_base))
{
return false;
}
/* Map HULK bss region. */
virt += hulk_bin_size();
if (!map4kregion(virt, bootinfo().bss_phys, hulk_bss_size(), pt_base))
{
return false;
}
/* Zero BSS region. */
memset64(cast(void *)bootinfo().bss_phys, 0u, hulk_bss_size() / 8);
/* Map HULK stack. */
virt = hulk_virt_stack_top() - hulk_stack_size();
if (!map4kregion(virt, bootinfo().stack_phys, hulk_stack_size(), pt_base))
{
return false;
}
return true;
}
/**
* Build page tables in preparation to jump to HULK.
*
* @param physical_address_limit Maximum physical address to identity map.
*/
private bool build_page_tables(ulong physical_address_limit)
{
PageTable * pt_base = new_page_table();
if (pt_base == null)
{
return false;
}
/* Map physical RAM. */
for (size_t addr = 0u; addr < physical_address_limit; addr += (2u * 1024u * 1024u))
{
if (!map2m(addr, addr, pt_base))
{
return false;
}
}
/* Map any memory regions that are outside physical RAM. */
for (size_t i = 0u; i < bootinfo().memory_map_count; i++)
{
ulong addr = bootinfo().memory_map[i].base;
if (addr >= physical_address_limit)
{
if (!map4kregion(addr, addr, bootinfo().memory_map[i].size, pt_base))
{
return false;
}
}
}
/* Map graphics framebuffer. */
ulong framebuffer_size = bootinfo().fb.height * bootinfo().fb.stride * 4u;
ulong fb_phys = cast(ulong)bootinfo().fb.buffer;
ulong fb_virt = hulk_virt_framebuffer_address();
if (!map4kregion(fb_virt, fb_phys, framebuffer_size, pt_base))
{
return false;
}
/* Map HULK regions. */
if (!map_hulk(pt_base))
{
return false;
}
/* Switch to the new page table. */
write_cr3(cast(ulong)pt_base);
return true;
}
/**
* Jump to HULK entry point.
*/
private void jump_to_hulk()
{
__asm(
"jmpq *$0",
"r,{rsp}",
hulk_header().entry, hulk_virt_stack_top());
}
/**
* Find the ACPI XSDT.
*
* @return Whether it was found.
*/
private bool find_acpi_xsdt()
{
for (size_t i = 0u; i < st.NumberOfTableEntries; i++)
{
if ((st.ConfigurationTable[i].VendorGuid == EFI_ACPI_TABLE_GUID) ||
(st.ConfigurationTable[i].VendorGuid == ACPI_TABLE_GUID))
{
enum ulong rsdp_magic = 0x20525450_20445352u;
const(EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER) * rdsp = cast(const(EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER) *)st.ConfigurationTable[i].VendorTable;
if (rdsp.Signature == rsdp_magic)
{
if (rdsp.Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION)
{
bootinfo().acpi_xsdt_phys = rdsp.XsdtAddress;
return true;
}
}
}
}
return false;
}
/**
* EFI application entry point.
*
* @param image_handle EFI application handle.
* @param st Pointer to EFI system table.
*/
extern (C) EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * st)
{
.st = st;
Console.clear();
Console.writefln("Welcome to HELLO, HOS EFI Lightweight LOader, v0.1.0");
Console.writefln("Firmware vendor: '%S', version: 0x%x", st.FirmwareVendor, st.FirmwareVendor);
if (!find_acpi_xsdt())
{
Console.writefln("Error: Could not locate ACPI XSDT");
Console.wait_key();
return EFI_SUCCESS;
}
if (!set_graphics_mode())
{
Console.wait_key();
return EFI_SUCCESS;
}
ulong physical_address_limit;
UINTN memory_map_key;
for (;;)
{
get_memory_map(&physical_address_limit, &memory_map_key);
EFI_STATUS status = st.BootServices.ExitBootServices(image_handle, memory_map_key);
if (status == EFI_INVALID_PARAMETER)
{
continue;
}
if (status == EFI_SUCCESS)
{
break;
}
Console.writefln("ExitBootServices: Error %x", status);
Console.wait_key();
return EFI_SUCCESS;
}
if (!build_page_tables(physical_address_limit))
{
return EFI_SUCCESS;
}
bootinfo().hulk_phys = hulk_bin_phys();
jump_to_hulk();
return EFI_SUCCESS;
}

25
src/hos_main.c Normal file
View File

@ -0,0 +1,25 @@
#include <stdint.h>
#include "fb.h"
#include "mbinfo.h"
#include "klog.h"
#include "gdt.h"
#include "mm.h"
void hos_main(uint32_t mbinfo_addr)
{
gdt_init();
if (!mbinfo_init(mbinfo_addr))
{
return;
}
if (!fb_ready())
{
return;
}
klog_init();
klog_printf("Welcome to HOS!\n");
mm_init();
mbinfo_load();
klog_printf("Found %dKB of usable RAM\n", mm_get_total_ram() / 1024u);
klog_printf("Kernel is %dKB at 0x%x\n", mm_get_kernel_size() / 1024u, mm_get_kernel_address());
}

281
src/hos_printf.c Normal file
View File

@ -0,0 +1,281 @@
#include "hos_printf.h"
#include <stdint.h>
#include <stdbool.h>
#include "string.h"
static size_t format_dec(char * buffer, int32_t v)
{
size_t sz = 0u;
bool printing = false;
if (v < 0)
{
buffer[sz++] = '-';
v = -v;
}
for (int32_t div = 1000000000; div >= 1; div /= 10)
{
int32_t digit = v / div;
v %= div;
if ((digit != 0) || (div == 1))
{
printing = true;
}
if (printing)
{
buffer[sz++] = digit + '0';
}
}
return sz;
}
static size_t format_dec64(char * buffer, int64_t v)
{
size_t sz = 0u;
bool printing = false;
if (v < 0)
{
buffer[sz++] = '-';
v = -v;
}
for (int64_t div = 1000000000000000000; div >= 1; div /= 10)
{
int64_t digit = v / div;
v %= div;
if ((digit != 0) || (div == 1))
{
printing = true;
}
if (printing)
{
buffer[sz++] = digit + '0';
}
}
return sz;
}
static size_t format_udec(char * buffer, int32_t v)
{
size_t sz = 0u;
for (int32_t div = 1000000000u; div >= 1u; div /= 10u)
{
int32_t digit = v / div;
v %= div;
if ((digit != 0) || (sz > 0u) || (div == 1))
{
buffer[sz++] = digit + '0';
}
}
return sz;
}
static size_t format_udec64(char * buffer, uint64_t v)
{
size_t sz = 0u;
for (uint64_t div = 10000000000000000000u; div >= 1u; div /= 10u)
{
uint64_t digit = v / div;
v %= div;
if ((digit != 0) || (sz > 0u) || (div == 1))
{
buffer[sz++] = digit + '0';
}
}
return sz;
}
static size_t format_hex(char * buffer, uint32_t v, bool upper)
{
const char upper_hex[] = "0123456789ABCDEF";
const char lower_hex[] = "0123456789abcdef";
size_t sz = 0u;
for (int i = 28; i >= 0; i -= 4)
{
uint8_t n = (v >> i) & 0xFu;
if ((sz > 0u) || (n != 0u) || (i == 0))
{
buffer[sz] = upper ? upper_hex[n] : lower_hex[n];
sz++;
}
}
return sz;
}
static size_t format_hex64(char * buffer, uint64_t v, bool upper)
{
const char upper_hex[] = "0123456789ABCDEF";
const char lower_hex[] = "0123456789abcdef";
size_t sz = 0u;
for (int i = 60; i >= 0; i -= 4)
{
uint8_t n = (v >> i) & 0xFu;
if ((sz > 0u) || (n != 0u) || (i == 0))
{
buffer[sz] = upper ? upper_hex[n] : lower_hex[n];
sz++;
}
}
return sz;
}
static void pad_write(const stream_t * stream, const char * data, size_t length, bool leading_zero, bool left_just, size_t width)
{
if (left_just)
{
stream->write(data, length);
while (length < width)
{
stream->write1(' ');
length++;
}
}
else
{
char fill = leading_zero ? '0' : ' ';
size_t l = length;
while (l < width)
{
stream->write1(fill);
l++;
}
stream->write(data, length);
}
}
void hos_vprintf(const stream_t * stream, const char * fmt, va_list va)
{
bool in_conv = false;
char c;
char buffer[22];
size_t width;
bool leading_zero;
bool left_just;
bool long_flag;
size_t length;
while ((c = *fmt))
{
if (in_conv)
{
switch (c)
{
case '%':
stream->write1('%');
break;
case 'X':
{
if (long_flag)
{
uint64_t v = va_arg(va, uint64_t);
length = format_hex(buffer, v, true);
}
else
{
uint32_t v = va_arg(va, uint32_t);
length = format_hex(buffer, v, true);
}
pad_write(stream, buffer, length, leading_zero, left_just, width);
}
break;
case 'c':
{
char ch = va_arg(va, int);
pad_write(stream, &ch, 1u, leading_zero, left_just, width);
}
break;
case 'd':
{
if (long_flag)
{
int64_t v = va_arg(va, int64_t);
length = format_dec64(buffer, v);
}
else
{
int32_t v = va_arg(va, int32_t);
length = format_dec(buffer, v);
}
pad_write(stream, buffer, length, leading_zero, left_just, width);
}
break;
case 's':
{
const char * s = va_arg(va, const char *);
pad_write(stream, s, strlen(s), leading_zero, left_just, width);
}
break;
case 'u':
{
if (long_flag)
{
uint64_t v = va_arg(va, uint64_t);
length = format_udec64(buffer, v);
}
else
{
uint32_t v = va_arg(va, uint32_t);
length = format_udec(buffer, v);
}
pad_write(stream, buffer, length, leading_zero, left_just, width);
}
break;
case 'x':
{
if (long_flag)
{
uint64_t v = va_arg(va, uint64_t);
length = format_hex64(buffer, v, false);
}
else
{
uint32_t v = va_arg(va, uint32_t);
length = format_hex(buffer, v, false);
}
pad_write(stream, buffer, length, leading_zero, left_just, width);
}
break;
}
in_conv = false;
}
else if (c == '%')
{
in_conv = true;
width = 0u;
leading_zero = false;
left_just = false;
long_flag = false;
if (fmt[1] == '-')
{
left_just = true;
fmt++;
}
if (fmt[1] == '0')
{
leading_zero = true;
fmt++;
}
while (('0' <= fmt[1]) && (fmt[1] <= '9'))
{
width *= 10u;
width += (fmt[1] - '0');
fmt++;
}
if (fmt[1] == 'l')
{
long_flag = true;
fmt++;
}
}
else
{
stream->write1(c);
}
fmt++;
}
}
void hos_printf(const stream_t * stream, const char * fmt, ...)
{
va_list va;
va_start(va, fmt);
hos_vprintf(stream, fmt, va);
va_end(va);
}

10
src/hos_printf.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef HOS_PRINTF_H
#define HOS_PRINTF_H
#include "stream.h"
#include <stdarg.h>
void hos_vprintf(const stream_t * stream, const char * fmt, va_list va);
void hos_printf(const stream_t * stream, const char * fmt, ...);
#endif

View File

@ -1,126 +0,0 @@
/**
* ACPI (Advanced Configuration and Power Interface) functionality.
*/
module hulk.acpi;
import hulk.hurl;
import hulk.klog;
import hulk.memory;
import hulk.hurl.a1;
struct Acpi
{
/**
* List of ACPI tables.
*/
private static __gshared Header *[] tables;
enum uint XSDT_SIGNATURE = signature("XSDT");
/**
* ACPI table header structure.
*/
public static struct Header
{
uint signature;
uint length;
ubyte revision;
ubyte checksum;
char[6] oemid;
char[8] oemtableid;
uint oem_revision;
uint creator_id;
uint creator_revision;
}
/**
* XSDT table.
*/
static struct XSDT
{
Header header;
/* Table pointers are not ulong-aligned! They begin at offset 36. */
align(4) ulong[1] tables;
}
static assert(XSDT.tables.offsetof == 36);
/**
* Initialize ACPI.
*/
public static void initialize(ulong acpi_xsdt_phys)
{
Klog.writefln("\a3Initializing ACPI");
/* Map the XSDT header. */
map_table(acpi_xsdt_phys, PAGE_SIZE);
const(XSDT) * xsdt = cast(const(XSDT) *)acpi_xsdt_phys;
if (xsdt.header.signature != XSDT_SIGNATURE)
{
Klog.fatal_error("XSDT signature invalid");
return;
}
/* Map the entire XSDT. */
if (xsdt.header.length > PAGE_SIZE)
{
map_table(acpi_xsdt_phys, xsdt.header.length);
}
size_t n_entries = (xsdt.header.length - xsdt.header.sizeof) / xsdt.tables[0].sizeof;
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++)
{
ulong address = xsdt.tables[i];
map_table(address, PAGE_SIZE);
Header * header = cast(Header *)address;
uint length = header.length;
if (length > PAGE_SIZE)
{
map_table(address, length);
}
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);
}
}
/**
* Get pointer to ACPI table by name.
*
* @param name
* Table name.
*/
public static Header * get_table(string name)
{
uint signature = signature(name);
foreach (table; tables)
{
if (table.signature == signature)
{
return table;
}
}
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);
}
}

View File

@ -1,382 +0,0 @@
/**
* APIC (Advanced Programmable Interrupt Controller) functionality.
*/
module hulk.apic;
import hulk.hurl;
import hulk.acpi;
import hulk.him;
import hulk.rtc;
import hulk.pit;
import hulk.klog;
import hulk.memory;
struct Apic
{
private enum DEBUG = false;
private enum DEFAULT_IO_APIC_ADDRESS = 0xFEC0_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
{
public uint value;
alias value this;
private ubyte[12] _padding;
}
/**
* APIC registers.
*/
static struct ApicRegisters
{
ApicRegister[2] _reserved0;
ApicRegister lapic_id;
ApicRegister lapic_version;
ApicRegister[4] _reserved1;
ApicRegister task_priority;
ApicRegister arbitration_priority;
ApicRegister processor_priority;
ApicRegister eoi;
ApicRegister remote_read;
ApicRegister logical_destination;
ApicRegister destination_format;
ApicRegister spurious_interrupt_vector;
ApicRegister[8] in_service;
ApicRegister[8] trigger_mode;
ApicRegister[8] interrupt_request;
ApicRegister error_status;
ApicRegister[6] _reserved2;
ApicRegister lvt_cmci;
ApicRegister[2] interrupt_command;
ApicRegister lvt_timer;
ApicRegister lvt_thermal_sensor;
ApicRegister lvt_performance_monitoring_counters;
ApicRegister[2] lvt_lint;
ApicRegister lvt_error;
ApicRegister initial_count;
ApicRegister current_count;
ApicRegister[4] _reserved3;
ApicRegister divide_configuration;
}
/**
* I/O APIC registers.
*/
static struct IoApicRegisters
{
ApicRegister address;
ApicRegister data;
}
alias ISRHandler = void function();
private static __gshared ApicRegisters * 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()
{
Klog.writefln("\a3Initializing APIC");
io_apic_registers = cast(IoApicRegisters *)DEFAULT_IO_APIC_ADDRESS;
for (ubyte i = 0u; i < INT_APIC_COUNT; i++)
{
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);
Hurl.map(cast(ulong)io_apic_registers, cast(ulong)io_apic_registers,
PT_WRITABLE | PT_WRITE_THROUGH | PT_DISABLE_CACHE | PT_NO_EXECUTE);
/* Enable local APIC to receive interrupts and set spurious interrupt
* vector to 0xFF. */
apic_registers.spurious_interrupt_vector.value = 0x100u | INT_APIC_SPURIOUS;
apic_registers.lvt_timer.value = INT_APIC_TIMER;
apic_registers.lvt_lint[0].value = INT_APIC_LINT0;
apic_registers.lvt_lint[1].value = INT_APIC_LINT1;
/* Enable PIT interrupt. */
configure_io_apic_irq(IRQ_PIT, &Pit.isr);
/* Enable RTC interrupt. */
configure_io_apic_irq(IRQ_RTC, &Rtc.isr);
}
/**
* 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 = INT_APIC_BASE + io_apic_irq;
io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u);
io_apic_registers.data.value = entry & 0xFFFF_FFFFu;
io_apic_registers.address.value = cast(uint)(0x10u + io_apic_irq * 2u + 1u);
io_apic_registers.data.value = entry >> 32u;
irq_handlers[io_apic_irq] = isr_handler;
}
/**
* Signal APIC end of interrupt.
*/
public static void eoi()
{
apic_registers.eoi.value = 0u;
}
/**
* APIC interrupt handler.
*/
public static void isr(ulong vector)
{
ulong io_apic_irq = vector - INT_APIC_BASE;
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();
}
}

View File

@ -1,77 +0,0 @@
/**
* HULK boot information.
*/
module hulk.bootinfo;
/**
* HULK boot information structure.
*/
struct BootInfo
{
/**
* Framebuffer description structure.
*/
struct Framebuffer
{
/** Framebuffer physical base address. */
uint * buffer;
/** Horizontal resolution. */
uint width;
/** Vertical resolution. */
uint height;
/** Number of pixels per scan line. */
uint stride;
/** Pixel format. */
uint format;
}
/**
* Memory map entry description structure.
*/
struct MemoryRegion
{
enum Type
{
Reserved,
Bootloader,
Conventional,
Unusable,
ACPIReclaim,
ACPINVS,
MemoryMappedIO,
MemoryMappedIOPortSpace,
PalCode,
}
/** Base address of the memory region. */
ulong base;
/** Size in bytes of the memory region. */
size_t size;
/** Type of the memory region. */
ubyte type;
}
/* Framebuffer parameters. */
Framebuffer fb;
/* Memory map. */
MemoryRegion[1000] memory_map;
/* Number of memory map entries. */
size_t memory_map_count;
/* Physical address of HULK. */
ulong hulk_phys;
/* Physical address used for HULK bss section. */
ulong bss_phys;
/* Physical address of stack while jumping to HULK. */
ulong stack_phys;
/* Physical address of framebuffer buffer1. */
ulong fb_buffer1_phys;
/* Physical address of ACPI XSDT table. */
ulong acpi_xsdt_phys;
}

View File

@ -1,276 +0,0 @@
/**
* HULK Console functionality.
*/
module hulk.console;
import hulk.fb;
import hulk.kfont;
import hulk.ver;
import hulk.rtc;
import hulk.writef;
/**
* Represent the HULK console.
*
* Coordinate (0,0) is the top-left console position.
*/
struct Console
{
/** Border color. */
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;
/** Console page height in text rows. */
private static __gshared size_t m_height;
/** Current console page. */
private static __gshared size_t m_page;
/** Current page cursor X position. */
private static __gshared size_t m_x;
/** Current page cursor Y position. */
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.
*/
public static void initialize()
{
size_t fb_console_height = Fb.height - Kfont.line_height - 1;
m_width = (Fb.width - 1) / 2 / Kfont.advance;
m_height = fb_console_height / Kfont.line_height;
}
/**
* Clear the console.
*/
public static void clear()
{
Fb.clear();
m_page = 0u;
m_x = 0u;
m_y = 0u;
draw();
}
/**
* Write a character to the console (buffer newlines).
*
* @param ch Character to write.
*/
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')
{
m_x = 0u;
m_y++;
}
else
{
render_char(fb_x(m_page, m_x), fb_y(m_y), ch);
m_x++;
if (m_x == m_width)
{
m_x = 0u;
m_y++;
}
}
if (m_y == m_height)
{
if (m_page == 0u)
{
m_page = 1u;
m_x = 0u;
m_y = 0u;
}
else
{
m_y--;
shift_rows();
}
}
}
/**
* Shift console rows.
*/
private static void shift_rows()
{
/* Shift up page 0 */
Fb.copy_rect(fb_x(0u, 0u), fb_y(1u),
m_width * Kfont.advance, (m_height - 1u) * Kfont.line_height,
fb_x(0u, 0u), fb_y(0u));
/* Copy page 1 first row to page 0 last row. */
Fb.copy_rect(fb_x(1u, 0u), fb_y(0u),
m_width * Kfont.advance, Kfont.line_height,
fb_x(0u, 0u), fb_y(m_height - 1u));
/* Shift up page 1 */
Fb.copy_rect(fb_x(1u, 0u), fb_y(1u),
m_width * Kfont.advance, (m_height - 1u) * Kfont.line_height,
fb_x(1u, 0u), fb_y(0u));
/* Erase page 1 last row. */
Fb.rect(fb_x(1u, 0u), fb_y(m_height - 1u),
m_width * Kfont.advance, Kfont.line_height, 0u);
}
/**
* Render a character.
*
* @param x X position.
* @param y Y position.
* @param ch Character to render.
*/
private static void render_char(size_t x, size_t y, char ch)
{
if (ch > 127)
{
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.
*/
private static size_t fb_x(size_t page, size_t x)
{
return x * Kfont.advance + page * (m_width * Kfont.advance + 1);
}
/**
* Get the framebuffer Y coordinate corresponding to the console page Y position.
*/
private static size_t fb_y(size_t y)
{
return (y + 1) * Kfont.line_height + 1;
}
/**
* Draw console.
*/
private static draw()
{
static __gshared string header_text = "Welcome to HOS v" ~ VERSION ~ "!";
Fb.rect(0u, Kfont.line_height, Fb.width, 1, BORDER_COLOR);
Fb.rect(m_width * Kfont.advance, Kfont.line_height, 1, Fb.height - Kfont.line_height, BORDER_COLOR);
size_t x = 0;
foreach (c; header_text)
{
render_char(x, 0, c);
x += Kfont.advance;
}
update_header();
}
/**
* Update console header.
*/
public static update_header()
{
__gshared uint x;
x = Fb.width - 8 * Kfont.advance;
Fb.rect(x - 1, 0, 1, Kfont.line_height, BORDER_COLOR);
Fb.rect(x, 0, Fb.width - x, Kfont.line_height, 0);
Rtc.time rtc_time = Rtc.read_rtc_time();
writef(function(ubyte ch) {
render_char(x, 0, ch);
x += Kfont.advance;
}, "%02u:%02u:%02u", rtc_time.hour, rtc_time.minute, rtc_time.second);
}
}

View File

@ -1,261 +0,0 @@
/**
* CPU functionality.
*/
module hulk.cpu;
import ldc.llvmasm;
/** CR0 bits. @{ */
enum ulong CR0_PE = 0x1u;
enum ulong CR0_MP = 0x2u;
enum ulong CR0_EM = 0x4u;
enum ulong CR0_TS = 0x8u;
enum ulong CR0_ET = 0x10u;
enum ulong CR0_NE = 0x20u;
enum ulong CR0_WP = 0x1_0000u;
enum ulong CR0_AM = 0x4_0000u;
enum ulong CR0_NW = 0x2000_0000u;
enum ulong CR0_CD = 0x4000_0000u;
enum ulong CR0_PG = 0x8000_0000u;
/** @} */
/** CR4 bits. @{ */
enum ulong CR4_VME = 0x1u;
enum ulong CR4_PVI = 0x2u;
enum ulong CR4_TSD = 0x4u;
enum ulong CR4_DE = 0x8u;
enum ulong CR4_PSE = 0x10u;
enum ulong CR4_PAE = 0x20u;
enum ulong CR4_MCE = 0x40u;
enum ulong CR4_PGE = 0x80u;
enum ulong CR4_PCE = 0x100u;
enum ulong CR4_OSFXSR = 0x200u;
enum ulong CR4_OSXMMEXCPT = 0x400u;
enum ulong CR4_UMIP = 0x800u;
enum ulong CR4_VMXE = 0x2000u;
enum ulong CR4_SMXE = 0x4000u;
enum ulong CR4_FSGSBASE = 0x1_0000u;
enum ulong CR4_PCIDE = 0x2_0000u;
enum ulong CR4_OSXSAVE = 0x4_0000u;
enum ulong CR4_SMEP = 0x10_0000u;
enum ulong CR4_SMAP = 0x20_0000u;
enum ulong CR4_PKE = 0x40_0000u;
enum ulong CR4_CET = 0x80_0000u;
enum ulong CR4_PKS = 0x100_0000u;
/** @} */
/** XCR0 bits. @{ */
enum ulong XCR0_X87 = 0x1u;
enum ulong XCR0_SSE = 0x2u;
enum ulong XCR0_AVX = 0x4u;
enum ulong XCR0_BNDREG = 0x8u;
enum ulong XCR0_BNDSCR = 0x10u;
enum ulong XCR0_OPMASK = 0x20u;
enum ulong XCR0_ZMM_HI256 = 0x40u;
enum ulong XCR0_HI16_ZMM = 0x80u;
enum ulong XCR0_PKRU = 0x100u;
/** @} */
/** MSR register numbers. @{ */
enum uint MSR_PAT = 0x277u;
enum uint MSR_EFER = 0xC000_0080u;
/** @} */
/** EFER flags. @{ */
enum uint EFER_SCE = 0x1u;
enum uint EFER_LME = 0x100u;
enum uint EFER_LMA = 0x400u;
enum uint EFER_NXE = 0x800u;
enum uint EFER_SVME = 0x1000u;
enum uint EFER_LMSLE = 0x2000u;
enum uint EFER_FFXSR = 0x4000u;
enum uint EFER_TCE = 0x8000u;
/** @} */
/** CPUID 1 bits. @{ */
enum uint CPUID_1_EDX_FPU = 0x1u;
enum uint CPUID_1_EDX_VME = 0x2u;
enum uint CPUID_1_EDX_DE = 0x4u;
enum uint CPUID_1_EDX_PSE = 0x8u;
enum uint CPUID_1_EDX_TSC = 0x10u;
enum uint CPUID_1_EDX_MSR = 0x20u;
enum uint CPUID_1_EDX_PAE = 0x40u;
enum uint CPUID_1_EDX_MCE = 0x80u;
enum uint CPUID_1_EDX_CX8 = 0x100u;
enum uint CPUID_1_EDX_APIC = 0x200u;
enum uint CPUID_1_EDX_SEP = 0x800u;
enum uint CPUID_1_EDX_MTRR = 0x1000u;
enum uint CPUID_1_EDX_PGE = 0x2000u;
enum uint CPUID_1_EDX_MCA = 0x4000u;
enum uint CPUID_1_EDX_CMOV = 0x8000u;
enum uint CPUID_1_EDX_PAT = 0x1_0000u;
enum uint CPUID_1_EDX_PSE36 = 0x2_0000u;
enum uint CPUID_1_EDX_PSN = 0x4_0000u;
enum uint CPUID_1_EDX_CLFSH = 0x8_0000u;
enum uint CPUID_1_EDX_DS = 0x20_0000u;
enum uint CPUID_1_EDX_ACPI = 0x40_0000u;
enum uint CPUID_1_EDX_MMX = 0x80_0000u;
enum uint CPUID_1_EDX_FXSR = 0x100_0000u;
enum uint CPUID_1_EDX_SSE = 0x200_0000u;
enum uint CPUID_1_EDX_SSE2 = 0x400_0000u;
enum uint CPUID_1_EDX_SS = 0x800_0000u;
enum uint CPUID_1_EDX_HTT = 0x1000_0000u;
enum uint CPUID_1_EDX_TM = 0x2000_0000u;
enum uint CPUID_1_EDX_IA64 = 0x4000_0000u;
enum uint CPUID_1_EDX_PBE = 0x8000_0000u;
enum uint CPUID_1_ECX_SSE3 = 0x1u;
enum uint CPUID_1_ECX_PCLMULQDQ = 0x2u;
enum uint CPUID_1_ECX_DTES64 = 0x4u;
enum uint CPUID_1_ECX_MONITOR = 0x8u;
enum uint CPUID_1_ECX_DSCPL = 0x10u;
enum uint CPUID_1_ECX_VMX = 0x20u;
enum uint CPUID_1_ECX_SMX = 0x40u;
enum uint CPUID_1_ECX_EST = 0x80u;
enum uint CPUID_1_ECX_TM2 = 0x100u;
enum uint CPUID_1_ECX_SSSE3 = 0x200u;
enum uint CPUID_1_ECX_CNXTID = 0x400u;
enum uint CPUID_1_ECX_SDBG = 0x800u;
enum uint CPUID_1_ECX_FMA = 0x1000u;
enum uint CPUID_1_ECX_CX16 = 0x2000u;
enum uint CPUID_1_ECX_XTPR = 0x4000u;
enum uint CPUID_1_ECX_PDCM = 0x8000u;
enum uint CPUID_1_ECX_PCID = 0x2_0000u;
enum uint CPUID_1_ECX_DCA = 0x4_0000u;
enum uint CPUID_1_ECX_SSE41 = 0x8_0000u;
enum uint CPUID_1_ECX_SSE42 = 0x10_0000u;
enum uint CPUID_1_ECX_X2APIC = 0x20_0000u;
enum uint CPUID_1_ECX_MOVBE = 0x40_0000u;
enum uint CPUID_1_ECX_POPCNT = 0x80_0000u;
enum uint CPUID_1_ECX_TSCDEADLINE = 0x100_0000u;
enum uint CPUID_1_ECX_AES = 0x200_0000u;
enum uint CPUID_1_ECX_XSAVE = 0x400_0000u;
enum uint CPUID_1_ECX_OSXSAVE = 0x800_0000u;
enum uint CPUID_1_ECX_AVX = 0x1000_0000u;
enum uint CPUID_1_ECX_F16C = 0x2000_0000u;
enum uint CPUID_1_ECX_RDRND = 0x4000_0000u;
enum uint CPUID_1_ECX_HYPERVISOR = 0x8000_0000u;
/** @} */
void cli()
{
__asm("cli", "");
}
void sti()
{
__asm("sti", "");
}
void hlt()
{
__asm("hlt", "");
}
ulong read_cr0()
{
return __asm!ulong("mov %cr0, %rax", "={rax}");
}
void write_cr0(ulong v)
{
__asm("mov $0, %cr0", "r", v);
}
ulong read_cr2()
{
return __asm!ulong("mov %cr2, %rax", "={rax}");
}
ulong read_cr3()
{
return __asm!ulong("mov %cr3, %rax", "={rax}");
}
void write_cr3(ulong v)
{
__asm("mov $0, %cr3", "r", v);
}
ulong read_cr4()
{
return __asm!ulong("mov %cr4, %rax", "={rax}");
}
void write_cr4(ulong 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)
{
return __asm!ulong(`
rdmsr
shl $$32, %rdx
or %rdx, %rax`, "={rax},{ecx},~{rdx}", msr);
}
void wrmsr(uint msr, ulong value)
{
__asm(`wrmsr`, "{ecx},{edx},{eax}", msr, value >> 32u, value);
}
ulong xgetbv(uint xcr)
{
return __asm!ulong(`
xgetbv
shl $$32, %rdx
or %rdx, %rax`, "={rax},{ecx},~{rdx}", xcr);
}
void xsetbv(uint xcr, ulong value)
{
__asm(`xsetbv`, "{ecx},{edx},{eax}", xcr, value >> 32u, value);
}
ubyte in8(ushort ioaddr)
{
return __asm!ubyte("inb %dx, %al", "={al},{dx}", ioaddr);
}
ushort in16(ushort ioaddr)
{
return __asm!ushort("inw %dx, %ax", "={ax},{dx}", ioaddr);
}
uint in32(ushort ioaddr)
{
return __asm!uint("inl %dx, %eax", "={eax},{dx}", ioaddr);
}
void out8(ushort ioaddr, ubyte v)
{
__asm("outb %al, %dx", "{dx},{al}", ioaddr, v);
}
void out16(ushort ioaddr, ushort v)
{
__asm("outw %ax, %dx", "{dx},{ax}", ioaddr, v);
}
void out32(ushort ioaddr, uint v)
{
__asm("outl %eax, %dx", "{dx},{eax}", ioaddr, v);
}
void cpuid1(uint * ebx, uint * ecx, uint * edx)
{
__asm("cpuid", "=*{ebx},=*{ecx},=*{edx},{eax}", ebx, ecx, edx, 1u);
}
void swint(ubyte swint_id)()
{
mixin(`__asm("int $$`, cast(int)cast(uint)swint_id, `", "");`);
}

View File

@ -1,242 +0,0 @@
/**
* HULK Framebuffer support.
*/
module hulk.fb;
import hulk.memory;
import hulk.kfont;
/**
* 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.
*/
struct Fb
{
/** Device frame buffer base address. */
private __gshared static uint * m_device_buffer;
/** Frame buffer 1 base address. */
private __gshared static uint * m_buffer1;
/** Frame buffer width. */
private __gshared static uint m_width;
/** Frame buffer height. */
private __gshared static uint m_height;
/** Frame buffer stride. */
private __gshared static uint m_stride;
/** Number of pixels in the frame buffer (whether visible or not). */
private __gshared static uint m_buffer_size;
/**
* Get the framebuffer width.
*/
public static @property uint width()
{
return m_width;
}
/**
* Get the framebuffer height.
*/
public static @property uint height()
{
return m_height;
}
/**
* Initialize a frame buffer.
*
* @param device_buffer Device frame buffer base address.
* @param buffer1 Frame buffer 1 base address.
* @param width Frame buffer width.
* @param height Frame buffer height.
* @param stride Frame buffer stride.
*/
static void initialize(uint * device_buffer, uint * buffer1, uint width, uint height, uint stride)
{
m_device_buffer = device_buffer;
m_buffer1 = buffer1;
m_width = width;
m_height = height;
m_stride = stride;
m_buffer_size = stride * height;
}
/**
* Clear the frame buffer to the given color.
*
* @param color Color to clear to.
*/
static void clear(uint color = 0u)
{
memset32(m_buffer1, color, m_buffer_size);
memset32(m_device_buffer, color, m_buffer_size);
}
/**
* Draw a solid rectangle on the framebuffer.
*
* @param x X coordinate of left of rectangle.
* @param y Y coordinate of top of rectangle.
* @param width Width of rectangle.
* @param height Height of rectangle.
* @param color Color of rectangle.
*/
static void rect(size_t x, size_t y, size_t width, size_t height, uint color)
{
for (size_t iy = 0u; iy < height; iy++)
{
size_t buffer_index = buffer_index(x, y);
memset32(&m_buffer1[buffer_index], color, width);
memset32(&m_device_buffer[buffer_index], color, width);
y++;
}
}
/**
* Draw a horizontal line.
*
* @param x X coordinate of left of line.
* @param y Y coordinate of line.
* @param width Width of line.
* @param color Color of line.
*/
static void hline(size_t x, size_t y, size_t width, uint color)
{
size_t buffer_index = buffer_index(x, y);
memset32(&m_buffer1[buffer_index], color, width);
memset32(&m_device_buffer[buffer_index], color, width);
}
/**
* Draw a vertical line.
*
* @param x X coordinate of line.
* @param y Y coordinate of top of line.
* @param height Height of line.
* @param color Color of line.
*/
static void vline(size_t x, size_t y, size_t height, uint color)
{
size_t buffer_index = buffer_index(x, y);
for (size_t iy = 0u; iy < height; iy++)
{
m_buffer1[buffer_index] = color;
m_device_buffer[buffer_index] = color;
buffer_index += m_stride;
}
}
/**
* Blit an 8-bit alpha bitmap to the framebuffer.
* The foreground will be white (based on the alpha value), and the
* background black.
*
* @param x X coordinate of left of target location.
* @param y Y coordinate of top of target location.
* @param alpha_bitmap 8-bit alpha-channel bitmap.
* @param width Bitmap width.
* @param height Bitmap height.
*/
static void blit_alpha_bitmap(size_t x, size_t y, const(ubyte) * alpha_bitmap,
size_t width, size_t height)
{
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];
m_buffer1[buffer_index] = (alpha << 16u) | (alpha << 8u) | alpha;
bitmap_index++;
}
memcpy32(&m_device_buffer[row_buffer_index], &m_buffer1[row_buffer_index], width);
y++;
}
}
/**
* Blit an 8-bit alpha bitmap to the framebuffer.
* The foreground will be white (based on the alpha value), and the
* background black.
*
* @param x X coordinate of left of target location.
* @param y Y coordinate of top of target location.
* @param alpha_bitmap 8-bit alpha-channel bitmap.
* @param width Bitmap width.
* @param height Bitmap height.
*/
static void blit_alpha_bitmap(size_t x, size_t y, const(ubyte)[] alpha_bitmap,
size_t width, size_t height)
{
blit_alpha_bitmap(x, y, alpha_bitmap.ptr, width, height);
}
/**
* Copy framebuffer rows up in the framebuffer.
*
* @param y Y coordinate of top of region to copy up.
* @param height Height of region to copy up.
* @param offset Number of rows to copy region up by.
*/
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 src_buffer_index = buffer_index(0u, y);
memcpy32(&m_buffer1[dest_buffer_index],
&m_buffer1[src_buffer_index],
(m_stride * height));
memcpy32(&m_device_buffer[dest_buffer_index],
&m_buffer1[dest_buffer_index],
(m_stride * height));
}
/**
* Copy a rectangle in the framebuffer.
*
* @param sx X coordinate of left of source rectangle.
* @param sy Y coordinate of top of source rectangle.
* @param width Width of rectangle to copy.
* @param height Height of rectangle to copy.
* @param dx X coordinate of left of destination rectangle.
* @param dy Y coordinate of top of destination rectangle.
*/
static void copy_rect(size_t sx, size_t sy, size_t width, size_t height,
size_t dx, size_t dy)
{
size_t dest_buffer_index = buffer_index(dx, dy);
size_t src_buffer_index = buffer_index(sx, sy);
for (size_t y = 0u; y < height; y++)
{
memcpy32(&m_buffer1[dest_buffer_index],
&m_buffer1[src_buffer_index],
width);
memcpy32(&m_device_buffer[dest_buffer_index],
&m_buffer1[dest_buffer_index],
width);
dest_buffer_index += m_stride;
src_buffer_index += m_stride;
}
}
/**
* Return the buffer index for the given X and Y coordinates.
*
* @param x X coordinate from left side of framebuffer.
* @param y Y coordinate from bottom side of framebuffer.
*
* @return Buffer index for the given X and Y coordinates.
*/
private static size_t buffer_index(size_t x, size_t y)
{
return y * m_stride + x;
}
}

View File

@ -1,58 +0,0 @@
/**
* GDT (Global Descriptor Table) functionality.
*/
module hulk.gdt;
import ldc.llvmasm;
struct Gdt
{
struct gdtr_t
{
ushort limit;
align(2) ulong offset;
}
static assert(gdtr_t.sizeof == 10u);
public enum size_t SELECTOR_NULL = 0x00u;
public enum size_t SELECTOR_KERNEL_CODE = 0x08u;
public enum size_t SELECTOR_KERNEL_DATA = 0x10u;
public enum size_t SELECTOR_COUNT = 3u;
static __gshared ulong[SELECTOR_COUNT] gdt;
static __gshared gdtr_t gdtr;
public static void initialize()
{
gdt[SELECTOR_KERNEL_CODE / ulong.sizeof] = 0x00_A_F_9A_000000_0000u;
gdt[SELECTOR_KERNEL_DATA / ulong.sizeof] = 0x00_C_F_92_000000_0000u;
gdtr.limit = gdt.sizeof - 1u;
gdtr.offset = cast(ulong)&gdt;
lgdt(&gdtr);
load_data_selectors(SELECTOR_KERNEL_DATA);
load_code_selector(SELECTOR_KERNEL_CODE);
}
private static void lgdt(gdtr_t * gdtr)
{
__asm("lgdt $0", "*m", gdtr);
}
private static void load_data_selectors(ulong selector)
{
__asm(`mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss`, "{rax}", selector);
}
private static void load_code_selector(ulong selector)
{
__asm(`push %rax
movabs $$1f, %rax
push %rax
lretq
1:`, "{rax}", selector);
}
}

View File

@ -1,40 +0,0 @@
/**
* HULK image header.
*/
module hulk.header;
import hulk.bootinfo;
/**
* Header describing the HULK image.
*/
struct HulkHeader
{
/**
* HULK BSS size.
*
* The OS loader must map memory to a zeroed range of this size.
*
* This value is really an integer, but is stored as a pointer because it
* is provided by the linker and LDC does not like us to cast it.
*/
void * hulk_bss_size;
/** Entry point. */
void * entry;
/** Stack size. */
ulong stack_size;
/** Virtual base address. */
ulong virt_base;
/** Stack top virtual address. */
ulong virt_stack_top;
/** Framebuffer virtual address. */
ulong virt_fb_buffer;
/** HULK boot information. */
BootInfo bootinfo;
}

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;
}
}

View File

@ -1,218 +0,0 @@
/**
* HIPPO, the HOS In-place Physical Page Organizer.
*
* HIPPO maintains a list of free physical pages "in place", meaning that the
* available page itself is used as the linked list entry so separate memory
* is not needed to keep track of the available pages.
*/
module hulk.hippo;
import hulk.hurl;
import hulk.klog;
struct Hippo
{
/**
* Linked list node entry for a single page.
*/
private static struct PhysicalPage
{
PhysicalPage * next;
}
/**
* Linked list node entry for a region.
*/
private static struct Region
{
ulong size;
Region * next;
}
/**
* Linked list of free physical pages.
*/
private static __gshared PhysicalPage * free_pages;
/**
* Linked list of free regions.
*/
private static __gshared Region * regions;
/**
* Number of free physical pages.
*/
private static __gshared size_t m_n_free_pages;
/**
* Free a physical page.
*
* @param phys Physical page address.
*/
public static void free_page(T)(T phys)
{
PhysicalPage * pp = cast(PhysicalPage *)phys;
pp.next = free_pages;
free_pages = pp;
m_n_free_pages++;
}
/**
* Allocate a physical page.
*
* @return Page address, or null if no pages are available.
*/
public static void * allocate_page()
{
PhysicalPage * pp;
if (free_pages !is null)
{
pp = free_pages;
free_pages = free_pages.next;
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;
}
/**
* 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.
*
* @return The number of free pages.
*/
public static @property size_t 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;
}
}
}

View File

@ -1,114 +0,0 @@
/**
* HULK, the HOS Ultra Light Kernel.
*/
module hulk.hulk;
import hulk.header;
import hulk.fb;
import hulk.console;
import hulk.memory;
import ldc.attributes;
import hulk.kfont;
import hulk.klog;
import hulk.hurl;
import hulk.hippo;
import hulk.pci;
import hulk.gdt;
import hulk.him;
import hulk.cpu;
import ldc.llvmasm;
import hulk.pic;
import hulk.acpi;
import hulk.apic;
import hulk.rtc;
import hulk.serial;
import hulk.usb;
import hulk.pit;
import hulk.time;
import hulk.test;
import hulk.thread;
extern extern(C) __gshared ubyte _hulk_bss_size;
@(ldc.attributes.section(".hulk_header"))
private __gshared HulkHeader hulk_header = {
&_hulk_bss_size, /* hulk_bss_size */
&hulk_start, /* entry */
Thread.STACK_SIZE, /* stack_size */
Hurl.HULK_BASE, /* virt_base */
Hurl.HULK_STACK_TOP, /* virt_stack_top */
Hurl.HULK_FRAMEBUFFER, /* virt_fb_buffer */
};
private void initialize_cpu()
{
/* Enable SSE. */
/* Turn off CR0.EM and turn on CR0.MP. */
write_cr0((read_cr0() & ~CR0_EM) | CR0_MP);
/* Set CR4.OSFXSR and CR4.OSXMMEXCPT. */
write_cr4(read_cr4() | CR4_OSFXSR | CR4_OSXMMEXCPT);
/* Enable OSXSAVE. */
write_cr4(read_cr4() | CR4_OSXSAVE);
/* Turn on NXE (no execute enable) flag in the EFER MSR. */
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NXE);
}
/**
* HULK entry point.
*/
void hulk_start()
{
suspend_interrupts();
initialize_cpu();
Serial.initialize();
Gdt.initialize();
Him.initialize();
Fb.initialize(cast(uint *)Hurl.HULK_FRAMEBUFFER,
cast(uint *)hulk_header.bootinfo.fb_buffer1_phys,
hulk_header.bootinfo.fb.width,
hulk_header.bootinfo.fb.height,
hulk_header.bootinfo.fb.stride);
Console.initialize();
Console.clear();
Klog.initialize();
Klog.writefln("\a5Welcome to HULK, the HOS UltraLight Kernel!");
Hurl.initialize(&hulk_header);
Pic.initialize();
Acpi.initialize(hulk_header.bootinfo.acpi_xsdt_phys);
Apic.initialize();
Rtc.initialize();
Pit.initialize();
Pci.initialize();
Thread.initialize();
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 (;;)
{
hlt();
}
}
void th()
{
for (;;)
{
Klog.writefln("Hello from thread.");
Time.msleep(1000);
}
}

View File

@ -1,65 +0,0 @@
SECTIONS
{
. = 0xFFFFFFFF80000000;
_hulk_mem_start = .;
_hulk_header_start = .;
.hulk_header :
{
KEEP(*(.hulk_header))
}
. = ALIGN(4K);
_hulk_header_end = .;
_hulk_text_start = .;
.text :
{
*(.text)
*(.text.*)
}
. = ALIGN(4K);
_hulk_text_end = .;
_hulk_rodata_start = .;
.rodata :
{
*(.rodata)
*(.rodata.*)
}
. = ALIGN(4K);
_hulk_rodata_end = .;
_hulk_data_start = .;
.data :
{
*(.data)
*(.data.*)
}
. = ALIGN(4K);
_hulk_data_end = .;
_hulk_binary_size = . - _hulk_mem_start;
_hulk_bss_start = .;
.bss :
{
*(COMMON)
*(.bss)
*(.bss.*)
}
. = ALIGN(4K);
_hulk_bss_end = .;
_hulk_mem_end = .;
_hulk_header_size = _hulk_header_end - _hulk_header_start;
_hulk_text_size = _hulk_text_end - _hulk_text_start;
_hulk_rodata_size = _hulk_rodata_end - _hulk_rodata_start;
_hulk_data_size = _hulk_data_end - _hulk_data_start;
_hulk_bss_size = _hulk_bss_end - _hulk_bss_start;
_hulk_mem_size = _hulk_mem_end - _hulk_mem_start;
}

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);
}
}

View File

@ -1,359 +0,0 @@
/**
* HURL, the HOS Unreal Region Locator.
*
* HURL provides virtual memory management for HULK.
*/
module hulk.hurl.hurl;
public import hulk.pagetable;
import hulk.cpu;
import hulk.hippo;
import hulk.memory;
import hulk.klog;
import hulk.bootinfo;
import hulk.header;
import hulk.linker_addresses;
import hulk.util;
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.
*/
private static __gshared PageTable * m_pt_base;
/**
* Build HULK page tables.
*
* @param physical_address_limit
* Limit of physical memory.
*/
private static void build_page_tables(HulkHeader * header, size_t physical_address_limit)
{
m_pt_base = allocate_pt();
/* Identity map all physical RAM. */
map_range(0u,
0u,
physical_address_limit,
PT_WRITABLE | PT_NO_EXECUTE);
ulong phys_address = header.bootinfo.hulk_phys;
ulong virt_address = HULK_BASE;
/* Map HULK header region. */
map_range(virt_address,
phys_address,
LinkerAddresses.hulk_header_size,
PT_NO_EXECUTE);
phys_address += LinkerAddresses.hulk_header_size;
virt_address += LinkerAddresses.hulk_header_size;
/* Map HULK text region. */
map_range(virt_address,
phys_address,
LinkerAddresses.hulk_text_size,
0u);
phys_address += LinkerAddresses.hulk_text_size;
virt_address += LinkerAddresses.hulk_text_size;
/* Map HULK rodata region. */
map_range(virt_address,
phys_address,
LinkerAddresses.hulk_rodata_size,
PT_NO_EXECUTE);
phys_address += LinkerAddresses.hulk_rodata_size;
virt_address += LinkerAddresses.hulk_rodata_size;
/* Map HULK data region. */
map_range(virt_address,
phys_address,
LinkerAddresses.hulk_data_size,
PT_WRITABLE | PT_NO_EXECUTE);
virt_address += LinkerAddresses.hulk_data_size;
/* Map HULK BSS region. */
map_range(virt_address,
header.bootinfo.bss_phys,
LinkerAddresses.hulk_bss_size,
PT_WRITABLE | PT_NO_EXECUTE);
/* Map HULK stack. */
map_range(HULK_STACK_TOP - header.stack_size,
header.bootinfo.stack_phys,
header.stack_size,
PT_WRITABLE | PT_NO_EXECUTE);
/* Map device framebuffer. */
map_range(HULK_FRAMEBUFFER,
cast(ulong)header.bootinfo.fb.buffer,
header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u,
PT_PAT | PT_WRITABLE | PT_NO_EXECUTE);
/* Map framebuffer buffer1. */
map_range(cast(ulong)header.bootinfo.fb_buffer1_phys,
cast(ulong)header.bootinfo.fb_buffer1_phys,
header.bootinfo.fb.height * header.bootinfo.fb.stride * 4u,
PT_WRITABLE | PT_NO_EXECUTE);
write_cr3(cast(ulong)m_pt_base);
}
/**
* Reclaim the pages that were used for the bootloader page tables.
*
* @param pt
* The page table to reclaim pages from.
* @param level
* Page table level (internally used while recursing the page tables).
*
* @return Number of pages reclaimed.
*/
private static size_t reclaim_bootloader_page_table_pages(PageTable * pt, size_t level = 0u)
{
map(pt, pt, PT_WRITABLE | PT_NO_EXECUTE);
size_t reclaimed_pages;
for (size_t i = 0u; i < PageTable.N_ENTRIES; i++)
{
PageTableEntry pte = (*pt)[i];
if (pte.present && (level < 2u) && !pte.huge)
{
/* For the first two levels of page tables, first
* recurse and free pages from the lower level page
* tables before reclaiming this entry. */
reclaimed_pages += reclaim_bootloader_page_table_pages(pte.follow(), level + 1u);
}
}
Hippo.free_page(pt);
reclaimed_pages++;
return reclaimed_pages;
}
/**
* Initialize HURL.
*
* @param bootinfo HULK boot information structure.
*/
public static void initialize(HulkHeader * header)
{
PageTable * bootloader_pt_base = cast(PageTable *)read_cr3();
/*
* Ok, what we do here is iterate through all of the memory map regions
* in the bootinfo memory_map array and mark each page that is not in
* use in a reserved range as available. Within the available memory
* regions, we have to watch out for the following items:
* 1) HULK binary (header + text + rodata + data)
* 2) Framebuffer
* In addition to these ranges, there are also pages for the following
* purposes that are not represented in the memory map from the
* bootloader:
* 3) HULK BSS
* 4) HULK stack
* 5) bootloader page table pages
* 6) framebuffer buffer1
*/
size_t usable_memory;
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);
ulong[2][2] reserved_regions = [
[header.bootinfo.hulk_phys, LinkerAddresses.hulk_binary_size],
[cast(ulong)header.bootinfo.fb.buffer, fb_size],
];
for (size_t ri = 0u; ri < reserved_regions.length; ri++)
{
Klog.writefln("Reserved region %p %uKB", reserved_regions[ri][0], reserved_regions[ri][1] >> 10);
reserved_regions[ri][1] += reserved_regions[ri][0];
}
for (size_t bii = 0u; bii < header.bootinfo.memory_map_count; bii++)
{
if ((header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Bootloader) ||
(header.bootinfo.memory_map[bii].type == BootInfo.MemoryRegion.Type.Conventional))
{
ulong phys = header.bootinfo.memory_map[bii].base;
ulong size = header.bootinfo.memory_map[bii].size;
ulong phys_end = phys + size;
if (phys_end > physical_address_limit)
{
physical_address_limit = phys_end;
}
usable_memory += header.bootinfo.memory_map[bii].size;
add_region(phys, size, reserved_regions);
}
}
/* 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
* build new page tables to replace the bootloader page tables.
*/
build_page_tables(header, physical_address_limit);
/*
* After we have switched to the newly constructed page tables, we can
* iterate through and free the bootloader page tables. They are most
* likely already mapped because we just identity mapped every page up
* to the physical_address_limit determined above. But just in case the
* bootloader used the last pages of RAM for the page table pages, we
* will make sure to map them as we traverse them.
*/
size_t reclaimed_bootloader_page_table_memory = reclaim_bootloader_page_table_pages(bootloader_pt_base) << 10u;
usable_memory += reclaimed_bootloader_page_table_memory;
usable_memory += LinkerAddresses.hulk_bss_size;
usable_memory += header.stack_size;
usable_memory += fb_size;
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("Free pages: %u", Hippo.n_free_pages);
// Hippo.dump_regions();
}
/**
* 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;
for (size_t level = 0; level < 4u; level++)
{
PageTableEntry * ppte = pt.entry(virtual, level);
if (level < 3u)
{
PageTableEntry pte = *ppte;
if (pte.present)
{
pt = pte.follow();
}
else
{
PageTable * next_pt = allocate_pt();
*ppte = PageTableEntry(next_pt, PT_WRITABLE | PT_PRESENT);
pt = next_pt;
}
}
else
{
*ppte = PageTableEntry(cast(ulong)physical, flags | PT_PRESENT);
}
}
}
public static void map(T, U)(T virtual, U physical, ulong flags)
{
map(cast(ulong)virtual, cast(ulong)physical, flags);
}
public static void map_range(ulong virtual, ulong physical, size_t length, ulong flags)
{
size_t end = virtual + length;
while (virtual < end)
{
map(virtual, physical, flags);
virtual += PAGE_SIZE;
physical += PAGE_SIZE;
}
}
public static void map_range(T, U)(T virtual, U physical, size_t length, ulong flags)
{
map_range(cast(ulong)virtual, cast(ulong)physical, length, flags);
}
public static void identity_map_range(ulong address, size_t length, ulong flags)
{
size_t end = address + length;
for (size_t page = address & 0xFFFF_FFFF_FFFF_F000u; page < end; page += PAGE_SIZE)
{
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)
{
Klog.writefln("Debugging page table lookup of 0x%x", address);
PageTable * pt = m_pt_base;
for (size_t level = 0; level < 4u; level++)
{
PageTableEntry pte = *pt.entry(address, level);
Klog.writefln("Level %u, index %u, entry = 0x%x", level, pt.index(address, level), pte);
if (pte.present)
{
pt = pte.follow();
}
else
{
break;
}
if (pte.huge)
{
break;
}
}
}
private static PageTable * allocate_pt()
{
PageTable * pt = cast(PageTable *)Hippo.allocate_page();
memset64(pt, 0u, PAGE_SIZE / 8u);
return pt;
}
}

View File

@ -1,3 +0,0 @@
module hulk.hurl;
public import hulk.hurl.hurl;

View File

@ -1,87 +0,0 @@
/**
* HULK Kernel Log buffer.
*/
module hulk.klog;
import core.stdc.stdarg;
import hulk.console;
static import hulk.writef;
import hulk.cpu;
struct Klog
{
/**
* Kernel buffer size log.
* 16 gives a kernel buffer size of 64KB.
*/
private enum size_t KLOG_SIZE_LOG = 16u;
/** Kernel buffer size. */
private enum size_t KLOG_SIZE = 1u << KLOG_SIZE_LOG;
/** Kernel log buffer. */
private static __gshared align(4096) ubyte[KLOG_SIZE] klog_buffer;
/** Write index in the kernel log buffer. */
private static __gshared size_t klog_index;
/**
* Initialize the klog module.
*/
public static void initialize()
{
}
/**
* Write a formatted string to the kernel log.
*
* @param s Format string.
* @param args Variable arguments structure.
*/
public static void writef(string s, va_list args)
{
hulk.writef.writefv(function void(ubyte ch) {
Console.write(ch);
}, s, args);
}
/**
* Write a formatted string to the kernel log.
*
* @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 kernel log.
*
* @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);
}
/**
* Print a fatal kernel error and loop forever.
*/
public static void fatal_error(T...)(T args)
{
writef("FATAL ERROR: ");
writefln(args);
cli();
for (;;)
{
}
}
}

View File

@ -1,139 +0,0 @@
/**
* This module provides access to linker-defined symbols.
*/
module hulk.linker_addresses;
private extern extern(C) __gshared ubyte _hulk_header_start;
private extern extern(C) __gshared ubyte _hulk_header_end;
private extern extern(C) __gshared ubyte _hulk_header_size;
private extern extern(C) __gshared ubyte _hulk_text_start;
private extern extern(C) __gshared ubyte _hulk_text_end;
private extern extern(C) __gshared ubyte _hulk_text_size;
private extern extern(C) __gshared ubyte _hulk_rodata_start;
private extern extern(C) __gshared ubyte _hulk_rodata_end;
private extern extern(C) __gshared ubyte _hulk_rodata_size;
private extern extern(C) __gshared ubyte _hulk_data_start;
private extern extern(C) __gshared ubyte _hulk_data_end;
private extern extern(C) __gshared ubyte _hulk_data_size;
private extern extern(C) __gshared ubyte _hulk_bss_start;
private extern extern(C) __gshared ubyte _hulk_bss_end;
private extern extern(C) __gshared ubyte _hulk_bss_size;
/* hulk_binary includes header, text, rodata, and data */
private extern extern(C) __gshared ubyte _hulk_binary_size;
/* hulk_mem includes header, text, rodata, data, and bss */
private extern extern(C) __gshared ubyte _hulk_mem_start;
private extern extern(C) __gshared ubyte _hulk_mem_end;
private extern extern(C) __gshared ubyte _hulk_mem_size;
/**
* This struct provides access to linker-defined symbols.
*/
public struct LinkerAddresses
{
public static @property ulong hulk_header_start()
{
return cast(ulong)&_hulk_header_start;
}
public static @property ulong hulk_header_end()
{
return cast(ulong)&_hulk_header_end;
}
public static @property ulong hulk_header_size()
{
return cast(ulong)&_hulk_header_size;
}
public static @property ulong hulk_text_start()
{
return cast(ulong)&_hulk_text_start;
}
public static @property ulong hulk_text_end()
{
return cast(ulong)&_hulk_text_end;
}
public static @property ulong hulk_text_size()
{
return cast(ulong)&_hulk_text_size;
}
public static @property ulong hulk_rodata_start()
{
return cast(ulong)&_hulk_rodata_start;
}
public static @property ulong hulk_rodata_end()
{
return cast(ulong)&_hulk_rodata_end;
}
public static @property ulong hulk_rodata_size()
{
return cast(ulong)&_hulk_rodata_size;
}
public static @property ulong hulk_data_start()
{
return cast(ulong)&_hulk_data_start;
}
public static @property ulong hulk_data_end()
{
return cast(ulong)&_hulk_data_end;
}
public static @property ulong hulk_data_size()
{
return cast(ulong)&_hulk_data_size;
}
public static @property ulong hulk_bss_start()
{
return cast(ulong)&_hulk_bss_start;
}
public static @property ulong hulk_bss_end()
{
return cast(ulong)&_hulk_bss_end;
}
public static @property ulong hulk_bss_size()
{
return cast(ulong)&_hulk_bss_size;
}
public static @property ulong hulk_binary_size()
{
return cast(ulong)&_hulk_binary_size;
}
public static @property ulong hulk_mem_start()
{
return cast(ulong)&_hulk_mem_start;
}
public static @property ulong hulk_mem_end()
{
return cast(ulong)&_hulk_mem_end;
}
public static @property ulong hulk_mem_size()
{
return cast(ulong)&_hulk_mem_size;
}
}

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;
}
}

View File

@ -1,62 +0,0 @@
/**
* Memory-related functions.
*/
module hulk.memory;
import ldc.llvmasm;
void memset(void * dest, ubyte v, size_t n)
{
__asm("rep stosb",
"{al},{rcx},{rdi},~{rcx},~{rdi},~{memory}",
v, n, dest);
}
void memset16(void * dest, ushort v, size_t n)
{
__asm("rep stosw",
"{ax},{rcx},{rdi},~{rcx},~{rdi},~{memory}",
v, n, dest);
}
void memset32(void * dest, uint v, size_t n)
{
__asm("rep stosl",
"{eax},{rcx},{rdi},~{rcx},~{rdi},~{memory}",
v, n, dest);
}
void memset64(void * dest, ulong v, size_t n)
{
__asm("rep stosq",
"{rax},{rcx},{rdi},~{rcx},~{rdi},~{memory}",
v, n, dest);
}
void memcpy(void * dest, const(void) * src, size_t n)
{
__asm("rep movsb",
"{rcx},{rsi},{rdi},~{rcx},~{rsi},~{rdi},~{memory}",
n, src, dest);
}
void memcpy16(void * dest, const(void) * src, size_t n)
{
__asm("rep movsw",
"{rcx},{rsi},{rdi},~{rcx},~{rsi},~{rdi},~{memory}",
n, src, dest);
}
void memcpy32(void * dest, const(void) * src, size_t n)
{
__asm("rep movsl",
"{rcx},{rsi},{rdi},~{rcx},~{rsi},~{rdi},~{memory}",
n, src, dest);
}
void memcpy64(void * dest, const(void) * src, size_t n)
{
__asm("rep movsq",
"{rcx},{rsi},{rdi},~{rcx},~{rsi},~{rdi},~{memory}",
n, src, dest);
}

View File

@ -1,70 +0,0 @@
/**
* MTRR (Memory Type Range Register) support for HULK.
*/
module hulk.mtrr;
import hulk.cpu;
import hulk.klog;
enum ulong MTRR_TYPE_UC = 0u; /* Uncacheable */
enum ulong MTRR_TYPE_WC = 1u; /* Write-Combining */
enum ulong MTRR_TYPE_WT = 4u; /* Writethrough */
enum ulong MTRR_TYPE_WP = 5u; /* Write-Protect */
enum ulong MTRR_TYPE_WB = 6u; /* Writeback */
enum uint MSR_MTRRCAP = 0xFEu;
enum uint MSR_MTRRDEFTYPE = 0x2FFu;
enum uint MSR_MTRRPHYSBASE0 = 0x200u;
enum uint MSR_MTRRPHYSMASK0 = 0x201u;
enum ulong MTRRCAP_WC = 0x400u;
enum ulong MTRRCAP_FIX = 0x100u;
enum ulong MTRRCAP_VCNT_MASK = 0xFFu;
enum ulong MTRRDEFTYPE_E = 0x800u;
enum ulong MTRRDEFTYPE_FE = 0x400u;
enum ulong MTRRDEFTYPE_TYPE_MASK = 0xFFu;
enum ulong MTRRPHYSBASE_PHYSBASE_MASK = 0x000F_FFFF_FFFF_F000u;
enum ulong MTRRPHYSBASE_TYPE_MASK = 0xFFu;
enum ulong MTRRPHYSMASK_PHYSMASK_MASK = 0x000F_FFFF_FFFF_F000u;
enum ulong MTRRPHYSMASK_VALID = 0x800u;
struct Mtrr
{
public static void initialize()
{
uint ebx;
uint ecx;
uint edx;
cpuid1(&ebx, &ecx, &edx);
if ((edx & CPUID_1_EDX_MTRR) == 0u)
{
Klog.writefln("CPU does not support MTRR");
return;
}
const(ulong) mtrrcap = rdmsr(MSR_MTRRCAP);
const(ulong) mtrrdeftype = rdmsr(MSR_MTRRDEFTYPE);
}
private static ulong get_phys_base(uint n)
{
return rdmsr(MSR_MTRRPHYSBASE0 + 2u * n);
}
private static void set_phys_base(uint n, ulong physbase)
{
wrmsr(MSR_MTRRPHYSBASE0 + 2u * n, physbase);
}
private static ulong get_phys_mask(uint n)
{
return rdmsr(MSR_MTRRPHYSMASK0 + 2u * n);
}
private static void set_phys_mask(uint n, ulong physmask)
{
wrmsr(MSR_MTRRPHYSMASK0 + 2u * n, physmask);
}
}

View File

@ -1,194 +0,0 @@
/**
* Page table structures.
*/
module hulk.pagetable;
/** Page table entry attributes. @{ */
enum ulong PT_PRESENT = 0x1u;
enum ulong PT_WRITABLE = 0x2u;
enum ulong PT_USER = 0x4u;
enum ulong PT_WRITE_THROUGH = 0x8u;
enum ulong PT_DISABLE_CACHE = 0x10u;
enum ulong PT_ACCESSED = 0x20u;
enum ulong PT_DIRTY = 0x40u;
enum ulong PT_PAT = 0x80u; /* Page table entry bit. */
enum ulong PT_HUGE_PAGE = 0x80u; /* Page directory entry bit. */
enum ulong PT_GLOBAL = 0x100u;
enum ulong PT_NO_EXECUTE = 0x8000_0000_0000_0000u;
/** @} */
/** Page size in bytes. */
enum ulong PAGE_SIZE = 4096u;
/**
* Structure that represents a page table entry.
*/
struct PageTableEntry
{
/** The raw page table entry is a 64-bit ulong. */
public ulong entry;
alias entry this;
/**
* Construct a page table entry.
*
* @param address
* Address pointed to by the page table entry (ulong or pointer).
* @param flags
* Page table flags (ORed set of PT_*).
*/
this(T)(T address, ulong flags = 0u)
{
entry = cast(ulong)address | flags;
}
/**
* Return whether the page is present.
*/
public @property bool present() const
{
return (entry & PT_PRESENT) != 0u;
}
/**
* Return whether the page is writable.
*/
public @property bool writable() const
{
return (entry & PT_WRITABLE) != 0u;
}
/**
* Return whether the page is a user page.
*/
public @property bool user() const
{
return (entry & PT_USER) != 0u;
}
/**
* Return whether the page is write-through.
*/
public @property bool write_through() const
{
return (entry & PT_WRITE_THROUGH) != 0u;
}
/**
* Return whether the page has disable cache set.
*/
public @property bool disable_cache() const
{
return (entry & PT_DISABLE_CACHE) != 0u;
}
/**
* Return whether the page is huge.
*/
public @property bool huge() const
{
return (entry & PT_HUGE_PAGE) != 0u;
}
/**
* Return whether the page is global.
*/
public @property bool global() const
{
return (entry & PT_GLOBAL) != 0u;
}
/**
* Return whether the page is no-execute.
*/
public @property bool no_execute() const
{
return (entry & PT_NO_EXECUTE) != 0u;
}
/**
* Return whether the page is dirty.
*/
public @property bool dirty() const
{
return (entry & PT_DIRTY) != 0u;
}
/**
* Return whether the page is accessed.
*/
public @property bool accessed() const
{
return (entry & PT_ACCESSED) != 0u;
}
/**
* Follow the page table entry to the next page table it points to.
*/
public PageTable * follow() const
{
return cast(PageTable *)(entry & 0x7FFF_FFFF_FFFF_F000u);
}
}
static assert(PageTableEntry.sizeof == 8u);
/**
* Structure that represents a page table.
*/
struct PageTable
{
/** Number of page table entries in a page table. */
enum size_t N_ENTRIES = 512u;
/** Page table entries. */
private PageTableEntry[N_ENTRIES] entries;
/**
* Access the PageTableEntry for the given address and page table level.
*
* @param address
* Address to look up the page table entry (ulong or pointer).
* @param level
* Page table level (0-4).
*/
public PageTableEntry * entry(T)(T address, ulong level)
{
return &entries[index(cast(ulong)address, level)];
}
/**
* Determine the index into the page table for the given address and page
* table level.
*
* @param address
* Address to look up the page table entry (ulong or pointer).
* @param level
* Page table level (0-4).
*/
public ulong index(T)(T address, ulong level)
{
return (cast(ulong)address >> (39u - (9u * level))) & 0x1FFu;
}
/**
* Access a page table entry by linear index.
*
* @param index
* Page table index (0-511).
*
* @return PageTableEntry at the given index.
*/
public PageTableEntry opIndex(size_t index)
{
return entries[index];
}
}
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));
}

View File

@ -1,452 +0,0 @@
/**
* PCI (Peripheral Component Interconnect) functionality.
*/
module hulk.pci;
import hulk.cpu;
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
{
/** IO address for PCI configuration address. */
enum IO_ADDR_CONFIG_ADDRESS = 0xCF8u;
/** IO address for PCI configuration data. */
enum IO_ADDR_CONFIG_DATA = 0xCFCu;
/** Maximum number of devices on a bus. */
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. */
static struct SGMADesc
{
uint[2] base_address_a;
ushort pci_segment_group_number;
ubyte start_pci_bus_number;
ubyte end_pci_bus_number;
uint _reserved;
public @property ulong base_address() const
{
return base_address_a[0] | (cast(ulong)base_address_a[1] << 32);
}
}
static assert(SGMADesc.sizeof == 16);
static assert(SGMADesc.alignof == 4);
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);
}
private static void write_config_register(Address address, uint register_id, uint value)
{
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);
out32(IO_ADDR_CONFIG_DATA, value);
}
static struct Configuration
{
static struct HeaderType0
{
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[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()
{
Klog.writefln("\a3Initializing PCI");
MCFG * mcfg = cast(MCFG *)Acpi.get_table("MCFG");
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];
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++)
{
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);
}
}
}
}
}

View File

@ -1,16 +0,0 @@
/**
* PIC (Programmable Interrupt Controller) functionality.
*/
module hulk.pic;
import hulk.cpu;
struct Pic
{
public static void initialize()
{
/* Disable the PIC. */
out8(0xA1u, 0xFFu);
out8(0x21u, 0xFFu);
}
}

View File

@ -1,45 +0,0 @@
/**
* PIT (Programmable Interval Timer) functionality.
*/
module hulk.pit;
import hulk.cpu;
import hulk.klog;
import hulk.console;
import hulk.time;
struct Pit
{
/** PIT input frequency in 1/1000 Hz. */
private enum ulong PIT_FREQUENCY_1000HZ = 1_193_181_667u;
private enum ulong TARGET_INTERRUPT_HZ = 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;
/** PIT channel 0 I/O port. */
private enum ubyte PORT_CH_0 = 0x40u;
/** PIT channel 1 I/O port. */
private enum ubyte PORT_CH_1 = 0x41u;
/** PIT channel 2 I/O port. */
private enum ubyte PORT_CH_2 = 0x42u;
/** PIT mode/command register I/O port. */
private enum ubyte PORT_MC = 0x43u;
private enum ubyte MC_CH_0 = 0x00u;
private enum ubyte MC_LO_HI = 0x30u;
private enum ubyte MC_RATE_GENERATOR = 0x04u;
private enum ubyte MC_BINARY = 0x00u;
public static void initialize()
{
Klog.writefln("\a3Initializing PIT");
out8(PORT_MC, MC_CH_0 | MC_LO_HI | MC_RATE_GENERATOR | MC_BINARY);
out8(PORT_CH_0, PIT_DIVIDER & 0xFFu);
out8(PORT_CH_0, PIT_DIVIDER >> 8u);
}
public static void isr()
{
Time.ms_isr();
}
}

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;
}
}

View File

@ -1,150 +0,0 @@
/**
* RTC (Real-Time Clock) functionality.
*/
module hulk.rtc;
import hulk.cpu;
import hulk.klog;
import hulk.console;
struct Rtc
{
private enum ubyte PORT_SELECT = 0x70u;
private enum ubyte PORT_DATA = 0x71u;
private enum ubyte DISABLE_NMI = 0x80u;
private enum ubyte REG_RTC_SECONDS = 0x0u;
private enum ubyte REG_RTC_MINUTES = 0x2u;
private enum ubyte REG_RTC_HOURS = 0x4u;
private enum ubyte REG_RTC_DAY = 0x7u;
private enum ubyte REG_RTC_MONTH = 0x8u;
private enum ubyte REG_RTC_YEAR = 0x9u;
private enum ubyte REG_SR_A = 0xAu;
private enum ubyte REG_SR_B = 0xBu;
private enum ubyte REG_SR_C = 0xCu;
private enum ubyte SR_A_RTC_UPDATE_IN_PROGRESS = 0x80u;
private enum ubyte SR_B_24HOUR = 0x02u;
private enum ubyte SR_B_BINARY = 0x04u;
private enum ubyte SR_B_ENABLE_IRQ = 0x40u;
private static __gshared bool rtc_24hour_mode;
private static __gshared bool rtc_binary_mode;
static struct time
{
public ubyte year;
public ubyte month;
public ubyte day;
public ubyte hour;
public ubyte minute;
public ubyte second;
}
public static void initialize()
{
Klog.writefln("\a3Initializing RTC");
/* Enable IRQ 8 to receive RTC interrupts. */
ubyte sr_b = read_register(REG_SR_B, true);
write_register(REG_SR_B, sr_b | SR_B_ENABLE_IRQ, true);
/* Record RTC mode flags from status register B. */
rtc_24hour_mode = (sr_b & SR_B_24HOUR) != 0u;
rtc_binary_mode = (sr_b & SR_B_BINARY) != 0u;
/* Send EOI to enable more RTC interrupts and re-enable NMIs. */
eoi();
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()
{
/* Read from status register C to clear the interrupt. */
read_register(REG_SR_C);
}
public static void isr()
{
static __gshared ulong count;
count++;
if ((count % 1024) == 0u)
{
Console.update_header();
}
eoi();
}
public static time read_rtc_time()
{
time[2] times;
size_t reads;
do
{
times[reads & 1] = read_rtc_time_unsafe();
reads++;
} while ((reads < 2u) || (times[0] != times[1]));
return times[0];
}
private static time read_rtc_time_unsafe()
{
time t;
while ((read_register(REG_SR_A) & SR_A_RTC_UPDATE_IN_PROGRESS) != 0u)
{
}
t.year = read_rtc_time_register(REG_RTC_YEAR);
t.month = read_rtc_time_register(REG_RTC_MONTH);
t.day = read_rtc_time_register(REG_RTC_DAY);
t.minute = read_rtc_time_register(REG_RTC_MINUTES);
t.second = read_rtc_time_register(REG_RTC_SECONDS);
/* Hour field is treated differently because it might have 'PM' flag
* in highest bit if we're in 24-hour mode. */
ubyte hour = read_register(REG_RTC_HOURS);
ubyte pm = hour >> 7u;
hour &= 0x7Fu;
if (!rtc_binary_mode)
{
/* Hour is in BCD format; convert to binary. */
hour = bcd_to_binary(hour);
}
if (!rtc_24hour_mode)
{
/* Hour is in 12-hour format; convert to 24-hour format. */
hour = cast(ubyte)((hour % 12u) + pm * 12u);
}
t.hour = hour;
return t;
}
private static ubyte read_rtc_time_register(ubyte register)
{
ubyte value = read_register(register);
if (!rtc_binary_mode)
{
value = bcd_to_binary(value);
}
return value;
}
private static ubyte bcd_to_binary(ubyte bcd)
{
return (bcd & 0xFu) + ((bcd & 0xF0u) >> 4u) * 10u;
}
}

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);
}
}

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);
}
}
}

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;
}
}
}

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)
{
}
}
}

View File

@ -1,3 +0,0 @@
module hulk.usb;
public import hulk.usb.usb;

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);
}
}
}
}

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);
}
}
}

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);
}

View File

@ -1,3 +0,0 @@
module hulk.ver;
enum VERSION = "0.18.0";

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;
}
}

View File

@ -1,191 +0,0 @@
/**
* HULK Formatted stream writing support.
*/
module hulk.writef;
import core.stdc.stdarg;
alias ch_out_fn = void function(ubyte);
/**
* Format a string and write characters to the given output function.
*
* @param ch_out Character output function.
* @param s Format string.
* @param args Variable arguments structure.
*
* @return Number of characters written.
*/
size_t writefv(ch_out_fn ch_out, string s, va_list args)
{
size_t length_written;
bool escape = false;
char pad = ' ';
size_t width;
foreach (char c; s)
{
if (escape)
{
if (c == 'c')
{
ulong ch;
va_arg(args, ch);
ch_out(cast(ubyte)ch);
length_written++;
escape = false;
}
else if (c == 'p')
{
ulong v;
va_arg(args, v);
length_written += write_hex(v, true, true, pad, width, ch_out);
escape = false;
}
else if (c == 'x')
{
ulong v;
va_arg(args, v);
length_written += write_hex(v, false, false, pad, width, ch_out);
escape = false;
}
else if (c == 'X')
{
ulong v;
va_arg(args, v);
length_written += write_hex(v, true, false, pad, width, ch_out);
escape = false;
}
else if (c == 'u')
{
ulong v;
va_arg(args, v);
length_written += write_udec(v, pad, width, ch_out);
escape = false;
}
else if ((c == '0') && (width == 0u))
{
pad = '0';
}
else if (('0' <= c) && (c <= '9'))
{
width = (width * 10u) + (c - '0');
}
else
{
ch_out(c);
escape = false;
}
}
else if (c == '%')
{
escape = true;
pad = ' ';
width = 0u;
}
else
{
ch_out(c);
}
}
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.
*
* @param v Value to format.
* @param upper Whether to use uppercase letters.
* @param pointer Whether to format as a pointer.
* @param pad Pad character to use.
* @param width Field width.
* @param ch_out Character output function.
*
* @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)
{
static __gshared const(char)[16] hex_chars_lower = "0123456789abcdef";
static __gshared const(char)[16] hex_chars_upper = "0123456789ABCDEF";
char[19] buf;
size_t digit;
size_t buf_idx;
do
{
ulong n = v & 0xFu;
buf[buf_idx] = upper ? hex_chars_upper[n] : hex_chars_lower[n];
v >>= 4u;
buf_idx++;
digit++;
if (pointer && ((digit % 4) == 0) && (v != 0u))
{
buf[buf_idx] = '_';
buf_idx++;
}
} while (v != 0u);
size_t length_written = buf_idx;
while (width > buf_idx)
{
ch_out(pad);
width--;
length_written++;
}
do
{
buf_idx--;
ch_out(buf[buf_idx]);
} while (buf_idx != 0u);
return length_written;
}
/**
* Format a value in decimal to the given output function.
*
* @param v Value to format.
* @param pad Pad character to use.
* @param width Field width.
* @param ch_out Character output function.
*
* @return Number of characters written.
*/
private size_t write_udec(ulong v, char pad, size_t width, ch_out_fn ch_out)
{
char[20] buf;
size_t i;
do
{
ulong d = v % 10u;
buf[i] = cast(char)(d + '0');
v /= 10u;
i++;
} while (v != 0u);
size_t length_written = i;
while (width > i)
{
ch_out(pad);
width--;
length_written++;
}
do
{
i--;
ch_out(buf[i]);
} while (i != 0u);
return length_written;
}

103
src/klog.c Normal file
View File

@ -0,0 +1,103 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h>
#include "klog.h"
#include "kfont.h"
#include "fb.h"
#include "fb_text.h"
#include "stream.h"
#include "hos_printf.h"
#include "mem.h"
static struct {
size_t console_width;
size_t console_height;
size_t x;
size_t y;
bool need_shift;
} klog;
static void shift_line(void)
{
uint32_t * fb = fb_addr();
uint32_t w = fb_width();
size_t console_fb_line_words = w * kfont.line_height;
for (size_t row = 0u; row < klog.console_height; row++)
{
memcpy32(fb, fb + console_fb_line_words, console_fb_line_words);
fb += console_fb_line_words;
}
fb_fill(0, kfont.line_height * (klog.console_height - 1u), w, kfont.line_height, 0u, 0x2Cu, 0x55u);
}
static void klog_fb_write1(char c)
{
if (klog.need_shift)
{
shift_line();
klog.need_shift = false;
}
if (c == '\n')
{
if (klog.y == (klog.console_height - 1u))
{
klog.need_shift = true;
}
else
{
klog.y++;
}
klog.x = 0u;
}
else
{
int px = klog.x * kfont.advance;
int py = klog.y * kfont.line_height;
fb_text_render_char(c, px, py, 0xFFu, 0x80u, 0u);
klog.x++;
if (klog.x == klog.console_width)
{
if (klog.y == (klog.console_height - 1u))
{
klog.need_shift = true;
}
else
{
klog.y++;
}
klog.x = 0u;
}
}
}
static void klog_fb_write(const char * src, size_t length)
{
for (size_t i = 0u; i < length; i++)
{
klog_fb_write1(src[i]);
}
}
static const stream_t klog_fb_stream = {
klog_fb_write,
klog_fb_write1,
};
void klog_init(void)
{
klog.console_width = fb_width() / kfont.advance;
klog.console_height = fb_height() / kfont.line_height;
klog.x = 0u;
klog.y = 0u;
klog.need_shift = false;
fb_fill(0, 0, fb_width(), fb_height(), 0u, 0x2Cu, 0x55u);
}
void klog_printf(const char * fmt, ...)
{
va_list va;
va_start(va, fmt);
hos_vprintf(&klog_fb_stream, fmt, va);
va_end(va);
}

7
src/klog.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef KLOG_H
#define KLOG_H
void klog_init(void);
void klog_printf(const char * fmt, ...);
#endif

41
src/link.ld Normal file
View File

@ -0,0 +1,41 @@
ENTRY(hos_start)
SECTIONS
{
. = 1M;
_hos_mem_start = .;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
_stack_size = 16K;
.stack (NOLOAD) : ALIGN(4K)
{
_stack_start = .;
. = . + _stack_size;
_stack_end = .;
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
. = ALIGN(4K);
_hos_mem_end = .;
}

100
src/mbinfo.c Normal file
View File

@ -0,0 +1,100 @@
#include "multiboot2.h"
#include "fb.h"
#include "mem.h"
#include <stdint.h>
#include "klog.h"
#include "mm.h"
static uint32_t mbinfo[2048];
static void mbinfo_process_tag(const multiboot2_info_tag_t * tag)
{
switch (tag->type)
{
case MULTIBOOT2_INFO_BOOT_COMMAND_LINE:
{
multiboot2_info_boot_command_line_t * cl =
(multiboot2_info_boot_command_line_t *)tag;
klog_printf("Kernel boot command line: '%s'\n", cl->string);
}
break;
case MULTIBOOT2_INFO_BOOT_LOADER_NAME:
{
multiboot2_info_boot_loader_name_t * bln =
(multiboot2_info_boot_loader_name_t *)tag;
klog_printf("Boot loader: '%s'\n", bln->string);
}
break;
case MULTIBOOT2_INFO_MEMORY_MAP:
{
multiboot2_info_memory_map_t * mmap_info =
(multiboot2_info_memory_map_t *)tag;
size_t sz = sizeof(mmap_info->header) +
sizeof(mmap_info->entry_size) +
sizeof(mmap_info->entry_version);
multiboot2_info_memory_map_entry_t * entry = &mmap_info->entries[0];
for (;;)
{
sz += mmap_info->entry_size;
if (sz > mmap_info->header.size)
{
break;
}
if (entry->type == MULTIBOOT2_MEMORY_MAP_TYPE_RAM)
{
klog_printf("Memory region %16lx : %16lx\n",
entry->base_addr,
entry->length);
mm_register_ram_region(entry->base_addr, entry->length);
}
entry = (multiboot2_info_memory_map_entry_t *)((uintptr_t)entry + mmap_info->entry_size);
}
}
break;
case MULTIBOOT2_INFO_FRAMEBUFFER_INFO:
{
multiboot2_info_framebuffer_info_t * fbinfo =
(multiboot2_info_framebuffer_info_t *)tag;
fb_init((uint32_t *)(uintptr_t)fbinfo->framebuffer_addr,
fbinfo->framebuffer_width,
fbinfo->framebuffer_height,
fbinfo->framebuffer_pitch);
}
break;
}
}
static void process_tags(const multiboot2_info_tag_t * tag, bool init)
{
while (tag->type != 0u)
{
if ((init && (tag->type == MULTIBOOT2_INFO_FRAMEBUFFER_INFO)) ||
(!init && (tag->type != MULTIBOOT2_INFO_FRAMEBUFFER_INFO)))
{
mbinfo_process_tag(tag);
}
tag = multiboot2_info_next_tag(tag);
}
}
bool mbinfo_init(uint32_t mbinfo_addr)
{
multiboot2_info_header_t * mbinfo_header = (multiboot2_info_header_t *)mbinfo_addr;
if (mbinfo_header->total_size <= sizeof(mbinfo))
{
memcpy32(mbinfo, mbinfo_header, mbinfo_header->total_size / 4u);
multiboot2_info_tag_t * tag = (multiboot2_info_tag_t *)(mbinfo + sizeof(multiboot2_info_header_t) / 4u);
process_tags(tag, true);
return true;
}
return false;
}
void mbinfo_load(void)
{
multiboot2_info_tag_t * tag = (multiboot2_info_tag_t *)(mbinfo + sizeof(multiboot2_info_header_t) / 4u);
process_tags(tag, false);
}

10
src/mbinfo.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef MBINFO_H
#define MBINFO_H
#include <stdint.h>
#include <stdbool.h>
bool mbinfo_init(uint32_t mbinfo_addr);
void mbinfo_load(void);
#endif

49
src/mem.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef MEM_H
#define MEM_H
#include <stddef.h>
static inline void memcpy(void * dest, const void * src, size_t n)
{
uint32_t r0, r1, r2;
__asm__ __volatile__ (
"cld\n\t"
"rep movsb"
: "=&c" (r0), "=&S" (r1), "=&D" (r2)
: "2" (dest), "1" (src), "0" (n)
: "memory");
}
static inline void memcpy32(void * dest, const void * src, size_t count)
{
uint32_t r0, r1, r2;
__asm__ __volatile__ (
"cld\n\t"
"rep movsd"
: "=&c" (r0), "=&S" (r1), "=&D" (r2)
: "2" (dest), "1" (src), "0" (count)
: "memory");
}
static inline void * memmove(void * dest, const void * src, size_t count)
{
return __builtin_memmove(dest, src, count);
}
static inline void * memset(void * dest, int val, size_t count)
{
return __builtin_memset(dest, val, count);
}
static inline void memset32(void * dest, uint32_t val, size_t count)
{
uint32_t r0, r1;
__asm__ __volatile__ (
"cld\n\t"
"rep stosl"
: "=&c" (r0), "=&D" (r1)
: "a" (val), "1" (dest), "0" (count)
: "memory");
}
#endif

91
src/mm.c Normal file
View File

@ -0,0 +1,91 @@
#include "mm.h"
typedef struct mm_page_entry_s {
size_t count;
struct mm_page_entry_s * next;
} mm_region_entry_t;
static mm_region_entry_t * mm_next_free_region;
static size_t mm_free_pages;
static size_t mm_total_ram;
static size_t kernel_start_address;
static size_t kernel_size;
extern uint8_t _hos_mem_start;
extern uint8_t _hos_mem_end;
static void mm_add_ram_region(size_t base, size_t size)
{
mm_region_entry_t * region = (mm_region_entry_t *)base;
size_t pages = size / PAGE_SIZE;
region->count = pages;
region->next = mm_next_free_region;
mm_next_free_region = region;
mm_total_ram += size;
mm_free_pages = pages;
}
void mm_register_ram_region(uint64_t base, size_t size)
{
/* Ignore any RAM region above 4GB. */
if (base >= 0x100000000ull)
{
return;
}
if ((base + size) > 0x100000000ull)
{
size = (size_t)(0x100000000ull - base);
}
size_t end_address = mm_page_floor((size_t)base + size);
size_t start_address = mm_page_ceil(base);
size = end_address - start_address;
if (size < PAGE_SIZE)
{
return;
}
size_t kernel_end_address = kernel_start_address + kernel_size;
/* Add regions before and after kernel RAM. */
if (start_address < kernel_start_address)
{
/* RAM region begins before kernel RAM. */
size_t this_sz = size;
if ((start_address + this_sz) > kernel_start_address)
{
this_sz = kernel_start_address - start_address;
}
mm_add_ram_region(start_address, this_sz);
}
if ((start_address + size) > kernel_end_address)
{
/* RAM region ends after kernel RAM. */
size_t this_sz = size;
if (start_address < kernel_end_address)
{
this_sz = (start_address + size) - kernel_end_address;
start_address = kernel_end_address;
}
mm_add_ram_region(start_address, this_sz);
}
}
void mm_init(void)
{
kernel_start_address = mm_page_floor((size_t)&_hos_mem_start);
kernel_size = mm_page_ceil((size_t)&_hos_mem_end - kernel_start_address);
mm_total_ram = kernel_size;
}
size_t mm_get_total_ram(void)
{
return mm_total_ram;
}
size_t mm_get_kernel_address(void)
{
return kernel_start_address;
}
size_t mm_get_kernel_size(void)
{
return kernel_size;
}

25
src/mm.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef MM_H
#define MM_H
#include <stdint.h>
#include <stddef.h>
#define PAGE_SIZE 4096u
static inline size_t mm_page_floor(size_t bytes)
{
return bytes & ~(PAGE_SIZE - 1u);
}
static inline size_t mm_page_ceil(size_t bytes)
{
return (bytes + PAGE_SIZE - 1u) & ~(PAGE_SIZE - 1u);
}
void mm_init(void);
void mm_register_ram_region(uint64_t base, size_t size);
size_t mm_get_total_ram(void);
size_t mm_get_kernel_address(void);
size_t mm_get_kernel_size(void);
#endif

127
src/multiboot2.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef MULTIBOOT2_H
#define MULTIBOOT2_H
#include <stdint.h>
#define MULTIBOOT2_MAGIC 0xE85250D6u
#define MULTIBOOT2_ARCHITECTURE_I386 0u
typedef struct {
uint32_t magic;
uint32_t architecture;
uint32_t header_length;
uint32_t checksum;
} multiboot2_header_t;
#define multiboot2_header(magic, architecture, header_length) \
{(magic), (architecture), (header_length), (uint32_t)(0x100000000u - (magic) - (architecture) - (header_length))}
#define multiboot2_header_default() \
multiboot2_header(MULTIBOOT2_MAGIC, MULTIBOOT2_ARCHITECTURE_I386, sizeof(multiboot2_header_t))
typedef struct {
uint16_t type;
uint16_t flags;
uint32_t size;
} multiboot2_tag_t;
#define multiboot2_end_tag() {0u, 0u, 8u}
typedef struct {
multiboot2_tag_t header;
uint32_t width;
uint32_t height;
uint32_t depth;
uint32_t _padding;
} multiboot2_framebuffer_tag_t;
#define multiboot2_framebuffer_tag(width, height, depth) \
{{5u, 0u, 20u}, width, height, depth}
#define MULTIBOOT2_INFO_TAG_ALIGNMENT 8u
#define MULTIBOOT2_INFO_BOOT_COMMAND_LINE 1u
#define MULTIBOOT2_INFO_BOOT_LOADER_NAME 2u
#define MULTIBOOT2_INFO_MODULES 3u
#define MULTIBOOT2_INFO_BASIC_MEMORY_INFO 4u
#define MULTIBOOT2_INFO_BIOS_BOOT_DEVICE 5u
#define MULTIBOOT2_INFO_MEMORY_MAP 6u
#define MULTIBOOT2_INFO_VBE_INFO 7u
#define MULTIBOOT2_INFO_FRAMEBUFFER_INFO 8u
#define MULTIBOOT2_INFO_ELF_SYMBOLS 9u
#define MULTIBOOT2_INFO_APM_TABLE 10u
#define MULTIBOOT2_INFO_EFI_32BIT_SYSTEM_TABLE_POINTER 11u
#define MULTIBOOT2_INFO_EFI_64BIT_SYSTEM_TABLE_POINTER 12u
#define MULTIBOOT2_INFO_SMBIOS_TABLES 13u
#define MULTIBOOT2_INFO_ACPI_OLD_RSDP 14u
#define MULTIBOOT2_INFO_NETWORKING_INFO 16u
#define MULTIBOOT2_INFO_EFI_MEMORY_MAP 17u
#define MULTIBOOT2_INFO_EFI_BOOT_SERVICES_NOT_TERMINATED 18u
#define MULTIBOOT2_INFO_EFI_32BIT_IMAGE_HANDLE_POINTER 19u
#define MULTIBOOT2_INFO_EFI_64BIT_IMAGE_HANDLE_POINTER 20u
#define MULTIBOOT2_INFO_IMAGE_LOAD_BASE_PHYSICAL_ADDRESS 21u
typedef struct {
uint32_t total_size;
uint32_t _reserved;
} multiboot2_info_header_t;
typedef struct {
uint32_t type;
uint32_t size;
} multiboot2_info_tag_t;
#define multiboot2_info_next_tag(current_tag) \
(multiboot2_info_tag_t *)(((uintptr_t)current_tag + current_tag->size + MULTIBOOT2_INFO_TAG_ALIGNMENT - 1u) & ~(MULTIBOOT2_INFO_TAG_ALIGNMENT - 1u))
typedef struct {
multiboot2_info_tag_t header;
char string[];
} multiboot2_info_boot_command_line_t;
typedef struct {
multiboot2_info_tag_t header;
char string[];
} multiboot2_info_boot_loader_name_t;
typedef struct {
multiboot2_info_tag_t header;
uint32_t mem_lower;
uint32_t mem_upper;
} multiboot2_info_basic_memory_info_t;
#define MULTIBOOT2_MEMORY_MAP_TYPE_RAM 1u
#define MULTIBOOT2_MEMORY_MAP_TYPE_ACPI 3u
#define MULTIBOOT2_MEMORY_MAP_TYPE_PRESERVE 4u
#define MULTIBOOT2_MEMORY_MAP_TYPE_DEFECTIVE 5u
typedef struct {
uint64_t base_addr;
uint64_t length;
uint32_t type;
uint32_t _reserved;
} multiboot2_info_memory_map_entry_t;
typedef struct {
multiboot2_info_tag_t header;
uint32_t entry_size;
uint32_t entry_version;
multiboot2_info_memory_map_entry_t entries[];
} multiboot2_info_memory_map_t;
typedef struct {
multiboot2_info_tag_t header;
uint16_t vbe_mode;
uint16_t vbe_interface_set;
uint16_t vbe_interface_off;
uint16_t vbe_interface_len;
uint8_t vbe_control_info[512];
uint8_t vbe_mode_info[256];
} multiboot2_info_vbe_info_t;
typedef struct {
multiboot2_info_tag_t header;
uint64_t framebuffer_addr;
uint32_t framebuffer_pitch;
uint32_t framebuffer_width;
uint32_t framebuffer_height;
uint8_t framebuffer_bpp;
uint8_t framebuffer_type;
uint8_t _reserved;
} multiboot2_info_framebuffer_info_t;
#endif

12
src/multiboot_header.c Normal file
View File

@ -0,0 +1,12 @@
#include "multiboot2.h"
/* Multiboot2 Header. */
struct {
multiboot2_header_t header;
multiboot2_framebuffer_tag_t framebuffer_tag;
multiboot2_tag_t end_tag;
} multiboot_header __attribute__((section(".multiboot"))) = {
multiboot2_header_default(),
multiboot2_framebuffer_tag(1600u, 900u, 32u),
multiboot2_end_tag(),
};

12
src/stream.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef STREAM_H
#define STREAM_H
#include <stddef.h>
#include <stdint.h>
typedef struct {
void (*write)(const char * src, size_t length);
void (*write1)(char c);
} stream_t;
#endif

26
src/string.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef STRING_H
#define STRING_H
#include <stddef.h>
static inline size_t strlen(const char * s)
{
size_t r = 0u;
while (*s++ != (char)0)
{
r++;
}
return r;
}
static inline char * strcpy(char * dest, const char * src)
{
return __builtin_strcpy(dest, src);
}
static inline char * strncpy(char * dest, const char * src, size_t n)
{
return __builtin_strncpy(dest, src, n);
}
#endif

1
uefi-d

@ -1 +0,0 @@
Subproject commit e70959ce8ad1b113b5f7aa2111ea4d88f6b6ea63