Compare commits

..

300 Commits
master ... d

Author SHA1 Message Date
bb65def53c XHCI: set dcbaap 2025-05-23 10:16:49 -04:00
0d970a6d25 XHCI: allocate scratchpad buffers 2025-05-23 10:13:54 -04:00
ea159db100 XHCI: allocate device context buffer for each slot 2025-05-23 09:21:48 -04:00
e3f27812ad Allocate memory regions for XHCI 2025-02-23 20:52:36 -05:00
d78669df9c Check for pkg-config 2025-02-23 20:46:31 -05:00
7313b01732 Allow allocating arbitrarily aligned physical memory regions
This will be needed for USB (XHCI controller driver).
2025-02-21 15:31:48 -05:00
b2497d1ee0 HIPPO: store memory regions in addition to individual free pages 2025-02-21 12:31:03 -05:00
30078417b6 Turn off test thread 2025-02-20 15:50:57 -05:00
c18cbf7be3 Remove unsupported avx CPU flags 2025-02-20 11:30:53 -05:00
2d95371aa1 Add hulk.volatile 2024-12-03 00:18:45 -05:00
13b6f39e1a Add OVMF search location 2024-12-02 10:54:02 -05:00
30aedfc97b Map framebuffer with write-combining access mode to speed up physical hardware 2024-07-29 00:01:47 -04:00
26bb071b4d Update base address to build with kernel code model 2024-07-28 23:14:24 -04:00
3a75e0505f Prepare for future XHCI setup 2024-02-20 11:41:23 -05:00
a02df12924 Add XHCI.reset() 2024-02-20 11:23:12 -05:00
699f53ac15 Initialize USB after enabling interrupts so Time can be used 2024-02-20 11:17:29 -05:00
1119855215 Fix mapping PCI regions as writable 2024-02-20 11:17:01 -05:00
0c82250bd2 Fix Time.msleep() when inlined in a loop 2024-02-20 11:16:34 -05:00
2a66443a49 Add DoorbellRegister comment 2024-02-09 13:02:05 -05:00
9874802ee4 Add new threads to runnable threads list
More safely handle nested interrupts.
Switch to newly created thread immediately.
2023-12-08 12:16:35 -05:00
44a305721e Allow use of {suspend,resume}_interrupts() from within an ISR
Move functions from hulk.cpu to hulk.him.
2023-12-01 07:54:37 -05:00
73f4d6b728 Rename hulk.idt to hulk.him 2023-12-01 07:49:52 -05:00
0efe8af5aa Commonize ISR handling as much as possible 2023-11-30 20:32:06 -05:00
edc9b2c3ff Remove thread test 2023-11-28 10:47:44 -05:00
c8a9fcf0d5 idt: add INT_KERNEL_SWINT 2023-11-28 10:30:16 -05:00
dc231741b7 cpu: add swint() 2023-11-28 10:30:03 -05:00
fbaf9df59f Add kernel threading test
Kernel multithreading is working!
Threads cannot exit for now.
Scheduling is round-robin for now.
2023-11-27 20:07:08 -05:00
58a74e4859 Fix PCI device list 2023-11-27 19:37:14 -05:00
cc1753a75d Move system time display to Rtc.initialize() 2023-11-27 19:36:04 -05:00
3c8587acf5 Redesign List as a mixin template 2023-11-27 15:30:09 -05:00
db2814596b Move PIT test to test module 2023-11-25 21:01:46 -05:00
8cfa7bc009 Add hulk.thread module 2023-11-25 20:58:11 -05:00
d100ab53a8 Add read_rflags() 2023-11-25 13:02:27 -05:00
35e5aa2ee5 Add hulk.test module to test kernel functionality 2023-11-22 20:47:21 -05:00
cd9a3a7284 Rework List to allow removing nodes 2023-11-22 20:46:48 -05:00
294500fb70 Squelch linker warning about a RWX segment 2023-11-21 12:13:29 -05:00
bb4404df23 Add suspend_interrupts() and resume_interrupts() 2023-11-21 11:36:22 -05:00
aa5ca83889 Disable the red zone
This fixes the interrupt handler from clobbering local variables.
2023-11-20 16:18:41 -05:00
f2839cdd0f Handle all APIC IRQ redirects specified in MADT 2023-11-07 17:19:56 -05:00
31bf82e2ae Store all registers in interrupt stack frame
Do not clear console when printing interrupt stack frame
2023-11-02 16:32:42 -04:00
4f9206af91 Build with debugging symbols 2023-11-02 11:54:08 -04:00
2eec448653 Update gcc and binutils 2023-11-02 11:53:35 -04:00
9932b92963 Change orange screen of death to red bar 2023-11-02 10:34:29 -04:00
5d8343ed40 Preserve registers and align stack in interrupt handlers 2023-11-02 10:29:27 -04:00
2fa4193517 Pass exception stack frame pointer to exception handler
Show exception stack frame contents on exception
2023-11-01 21:22:54 -04:00
01c73f8474 Add check that PIT ISR is firing 2023-11-01 14:45:43 -04:00
7264a31ff3 Rework scanning MADT table entries
Get I/O APIC address from MADT entry.
Add DEBUG flag for APIC.
Show number of CPUs found.
2023-10-31 13:29:23 -04:00
c0a00b528e Run in QEMU with 4 CPUs by default 2023-10-31 11:56:39 -04:00
9f8a4ea100 Add hulk.time and msleep() 2023-10-26 16:18:26 -04:00
6251fee3ff Store item in List rather than pointer to item 2023-10-25 21:28:27 -04:00
f5c6a35cef Move MCFG ACPI table processing from ACPI to PCI 2023-10-25 21:18:36 -04:00
e32282f6f0 Move MADT table processing from ACPI to APIC 2023-10-25 21:11:21 -04:00
2b87792c67 Store ACPI table pointers in list 2023-10-25 19:13:36 -04:00
e480bd0ed0 Enable PIT ISR to count milliseconds 2023-10-25 12:02:44 -04:00
e357f2ba44 Initialize USB controllers from Usb instead of Pci 2023-10-24 10:44:48 -04:00
f8f9b72588 Store PCI addresses and types as 32-bit uints 2023-10-24 10:25:16 -04:00
d86bf47211 Store a List of discovered PCI devices 2023-10-24 08:49:11 -04:00
757f8f8c65 Make A1.allocate() zero returned memory 2023-10-24 08:48:26 -04:00
8303451929 Add List class 2023-10-24 08:48:00 -04:00
1c6c922b45 Add Pci.Type struct 2023-10-22 21:30:36 -04:00
7632445d43 Add console escape sequences to draw headings 2023-10-21 22:13:20 -04:00
601ea50e4b Console: delay newline rendering until non newline character 2023-10-21 21:21:46 -04:00
722494af44 Console: do not index nonexistent character codes 2023-10-21 21:04:18 -04:00
e1f38bd5ea Initialize RTC after APIC 2023-10-21 20:47:15 -04:00
bcf1bd3bde HELLO: Zero HULK BSS region after mapping it 2023-10-21 20:46:27 -04:00
f7dde241ad Split console into two pages 2023-10-21 20:15:11 -04:00
8638840cd8 Add console header line 2023-10-21 17:08:45 -04:00
4273c703e9 writef: split into writef() and writefv() 2023-10-21 16:45:41 -04:00
b2597fb5da Add hulk.ver module 2023-10-18 19:33:43 -04:00
291ba2bc1c Remove xHCI TODO 2023-10-13 14:17:34 -04:00
71bc3543a1 fontgen: show more error info for invalid character metrics 2023-09-26 12:00:22 -04:00
a87d24d417 Reduce kernel console font size slightly 2023-09-26 09:59:22 -04:00
6fcc6da022 Remove APIC debug outputs 2023-09-24 11:03:21 -04:00
dc07fd0255 Document xHCI runtime registers and interrupter registers 2023-09-23 19:48:14 -04:00
eb0a4b2a4d Document xHCI port registers 2023-09-23 19:28:35 -04:00
0ae3c6c96d Document xHCI operational registers 2023-09-23 11:59:20 -04:00
4814b51daf Dump xHCI extended capabilities 2023-09-19 11:07:18 -04:00
14bb3adf37 Document xHCI capability registers 2023-09-19 11:07:02 -04:00
cda45b205d Remove legacy PCI bus scanning 2023-09-17 16:13:37 -04:00
cfd42550fc Add struct definitions for all PCI header types 2023-09-17 15:22:58 -04:00
673ce8f097 Scan PCI devices via MCFG table 2023-09-17 14:49:02 -04:00
ba0befa21b Check that needed ACPI tables are found 2023-09-17 13:40:26 -04:00
fa4de31aa4 Store pertinent ACPI table pointers 2023-09-17 11:22:58 -04:00
3412d9ece9 Add Klog.fatal_error() 2023-09-17 11:15:02 -04:00
e8978c73f2 Move PCI initialization after ACPI/APIC/RTC 2023-09-17 11:00:48 -04:00
f79b0098c5 Map ACPI tables as disable cache + no execute 2023-09-17 09:12:20 -04:00
10d7e7f0c6 Move parsing of ACPI tables into table structures
Move mapping ACPI table memory into common location
2023-09-17 09:08:01 -04:00
e088f942d1 Use Acpi.Header in ACPI table structures 2023-09-17 08:53:34 -04:00
6514af8e94 Begin parsing MCFG ACPI table 2023-09-17 08:41:08 -04:00
602a7525d2 Add %p format specifier for printing pointers 2023-09-16 17:09:55 -04:00
617b861524 Size to hulk.elf.size 2023-09-15 13:38:25 -04:00
2f968de7ed Disassemble to hulk.elf.txt 2023-09-15 13:37:48 -04:00
68e85999fb Drop unused sections when linking HULK 2023-09-15 13:37:00 -04:00
6b1445935c Update mk-vmdk task to have a file template since VBoxManage createmedium command was failing 2023-09-15 11:04:36 -04:00
665d8aef80 Update qemu command to change machine to q35
This enables PCIe
2023-09-15 11:04:12 -04:00
af5ad73cf7 Update instructions for VirtualBox to change machine chipset
This enables PCIe
2023-09-15 11:03:39 -04:00
4c67faaad4 Rework Kfont to store glyph top as offset from top of text row 2023-09-13 13:48:11 -04:00
4fcea6a283 Reverse framebuffer vertical coordinates to start with 0 at top 2023-09-13 11:57:47 -04:00
c958b9ffcc Remove alpha-blending framebuffer functions 2023-09-13 10:57:45 -04:00
3d911ab191 Adjust console text height by font baseline offset
This fixes having leftover pixels on screen bottom when scrolling the
console up.
2023-09-13 10:52:24 -04:00
d04c5444c7 fontgen: put character number before character data 2023-09-13 08:54:09 -04:00
209658ee09 Use cache to mkdir_p for HulkBinObj builder 2023-09-10 09:48:07 -04:00
f3f45445b3 Determine XHCI controller addresses 2023-09-07 14:04:30 -04:00
459d5c36aa Set alignment of XSDT table pointers to 4 instead of 8 2023-09-07 12:57:26 -04:00
22c9891c4f Add page_address() 2023-09-07 12:51:41 -04:00
2a832bfb8b Print progress messages for cross compiler toolchain installation 2023-09-06 22:58:18 -04:00
496445e533 Fix linker script section placement 2023-09-06 16:40:17 -04:00
156642293f Debug output HULK memory range end addresses 2023-09-04 21:20:10 -04:00
4e46b98efc Capture QEMU serial port output 2023-09-04 21:13:35 -04:00
ce3e2c55df Store QEMU files in local qemu directory 2023-09-04 21:12:36 -04:00
cb24d00e5f Document running in QEMU in README 2023-09-04 21:04:30 -04:00
4500f3cbca Write HELLO memory map debug data to serial port 2023-09-04 20:59:38 -04:00
0781962e9f Rework HELLO console module to use hulk.writef 2023-09-04 16:17:47 -04:00
e25d28ebc7 Add serial module 2023-09-04 14:56:12 -04:00
93a4065dea Add path to OVMF.fd for Arch 2023-09-04 14:07:05 -04:00
5b017fb810 Add some xHCI register structures 2023-06-11 17:22:50 -04:00
37814b4f86 Start on XHCI struct 2023-06-10 22:55:08 -04:00
238659e94b Store PCI device mapped memory ranges 2023-06-10 22:22:09 -04:00
c16aa0e9c3 Add hulk.range 2023-06-10 22:21:55 -04:00
2935903bae Map PCI device memory regions 2023-06-10 20:12:46 -04:00
5179b5881b Read PCI device header type 2023-06-10 12:01:06 -04:00
a0c62937ff Remove Pci.init_device() for now 2023-06-10 11:28:51 -04:00
657287c160 Move Pci.scan() recursion into scan() itself 2023-06-10 11:20:50 -04:00
d600e089e5 Add Pci.Device class 2023-06-10 11:16:27 -04:00
dd07a08388 Fix A1 allocator to map the correct pages 2023-06-10 11:06:41 -04:00
63fbbc9051 Use ulong for round_up_power_2() 2023-06-10 10:59:46 -04:00
3e32802862 Add A1.allocate() template to allocate struct/class instances 2023-06-10 10:30:56 -04:00
40ff805f7d Add Pci.Address struct 2023-06-10 09:35:52 -04:00
9ac69c9003 Add Pci.write_config_register() 2023-06-10 09:19:58 -04:00
7aa28da1ef Allow pointers or integers for Hurl.*map_range() 2023-06-09 23:38:52 -04:00
222a1efea4 Fix address calculation in Hurl.identity_map_range() 2023-06-09 23:27:20 -04:00
7a4edf38f0 Scan for multi-function PCI devices 2023-06-08 20:28:32 -04:00
dda1b6379f Fix in32()/out32() to use uint instead of ulong 2023-06-08 16:07:44 -04:00
98134957e7 Print APIC table signatures found 2023-06-08 13:21:43 -04:00
22463e729b writef: add %c format specifier support 2023-06-08 13:21:24 -04:00
8de86dd647 Add Pci.init_device() skeleton 2023-06-07 16:49:54 -04:00
f6fde01b06 Fix build error -- import hulk.util 2023-04-05 01:08:55 -04:00
ea6f485bd9 Add hulk.util and round_up_power_2() 2023-02-24 21:15:33 -05:00
2ab388f2e9 Remove seconds counter printout 2023-02-24 20:58:11 -05:00
cc891f6d4d Assign PCI header fields to named variables 2023-02-24 20:53:46 -05:00
f5e5c40c12 Reduce Hurl.map() template overhead 2023-01-02 20:46:12 -05:00
7b09c9beed Add A1 memory allocator 2023-01-01 22:02:48 -05:00
103205bff2 Move address constants into Hurl struct 2023-01-01 21:04:29 -05:00
25ee1775f0 Capitalize namespacing structs
This helps to avoid name clashes when breaking up modules into packages.
2023-01-01 17:34:41 -05:00
a423cdf1b1 Fix RTC clock reading in 24-hour mode 2022-12-19 11:32:08 -05:00
7c8744d1e0 Read RTC date and time 2022-12-18 22:29:12 -05:00
4081a220ef Route I/O APIC interrupts, enable RTC tick interrupt 2022-12-16 00:32:49 -05:00
9adc248225 Initialize rtc module 2022-12-15 20:41:44 -05:00
676fd17bf5 Add pit and rtc modules 2022-12-15 20:41:15 -05:00
b1a8f8d348 Handle local APIC exceptions 2022-12-07 23:24:01 -05:00
9b52ae58e8 Get local APIC timer ready to run 2022-12-07 23:13:51 -05:00
5799f94efe Configure local APIC interrupt vectors 2022-12-06 22:06:57 -05:00
400a7b22db Enable local APIC interrupts 2022-12-06 21:22:56 -05:00
2bba0dfca4 Add hulk.mtrr module 2022-11-08 21:49:41 -05:00
edba40b2c6 Implement frame buffer double-buffering
This slowed down drawing to video in QEMU/VirtualBox but greatly sped up
doing so on real hardware (no longer reading from video memory).
Despite the speed-up, it is still quite slow though.
2022-11-08 06:19:03 -05:00
55604468c8 Allocate framebuffer buffer1 in HELLO 2022-11-07 21:27:52 -05:00
71c30fa932 Reclaim bootloader page table pages 2022-11-06 21:59:42 -05:00
b3fb599b25 Move EFER initialization to initialize_cpu() 2022-11-05 22:49:57 -04:00
d357aada75 Map HULK regions separately 2022-11-05 22:45:35 -04:00
1fef8ada29 Define acpi structs as static 2022-11-05 22:26:21 -04:00
5231930543 Clean up ApicRegister 2022-11-05 22:22:09 -04:00
95b9e4558a Move hulk.writef buffer variables to stack
Previously ldc2 was emitting AVX/SSE instructions to initialize these
buffers.
2022-11-05 22:06:18 -04:00
354555d7f1 Disable AVX and SSE instruction generation for HULK 2022-11-05 22:03:13 -04:00
1b69810e8f Turn on OSXSAVE 2022-11-05 21:17:30 -04:00
64190b04cb Add initialize_cpu() 2022-11-05 20:27:48 -04:00
d7b12c7896 Run qemu with maximum supported host CPU features 2022-11-05 20:15:30 -04:00
11f922da33 Add write_cr0() 2022-11-05 19:58:10 -04:00
59e084c234 Add cpuid1() and output bit enums 2022-11-04 19:28:34 -04:00
f21ee0d149 Add enums for a few CPU register bits 2022-11-04 00:08:40 -04:00
2a7c783bba Add write_cr4() 2022-11-03 23:31:55 -04:00
1f2a2bea48 Add xgetbv()/xsetbv() 2022-11-03 22:45:35 -04:00
8eedf4e267 Switch klog buffer to ubyte array to move it to bss section 2022-11-03 16:34:03 -04:00
ae49c29c75 Fail build if thread-local data is found 2022-11-03 16:17:42 -04:00
4d88810fc1 Move hulk.writef hex character strings out of data section 2022-11-03 16:03:12 -04:00
ba9cf30e67 Clean up linker script symbols 2022-11-03 15:41:14 -04:00
b9675019e7 Rename BOOTX64.EFI -> HOS.EFI 2022-11-03 13:54:42 -04:00
aa9809e0ed README: add building instructions 2022-11-03 13:34:21 -04:00
07202b902b README: fix headings 2022-11-03 13:30:09 -04:00
08501e86bb README: Add HOS System Requirements 2022-11-03 13:29:05 -04:00
10a2d63e08 Add instructions to run HOS from GRUB 2022-11-03 13:27:24 -04:00
8aa01ba122 Build HELLO page tables from unused memory instead of scratch buffer 2022-11-02 22:49:42 -04:00
0b326e2e36 Move PAGE_SIZE from hulk.hippo to hulk.pagetable 2022-11-01 22:23:26 -04:00
e49c27cbbc Increase scratch space to 10MB 2022-10-30 21:29:19 -04:00
49677c8499 PageTableEntry improvements
- fix alias this
- add properties for dirty/accessed
- add opIndex for direct indexing
2022-10-29 22:07:38 -04:00
1e1c389caf Turn on EFER NXE bit to allow no-execute pages 2022-10-27 23:42:18 -04:00
7b531e36ea Fix HELLO check that HULK bss and stack region could be found 2022-10-26 23:26:25 -04:00
7540d4302e Add a couple more page table flags 2022-10-26 23:18:08 -04:00
ba7c2996d5 Add MSR_EFER and rdmsr() 2022-10-26 22:45:09 -04:00
707d42bec4 Remove PageTable.opIndex and .opIndexAssign, use .entry 2022-10-26 21:58:22 -04:00
476ae7d6fd Move hos.* modules to hulk.* 2022-10-26 00:16:07 -04:00
4a431f7d93 Use hulk.pagetable from hello 2022-10-26 00:05:49 -04:00
555ec9b627 Split PageTable struct out from hulk.hurl to hulk.pagetable 2022-10-25 23:30:17 -04:00
c11361d40f Build new page tables in HULK 2022-10-19 23:59:11 -04:00
c8a81b1101 Add PAGE_SIZE constant 2022-10-18 21:53:38 -04:00
41531cf4d2 Rename Hurl to hurl for consistency 2022-10-18 20:59:13 -04:00
edbe33bcfa Add ApicRegisters structure 2022-10-18 20:48:46 -04:00
747e824dae Fix calcuation of reserved HULK binary pages 2022-10-18 16:23:13 -04:00
b1be5b888f Add apic module that does not do anything yet 2022-10-18 12:56:02 -04:00
dba58b2277 acpi: show APIC address found 2022-10-18 12:55:41 -04:00
72b83b0c3c Hurl.debug_lookup(): show page table entry index 2022-10-18 12:54:27 -04:00
90711adada Actually calling Hurl.initialize() helps a lot 2022-10-18 12:51:08 -04:00
4f98c469b7 Add Hurl.debug_lookup() 2022-10-18 11:18:19 -04:00
7d2b535df5 Add hulk.acpi module 2022-10-18 00:48:56 -04:00
2f1a12be81 Add Hurl.map_range() 2022-10-18 00:48:40 -04:00
5ca95a5662 memcpy*() should take const src ptr 2022-10-18 00:47:46 -04:00
7471a846ec Locate the ACPI XSDT 2022-10-17 21:48:43 -04:00
2ee920fdd2 Reset console position on OSOD 2022-10-14 17:10:32 -04:00
d24eafbd63 Tweak VirtualBox instructions 2022-10-14 16:05:35 -04:00
6f94bc3ddd Add VirtualBox instructions and task to create a VMDK 2022-10-14 15:57:26 -04:00
777820d471 Tell rscons the map files are side-effect files 2022-10-14 15:28:38 -04:00
b3ca9af48a Disable PIC 2022-09-06 22:35:41 -04:00
43d945b5e8 Enable interrupts and halt CPU 2022-09-04 23:03:20 -04:00
bc7fd3f2fc Add idt module and set up IDT 2022-09-04 22:25:46 -04:00
b72c69ef1c Clear interrupts on HULK startup just in case 2022-09-01 15:50:59 -04:00
66bd51ffb0 Add hulk.gdt module and create our own GDT 2022-09-01 15:43:00 -04:00
16143a727a Avoid using SSE instructions in writef functions 2022-08-23 22:48:52 -04:00
63e6fd4bb5 Add run-vb task 2022-08-20 19:57:10 -04:00
b4a4638a82 Create HOS.img with a GPT partition table and EFI partition 2022-08-20 19:01:25 -04:00
93763917c1 Rename hos.img -> HOS.img 2022-08-18 23:03:16 -04:00
743cf9ffef Update to rscons 3.1.0 2022-08-11 15:08:49 -04:00
b9f051017c Rename max_physical_address -> physical_address_limit 2022-05-03 19:46:46 -04:00
59890868c1 Fix efibootmgr command to install HOS to physical machine 2022-05-03 16:18:47 -04:00
6b715c9051 Add README.md with installation instructions 2022-05-01 16:17:11 -04:00
89f7d2120f Add Hurl struct with map() method 2022-05-01 09:26:33 -04:00
7515ebdf1c Fix hippo.allocate_page() 2022-05-01 09:20:41 -04:00
b0e59b3e26 Add hippo.allocate_page() 2022-04-26 21:45:17 -04:00
153a8d43f1 Add hulk.linker_addresses module 2022-04-26 19:37:05 -04:00
bf9a005dbc Launch qemu with a USB XHCI controller and USB tablet device 2022-04-13 21:55:06 -04:00
53e2e52bbe Scan for PCI devices 2022-04-13 21:54:28 -04:00
12067b8e1a Remove src-old 2022-04-13 17:12:16 -04:00
8278c4211b Add HIPPO module and starting tracking free physical pages 2022-03-29 22:24:14 -04:00
3bdc5bccdd Pass page table address range to HULK on boot 2022-03-28 21:22:10 -04:00
f904ec2b48 Convert hello.console to use a namespacing struct 2022-03-28 15:18:31 -04:00
bcec23ef89 Update hello.scratch to use namespacing struct 2022-03-28 15:17:07 -04:00
d86745d91d Move bootinfo into HULK header 2022-03-27 16:14:54 -04:00
cf5956dcd3 Rename Header to HulkHeader 2022-03-27 09:45:09 -04:00
8498de6b62 Add HURL; relocate kernel virtual regions 2022-03-26 10:41:53 -04:00
f761d0a445 Jump to HULK from assembly 2022-03-26 00:36:23 -04:00
f29bd9d00c Allocate HULK stack region from HELLO, translate memory region types 2022-03-26 00:29:19 -04:00
f236a64b21 HELLO: Be more welcoming 2022-03-25 13:17:22 -04:00
1225e98276 Pass HULK physical memory info to kernel from HELLO 2022-03-25 13:16:36 -04:00
cb31e36e90 Clean up console 2022-03-25 12:37:18 -04:00
0568a813f2 HELLO: Zero out HULK bss section 2022-03-25 11:53:01 -04:00
37d5621e41 HULK console: clear last row when shifting rows up 2022-03-25 10:51:52 -04:00
98e992aad0 Convert fb, console, klog to "static instance" namespacing structs 2022-03-25 10:48:32 -04:00
06242a0c9f Add console, klog, and writef modules.
They are not complete, but adding to checkpoint progress.
2022-03-24 17:52:39 -04:00
e214a9c3de Fix call to fb.blend_alpha_bitmap() 2022-03-24 09:52:26 -04:00
c259ae4cee Add Framebuffer.copy_rows_up() 2022-03-23 22:12:38 -04:00
b4d8707243 Rename blending blit methods to blend 2022-03-22 22:32:45 -04:00
6b7b14d954 Add Framebuffer.character() 2022-03-22 22:11:12 -04:00
9e7c3ee676 Add Framebuffer.rect(), .hline(), .vline() 2022-03-22 21:55:25 -04:00
2d7bf5197f Drawing text working using Framebuffer.blit_alpha_bitmap() 2022-03-22 20:01:08 -04:00
7562be585f Add Framebuffer.blit_alpha_bitmap() 2022-03-22 20:00:32 -04:00
3d04eec427 Compile HULK with large code model 2022-03-22 19:29:30 -04:00
5c97acba5f Update fontgen to output D instead of C 2022-03-22 13:01:20 -04:00
c3ead56582 Add Framebuffer object 2022-03-21 16:32:11 -04:00
15559b0a56 Add HULK header; map virtual memory for HULK bss section 2022-03-21 16:31:01 -04:00
c58284761a Comment hulk.hulk module a bit 2022-03-20 23:05:10 -04:00
bd99e429a9 Comment hulk.bootinfo a bit 2022-03-20 22:58:25 -04:00
d0acf5a718 Comment hello.hello module a bit better 2022-03-20 22:38:42 -04:00
b40151055c Rework scratch module and use qualified names for function calls 2022-03-20 22:21:10 -04:00
1e00d7a9e9 Rename hello.output to hello.console 2022-03-20 22:12:46 -04:00
79b34477ed Reorganize D modules a bit 2022-03-19 22:39:12 -04:00
163ef9f79e Increase scratch buffer size 2022-03-19 22:08:35 -04:00
3c549f0838 Write size file for HULK/HELLO 2022-03-19 22:07:37 -04:00
d8c85ad045 Map all memory regions and graphics framebuffer 2022-03-19 21:54:30 -04:00
7c5af33ace Fix receiving correct argument to hulk_start() 2022-03-19 20:02:49 -04:00
6f3521c576 Make sure to rebuild HELLO properly when HULK changes 2022-03-19 00:27:10 -04:00
5a5d60abae Fix HULK virtual start address to be addressable 2022-03-19 00:04:59 -04:00
9182a83ef2 HELLO: Make sure scratch area is page-aligned 2022-03-18 23:43:05 -04:00
195b7496f4 HELLO: Include EfiLoaderCode and EfiLoaderData memory regions in memory map 2022-03-18 23:33:42 -04:00
b1042fd649 Jump to HULK
Unfortunately, crashes... :(
2022-03-18 23:16:07 -04:00
6beb7da4cd Map HULK virtual address space 2022-03-18 23:04:09 -04:00
f27851def9 Bundle HULK in HELLO 2022-03-18 22:43:37 -04:00
18f1bfc88f Build HELLO after HULK 2022-03-18 22:02:07 -04:00
cd6983e2cd Generate HULK binary 2022-03-18 22:01:27 -04:00
8df1646c61 Add initial HULK source and linker script 2022-03-18 21:55:38 -04:00
3e25ab6ae4 HELLO: Zero out page tables after allocating, before using 2022-03-18 21:21:00 -04:00
05cc2da2e3 HULK: Add virtual start address 2022-03-18 21:19:06 -04:00
a54536660b HELLO: Create page tables to identity map available RAM prior to jumping to kernel 2022-03-18 21:15:58 -04:00
9e3d9b18ea Add write_cr3() 2022-03-18 16:40:46 -04:00
ef01239468 HELLO: Add scratch module 2022-03-18 16:03:50 -04:00
372b7265f1 Add src/common/hos/cpu.d 2022-03-18 10:51:35 -04:00
1bc8722098 Add hos.memory module in src/common 2022-03-18 00:07:10 -04:00
cd2f3f9a84 Rename kernel to hulk 2022-03-17 16:57:18 -04:00
08a35e40d4 Rename HEL -> HELLO 2022-03-16 16:11:26 -04:00
a855702214 Fix checking for EFI_INVALID_PARAMETER 2022-03-16 14:37:46 -04:00
706f2c1c5d HEL: Call ExitBootServices 2022-03-16 01:12:07 -04:00
704fd15f7b Get memory map; remove heap 2022-03-15 23:58:37 -04:00
f054bab5cc Set bootinfo framebuffer fields 2022-03-15 23:03:28 -04:00
3fa8e4a24b Set maximum resolution for running in qemu 2022-03-15 22:52:56 -04:00
a38f95a7b8 Set graphics mode 2022-03-14 22:26:55 -04:00
dd57a31d38 HEL: Add heap module; locate GOP handle 2022-03-14 21:43:40 -04:00
406f17c1db HEL: format hex, decimal, and CHAR16 strings in output module 2022-03-14 15:09:02 -04:00
0be1b113aa Add output module for console output routines 2022-03-14 13:53:02 -04:00
d782b59768 Write map file 2022-03-14 01:35:28 -04:00
c3a6139f83 Run image from temporary copy to avoid modified disk image file 2022-03-14 00:57:24 -04:00
3d3a433800 Update to rscons 3.0.2 2022-03-14 00:51:54 -04:00
54451bcf5b Add initial HOS EFI Loader sources 2022-03-13 23:16:37 -04:00
1fc2dd34e5 Update build system for 64-bit, EFI, and D 2022-03-13 22:46:59 -04:00
71 changed files with 6384 additions and 1521 deletions

3
.gitignore vendored
View File

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

3
.gitmodules vendored Normal file
View File

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

81
README.md Normal file
View File

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

View File

@ -1,224 +0,0 @@
#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;
}

6
rscons

File diff suppressed because one or more lines are too long

View File

@ -1,18 +0,0 @@
.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
View File

@ -1,116 +0,0 @@
#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;
}
}

View File

@ -1,16 +0,0 @@
#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

View File

@ -1,11 +0,0 @@
#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);
}

View File

@ -1,8 +0,0 @@
#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

235
src/fontgen/fontgen.c Normal file
View File

@ -0,0 +1,235 @@
#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;
}

View File

@ -1,16 +0,0 @@
.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

View File

@ -1,20 +0,0 @@
#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();
}

View File

@ -1,34 +0,0 @@
#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

88
src/hello/console.d Normal file
View File

@ -0,0 +1,88 @@
/**
* 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);
}
}

579
src/hello/hello.d Normal file
View File

@ -0,0 +1,579 @@
/**
* 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;
}

View File

@ -1,25 +0,0 @@
#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());
}

View File

@ -1,281 +0,0 @@
#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);
}

View File

@ -1,10 +0,0 @@
#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

126
src/hulk/acpi.d Normal file
View File

@ -0,0 +1,126 @@
/**
* 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);
}
}

382
src/hulk/apic.d Normal file
View File

@ -0,0 +1,382 @@
/**
* 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();
}
}

77
src/hulk/bootinfo.d Normal file
View File

@ -0,0 +1,77 @@
/**
* 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;
}

276
src/hulk/console.d Normal file
View File

@ -0,0 +1,276 @@
/**
* 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);
}
}

261
src/hulk/cpu.d Normal file
View File

@ -0,0 +1,261 @@
/**
* 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, `", "");`);
}

242
src/hulk/fb.d Normal file
View File

@ -0,0 +1,242 @@
/**
* 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;
}
}

58
src/hulk/gdt.d Normal file
View File

@ -0,0 +1,58 @@
/**
* 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);
}
}

40
src/hulk/header.d Normal file
View File

@ -0,0 +1,40 @@
/**
* 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;
}

329
src/hulk/him.d Normal file
View File

@ -0,0 +1,329 @@
/**
* 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;
}
}

218
src/hulk/hippo.d Normal file
View File

@ -0,0 +1,218 @@
/**
* 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;
}
}
}

114
src/hulk/hulk.d Normal file
View File

@ -0,0 +1,114 @@
/**
* 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);
}
}

65
src/hulk/hulk.ld Normal file
View File

@ -0,0 +1,65 @@
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;
}

67
src/hulk/hurl/a1.d Normal file
View File

@ -0,0 +1,67 @@
/**
* 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);
}
}

359
src/hulk/hurl/hurl.d Normal file
View File

@ -0,0 +1,359 @@
/**
* 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;
}
}

3
src/hulk/hurl/package.d Normal file
View File

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

87
src/hulk/klog.d Normal file
View File

@ -0,0 +1,87 @@
/**
* 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 (;;)
{
}
}
}

139
src/hulk/linker_addresses.d Normal file
View File

@ -0,0 +1,139 @@
/**
* 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;
}
}

100
src/hulk/list.d Normal file
View File

@ -0,0 +1,100 @@
/**
* 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;
}
}

62
src/hulk/memory.d Normal file
View File

@ -0,0 +1,62 @@
/**
* 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);
}

70
src/hulk/mtrr.d Normal file
View File

@ -0,0 +1,70 @@
/**
* 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);
}
}

194
src/hulk/pagetable.d Normal file
View File

@ -0,0 +1,194 @@
/**
* 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));
}

452
src/hulk/pci.d Normal file
View File

@ -0,0 +1,452 @@
/**
* 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);
}
}
}
}
}

16
src/hulk/pic.d Normal file
View File

@ -0,0 +1,16 @@
/**
* 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);
}
}

45
src/hulk/pit.d Normal file
View File

@ -0,0 +1,45 @@
/**
* 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();
}
}

27
src/hulk/range.d Normal file
View File

@ -0,0 +1,27 @@
module hulk.range;
/**
* Representation of a memory range.
*/
struct Range
{
/** Range base address. */
void * _address;
/** Range length. */
size_t length;
/** Set the range address. */
@property void * address(T)(T a)
{
static assert(T.sizeof == size_t.sizeof);
_address = cast(void *)a;
return _address;
}
/** Get the range address. */
@property void * address()
{
return _address;
}
}

150
src/hulk/rtc.d Normal file
View File

@ -0,0 +1,150 @@
/**
* 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;
}
}

74
src/hulk/serial.d Normal file
View File

@ -0,0 +1,74 @@
/**
* Serial port access.
*/
module hulk.serial;
import hulk.cpu;
import core.stdc.stdarg;
static import hulk.writef;
struct Serial
{
enum BASE_PORT = 0x3F8;
/**
* Initialize the serial module.
*/
static void initialize()
{
}
/**
* Write a character to the serial port.
*
* @param ch Character to write.
*/
static void write(ubyte ch)
{
if (ch == '\n')
{
out8(BASE_PORT, '\r');
}
out8(BASE_PORT, ch);
}
/**
* Write a formatted string to the serial port.
*
* @param s Format string.
* @param args Variable arguments structure.
*/
public static void writef(string s, va_list args)
{
hulk.writef.writefv(function void(ubyte ch) {
Serial.write(ch);
}, s, args);
}
/**
* Write a formatted string to the serial port.
*
* @param s Format string.
*/
public static extern (C) void writef(string s, ...)
{
va_list args;
va_start(args, s);
writef(s, args);
va_end(args);
}
/**
* Write a formatted string and newline to the serial port.
*
* @param s Format string.
*/
public static extern (C) void writefln(string s, ...)
{
va_list args;
va_start(args, s);
writef(s, args);
writef("\n", args);
va_end(args);
}
}

139
src/hulk/test.d Normal file
View File

@ -0,0 +1,139 @@
/**
* Kernel tests.
*/
module hulk.test;
import hulk.klog;
import hulk.list;
import hulk.time;
import hulk.hurl.a1;
import core.volatile;
struct Test
{
/**
* Run tests.
*/
public static void run()
{
Klog.writefln("\a3Running kernel tests");
test_pit();
test_list();
Klog.writefln("\a3Kernel tests complete");
}
private static void test_pit()
{
Klog.writefln("Testing PIT...");
/* Check that PIT millisecond interrupt is firing. */
size_t uptime = Time.uptime();
while (Time.uptime() <= uptime)
{
}
}
private static void test_list()
{
Klog.writefln("Testing list...");
struct S
{
size_t v;
mixin List!S;
void initialize()
{
list_init();
}
}
S * s1 = A1.allocate!S();
s1.initialize();
s1.v = 33;
assert_eq(1, s1.list_count);
S * s2 = A1.allocate!S();
s2.initialize();
s2.v = 42;
s2.list_insert_after(s1);
S * s3 = A1.allocate!S();
s3.initialize();
s3.v = 55;
s3.list_insert_before(s1);
size_t count;
assert_eq(3, s1.list_count);
assert_eq(3, s2.list_count);
assert_eq(3, s3.list_count);
foreach (entry; *s1)
{
switch (count)
{
case 0:
assert_eq(33, entry.v);
break;
case 1:
assert_eq(42, entry.v);
break;
case 2:
assert_eq(55, entry.v);
break;
case 3:
assert_eq(0, 1);
break;
default:
break;
}
count++;
}
foreach (entry; *s3)
{
if (entry.v == 33)
{
entry.list_remove();
}
}
assert_eq(2, s3.list_count);
count = 0;
foreach (entry; *s3)
{
switch (count)
{
case 0:
assert_eq(55, entry.v);
break;
case 1:
assert_eq(42, entry.v);
break;
case 2:
assert_eq(0, 1);
break;
default:
break;
}
count++;
}
foreach (entry; *s2)
{
if (entry.v == 42)
{
entry.list_remove();
}
}
assert_eq(1, s2.list_count);
assert_eq(1, s3.list_count);
}
private static void assert_eq(T)(T first, T second, size_t line_number = __LINE__)
{
if (first != second)
{
Klog.fatal_error("Assertion failed! %x != %x (line %u)", first, second, line_number);
}
}
private static void assert_neq(T)(T first, T second, size_t line_number = __LINE__)
{
if (first == second)
{
Klog.fatal_error("Assertion failed! %x == %x (line %u)", first, second, line_number);
}
}
}

143
src/hulk/thread.d Normal file
View File

@ -0,0 +1,143 @@
/**
* Kernel thread functionality.
*/
module hulk.thread;
import hulk.hurl.a1;
import hulk.him;
import hulk.gdt;
import hulk.memory;
import hulk.list;
import hulk.cpu;
import hulk.hurl;
import ldc.llvmasm;
struct Thread
{
/* TODO: currently kernel thread stack size is fixed. */
enum STACK_SIZE = 16 * 1024;
enum ulong INT_TCTRL_YIELD = 0;
/** Interrupt stack template. */
static immutable __gshared ulong[ISF_COUNT] interrupt_stack_template = [
0x16161616_16161616u, /* RBP */
0x15151515_15151515u, /* R15 */
0x14141414_14141414u, /* R14 */
0x13131313_13131313u, /* R13 */
0x12121212_12121212u, /* R12 */
0x11111111_11111111u, /* R11 */
0x10101010_10101010u, /* R10 */
0x09090909_09090909u, /* R9 */
0x08080808_08080808u, /* R8 */
0x06060606_06060606u, /* RDI */
0x05050505_05050505u, /* RSI */
0x04040404_04040404u, /* RDX */
0x03030303_03030303u, /* RCX */
0x02020202_02020202u, /* RBX */
0x01010101_01010101u, /* RAX */
0u, /* Error Code */
0u, /* RIP */
Gdt.SELECTOR_KERNEL_CODE, /* CS */
0u, /* RFLAGS */
0u, /* RSP */
Gdt.SELECTOR_KERNEL_DATA, /* SS */
];
/** List of all runnable kernel threads. */
private static __gshared Thread * runnable_threads;
/** Currently executing kernel thread. */
static __gshared Thread * current_thread;
/** Current kernel thread stack pointer. */
ulong * stack_pointer;
static assert(stack_pointer.offsetof == 0);
/** Base address of kernel thread stack. */
void * stack_addr;
mixin List!Thread;
/**
* Initialize a kernel thread.
*/
public void initialize(void function() fn)
{
list_init();
suspend_interrupts();
list_insert_after(current_thread);
resume_interrupts();
stack_addr = A1.allocate(STACK_SIZE);
ulong * stack_top = cast(ulong *)(stack_addr + STACK_SIZE);
stack_pointer = stack_top - ISF_COUNT;
memcpy64(stack_pointer, &interrupt_stack_template[0], ISF_COUNT);
stack_pointer[ISF_RIP] = cast(ulong)fn;
stack_pointer[ISF_RFLAGS] = read_rflags();
stack_pointer[ISF_RSP] = cast(ulong)stack_top;
}
/**
* Initialize first kernel thread.
*/
public void initialize_first_thread()
{
list_init();
runnable_threads = &this;
stack_addr = cast(void *)(Hurl.HULK_STACK_TOP - STACK_SIZE);
}
/**
* Initialize kernel threading.
*/
public static void initialize()
{
Thread * thread = A1.allocate!Thread();
thread.initialize_first_thread();
current_thread = thread;
}
/**
* Create and start a kernel thread.
*
* TODO: fn must not return.
* TODO: threads are always runnable.
* TODO: threads cannot be destroyed.
*/
public static Thread * start(void function() fn)
{
Thread * thread = A1.allocate!Thread();
thread.initialize(fn);
swint(INT_TCTRL_YIELD);
return thread;
}
/**
* Trigger a thread management software interrupt.
*/
public static void swint(T1)(T1 v1)
{
mixin(`__asm("int $$`, cast(int)INT_KERNEL_SWINT, `", "{rax}", v1);`);
}
/**
* Handle a thread management software interrupt.
*
* This function is called in interrupt context.
*
* @param stack_frame
* Pointer to the stack frame.
*/
public static void handle_swint(ulong * stack_frame)
{
switch (stack_frame[ISF_RAX])
{
case INT_TCTRL_YIELD:
Thread.current_thread = Thread.current_thread.list_next;
break;
default:
break;
}
}
}

42
src/hulk/time.d Normal file
View File

@ -0,0 +1,42 @@
/**
* Time functionality.
*/
module hulk.time;
import hulk.volatile;
struct Time
{
/** System uptime (ms). */
private static __gshared Volatile!ulong s_uptime;
/**
* Millisecond ISR.
*/
public static void ms_isr()
{
s_uptime++;
}
/**
* Get system uptime (ms).
*/
public static @property ulong uptime()
{
return s_uptime;
}
/**
* Sleep for the given amount of time (ms).
*
* @param count
* Number of milliseconds to sleep for.
*/
public static void msleep(ulong count)
{
ulong wait_for = uptime() + count + 1;
while (uptime() < wait_for)
{
}
}
}

3
src/hulk/usb/package.d Normal file
View File

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

24
src/hulk/usb/usb.d Normal file
View File

@ -0,0 +1,24 @@
/**
* USB (Universal Serial Bus) functionality.
*/
module hulk.usb.usb;
import hulk.klog;
import hulk.usb.xhci;
import hulk.pci;
struct Usb
{
public static void initialize()
{
Klog.writefln("\a3Initializing USB");
foreach (pci_device; *Pci.devices)
{
if (pci_device.type == Pci.XHCI_CONTROLLER)
{
XHCI.build(pci_device);
}
}
}
}

535
src/hulk/usb/xhci.d Normal file
View File

@ -0,0 +1,535 @@
/**
* eXtensible Host Controller Interface for Universal Serial Bus
*
* Documentation:
* - https://wiki.osdev.org/EXtensible_Host_Controller_Interface
* - https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
*/
module hulk.usb.xhci;
import hulk.pci;
import hulk.hurl;
import hulk.hurl.a1;
import hulk.hippo;
import hulk.klog;
import hulk.time;
import hulk.volatile;
struct XHCI
{
/**
* Located at PCI base address.
*
* All are read-only.
*/
struct CapabilityRegisters
{
/**
* Offset from beginning of capability registers to beginning of
* operational registers.
*/
Volatile!ubyte capability_length;
ubyte _reserved01;
/**
* Interface version number supported (psuedo-BSD).
*/
Volatile!ushort hci_version;
/**
* HCSPARAMS1.
* 7:0 number of device slots (MaxSlots)
* 18:8 number of interrupters (MaxIntrs)
* 23:19 reserved
* 31:24 number of ports (MaxPorts)
*/
Volatile!uint hcs_params1;
@property ubyte max_slots()
{
return this.hcs_params1 & 0xFFu;
}
/**
* 3:0 isochronous scheduling threshold (IST)
* 7:4 event ring segment table max (ERST Max)
* 20:8 reserved
* 25:21 max scratchpad buffers (high 5 bits; xHCI v1.1+)
* 26 scratchpad restore (SPR)
* 31:27 max scratchpad buffers (low 5 bits)
*/
Volatile!uint hcs_params2;
@property size_t max_scratchpad_buffers()
{
uint v = this.hcs_params2;
return (v >> 27u) | ((v >> (21 - 5)) & 0x3E0u);
}
/**
* 7:0 U1 device exit latency
* 15:8 reserved
* 31:16 U2 device exit latency
*/
Volatile!uint hcs_params3;
/**
* 0 64-bit addressing capability (AC64)
* 1 BW negotiation capability (BNC)
* 2 context size (CSZ)
* 3 port power control (PPC)
* 4 port indicators (PIND)
* 5 light HC reset capability (LHRC)
* 6 latency tolerance messaging capability (LTC)
* 7 no secondary SID support (NSS)
* 8 parse all event data (PAE; xHCI v1.1+)
* 9 stopped - short packet capability (SPC; xHCI v1.1+)
* 10 stopped EDTLA capability (SEC; xHCI v1.1+)
* 11 continuous frame ID capability (CFC; xHCI v1.1+)
* 15:12 maximum primary stream array size (MaxPSASize)
* 31:16 xHCI extended capabilities pointer (xECP)
* offset in 32-bit words from base of capability registers to base
* of capabilities list
*/
Volatile!uint hcc_params1;
/**
* 1:0 reserved
* 31:2 doorbell array offset in 32-bit words
* offset in 32-bit words from base of capability registers to the
* doorbell array
*/
Volatile!uint doorbell_offset;
/**
* 4:0 reserved
* 31:5 runtime register space offset
*/
Volatile!uint rts_offset;
/**
* 0 U3 entry capability (U3C)
* 1 ConfigEP command max exit latency too large (CMC)
* 2 force save context capability (FSC)
* 3 compliance transition capability (CTC)
* 4 large ESIT payload capability (LEC)
* 5 configuration information capability (CIC)
* 6 extended TBC capability (ETC)
* 7 extended TBC TRB status capability (ETC_TSC)
* 8 get/set extended property capability (GSC; xHCI v1.1+)
* 9 virtualization based trusted I/O capability (VTC; xHCI v1.2+)
* 31:10 reserved
*/
Volatile!uint hcc_params2;
/**
* 11:0 reserved
* 31:12 VTIO register space offset (xHCI v1.2+)
*/
Volatile!uint vtios_offset;
}
static assert(CapabilityRegisters.sizeof == 0x24u);
/**
* Located at offset capability_length from PCI base address.
*/
struct OperationalRegisters
{
/**
* 0 run/stop (RS)
* 1 host controller reset (HCRST)
* 2 interrupter enable (INTE)
* 3 host system error enable (HSEE)
* 6:4 reserved
* 7 light host controller reset (LHCRST)
* 8 controller save state (CSS)
* 9 controller restore state (CRS)
* 10 enable wrap event (EWE)
* 11 enable U3 MFINDEX stop (UE3S)
* 12 stopped short packet enable (SPE; xHCI v1.1+)
* 13 CEM enable (CME; xHCI v1.1+)
* 14 extended TBC enable (ETE; xHCI v1.2+)
* 15 extended TBC TRB status enable (TSC_EN; xHCI v1.2+)
* 16 VTIO enable (VTIOE; xHCI v1.2+)
* 31:17 reserved
*/
Volatile!uint usb_command;
/**
* 0 HCHalted
* 1 reserved
* 2 Host System Error (HSE)
* 3 Event Interrupt (EINT)
* 4 Port Change Detected (PCD)
* 7:5 reserved
* 8 Save State Status (SSS)
* 9 Restore State Status (RSS)
* 10 Save/Restore Error (SRE)
* 11 Controller Not Ready (CNR)
* 12 Host Controller Error (HCE)
* 31:13 reserved
*/
Volatile!uint usb_status;
/**
* 15:0 Page Size (actual page size is 2^(x+12))
* 31:16 reserved
*/
Volatile!uint page_size;
uint[2] _reserved01;
/**
* 15:0 Notification Enable
* 31:16 reserved
*/
Volatile!uint dn_ctrl;
/**
* 0 Ring Cycle State (RCS)
* 1 Command Stop (CS)
* 2 Command Abort (CA)
* 3 Command Ring Running (CRR)
* 5:4 reserved
* 63:6 Command Ring Pointer
*/
Volatile!ulong cr_ctrl;
uint[4] _reserved02;
/**
* 5:0 reserved
* 63:6 Device Context Base Address Array Pointer
*/
Volatile!ulong dcbaap;
/**
* 7:0 Max Device Slots Enabled (MaxSlotsEn)
* 8 U3 Entry Enable (U3E; xHCI v1.1+)
* 9 Configuration Information Enable (CIE; xHCI v1.1+)
* 31:10 reserved
*/
Volatile!uint config;
uint _reserved03;
}
static assert(OperationalRegisters.sizeof == 0x40u);
/**
* Located at offset 0x400 from start of Operational Registers.
*
* There is one set of these registers for each port of the root hub.
*/
struct PortRegisters
{
/**
* Port Status and Control
*
* 0 Current Connect Status
* 1 Port Enabled/Disabled
* 2 reserved
* 3 Over-current active
* 4 Port Reset
* 8:5 Port Link State
* 9 Port Power
* 13:10 Port Speed
* 15:14 Port Indicator Control
* 16 Port Link State Write Strobe
* 17 Connect Status Change
* 18 Port Enable/Disable Change
* 19 Warm Port Reset Change
* 20 Over-current Change
* 21 Port Reset Change
* 22 Port Link State Change
* 23 Port Config Error Change
* 24 Cold Attach Status
* 25 Wake on Connect Enable
* 26 Wake on Disconnect Enable
* 27 Wake on Over-current Enable
* 29:28 reserved
* 30 Device Removable
* 31 Warm Port Reset
*/
Volatile!uint portsc;
/**
* Port Power Management Status and Control
*/
Volatile!uint portpmsc;
/**
* Port Link Info
*/
Volatile!uint portli;
/**
* Port Hardware LPM Control (xHCI v1.1+)
*/
Volatile!uint porthlpmc;
}
static assert(PortRegisters.sizeof == 0x10u);
/**
* Located at offset rts_offset from PCI base address.
*/
struct RuntimeRegisters
{
/**
* Microframe index. Incremented by controller each microframe.
*/
Volatile!uint mfindex;
uint[7] _reserved01;
/**
* Interrupter register sets.
*/
InterrupterRegister[0] ir;
}
/**
* Interrupter register set.
*
* A copy of this register set is present for each interrupter.
*/
struct InterrupterRegister
{
/**
* Interrupter Management Register.
* 0 Interrupt Pending (IP)
* 1 Interrupt Enable
* 31:2 reserved
*/
Volatile!uint imr;
/**
* Interrupter Moderation.
* 15:0 Interrupt Moderation Interval
* 31:16 Interrupt Moderation Counter
*/
Volatile!uint im;
/**
* Event Ring Segment Table Size.
* 15:0 Event Ring Segment Table Size
* 31:16 reserved
*/
Volatile!uint ersts;
uint reserved;
/**
* Event Ring Segment Table Base Address.
* 5:0 reserved
* 63:6 Event Ring Segment Table Base Address
*/
Volatile!ulong erstba;
/**
* Event Ring Dequeue Pointer.
* 2:0 Dequeue ERST Segment Index
* 3 Event Handler Busy
* 63:4 Event Ring Dequeue Pointer
*/
Volatile!ulong erdp;
}
/**
* Located at offset doorbell_offset from PCI base address.
*
* The number of these is given by HCSPARAMS1.MaxSlots + 1 (for the command
* ring).
*/
struct DoorbellRegister
{
Volatile!ubyte target;
ubyte _reserved01;
Volatile!ushort task_id;
}
static assert(DoorbellRegister.sizeof == 4u);
private CapabilityRegisters * m_capability_registers;
private OperationalRegisters * m_operational_registers;
private PortRegisters * m_port_registers;
private RuntimeRegisters * m_runtime_registers;
private DoorbellRegister * m_doorbell_registers;
void initialize(Pci.Device * pci_device)
{
void * base_address = pci_device.memory_ranges[0].address;
Klog.writefln("Found XHCI controller at address %p", base_address);
m_capability_registers = cast(CapabilityRegisters *)base_address;
m_operational_registers = cast(OperationalRegisters *)(base_address + m_capability_registers.capability_length);
m_port_registers = cast(PortRegisters *)(cast(void *)m_operational_registers + 0x400);
m_runtime_registers = cast(RuntimeRegisters *)(base_address + m_capability_registers.rts_offset);
m_doorbell_registers = cast(DoorbellRegister *)(base_address + m_capability_registers.doorbell_offset);
dump_extended_capabilities();
if (!reset())
{
Klog.writefln("XHCI controller failed to initialize");
return;
}
/* Allocate memory for various controller data structures. */
ubyte * page = cast(ubyte *)Hippo.allocate_page();
/* Device Context Index: 2K */
void ** device_context_index = cast(void **)page;
/* Input Control Context: 64 bytes */
void * input_control_context = &page[2048];
/* Slot Context: 64 bytes */
void * slot_context = &page[2048 + 64];
/* Endpoint Context: 64 bytes */
void * endpoint_context = &page[2048 + 64 + 64];
/* Stream Context: 16 bytes */
void * stream_context = &page[2048 + 64 + 64 + 64];
/* Stream Array (Linear): 1M */
void * stream_array_linear = Hippo.allocate_aligned_region(1024 * 1024, PAGE_SIZE);
/* Stream Array (Pri/Sec): 4K */
void * stream_array_pri_sec = Hippo.allocate_page();
/* Transfer Ring Segments: 64K */
void * transfer_ring_segments = Hippo.allocate_aligned_region(64 * 1024, 64 * 1024);
/* Command Ring Segments: 64K */
void * command_ring_segments = Hippo.allocate_aligned_region(64 * 1024, 64 * 1024);
/* Event Ring Segments: 64K */
void * event_ring_segments = Hippo.allocate_aligned_region(64 * 1024, 64 * 1024);
/* Event Ring Segment Table: 512K */
void * event_ring_segment_table = Hippo.allocate_aligned_region(512 * 1024, PAGE_SIZE);
/* Scratchpad Buffer Array: 248 bytes */
void ** scratchpad_buffer_array = cast(void **)Hippo.allocate_aligned_region(2 * PAGE_SIZE, PAGE_SIZE);
size_t n_scratchpad_buffers = m_capability_registers.max_scratchpad_buffers;
for (size_t i = 0u; i < n_scratchpad_buffers; i++)
{
void * scratchpad_buffer = Hippo.allocate_page();
scratchpad_buffer_array[i] = scratchpad_buffer;
}
device_context_index[0] = scratchpad_buffer_array;
size_t max_slots = m_capability_registers.max_slots;
for (size_t i = 0u; i < max_slots; i++)
{
void * addr;
if ((i & 1) == 0)
{
page = cast(ubyte *)Hippo.allocate_page();
addr = page;
}
else
{
addr = &page[2048];
}
device_context_index[1 + i] = addr;
}
m_operational_registers.dcbaap = cast(ulong)device_context_index;
/* TODO: write cr_ctrl. */
/+ TODO:
m_operational_registers.config = m_capability_registers.hcs_params1 & 0xFu;
m_operational_registers.dn_ctrl = 1u << 1u;
+/
/* TODO: set up interrupt register set. */
Klog.writefln("XHCI controller initialization successful");
}
private void dump_extended_capabilities()
{
size_t ext_cap_off = (m_capability_registers.hcc_params1 >> 16u);
if (ext_cap_off != 0u)
{
Klog.writefln("Extended capabilities:");
uint * extended_capabilities = cast(uint *)m_capability_registers + ext_cap_off;
for (;;)
{
uint ec0 = extended_capabilities[0];
size_t next_offset = (ec0 >> 8u) & 0xFFu;
size_t len = next_offset;
if (len < 1u)
{
len = 1u;
}
ubyte type = ec0 & 0xFFu;
if (type == 2u)
{
len = 4u;
}
for (size_t i = 0u; i < len; i++)
{
uint ecv = extended_capabilities[i];
Klog.writef(" %02X %02X %02X %02X",
ecv & 0xFFu,
(ecv >> 8u) & 0xFFu,
(ecv >> 16u) & 0xFFu,
(ecv >> 24u) & 0xFFu);
}
Klog.writef("\n");
if (next_offset == 0u)
{
break;
}
extended_capabilities += next_offset;
}
}
}
private bool reset()
{
/* Clear run/stop bit. */
m_operational_registers.usb_command &= ~0x1u;
/* Wait for HCHalted bit to be set. */
enum halt_timeout = 40u;
for (size_t halt_timeout_counter = 0u;;)
{
Time.msleep(1u);
if ((m_operational_registers.usb_status & 1u) != 0u)
{
break;
}
halt_timeout_counter++;
if (halt_timeout_counter >= halt_timeout)
{
Klog.writefln("XHCI controller failed to halt");
return false;
}
}
/* Set HC reset bit. */
m_operational_registers.usb_command |= (1u << 1u);
/* Wait for controller to be ready. */
enum reset_timeout = 100u;
for (size_t reset_timeout_counter = 0u;;)
{
Time.msleep(1u);
if (((m_operational_registers.usb_command & (1u << 1u)) == 0u) &&
((m_operational_registers.usb_status & (1u << 11u)) == 0u))
{
break;
}
reset_timeout_counter++;
if (reset_timeout_counter >= reset_timeout)
{
Klog.writefln("XHCI controller failed to reset");
return false;
}
}
return true;
}
static void build(Pci.Device * pci_device)
{
if (pci_device.memory_ranges[0].address != null)
{
XHCI * xhci = A1.allocate!XHCI();
xhci.initialize(pci_device);
}
}
}

17
src/hulk/util.d Normal file
View File

@ -0,0 +1,17 @@
/**
* HULK utility functions.
*/
module hulk.util;
/**
* Round a value up to the next power of 2.
*
* @param v Value to round up.
* @param p Power of 2 to round up to.
*
* @return Rounded up value.
*/
ulong round_up_power_2(ulong v, ulong p)
{
return (v + p - 1u) & ~(p - 1u);
}

3
src/hulk/ver.d Normal file
View File

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

35
src/hulk/volatile.d Normal file
View File

@ -0,0 +1,35 @@
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;
}
}

191
src/hulk/writef.d Normal file
View File

@ -0,0 +1,191 @@
/**
* 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;
}

View File

@ -1,103 +0,0 @@
#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);
}

View File

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

View File

@ -1,41 +0,0 @@
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 = .;
}

View File

@ -1,100 +0,0 @@
#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);
}

View File

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

View File

@ -1,49 +0,0 @@
#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

View File

@ -1,91 +0,0 @@
#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;
}

View File

@ -1,25 +0,0 @@
#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

View File

@ -1,127 +0,0 @@
#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

View File

@ -1,12 +0,0 @@
#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(),
};

View File

@ -1,12 +0,0 @@
#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

View File

@ -1,26 +0,0 @@
#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 Submodule

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

View File

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