317 Commits

Author SHA1 Message Date
Justin C. Miller
67b5f33d46 [general] Remove the last bits of gnu-efi
With the new bootloader changes that use clang to directly build an EFI
application, the last piece of GNU-EFI that was used (the linker script)
is no longer necessary.

Thanks for being a great starting point, GNU-EFI!
2020-05-29 00:24:24 -07:00
Justin C. Miller
b675dfd014 [boot] Fix header include path for uefi headers
After the previous commit, the header path was different. This updates
the build scripts to point to the right location.
2020-05-29 00:21:42 -07:00
Justin C. Miller
fc2b884af9 [build] Copy uefi headers into project
Eventually the UEFI headers should be brought in from their own project,
but for now, like the other projects under external/, these are being
copied into this repository.

Tags: boot uefi
2020-05-25 02:40:51 -07:00
Justin C. Miller
cbd19fa070 [kernel] Don't use deferred logging for now
The last bug getting back to par with master - looks like there might be
threading issues with the logger task at the moment. Turning it off for
now.

Tags: log bug todo
2020-05-24 22:11:54 -07:00
Justin C. Miller
83b330bf2b [kernel] Use constants for known pml4e indices
There were a few lingering bugs due to places where 510/511 were
hard-coded as the kernel-space PML4 entries. These are now constants
defined in kernel_memory.h instead.

Tags: boot memory paging
2020-05-24 22:06:24 -07:00
Justin C. Miller
cc9cde9bfe [tooling] Remove old GDB workarounds
GDB works far better now with QEMU's `-S` flag. No longer does it
complain about changing the target from 32 to 64 bits. Get rid of the
old `waiting` loop and `sleep` call in the GDB config for the kernel.

Tags: debugging
2020-05-24 19:50:45 -07:00
Justin C. Miller
774f6fc334 [kutil] Don't use delete on non-new pointers
`kutil::vector` was calling `operator delete []` on memory that had not
been allocated with `operator new []`, and so was deleting the wrong
pointer.

Tags: bug memory allocator
2020-05-24 19:48:03 -07:00
Justin C. Miller
bfd13e7a9b [kernel] Re-enable most of kernel_main
The `kernel_main()` had a lot change out from under it with the
bootloader changes. This change brings most of it back in line with the
new kernel arguments.

Tags: pml4 paging boot
2020-05-24 17:58:45 -07:00
Justin C. Miller
35b1d37df0 [memory] Rework memory_initialize for new loader
Created a new `memory_initialize()` function that uses the new-style
kernel args structure from the new bootloader.

Additionally:
* Fixed a hard-coded interrupt EOI address that didn't work with new
  memory locations
* Make the `page_manager::fault_handler()` automatically grant pages
  in the kernel heap

Tags: boot page fault
2020-05-24 16:43:36 -07:00
Justin C. Miller
fc3d919f25 [kernel] Fix initial kernel_main triple-fault
At some point, `init_console()` ended up not being before the first
usage of some `log::` functions, which were jumping off into garbage.

Tags: initialization boot
2020-05-23 12:40:47 -07:00
Justin C. Miller
75641a4394 [boot] Add explicit memory map pointer to args
The bootloader was previously just passing the memory map as a module,
but the memory map is important enough to want a direct pointer, instead
of having to search the modules.
2020-05-23 12:39:24 -07:00
Justin C. Miller
ce0bcbd3b6 [boot] Set up CR4 in bootloader
Moving the initial CR4 settings from the kernel's `memory_initialize`
(where it doesn't really fit anyway) to the bootloader's `hardware.cpp`.
2020-05-23 12:35:59 -07:00
Justin C. Miller
3194b460cc [memory] Update kernel_memory to current layout
The `kernel_offset` and `page_offset` had already been updated with
previous bootloader changes, but `kernel_max_heap` had not. Also, make
all the constants `constexpr` instead of `static const` that would live
in multiple TUs.
2020-05-23 12:33:28 -07:00
Justin C. Miller
e1d148a34d [boot] Fix a bug with address-index translation
When `page_entry_iterator` became a template and changed its static shifts
translating virtual address to table indices into a for loop, that loop
was getting the indices backwards (ie, PML4E index was really the PTE
index, and so on).

Tags: paging
2020-05-22 00:32:04 -07:00
Justin C. Miller
b491a09686 [boot] Virtualize memory in the bootloader
Finish updating the page tables, call UEFI's `set_virtual_address_map`
and jump to the kernel!
2020-05-21 23:49:49 -07:00
Justin C. Miller
6a538ad4f3 [boot] Fix several errors getting to kernel
* When using the non-allocating version of `get_uefi_mappings` the
  length was not getting set. Reworked this function.
* Having `build_kernel_mem_map` from `bootloader_main_uefi` caused it to
  get an out of date map key. Moved this function into `efi_main` right
  before exiting boot services.
2020-05-21 23:00:32 -07:00
Justin C. Miller
6ccc172f33 [boot] Centralize where to hlt on error
Searching for `hlt` in disassembly is an easy way to find the error
handler. This change centralizes it to just one, to better match
disassembly with code.
2020-05-21 22:41:33 -07:00
Justin C. Miller
66ca3a3f9b [boot] Consolidate mapping code into iterator obj
The page table code had been copied mostly verbatim from the kernel, and
was a dense mess. I abstraced the `page_table_indices` class and the old
loop behavior of `map_in` into a new `page_entry_iterator` class, making
both `map_pages` and the initial offset mapping code much cleaner.

Tags: vmem paging
2020-05-20 01:02:15 -07:00
Justin C. Miller
4f4a35a7be [boot] Set up initial page tables
Set up initial page tables for both the offset-mapped area and the
loaded kernel code and data.

* Got rid of the `loaded_elf` struct - the loader now runs after the
  initial PML4 is created and maps the ELF sections itself.
* Copied in the `page_table` and `page_table_indices` from the kernel,
  still need to clean this up and extract it into shared code.
* Added `page_table_cache` to the kernel args to pass along free pages
  that can be used for initial page tables.

Tags: paging
2020-05-17 22:03:44 -07:00
Justin C. Miller
c9722a07f3 [boot] Provide memset implementation
Clang needs memset, memcpy, etc to exist even in freestanding situations
because it will emit calls to those functions. This commit adds a simple
weak-linked memset implementation.
2020-05-17 22:00:50 -07:00
Justin C. Miller
42dfa6ccfe [boot] Only allocate memory map once
The `build_kernel_mem_map` function now calls `get_uefi_mappings`
itself, instead of having the efi map passed in. `get_uefi_mappings`
also now takes a `bool allocate` to direct it to actually allocate
the map or not. If it doesn't, it instead just returns the size of
the map and the metadata - which `build_kernel_mem_map`	uses to decide
how much space to first allocate for the kernel's map.
2020-05-16 18:48:28 -07:00
Justin C. Miller
2adef874ee [boot] Make sure the kernel entrypoint abi is sysv
Adding this now because I'm sure I'll forget later. Make sure to
annotate the entrypoint function pointer as `__attribute__((sysv_abi))`
so that it's not called via ms abi like the rest of the loader.
2020-05-16 18:44:35 -07:00
Justin C. Miller
a6e4995963 [boot] Fix call to exit_boot_services
Exiting boot services can't actually be done from inside
`bootloader_uefi_main`, because there are objects in that scope that run
code requiring boot services in their destructors.

Also added `support.cpp` with `memcpy` because clang will emit
references to `memcpy` even in freestanding mode.

Added a `debug_break` function to allow for faking breakpoints when
connecting to the bootloader with GDB.

Tags: debug
2020-05-13 02:08:47 -07:00
Justin C. Miller
2bd91c2d94 [boot] Split get_uefi_mappings and module creation
The `get_mappings()` function was getting too large, and some of its
output is needed by more than just the building of the kernel map. Split
it out into two.

Tags: boot memory
2020-05-10 16:43:18 -07:00
Justin C. Miller
c713f4ff6f [boot] Build the kernel mem map from the UEFI one
Created kernel args memory map structure, looping through UEFI's memory
map to copy and condense.

Tags: boot memory
2020-05-10 16:26:17 -07:00
Justin C. Miller
21b0b08908 [general] Add a git commit template
I've been pretty lax with my commit messages despite attempts to
standardize them. Trying out setting a template. This template can be
applied with:

`git config commit.template .git-commit-template`

See: [another template](https://github.com/joelparkerhenderson/git_commit_template)
See: [another template](https://gist.github.com/adeekshith/cd4c95a064977cdc6c50)

Tags: git
2020-05-10 11:19:33 -07:00
Justin C. Miller
88ace0a99f Fix a bug in print_long_hex()
Because of order of shifting operations and a literal that defaulted
to int, the high 32 bits were printed incorrectly.
2020-05-10 02:31:53 -07:00
Justin C. Miller
10c8f6e4b5 Clean and document header files.
- Add missing doc comments to header files
- Move allocate_kernel_args to main.cpp
- Split functions out into pointer_manipulation.h
2020-05-10 01:42:22 -07:00
Justin C. Miller
9aa749e877 Parse ELF and load kernel, specify mem types
* Very bare-bones ELF parsing to load the kernel
* Custom memory type values for allocated memory
2020-05-09 21:25:45 -07:00
Justin C. Miller
f78a99927a [boot] Add initial stubs for loading kernel ELF 2020-05-02 23:58:41 -07:00
Justin C. Miller
ec794f4f99 Move module loading in main into helper function 2020-03-02 19:43:44 -08:00
Justin C. Miller
884182217c Fix file loading size bug 2020-03-02 19:31:40 -08:00
Justin C. Miller
d94907ae79 Loading files 2020-02-25 22:22:12 -08:00
Justin C. Miller
4accfd136e Add file functionality in fs.cpp 2020-02-24 02:19:15 -08:00
Justin C. Miller
1a223d6ef5 Move args structure allocation to a function in memory.cpp 2020-02-24 02:18:14 -08:00
Justin C. Miller
36b3ad8154 Add an optional context string for status line messges 2020-02-24 02:15:45 -08:00
Justin C. Miller
460973954e Remove utility.* 2020-02-23 18:32:10 -08:00
Justin C. Miller
303a78065e Move find_acpi_table to new hardware.cpp 2020-02-23 18:28:20 -08:00
Justin C. Miller
93f0b70eba Move to RAII-style status_line objects for console status 2020-02-23 17:55:53 -08:00
Justin C. Miller
6f5a2a3d3f Update kernel args to be module-based
- The old kernel_args structure is now mostly represented as a series of
  'modules' or memory ranges, tagged with a type. An arbitrary number
  can be passed to the kernel
- Update bootloader to allocate space for the args header and 10 module
  descriptors
2020-02-23 00:07:50 -08:00
Justin C. Miller
ec563ea8e4 Allow multiple calls to console::status_* functions 2020-02-22 17:52:51 -08:00
Justin C. Miller
570638bba6 Don't require system_table in console 2020-02-22 15:47:07 -08:00
Justin C. Miller
f627ea7de0 Re-add functionality to boot with new UEFI headers
- Pointer fixup event
- ACPI searching
- Move CHECK_* to using try_or_raise()
2020-02-22 14:57:28 -08:00
Justin C. Miller
521c132801 Back to a basic UEFI stub 2020-02-22 01:54:00 -08:00
Justin C. Miller
bc5115b9ea Removed old UEFI headers 2020-02-22 01:52:49 -08:00
Justin C. Miller
6963304c01 Changing jsix license
jsix is now licensed under the MPL2.
2019-10-06 01:37:46 -07:00
Justin C. Miller
ae651a4fcd Move cpptoml.h to external directory 2019-10-06 00:46:30 -07:00
Justin C. Miller
17c2fe6e4e Add debig-exit device to qemu.sh for future tests 2019-10-06 00:29:10 -07:00
Justin C. Miller
f066ac3ffd Move catch.hpp to external directory 2019-10-06 00:01:27 -07:00
Justin C. Miller
edcf633e84 Remove cargo-culted znocombreloc ld flag 2019-08-10 13:49:57 -07:00
Justin C. Miller
d09a454b8b Normalize bootloader's con_debug line endings 2019-07-21 22:29:19 -07:00
Justin C. Miller
d6329ea9bf Update to bonnibel 2.1 2019-07-21 21:10:43 -07:00
Justin C. Miller
83897048ab Update for bonnibel 2.0 2019-07-20 23:19:21 -07:00
Justin C. Miller
7cc59770b8 Update libc for new sysall values via peru 2019-07-07 10:07:00 -07:00
Justin C. Miller
b056d95920 Organize system calls
* syscalls should all return j6_status_t now
* syscalls are grouped by category in name as well as in files
2019-07-07 09:54:29 -07:00
Justin C. Miller
19cd01ef8d Add initial pass of syscall API kobjects 2019-07-07 09:54:29 -07:00
Justin C. Miller
b3f88bbe02 Further refine sysroot.
* remove need for NASM from sysroot
* have peru sync libc separately
2019-07-07 09:52:06 -07:00
Justin C. Miller
f57f38edbd Remove old ident_page_flags 2019-07-05 20:52:04 -07:00
Justin C. Miller
12377ae730 Ignore .peru 2019-07-05 17:29:56 -07:00
Justin C. Miller
bb93dcef44 Remove sysroot binutils dependency
* Link host-targeted binaries with lld
 * Add peru script for getting prebuilt sysroot
 * Add readme for prebuilt sysroots
 * Remove non-working build_sysroot_gcc.sh, rename clang version to just
   build_sysroot.sh
2019-07-05 17:26:24 -07:00
Justin C. Miller
678a12dc90 Ignore .gdb_history 2019-07-04 17:43:42 -07:00
Justin C. Miller
c3dacb2906 Fix mis-flagged page table entries 2019-07-04 17:43:08 -07:00
Justin C. Miller
3a68ec439d Add CPU feature checking.
Introduces the cpu_features.inc table to enumerate the CPU features that
j6 cares about. Features in this table marked CPU_FEATURE_REQ are
considered required, and the boot process will log an error and halt
when any of these features are not supported. This should save me from
banging my head against the wall like I did last night with the missing
pdpe1gb feature.
2019-07-04 16:41:25 -07:00
Justin C. Miller
7ce418aabc Fix KVM page faults from lack of 1GB page support 2019-07-04 02:51:50 -07:00
Justin C. Miller
8ee5091f41 update gitignore 2019-07-03 21:55:23 -07:00
Justin C. Miller
6285517ef7 Rename Popcorn to jsix.
See README.md for more information.
2019-05-27 14:07:29 -07:00
Justin C. Miller
2b0cd6f2f2 Make qemu.sh work with i3 as well 2019-05-26 10:35:22 -07:00
Justin C. Miller
a653c55941 Use 0 instead of syscall_invalid in syscall jump list 2019-05-18 18:11:08 -07:00
Justin C. Miller
ce035d2a43 Finish address_manager to vm_space transition 2019-05-18 18:06:57 -07:00
Justin C. Miller
2d54eb5143 Add vmem log area 2019-05-11 11:32:22 -07:00
Justin C. Miller
b9c8edb657 Allow clang to colorize output in ninja 2019-04-18 00:28:23 -07:00
Justin C. Miller
88315c25a5 Allow vm_space to merge ranges 2019-04-18 00:22:01 -07:00
Justin C. Miller
806bfd1fbf First pass at vm_space (address_manager replacement) 2019-04-17 19:16:54 -07:00
Justin C. Miller
910b5116f4 Fix stack overruns 2019-04-16 23:39:52 -07:00
Justin C. Miller
6302e8b73a Overhaul memory allocation model
This commit makes several fundamental changes to memory handling:

- the frame allocator is now only an allocator for free frames, and does
  not track used frames.
- the frame allocator now stores its free list inside the free frames
  themselves, as a hybrid stack/span model.
  - This has the implication that all frames must currently fit within
    the offset area.
- kutil has a new allocator interface, which is the only allowed way for
  any code outside of src/kernel to allocate. Code under src/kernel
  _may_ use new/delete, but should prefer the allocator interface.
- the heap manager has become heap_allocator, which is merely an
  implementation of kutil::allocator which doles out sections of a given
  address range.
- the heap manager now only writes block headers when necessary,
  avoiding page faults until they're actually needed
- page_manager now has a page fault handler, which checks with the
  address_manager to see if the address is known, and provides a frame
  mapping if it is, allowing heap manager to work with its entire
  address size from the start. (Currently 32GiB.)
2019-04-16 01:13:09 -07:00
Justin C. Miller
fd1adc0262 Put MAX_SYSCALLS in hex to match syscalls list 2019-04-09 23:35:32 -07:00
Justin C. Miller
070be0b005 Allow map_page call with 0 address to allocate address space 2019-04-09 23:20:27 -07:00
Justin C. Miller
5034ffbe59 Increase max kernel heap allocation to 4MiB 2019-04-09 22:31:06 -07:00
Justin C. Miller
cd13b88540 Log about additional CPU/APICs 2019-04-08 14:33:10 -07:00
Justin C. Miller
e050d6f151 Move more logging infrastructure into kutil 2019-04-06 18:25:09 -07:00
Justin C. Miller
863555ec6b Clean up process memory on exit.
Additionally, there were several bug fixes needed to allow this:
- frame_allocator was allocating its frame_blocks from the heap, causing
  a circular dependency. Now it gives itself a page on its own when
  needed.
- frame_allocator::free was putting any trailing pages in a block back
  into the list after the current block, so they would be the next block
  iterated to.
- frame_allocator::free was updating the address it was looking for
  after freeing some pages, but not the count it was looking for, so it
  would eventually free all pages after the initial address.
2019-04-06 11:19:38 -07:00
Justin C. Miller
c605793a9d Fix fork() for new task switching model 2019-04-03 10:08:26 -07:00
Justin C. Miller
8375870af6 Improve syscall definitions
- Allow constant id specification
- Define function signature in SYSCALL macro
- Move implementation into src/kernel/syscalls/*.cpp
2019-04-03 10:03:15 -07:00
Justin C. Miller
11a53e792f Improve syscalls for new task switching
There are a lot of under the hood changes here:
- Move syscalls to be a dispatch table, defined by syscalls.inc
- Don't need a full process state (push_all) in syscalls now
- In push_all, define REGS instead of using offsets
- Save TWO stack pointers as well as current saved stack pointer in TCB:
  - rsp0 is the base of the kernel stack for interrupts
  - rsp3 is the saved user stack from cpu_data
- Update syscall numbers in nulldrv
- Some asm-debugging enhancements to the gdb script
- fork() still not working
2019-04-02 00:25:36 -07:00
Justin C. Miller
ca2362f858 Simplify task switches
No longer using the rsp from the entry to the kernel, but instead
switching rsp at task-switching time in assembly.

This currently breaks fork()
2019-03-31 22:49:24 -07:00
Justin C. Miller
5cdbedd4d1 Move console to use kutil's printf 2019-03-31 22:43:37 -07:00
Justin C. Miller
ee6d69bcd2 Move builds to bonnibel 0.2 2019-03-30 01:33:00 -07:00
Justin C. Miller
f9193b4602 Extract python build scripts as 'bonnibel' 2019-03-27 17:25:50 -07:00
Justin C. Miller
979e5f036e Improve QEMU settings for laptop 2019-03-26 15:27:10 -07:00
Justin C. Miller
39baec852b Allow larger initrd images 2019-03-25 01:42:36 -07:00
Justin C. Miller
ed3f9410a6 Make nulldrv a small C++ program 2019-03-24 13:44:25 -07:00
Justin C. Miller
b18243f098 Improve process switching and syscall log spam 2019-03-22 17:43:29 -07:00
Justin C. Miller
9472dab522 Fix asm int call for LLVM 8 2019-03-22 17:42:51 -07:00
Justin C. Miller
9067f8d298 Add kernel logging task
- Enable creating kernel tasks
- Create kernel task that disables immediate-mode logging and prints
  logs to the console forever
2019-03-20 23:45:01 -07:00
Justin C. Miller
866073ae8a Fix RFLAGS-clobbering syscalls 2019-03-20 23:42:42 -07:00
Justin C. Miller
91cb00fde2 Add immediate log output for early kernel 2019-03-20 20:58:44 -07:00
Justin C. Miller
c435bcfb67 Removed empty unused output_log 2019-03-20 20:58:44 -07:00
Justin C. Miller
7fe1b7d1f5 Add temporary log output
Currently the kernel idle process is an infinite loop printing logs,
with no locking.
2019-03-20 20:58:44 -07:00
Justin C. Miller
39524b2b9f Move kernel over to new logger 2019-03-20 20:58:44 -07:00
Justin C. Miller
c592f5f537 Add buffer-based logging system 2019-03-20 20:58:44 -07:00
Justin C. Miller
94c491d286 Update to LLVM 8.0 2019-03-20 15:40:01 -07:00
Justin C. Miller
645938a194 Add bip buffer and constexpr hash 2019-03-17 10:02:57 -07:00
Justin C. Miller
73756820bd Fix page_manager::unmap_pages() free tracking 2019-03-16 00:36:45 -07:00
Justin C. Miller
33374ab257 Clean up process loader
- cpu_state was being passed 'by value' to modify outer stack frame
- don't pass loader args as rax, rbx, etc - pass in ABI order
2019-03-15 01:05:45 -07:00
Justin C. Miller
bf8286d15f Improve debugging functions
- More sensible stack tracer, in C++ (no symbols yet)
- Was forgetting to add null frame to new kernel stacks
- __kernel_assert was using an old vector
- A GP fault will only print its associated table entry
2019-03-15 00:47:46 -07:00
Justin C. Miller
6410c898c5 Switch push/pop_all macros from push/pop to mov 2019-03-14 23:25:26 -07:00
Justin C. Miller
be007c6278 Implement exit syscall 2019-03-14 22:28:21 -07:00
Justin C. Miller
f7558e3d18 Implement fast syscall/sysret for sytem calls 2019-03-13 23:51:29 -07:00
Justin C. Miller
97e8f2c69a Add get_gsbase() 2019-03-13 22:49:20 -07:00
Justin C. Miller
49bdf47514 Remove segments from push_all 2019-03-13 22:45:02 -07:00
Justin C. Miller
81162f30dc Add popc_stack command to gdb 2019-03-13 22:37:28 -07:00
Justin C. Miller
4379256c11 Split OVMF into _code and _vars 2019-03-12 10:02:37 -07:00
Justin C. Miller
870ca1db45 Allow debug option to be communicated at boot 2019-03-11 03:04:57 -07:00
Justin C. Miller
74a5c301f8 Add guid_device_path GUIDs 2019-03-10 23:35:23 -07:00
Justin C. Miller
2955e6c9c1 Add debugging files to build process 2019-03-10 23:34:47 -07:00
Justin C. Miller
cd2ccb4e06 Move QEMU monitor to telnet 2019-03-10 12:57:43 -07:00
Justin C. Miller
722ee4c52c Recycle old initial pml4 2019-03-10 00:51:39 -08:00
Justin C. Miller
67b9f45004 Offset-map the entire offset region with 1G pages
Instead of building nested page tables for the offset region, just
offset map the entire thing into kernel memory with one PDP mapping
1GiB large pages. This is more efficient and avoids the "need a
page table to map in a page table" dependency loop.
2019-03-09 14:55:48 -08:00
Justin C. Miller
2035fffa1c Fix loading large process images.
2MiB large pages were being used for any large page mapping, but the
page manager doesn't correctly handle them everywhere yet. Now only
allow them for offset pointers (eg MMIO space) that will never be
unmapped.
2019-03-09 13:10:10 -08:00
Justin C. Miller
97ac3c09fa Implement initial fork syscall 2019-03-09 12:18:21 -08:00
Justin C. Miller
241e1dacb0 Consolidate testing memory setup 2019-03-07 23:53:38 -08:00
Justin C. Miller
ac19d3f532 Allow page table copying and unmapping
Lots of rearranging in page_manager as well, moving constants out as
well as helper structs.
2019-03-03 01:52:21 -08:00
Justin C. Miller
194527e0fe Fix address-marking bugs
* Non-blocksize-aligned regions could fail to be found. Have the
  bootloader load them aligned.
* Consolidating used frame blocks in the bootstrap means these would
  have been impossible to free as address space
* mark_permanent wasn't actually removing blocks from the free list
2019-03-03 01:42:32 -08:00
Justin C. Miller
28cf5562ac Use the address_manager to place allocations 2019-02-28 00:37:00 -08:00
Justin C. Miller
8cdc39fdee Switch page_manager to use frame_allocator.
Removed the frame allocation logic from page_manager and replaced it
with using an instance of frame_allocator instead. This had several
major ripple effects:

- memory_initalize() had to change to support this new world
  - Where to map used blocks is now passed as a flag, since blocks don't
    track their virtual address anymore
  - Instead of the complicated "find N contiguous pages that can be
    mapped in with one page table", we now just have the bootloader give
    us some (currently 64) pages to use both for tables and scratch
    space.
  - frame_allocator initialization was split into two steps to allow
    mapping used blocks before std::move()ing them over
2019-02-28 00:37:00 -08:00
Justin C. Miller
626eec4a31 Frame allocator class added 2019-02-28 00:37:00 -08:00
Justin C. Miller
5901237fee Genericize buddy allocator 2019-02-28 00:37:00 -08:00
Justin C. Miller
24316ca0c4 Build native targets with debug symbols 2019-02-28 00:37:00 -08:00
Justin C. Miller
f9d964cccb Adding address manager 2019-02-28 00:37:00 -08:00
Justin C. Miller
a9ac30b991 Allow heap_manager to use non-contiguous blocks.
* Heap manager can now manage non-contiguous blocks of memory (currently
  all sized at the max block size only)
* Fix a bug where heap manager would try to buddy-merge max-sized blocks
2019-02-28 00:37:00 -08:00
Justin C. Miller
61df9cf32c Add -ggdb to tests build 2019-02-28 00:37:00 -08:00
Justin C. Miller
bbd31929ba Rename memory_manager to heap_manager 2019-02-28 00:37:00 -08:00
Justin C. Miller
ec20e9f3d9 Stripping the kernel
Strip the kernel version that we put into the disk image, but keep the
debug symbols in a separate file for GDB.
2019-02-17 23:43:59 -08:00
Justin C. Miller
3bcd83f5a3 Notes update 2019-02-17 23:38:40 -08:00
Justin C. Miller
341ba5146a Forgot to comment third arg for gdt_write 2019-02-17 23:38:40 -08:00
Justin C. Miller
83b37ef536 Give qemu.sh better option handling 2019-02-10 10:31:43 -08:00
Justin C. Miller
1965197ccd Fix sysroot ld setting 2019-02-10 10:31:16 -08:00
Justin C. Miller
29747f4891 Allow modules to specify defines
The modules.yaml now has an optional defines: list per module that adds
preprocessor definitions to the build scripts. Also added a --debug flag
to qemu.sh to run QEMU's debugger host.
2019-02-08 21:22:53 -08:00
Justin C. Miller
aca442ee87 First pass at message syscalls 2019-02-07 18:19:22 -08:00
Justin C. Miller
8e85ae5318 Added getpid system call 2019-02-07 17:52:57 -08:00
Justin C. Miller
8c32471e0d Pass CPU state as a pointer
Previously CPU statue was passed on the stack, but the compiler is
allowed to clobber values passed to it on the stack in the SysV x86 ABI.
So now leave the state on the stack but pass a pointer to it into the
ISR functions.
2019-02-07 17:47:42 -08:00
Justin C. Miller
79711be46a Dump compiler args and defines.
As part of the build, dump the compiler arguments and defines per target
into files.
2019-02-07 17:39:10 -08:00
Justin C. Miller
863e5bda15 Turning console into a class 2019-02-04 00:48:18 -08:00
Justin C. Miller
d19cedb12a adding kernel crti/crtn but ctors/dtors not called yet 2019-02-03 18:59:09 -08:00
Justin C. Miller
f2d39f7df8 Refactoring build system for more control of inputs 2019-02-03 18:32:45 -08:00
Justin C. Miller
579f6f64e6 First step of moving bootloader to C++ 2019-02-03 01:38:12 -08:00
Justin C. Miller
a71af1be96 Updating NOTES 2019-02-03 00:26:35 -08:00
Justin C. Miller
237c242f96 Fix ninja not reloading buildfiles on regen 2019-02-03 00:20:01 -08:00
Justin C. Miller
c4dc52c06c Fix a version parsing issue when on a tagged version 2019-02-03 00:06:39 -08:00
Justin C. Miller
e1d8dd3124 Updating README to reflect new build process 2019-02-02 23:56:47 -08:00
Justin C. Miller
38a1197d9e Removing old waf build scripts and vendored libcxx 2019-02-02 21:39:19 -08:00
Justin C. Miller
bc01a37452 Ninja-based buildsystem now building a running kernel! 2019-02-02 21:35:39 -08:00
Justin C. Miller
acdca19f59 Ninja buildsystem produces working bootloader 2019-02-02 18:24:58 -08:00
Justin C. Miller
a1fe745a53 Changing to __POPCORN__ for defining code that is host-only 2019-02-02 14:44:35 -08:00
Justin C. Miller
73df20d195 Ninja-based system now builds the disk images 2019-02-02 14:43:55 -08:00
Justin C. Miller
7e1933d79b Give makerd a cwd argument, and upgrade cpptoml 2019-02-02 12:18:20 -08:00
Justin C. Miller
8d23fac6cc Allow for ninja files to regenerate themselves 2019-02-02 11:52:05 -08:00
Justin C. Miller
0f8efdb55e Moving to a ninja-based build system 2019-02-02 02:59:45 -08:00
Justin C. Miller
523d0b3b8c sysroot and cross-compiler based build WIP 2019-01-17 00:51:45 -08:00
Justin C. Miller
591ca7c83c libc WIP 2018-09-24 11:13:18 -07:00
Justin C. Miller
dffdcc095d Vendoring libc++ in external/ 2018-09-22 07:55:00 -07:00
Justin C. Miller
229c1e4965 Moved cpptoml to just makerd's includes 2018-09-21 20:36:01 -07:00
Justin C. Miller
d8399e3c07 Fix for page faults under KVM
Under KVM we were hitting what look like out-of-order and/or issues
during initialization when writing to the page tables and then
immediately writing to the mapped memory.  Adding a memory barrier and
an io_wait() in memory_bootstrap.cpp fixed it.
2018-09-21 20:34:26 -07:00
Justin C. Miller
f1bb3556eb Update NOTES 2018-09-21 09:52:21 -07:00
Justin C. Miller
cef0a71bce Use uintptr_t instead of addr_t
They're never actually going to change independently, and it's also
brining in kutil headers more places than they should be.
2018-09-20 09:37:30 -07:00
Justin C. Miller
a9d72b8102 Fixing APIC timer log message 2018-09-18 17:33:11 -07:00
Justin C. Miller
d469482a7f Better spurious interrupt handling 2018-09-16 23:50:54 -07:00
Justin C. Miller
c67c1bd6a2 Give processes multiple quanta before rescheduling 2018-09-16 23:34:42 -07:00
Justin C. Miller
5e6769036c APIC timer calibration
Now the APIC timer is calibrated against the PIT, and the interval for
timer_enable takes a number of microseconds instead of raw ticks and a
divisor.
2018-09-16 18:56:01 -07:00
Justin C. Miller
482b9f50fc Initial process waiting/waking
Processes can now wait on signals/children/time. There is no clock
currently so "time" is just a monotonically increating tick count. Added
a SLEEP syscall to test this waiting/waking.
2018-09-16 12:22:52 -07:00
Justin C. Miller
f4e7eaeb40 Fixing #include error in linked_list.h 2018-09-16 12:20:14 -07:00
Justin C. Miller
8c2ff33c40 Reduce number of DEbuG syscalls in nulldrv 2018-09-15 00:40:30 -07:00
Justin C. Miller
1308864061 MSR and syscall changes
- Moved MSR code to separate files with an enum class
- Implemented syscall_enable in C++ using new MSR calls
2018-09-15 00:37:49 -07:00
Justin C. Miller
62c559043d Pause syscall and int 0xee interrupt syscalls
The syscall/sysret instructions don't swap stacks. This was bad but
passable until syscalls caused the scheduler to run, and scheduling a
task that paused due to interrupt.

Adding a new (hopefully temporary) syscall interrupt `int 0xee` to allow
me to test syscalls without stack issues before I tackle the
syscall/sysret issue.

Also implemented a basic `pause` syscall that causes the calling process
to become unready. Because nothing can wake a process yet, it never
returns.
2018-09-12 20:59:08 -07:00
Justin C. Miller
c2f85ce61b Some enum_bitfield helper operators
Added:
  set += flag -> set = set | flag
  set -= flag -> set = set & ~flag
  set && flag -> (set & flag) == flag
2018-09-12 20:57:15 -07:00
Justin C. Miller
5808599005 Getting rid of 'boogity!'.. end of an era.
Since I'm doing a lot of work on task scheduling, 'boogity!' simply
isn't the "we're all good and we're done!" message that it used to be.
2018-09-12 20:51:50 -07:00
Justin C. Miller
fafe582802 Initial priority-based scheduler
- Scheduler now has multiple linked_lists of processes at different
  priorities
- Process structure improvements
- scheduler::tick() and scheduler::schedule() separation
2018-09-11 22:37:00 -07:00
Justin C. Miller
593cda3ee8 Convert page_block to use kutil::linked_list
- Created a new linked_list-based slab allocator
- Simplified memory bootstrap code by using the slab allocator and
  linked_lists
2018-09-11 20:46:48 -07:00
Justin C. Miller
d5c44645eb New templatized linked_list collection
Also updated tests to work with memory changes
2018-09-09 15:32:10 -07:00
Justin C. Miller
e7a509176d Move makerd to TOML-based manifest
Added the cpptoml library (and license), and moved to using that for
the initrd manifest. It's now possible to specify the `executable`
flag for files, and the kernel correctly only launches new processes
for the initrd files marked `executable`.
2018-09-08 12:54:35 -07:00
Justin C. Miller
3a39d9440a Made syscall ids 64 bits in rax 2018-09-07 10:29:22 -07:00
Justin C. Miller
cabfec3f1e Clearing up kutil/kernel memory code separation 2018-09-07 10:08:47 -07:00
Justin C. Miller
956efabd8f Update NOTES 2018-09-06 09:49:44 -07:00
Justin C. Miller
f146a96298 Cleaning up interrupts.s and adding missing IRQs 2018-09-06 09:48:18 -07:00
Justin C. Miller
585abe9a18 Simple ELF program loader
Now any initrd file is treated like a program image and passed to the
loader to load as a process. Very rudimentary elf loading just allocates
pages, copies sections, and sets the ELF's entrypoint as the RIP to
iretq to.
2018-09-06 01:35:56 -07:00
Justin C. Miller
3d0b262435 Add null driver
This will be the target of our real ELF loader
2018-09-05 23:01:05 -07:00
Justin C. Miller
3f264b4490 Add syscall enum, clean up handler debug prints 2018-09-05 22:49:56 -07:00
Justin C. Miller
1758ee4215 Initial ramdisk support
- Create initrd library to support definitions and loading
- Allow tools compiled for the host machine to be built by wscript
- Create makerd tool to build initrd from manifest
- Move screenfont to initrd, so don't load framebuffer initially
2018-09-05 22:45:30 -07:00
Justin C. Miller
dc40c2f6ad Changes from the reorg branch
Add CR4 options: global pages, FXSAVE, PCIDs
Better page manager page-in flags
Remove obsolete rflags-saving in create_process
2018-09-05 22:26:23 -07:00
Justin C. Miller
2fb92e8592 Move AHCI driver into separate drivers/ directory 2018-09-05 22:17:56 -07:00
Justin C. Miller
57829e1b79 Correct the name of 'modules' folder to 'libraries' 2018-09-05 22:15:05 -07:00
Justin C. Miller
bc26d7d01d Fixing test compilation 2018-09-05 20:17:29 -07:00
Justin C. Miller
b93519e06f Updating README build instructions 2018-09-05 10:17:01 -07:00
Justin C. Miller
5d861d243a Loading processes from within their memory space
The scheduler's create_process now sets up the stack to iretq into a
load_process function, which will load the process image into memory
from within the process' own virtual memory space. Currently this
loading is just copying the old 'taskA' function from kernel space.
2018-09-05 10:09:00 -07:00
Justin C. Miller
f1b84ab370 Default to non-user in all kernel pages now
This causes the user tasks to just PF, so we'll need to actually have a
real loader now.
2018-09-04 09:27:57 -07:00
Justin C. Miller
d5b8902d8f Moving the rest (except ACPI tables) to high mem
Also the debug messaging to verify it.
2018-09-03 15:15:19 -07:00
Justin C. Miller
799fbbdd10 _Actually_ move the kernel to the last TiB.
More work on process page tables, including only mapping the last 2 pml4
entries (the highest 1TiB of the address space, ie, kernel space) into a
new table.

Includes the work of actually moving the kernel there, which I had
apparently done in name only previously. Oops.
2018-09-01 14:54:12 -07:00
Justin C. Miller
d33f1bc6f2 Page index to address translation script 2018-09-01 14:50:49 -07:00
Justin C. Miller
28a90e550e wscript change to dynamically detect KVM support for QEMU 2018-08-31 09:32:32 -07:00
Justin C. Miller
647801f096 Initial work on swapping page tables per process 2018-08-29 15:49:02 -07:00
Justin C. Miller
1664566bd2 enable KVM for qemu 2018-08-27 06:45:36 -07:00
Justin C. Miller
cd09c17d71 Commented out CPUID log messages, they're never differnet under qemu 2018-08-27 06:41:09 -07:00
Justin C. Miller
f74f3f03d1 Include prog_if in PCI device class log message 2018-08-27 06:40:30 -07:00
Justin C. Miller
23006b2b43 Fixed number of args in ahci interrupt log call 2018-08-27 06:39:31 -07:00
Justin C. Miller
7f69a6c9b1 Clean up AHCI: volatile, and sata_reset 2018-05-22 00:31:01 -07:00
Justin C. Miller
1726d10554 Unify syscall/interrupt handling of rsp 2018-05-21 22:57:43 -07:00
Justin C. Miller
757bc21550 Add note to implement FSXAVE 2018-05-21 09:07:53 -07:00
Justin C. Miller
e187679f93 Add 2 more chars to log names 2018-05-21 09:07:53 -07:00
Justin C. Miller
2597e2002b Get super basic ring3 task switching working
* It looks like UEFI enables SSE, so we need to tell clang -mno-sse for
  now to not use XMM* until we're ready to save them.
* SYSCALL is working from ring3 tasks, calling console printf!
2018-05-21 09:07:53 -07:00
Justin C. Miller
e6f819ed90 Fix non-packed TSS struct 2018-05-21 09:07:53 -07:00
Justin C. Miller
0c8bcb2400 Add get_rip/get_rsp helpers 2018-05-21 09:07:53 -07:00
Justin C. Miller
c5761cc51e Add more wscript options for qemu/vbox debugging 2018-05-21 09:07:53 -07:00
Justin C. Miller
24ccf65aba WIP ring3 2018-05-21 09:07:52 -07:00
Justin C. Miller
814d6f1de6 Minor GDT fixes 2018-05-21 09:07:52 -07:00
Justin C. Miller
bfaab294e6 Set up initial task switching (ring0 only) 2018-05-21 09:07:52 -07:00
Justin C. Miller
0ddcf668cb Allow for 2MiB large pages 2018-05-21 09:07:52 -07:00
Justin C. Miller
4005e9e791 Split gdt.* from interrupts.* 2018-05-21 09:07:52 -07:00
Justin C. Miller
abaa007c54 Set TSS and load it 2018-05-21 09:07:52 -07:00
Justin C. Miller
87d80f84c2 Remove AHCI debug dumps 2018-05-21 09:07:32 -07:00
Justin C. Miller
3fdf246a22 Split waf listen command out from vbox command 2018-05-20 17:59:59 -07:00
Justin C. Miller
79b95d0045 Move FIS creation into make_command 2018-05-20 17:59:08 -07:00
Justin C. Miller
1e66e5cd82 Re-add CFL setting that was lost 2018-05-20 16:34:15 -07:00
Justin C. Miller
193d9939f0 Add some AHCI debugging dumps 2018-05-20 02:02:06 -07:00
Justin C. Miller
81fc559802 Add initial ATA identify support to AHCI driver 2018-05-17 00:34:29 -07:00
Justin C. Miller
0d75cc999c Add GPT partition handling as virtual block devices 2018-05-16 10:14:40 -07:00
Justin C. Miller
a5da56d02f Add guid type 2018-05-16 09:52:06 -07:00
Justin C. Miller
a7e20fd390 Update notes about VBox 2018-05-15 21:51:20 -07:00
Justin C. Miller
9f38e7e5f5 Switch to building VBox images on-demand from QEMU image 2018-05-15 21:39:12 -07:00
Justin C. Miller
93e60cc136 Give kassert its own vector instead of DBZ 2018-05-15 21:38:44 -07:00
Justin C. Miller
5f7ec50055 Add fixes I made while looking for VBox bug 2018-05-15 21:37:27 -07:00
Justin C. Miller
ff0019841f Fix message in loader 2018-05-15 21:28:46 -07:00
Justin C. Miller
7eeeced2ca Change wscript vbox copy 2018-05-14 22:53:01 -07:00
Justin C. Miller
0fc369789e Change GDT code to enforce correct CS 2018-05-14 22:52:28 -07:00
Justin C. Miller
09f72f5ac6 GDT and GPF changes to track down Vbox bugs 2018-05-13 23:22:39 -07:00
Justin C. Miller
716109bab5 Add block device management to device manager 2018-05-12 20:27:46 -07:00
Justin C. Miller
0684fcf7e9 Separate read function into blocking and async portions 2018-05-12 20:16:25 -07:00
Justin C. Miller
289104cde0 Enable AHCI interrupts.
* Implement MSI style interrupts
* Move interrupt handling to device_manager for IRQs
* Give device_manager the ability to allocate IRQs
* Move achi::port to an interrupt-based scheme
2018-05-12 18:38:47 -07:00
Justin C. Miller
c9277e4b12 Split ahci read into separate functions 2018-05-12 13:55:09 -07:00
Justin C. Miller
08125fc2a5 Fix AHCI reads 2018-05-12 00:35:04 -07:00
Justin C. Miller
d06dd2ef43 Rearrange AHCI code, attempt to read WIP 2018-05-11 01:45:39 -07:00
Justin C. Miller
8ae3eea19c Move AHCI ports to their own class 2018-05-11 01:25:54 -07:00
Justin C. Miller
a1bc76f305 Move malloc into kutil 2018-05-11 01:25:54 -07:00
Justin C. Miller
045bede481 Improve stack tracing 2018-05-11 01:25:40 -07:00
Justin C. Miller
0a231f2e0e Return to disk-based booting 2018-05-10 01:24:31 -07:00
Justin C. Miller
87e7c5f00a Updating wscripts to use custom tasks 2018-05-09 10:21:21 -07:00
Justin C. Miller
7ded9fe219 Add initial AHCI structures and probe capabilities 2018-05-09 02:30:06 -07:00
Justin C. Miller
b389e75d33 Move PCI classes to separate files 2018-05-09 01:21:30 -07:00
Justin C. Miller
9128bfc5f1 Switch to clang and improve cpprt 2018-05-09 01:17:18 -07:00
Justin C. Miller
bb227d2c37 Update README.md 2018-05-08 22:16:11 -07:00
Justin C. Miller
954da93301 Add LICENSE.md 2018-05-08 21:53:54 -07:00
Justin C. Miller
1dce0f265d Add memory manager tests 2018-05-08 21:53:43 -07:00
Justin C. Miller
0f54630725 Move memory_manager and assert into kutil. 2018-05-08 01:11:03 -07:00
Justin C. Miller
712cd69242 Put devices into a device vector. 2018-05-08 01:02:34 -07:00
Justin C. Miller
ff3bd640f0 Add simple vector implementation to kutil for device_manager 2018-05-07 09:47:34 -07:00
Justin C. Miller
abb347e1a8 Implement free() to finish buddy allocator 2018-05-07 00:59:45 -07:00
Justin C. Miller
949c9c0b8c Remove boot elf loader debug spam 2018-05-06 23:01:03 -07:00
Justin C. Miller
627a9f7972 Add structures in prep for better device tracking 2018-05-06 22:59:59 -07:00
Justin C. Miller
cce892e92f Load ELF file by sections to get addresses right 2018-05-06 22:03:44 -07:00
Justin C. Miller
97fb8ef653 Map 1MiB instead of 1 page for APIC (prep for MSI) 2018-05-06 18:31:08 -07:00
Justin C. Miller
649d6169c9 Ditch BAR logging for PIC devices 2018-05-06 18:31:08 -07:00
Justin C. Miller
9efb97c2a7 Increase to 64 IRQs 2018-05-06 18:24:12 -07:00
Justin C. Miller
d876aa141c Add better number formatting to printf 2018-05-06 02:18:24 -07:00
Justin C. Miller
f64efad057 Add initial PCIe enumeration 2018-05-06 01:38:19 -07:00
Justin C. Miller
20edb87505 Move block list dump to separate method in page manager 2018-05-06 01:37:39 -07:00
Justin C. Miller
34156c55ae Add simple stack trace to exception handler 2018-05-05 17:12:02 -07:00
Justin C. Miller
569bc243f1 Fix bug in log::enable 2018-05-05 15:33:56 -07:00
Justin C. Miller
bc6a42735c Bring Intel/HP efi source into project, remove gnu-efi 2018-05-05 14:13:38 -07:00
Justin C. Miller
b2f2a9c721 Set chipset to q35 2018-05-05 11:26:59 -07:00
Justin C. Miller
8a00b9c77d Spend a few more bytes on 'push' instruction to clear warnings 2018-05-05 11:26:13 -07:00
Justin C. Miller
d7506b6aaf Rename intr log to apic, remove debug defaults 2018-05-05 11:02:41 -07:00
Justin C. Miller
3a86e89116 Update wscripts 2018-05-05 11:01:34 -07:00
Justin C. Miller
0e71bdab65 add FADT acpi table 2018-05-04 23:54:37 -07:00
Justin C. Miller
0c553b3406 Switch to waf build system, first attempt 2018-05-04 23:50:48 -07:00
Justin C. Miller
33012f35ef Re-integrate framebuffer console 2018-05-03 22:01:33 -07:00
Justin C. Miller
a6b915f6b4 Updating NOTES 2018-05-03 21:59:47 -07:00
Justin C. Miller
772c981c39 Update NOTES.md 2018-05-03 01:15:42 -07:00
Justin C. Miller
05905f8c3c Move LAPIC LINT enable after log message for better debugging 2018-05-03 00:58:45 -07:00
Justin C. Miller
9542bd8a44 Add beginning of better vmem allocator 2018-05-03 00:57:58 -07:00
Justin C. Miller
d9fe457b44 Add beginning basic serial driver 2018-05-03 00:08:22 -07:00
Justin C. Miller
59700b07db Add initial IO APIC support
- IO APIC vector mapping
- Legacy PIC disable
- Real interrupts happening
2018-05-02 16:46:37 -07:00
Justin C. Miller
428e4563d0 Add initial classes representing APIC 2018-05-01 01:03:19 -07:00
Justin C. Miller
6c3bbaa686 Update ISO make tartget to xorrisofs 2018-04-30 20:27:37 -07:00
Justin C. Miller
7009bb6d05 Move parse_version.py into scripts 2018-04-30 17:39:17 -07:00
Justin C. Miller
23a5692d59 Switch to building floppy images instead of GPT disks 2018-04-30 08:36:17 -07:00
Justin C. Miller
2d4e7cfdee Check CPUID info, switch cpu flag to Broadwell 2018-04-30 08:25:35 -07:00
Justin C. Miller
99222d8ab9 Log more info about ACPI APIC table data 2018-04-29 23:50:01 -07:00
Justin C. Miller
a845fee689 Remove test kalloc from main 2018-04-29 18:09:19 -07:00
Justin C. Miller
cfecf4f1d4 Add rdmsr/wrmsr to io.cpp 2018-04-29 13:24:02 -07:00
Justin C. Miller
b3e49590a7 Add logging framework 2018-04-28 19:18:53 -07:00
Justin C. Miller
358837ed69 Implement first-pass simple virtual memory manager 2018-04-28 02:17:17 -07:00
Justin C. Miller
2a353830c2 Update notes 2018-04-28 02:12:46 -07:00
Justin C. Miller
14f51436d7 Load ELF file with bootloader instead of flat binary 2018-04-28 02:12:08 -07:00
Justin C. Miller
57e5465c2d Add -fno-exceptions and QEMU logging to Makefile 2018-04-27 22:20:46 -07:00
Justin C. Miller
d9619e65a2 Fix indirection bug with marked offset pointer mapping 2018-04-26 17:27:55 -07:00
Justin C. Miller
9754994e0c Standardize types used in memory_pages 2018-04-26 16:47:51 -07:00
Justin C. Miller
34c894b15d Enable allocation and mapping of pages 2018-04-26 11:10:32 -07:00
Justin C. Miller
a2665d9247 Fix printf bug in page_block::dump 2018-04-26 11:09:34 -07:00
Justin C. Miller
1e3ae67646 Add -no-reboot to QEMUOPTS 2018-04-26 11:07:58 -07:00
Justin C. Miller
25b9625635 paging finally works 2018-04-25 19:53:22 -07:00
Justin C. Miller
2404b22c1f support widths in printf 2018-04-25 19:52:27 -07:00
Justin C. Miller
bed882f41c Enable paging WIP 2018-04-25 10:48:14 -07:00
Justin C. Miller
fd9e0944cb Add rudimentary printf to console 2018-04-25 10:43:17 -07:00
Justin C. Miller
7e462319c9 Fix inconsistenly-named frame_buffer_size 2018-04-24 09:50:07 -07:00
Justin C. Miller
94de87ef86 Refactor screen ouput from main console code 2018-04-24 09:32:57 -07:00
Justin C. Miller
eb13f1f4fb Fix missing return 2018-04-24 08:54:38 -07:00
Justin C. Miller
0a6c39ded4 Remove -ggdb from LDFLAGS 2018-04-23 20:37:15 -07:00
Justin C. Miller
ff1aac64c1 Use our own stack space, not efi's. 2018-04-23 10:22:43 -07:00
Justin C. Miller
ef24894211 Add stupid first serial output 2018-04-23 10:22:02 -07:00
Justin C. Miller
1113164505 Join page_block insert methods into one 2018-04-22 23:27:15 -07:00
Justin C. Miller
1de73de2e3 Move page table allocation to top 256GiB.
I forgot to account for tracking page table physical addresses, so
this is a bit of an overhaul. Major changes:
- Refactor bootstrap code into more functions and:
  - Only allocate 32 pages of scratch space
  - Remap remaining space into top 256GiB, the "page table space"
- Use the page table space to directly offset-map page table pages
  from their physical addresses, to avoid tracking overhead.
- Refactor page_block list functions into static functions to better
  handle null/empty lists
2018-04-22 21:52:59 -07:00
Justin C. Miller
571cc5a1da Add mmu logging to qemu.bat 2018-04-22 21:50:35 -07:00
Justin C. Miller
8cb0803605 Make page_manager::unmap_pages() handle multiple blocks 2018-04-22 13:37:44 -07:00
Justin C. Miller
95d52b87f4 Initialize page_manager.
Page manager now:
- Caches mapped pages and page_block structs
- Can unmap memory ranges
- Unmaps extra kernel memory during it's init
2018-04-22 02:48:45 -07:00
Justin C. Miller
07fd3abe2c Move page size and higher half offset to constants 2018-04-21 20:58:58 -07:00
Justin C. Miller
5dedd2e0e0 Finish memory bootstrap sequence.
Now we're setting up all our own page tables, and handing off to
page_manager's init function. (Which is still NYI.)
2018-04-21 19:32:39 -07:00
Justin C. Miller
57abb03deb Rearrange memory manager into two classes.
page_manager and memory_manager are now separate, and are also pre
allocated in the kernel so they don't have to allocate themselves.
2018-04-21 17:34:33 -07:00
Justin C. Miller
4a38a74b16 Refactor memory code.
Break out some more bootstrap code into functions. Add the start of
some Doxygen doc comments to help organize my thoughts.
2018-04-21 16:49:39 -07:00
221 changed files with 36998 additions and 2673 deletions

11
.git-commit-template Normal file
View File

@@ -0,0 +1,11 @@
[section] Imperative-voiced title in less than 50
# Body describes what was done, and why. New obviously-needed features
# don't necessarily require a why.
# Links to relevant bugs or web pages
See: Github bug #242
See: [frobozz blog post](https://jsix.dev/posts/frobozz/)
# Tags and keywords useful for searching
Tags:

10
.gitignore vendored
View File

@@ -1,6 +1,10 @@
.lock*
build
/build*
*.bak
tags
.gdbinit
popcorn.log
jsix.log
*.o
*.a
sysroot
.gdb_history
.peru

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "external/gnu-efi"]
path = external/gnu-efi
url = https://github.com/justinian/gnu-efi.git

354
LICENSE.md Normal file
View File

@@ -0,0 +1,354 @@
# License
jsix is (c) 2017-2019 Justin C Miller, and distributed under the terms of the
Mozilla Public License 2.0.
---
## Mozilla Public License Version 2.0
### 1. Definitions
#### 1.1. "Contributor"
means each individual or legal entity that creates, contributes to the creation
of, or owns Covered Software.
#### 1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
#### 1.3. "Contribution
means Covered Software of a particular Contributor.
#### 1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the notice
in Exhibit A, the Executable Form of such Source Code Form, and Modifications
of such Source Code Form, in each case including portions thereof.
#### 1.5. "Incompatible With Secondary Licenses"
means
* **(a)** that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
* **(b)** that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of a
Secondary License.
#### 1.6. "Executable Form"
means any form of the work other than Source Code Form.
#### 1.7. "Larger Work"
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
#### 1.8. "License"
means this document.
#### 1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed
by this License.
#### 1.10. "Modifications"
means any of the following:
* **(a)** any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
* **(b)** any new file in Source Code Form that contains any Covered Software.
#### 1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method, process, and
apparatus claims, in any patent Licensable by such Contributor that would be
infringed, but for the grant of the License, by the making, using, selling,
offering for sale, having made, import, or transfer of either its Contributions
or its Contributor Version.
#### 1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public License,
Version 3.0, or any later versions of those licenses.
#### 1.13. "Source Code Form"
means the form of the work preferred for making modifications.
#### 1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this License. For
legal entities, "You" includes any entity that controls, is controlled by, or
is under common control with You. For purposes of this definition, "control"
means **(a)** the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or **(b)**
ownership of more than fifty percent (50%) of the outstanding shares or
beneficial ownership of such entity.
### 2. License Grants and Conditions
#### 2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive
license:
* **(a)** under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available, modify,
display, perform, distribute, and otherwise exploit its Contributions, either
on an unmodified basis, with Modifications, or as part of a Larger Work; and
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions or
its Contributor Version.
#### 2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
#### 2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
* **(a)** for any code that a Contributor has removed from Covered Software; or
* **(b)** for infringements caused by: **(i)** Your and any other third party's
modifications of Covered Software, or **(ii)** the combination of its
Contributions with other software (except as part of its Contributor
Version); or
* **(c)** under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the notice
requirements in Section 3.4).
#### 2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to distribute
the Covered Software under a subsequent version of this License (see Section
10.2) or under the terms of a Secondary License (if permitted under the terms
of Section 3.3).
#### 2.5. Representation
Each Contributor represents that the Contributor believes its Contributions are
its original creation(s) or it has sufficient rights to grant the rights to its
Contributions conveyed by this License.
#### 2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
#### 2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
### 3. Responsibilities
#### 3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form of
the Covered Software is governed by the terms of this License, and how they can
obtain a copy of this License. You may not attempt to alter or restrict the
recipients' rights in the Source Code Form.
#### 3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
* **(a)** such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost of
distribution to the recipient; and
* **(b)** You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the license
for the Executable Form does not attempt to limit or alter the recipients'
rights in the Source Code Form under this License.
#### 3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software with
a work governed by one or more Secondary Licenses, and the Covered Software is
not Incompatible With Secondary Licenses, this License permits You to
additionally distribute such Covered Software under the terms of such Secondary
License(s), so that the recipient of the Larger Work may, at their option,
further distribute the Covered Software under the terms of either this License
or such Secondary License(s).
#### 3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations of
liability) contained within the Source Code Form of the Covered Software,
except that You may alter any license notices to the extent required to remedy
known factual inaccuracies.
#### 3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support, indemnity
or liability obligations to one or more recipients of Covered Software.
However, You may do so only on Your own behalf, and not on behalf of any
Contributor. You must make it absolutely clear that any such warranty, support,
indemnity, or liability obligation is offered by You alone, and You hereby
agree to indemnify every Contributor for any liability incurred by such
Contributor as a result of warranty, support, indemnity or liability terms You
offer. You may include additional disclaimers of warranty and limitations of
liability specific to any jurisdiction.
### 4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: **(a)** comply with the terms of this
License to the maximum extent possible; and **(b)** describe the limitations
and the code they affect. Such description must be placed in a text file
included with all distributions of the Covered Software under this License.
Except to the extent prohibited by statute or regulation, such description must
be sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
### 5. Termination
**5.1.** The rights granted under this License will terminate automatically if
You fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor are
reinstated **(a)** provisionally, unless and until such Contributor explicitly
and finally terminates Your grants, and **(b)** on an ongoing basis, if such
Contributor fails to notify You of the non-compliance by some reasonable means
prior to 60 days after You have come back into compliance. Moreover, Your
grants from a particular Contributor are reinstated on an ongoing basis if such
Contributor notifies You of the non-compliance by some reasonable means, this
is the first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after Your
receipt of the notice.
**5.2.** If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims, and
cross-claims) alleging that a Contributor Version directly or indirectly
infringes any patent, then the rights granted to You by any and all
Contributors for the Covered Software under Section 2.1 of this License shall
terminate.
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all end
user license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
### 6. Disclaimer of Warranty
> Covered Software is provided under this License on an "as is" basis, without
> warranty of any kind, either expressed, implied, or statutory, including,
> without limitation, warranties that the Covered Software is free of defects,
> merchantable, fit for a particular purpose or non-infringing. The entire risk
> as to the quality and performance of the Covered Software is with You.
> Should any Covered Software prove defective in any respect, You (not any
> Contributor) assume the cost of any necessary servicing, repair, or
> correction. This disclaimer of warranty constitutes an essential part of this
> License. No use of any Covered Software is authorized under this License
> except under this disclaimer.
### 7. Limitation of Liability
> Under no circumstances and under no legal theory, whether tort (including
> negligence), contract, or otherwise, shall any Contributor, or anyone who
> distributes Covered Software as permitted above, be liable to You for any
> direct, indirect, special, incidental, or consequential damages of any
> character including, without limitation, damages for lost profits, loss of
> goodwill, work stoppage, computer failure or malfunction, or any and all
> other commercial damages or losses, even if such party shall have been
> informed of the possibility of such damages. This limitation of liability
> shall not apply to liability for death or personal injury resulting from such
> party's negligence to the extent applicable law prohibits such limitation.
> Some jurisdictions do not allow the exclusion or limitation of incidental or
> consequential damages, so this exclusion and limitation may not apply to You.
### 8. Litigation
Any litigation relating to this License may be brought only in the courts of a
jurisdiction where the defendant maintains its principal place of business and
such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a party's ability to bring cross-claims or counter-claims.
### 9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
### 10. Versions of the License
#### 10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section 10.3,
no one other than the license steward has the right to modify or publish new
versions of this License. Each version will be given a distinguishing version
number.
#### 10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of the
License under which You originally received the Covered Software, or under the
terms of any subsequent version published by the license steward.
#### 10.3. Modified Versions
If you create software not governed by this License, and you want to create a
new license for such software, you may create and use a modified version of
this License if you rename the license and remove any references to the name of
the license steward (except to note that such modified license differs from
this License).
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the notice
described in Exhibit B of this License must be attached.
### Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this file,
You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
### Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible With Secondary Licenses", as defined
by the Mozilla Public License, v. 2.0.

241
Makefile
View File

@@ -1,241 +0,0 @@
ARCH ?= x86_64
include src/arch/$(ARCH)/config.mk
BUILD_D := build
KERN_D := src/kernel
ARCH_D := src/arch/$(ARCH)
VERSION ?= $(shell git describe --dirty --always)
GITSHA ?= $(shell git rev-parse --short HEAD)
KERNEL_FILENAME:= popcorn.bin
KERNEL_FONT := assets/fonts/tamsyn8x16r.psf
MODULES := kutil
EFI_DIR := external/gnu-efi
EFI_DATA := $(EFI_DIR)/gnuefi
EFI_LDS := $(EFI_DATA)/elf_$(ARCH)_efi.lds
EFI_ARCH_DIR := $(EFI_DIR)/$(ARCH)
EFI_ARCH_DATA := $(EFI_ARCH_DIR)/gnuefi
EFI_CRT_OBJ := $(EFI_ARCH_DATA)/crt0-efi-$(ARCH).o
EFI_LIB := $(EFI_ARCH_DIR)/lib/libefi.a
EFI_INCLUDES := $(EFI_DIR)/inc
DEPENDFLAGS := -MMD
INCLUDES := -I $(ARCH_D)
INCLUDES += -I src/modules
INCLUDES += -I src/include
INCLUDES += -isystem $(EFI_INCLUDES)
INCLUDES += -isystem $(EFI_INCLUDES)/$(ARCH)
INCLUDES += -isystem $(EFI_INCLUDES)/protocol
BASEFLAGS := -ggdb -nostdlib
BASEFLAGS += -ffreestanding -nodefaultlibs
BASEFLAGS += -fno-builtin -fomit-frame-pointer
BASEFLAGS += -mno-red-zone -fno-stack-protector
ifdef CPU
BASEFLAGS += -mcpu=$(CPU)
endif
# Removed Flags:: -Wcast-align
WARNFLAGS += -Wformat=2 -Winit-self -Wfloat-equal -Winline
WARNFLAGS += -Winvalid-pch -Wmissing-format-attribute
WARNFLAGS += -Wmissing-include-dirs -Wswitch -Wundef
WARNFLAGS += -Wdisabled-optimization -Wpointer-arith
WARNFLAGS += -Wno-attributes -Wno-sign-compare -Wno-multichar
WARNFLAGS += -Wno-div-by-zero -Wno-endif-labels -Wno-pragmas
WARNFLAGS += -Wno-format-extra-args -Wno-unused-result
WARNFLAGS += -Wno-deprecated-declarations -Wno-unused-function
WARNFLAGS += -Wno-unused-but-set-parameter
ASFLAGS ?=
ASFLAGS += -p $(BUILD_D)/versions.s
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
CFLAGS += -DGIT_VERSION="\"$(VERSION)\""
CFLAGS += -std=c11 -mcmodel=large
CXXFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
CXXFLAGS += -DGIT_VERSION="\"$(VERSION)\""
CXXFLAGS += -std=c++14 -mcmodel=large
BOOT_CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
BOOT_CFLAGS += -std=c11 -I src/boot -fPIC -fshort-wchar
BOOT_CFLAGS += -DKERNEL_FILENAME="L\"$(KERNEL_FILENAME)\""
BOOT_CFLAGS += -DGIT_VERSION_WIDE="L\"$(VERSION)\""
BOOT_CFLAGS += -DGNU_EFI_USE_MS_ABI -DHAVE_USE_MS_ABI
BOOT_CFLAGS += -DEFI_DEBUG=0 -DEFI_DEBUG_CLEAR_MEMORY=0
#BOOT_CFLAGS += -DEFI_FUNCTION_WRAPPER
ifdef MAX_HRES
BOOT_CFLAGS += -DMAX_HRES=$(MAX_HRES)
endif
LDFLAGS := -L $(BUILD_D) -ggdb
LDFLAGS += -nostdlib -znocombreloc -Bsymbolic -nostartfiles
BOOT_LDFLAGS := $(LDFLAGS) -shared
BOOT_LDFLAGS += -L $(EFI_ARCH_DIR)/lib -L $(EFI_ARCH_DIR)/gnuefi
AS ?= $(CROSS)nasm
AR ?= $(CROSS)ar
CC ?= $(CROSS)gcc
CXX ?= $(CROSS)g++
LD ?= $(CROSS)ld
OBJC := $(CROSS)objcopy
OBJD := $(CROSS)objdump
INIT_DEP := $(BUILD_D)/.builddir
BOOT_SRCS := $(wildcard src/boot/*.c)
BOBJS += $(patsubst src/boot/%,$(BUILD_D)/boot/%,$(patsubst %,%.o,$(BOOT_SRCS)))
KERN_SRCS := $(wildcard $(KERN_D)/*.s)
KERN_SRCS += $(wildcard $(KERN_D)/*.c)
KERN_SRCS += $(wildcard $(KERN_D)/*.cpp)
KERN_SRCS += $(wildcard $(ARCH_D)/*.s)
KERN_SRCS += $(wildcard $(ARCH_D)/*.c)
KERN_OBJS := $(patsubst src/%, $(BUILD_D)/%, $(patsubst %,%.o,$(KERN_SRCS)))
MOD_TARGETS :=
PARTED ?= /sbin/parted
QEMU ?= qemu-system-x86_64
GDBPORT ?= 27006
CPUS ?= 1
OVMF ?= assets/ovmf/x64/OVMF.fd
QEMUOPTS := -pflash $(BUILD_D)/flash.img
QEMUOPTS += -drive file=$(BUILD_D)/fs.img,format=raw
QEMUOPTS += -smp $(CPUS)
QEMUOPTS += -m 512
QEMUOPTS += -d guest_errors
QEMUOPTS += $(QEMUEXTRA)
all: $(BUILD_D)/fs.img
init: $(INIT_DEP)
$(INIT_DEP):
mkdir -p $(BUILD_D) $(patsubst %,$(BUILD_D)/d.%,$(MODULES))
mkdir -p $(BUILD_D)/boot
mkdir -p $(patsubst src/%,$(BUILD_D)/%,$(ARCH_D))
mkdir -p $(patsubst src/%,$(BUILD_D)/%,$(KERN_D))
touch $(INIT_DEP)
clean:
rm -rf $(BUILD_D)/* $(BUILD_D)/.version $(BUILD_D)/.builddir
vars:
@echo "KERN_SRCS: " $(KERN_SRCS)
@echo "KERN_OBJS: " $(KERN_OBJS)
dist-clean: clean
make -C external/gnu-efi clean
dump: $(BUILD_D)/kernel.dump
vim $<
.PHONY: all clean dist-clean init dump vars
$(BUILD_D)/.version:
echo '$(VERSION)' | cmp -s - $@ || echo '$(VERSION)' > $@
$(BUILD_D)/versions.s:
./parse_version.py "$(VERSION)" "$(GITSHA)" > $@
-include x $(patsubst %,src/modules/%/module.mk,$(MODULES))
-include x $(shell find $(BUILD_D) -type f -name '*.d')
$(EFI_LIB):
make -C external/gnu-efi all
$(BUILD_D)/boot.elf: $(BOBJS) $(EFI_LIB)
$(LD) $(BOOT_LDFLAGS) -T $(EFI_LDS) -o $@ \
$(EFI_CRT_OBJ) $(BOBJS) -lefi -lgnuefi
$(BUILD_D)/boot.efi: $(BUILD_D)/boot.elf
$(OBJC) -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(ARCH) $^ $@
$(BUILD_D)/boot.debug.efi: $(BUILD_D)/boot.elf
$(OBJC) -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
-j .debug_info -j .debug_abbrev -j .debug_loc -j .debug_str \
-j .debug_aranges -j .debug_line -j .debug_macinfo \
--target=efi-app-$(ARCH) $^ $@
$(BUILD_D)/%.bin: $(BUILD_D)/%.elf
$(OBJC) $< -O binary $@
$(BUILD_D)/boot.dump: $(BUILD_D)/boot.efi
$(OBJD) -D -S $< > $@
$(BUILD_D)/boot/%.s.o: src/boot/%.s $(BUILD_D)/versions.s $(INIT_DEP)
$(AS) $(ASFLAGS) -i src/boot/ -o $@ $<
$(BUILD_D)/boot/%.c.o: src/boot/%.c $(INIT_DEP)
$(CC) $(BOOT_CFLAGS) -c -o $@ $<
$(BUILD_D)/kernel.elf: $(KERN_OBJS) $(MOD_TARGETS) $(ARCH_D)/kernel.ld
$(LD) $(LDFLAGS) -u _header -T $(ARCH_D)/kernel.ld -o $@ $(KERN_OBJS) $(patsubst %,-l%,$(MODULES))
$(OBJC) --only-keep-debug $@ $@.sym
$(BUILD_D)/kernel.dump: $(BUILD_D)/kernel.elf
$(OBJD) -D -S $< > $@
$(BUILD_D)/%.s.o: src/%.s $(BUILD_D)/versions.s $(INIT_DEP)
$(AS) $(ASFLAGS) -i $(ARCH_D)/ -i $(KERN_D)/ -o $@ $<
$(BUILD_D)/%.c.o: src/%.c $(INIT_DEP)
$(CC) $(CFLAGS) -c -o $@ $<
$(BUILD_D)/%.cpp.o: src/%.cpp $(INIT_DEP)
$(CXX) $(CXXFLAGS) -c -o $@ $<
$(BUILD_D)/flash.img: $(OVMF)
cp $^ $@
$(BUILD_D)/screenfont.psf: $(KERNEL_FONT)
cp $^ $@
$(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.bin $(BUILD_D)/screenfont.psf
$(eval TEMPFILE := $(shell mktemp --suffix=.img))
dd if=/dev/zero of=$@.tmp bs=512 count=93750
$(PARTED) $@.tmp -s -a minimal mklabel gpt
$(PARTED) $@.tmp -s -a minimal mkpart EFI FAT16 2048s 93716s
$(PARTED) $@.tmp -s -a minimal toggle 1 boot
dd if=/dev/zero of=$(TEMPFILE) bs=512 count=91669
mformat -i $(TEMPFILE) -h 32 -t 32 -n 64 -c 1
mmd -i $(TEMPFILE) ::/EFI
mmd -i $(TEMPFILE) ::/EFI/BOOT
mcopy -i $(TEMPFILE) $(BUILD_D)/boot.efi ::/EFI/BOOT/BOOTX64.efi
mcopy -i $(TEMPFILE) $(BUILD_D)/kernel.bin ::$(KERNEL_FILENAME)
mcopy -i $(TEMPFILE) $(BUILD_D)/screenfont.psf ::screenfont.psf
mlabel -i $(TEMPFILE) ::Popcorn_OS
dd if=$(TEMPFILE) of=$@.tmp bs=512 count=91669 seek=2048 conv=notrunc
rm $(TEMPFILE)
mv $@.tmp $@
$(BUILD_D)/fs.iso: $(BUILD_D)/fs.img
mkdir -p $(BUILD_D)/iso
cp $< $(BUILD_D)/iso/
xorriso -as mkisofs -R -f -e fs.img -no-emul-boot -o $@ $(BUILD_D)/iso
qemu: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
"$(QEMU)" $(QEMUOPTS) -nographic
qemu-window: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
"$(QEMU)" $(QEMUOPTS)
qemu-gdb: $(BUILD_D)/fs.img $(BUILD_D)/boot.debug.efi $(BUILD_D)/flash.img $(BUILD_D)/kernel.elf
"$(QEMU)" $(QEMUOPTS) -d mmu,guest_errors,page -D popcorn.log -s -nographic
# vim: ft=make ts=4

View File

@@ -2,6 +2,37 @@
## TODO
- Better page-allocation model
- Paging manager
- Copy-on-write pages
- Better page-allocation model?
- Allow for more than one IOAPIC in ACPI module
- The objects get created, but GSI lookup only uses the one at index 0
- mark kernel memory pages global
- Serial out based on circular/bip biffer and interrupts, not spinning on
`write_ready()`
- Split out more code into kutil for testing
- AHCI / MSI interrupts on Vbox break?
- FXSAVE to save XMM registers.
- optimization using #NM (0x7) to detect SSE usage
- Clean up of process memory maps
- Better stack tracer
- Bootloader rewrite
- C++ and sharing library code for ELF, initrd, etc
- Parse initrd and pre-load certain ELF images, eg the process loader process?
- Do initial memory bootstrap?
- Calling global ctors
- Device Tree
- Actual serial driver
- Disk driver
- File system
- Multiprocessing
- Fast syscalls using syscall/sysret
### Build
- Clean up build generator and its templates
- More robust objects to represent modules & targets to pass to templates
- Read project setup from a simple JSON/TOML/etc
- Better compartmentalizing when doing template inheritance
- Move to LLD as sysroot linker

View File

@@ -1,7 +1,78 @@
# popcorn: A toy microkernel x64 UEFI OS
# jsix: A toy OS kernel
**popcorn** is a hobby OS for x64 UEFI environments to play with building a
microkenerl architecture. It's far from finished, or even being usable - for
now, it's a sandbox for me to explore the UEFI architecture, microkernels, and
OS-related concepts that I want to play with.
**jsix** is the kernel for the hobby OS that I am currently building. It's
far from finished, or even being usable. Instead, it's a sandbox for me to play
with kernel-level code and explore architectures.
The design goals of the project are:
* Modernity - I'm not interested in designing for legacy systems, or running on
all hardware out there. My target is only 64 bit architecutres, and modern
commodity hardware. Currently that means x64 systems with Nehalem or newer
CPUs and UEFI firmware. Eventually I'd like to work on an AArch64 port,
partly to force myself to factor out the architecture-dependent pieces of the
code base.
* Modularity - I'd like to pull as much of the system out into separate
processes as possible, in the microkernel fashion. A sub-goal of this is to
explore where the bottlenecks of such a microkernel are now, and whether
eschewing legacy hardware will let me design a system that's less bogged down
by the traditional microkernel problems. Given that there are no processes
yet, the kernel is monolithic by default.
* Exploration - I'm really mostly doing this to have fun learning and exploring
modern OS development. Modular design may be tossed out (hopefully
temporarily) in some places to allow me to play around with the related
hardware.
A note on the name: This kernel was originally named Popcorn, but I have since
discovered that the Popcorn Linux project is also developing a kernel with that
name, started around the same time as this project. So I've renamed this kernel
jsix (Always styled _jsix_ or `j6`, never capitalized) as an homage to L4, xv6,
and my wonderful wife.
## Building
jsix uses the [Ninja][] build tool, and generates the build files for it with a
custom tool called [Bonnibel][]. Bonnibel can be installed with [Cargo][], or
downloaded as a prebuilt binary from its Github repository.
[Ninja]: https://ninja-build.org
[Bonnibel]: https://github.com/justinian/bonnibel
[Cargo]: https://crates.io/crates/bonnibel
Requrirements:
* bonnibel
* ninja
* clang
* nasm
* mtools
* curl for downloading the toolchain
### Setting up the cross toolchain
Running `pb sync` will download and unpack the toolchain into `sysroot`.
#### Compiling the toolchain yourself
If you have `clang` and `curl` installed, runing the `scripts/build_sysroot.sh`
script will download and build a LLVM toolchain configured for building jsix
host binaries.
### Building and running jsix
Once the toolchain has been set up, running Bonnibel's `pb init` command will
set up the build configuration, and `pb build` will actually run the build. If
you have `qemu-system-x86_64` installed, the `qemu.sh` script will to run jsix
in QEMU `-nographic` mode.
I personally run this either from a real debian amd64 testing/buster machine or
a windows WSL debian testing/buster installation. The following should be
enough to set up such a system to build the kernel:
sudo apt install qemu-system-x86 nasm clang-6.0 mtools curl
sudo update-alternatives /usr/bin/clang clang /usr/bin/clang-6.0 1000
sudo update-alternatives /usr/bin/clang++ clang++ /usr/bin/clang++-6.0 1000
curl -L -o pb https://github.com/justinian/bonnibel/releases/download/2.0.0/pb_linux_amd64 && chmod a+x pb

View File

@@ -0,0 +1,28 @@
import gdb
class PrintStackCommand(gdb.Command):
def __init__(self):
super().__init__("popc_stack", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
args = gdb.string_to_argv(arg)
base = "$rsp"
if len(args) > 0:
base = args[0]
depth = 22
if len(args) > 1:
depth = int(args[1])
for i in range(depth-1, -1, -1):
offset = i * 8
base_addr = gdb.parse_and_eval(base)
value = gdb.parse_and_eval(f"*(uint64_t*)({base} + {offset})")
print("{:016x} (+{:04x}): {:016x}".format(int(base_addr) + offset, offset, int(value)))
PrintStackCommand()
gdb.execute("target remote :1234")
gdb.execute("display/i $rip")

BIN
assets/diskbase.img Normal file

Binary file not shown.

BIN
assets/floppy.img Normal file

Binary file not shown.

23
assets/initrd.toml Normal file
View File

@@ -0,0 +1,23 @@
# This is the manifest for the initial ramdisk, read by the `makerd` tool.
# The contents should be a table array of files to add to the ramdistk:
#
# [[files]]
# dest = "foo.bar" # Name of the file in the ramdisk
# source = "build/foo/foo.bar" # Location of the file from the project root
# executable = true # Optional, default false. Whether this is an
# # initial application for the kernel to execute
# # on startup
[[files]]
dest = "screenfont.psf"
source = "../assets/fonts/tamsyn8x16r.psf"
[[files]]
dest = "nulldrv1"
source = "user/nulldrv"
executable = true
[[files]]
dest = "nulldrv2"
source = "user/nulldrv"
executable = true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

17075
external/catch/catch.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

3669
external/cpptoml/cpptoml.h vendored Normal file

File diff suppressed because it is too large Load Diff

1
external/gnu-efi vendored

Submodule external/gnu-efi deleted from fc5af9e47f

101
external/uefi/boot_services.h vendored Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#ifndef _uefi_boot_services_h_
#define _uefi_boot_services_h_
// This Source Code Form is part of the j6-uefi-headers project and is subject
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#include <stdint.h>
#include <uefi/tables.h>
#include <uefi/types.h>
namespace uefi {
namespace bs_impl {
using allocate_pages = status (*)(allocate_type, memory_type, size_t, void**);
using get_memory_map = status (*)(size_t*, memory_descriptor*, size_t*, size_t*, uint32_t*);
using allocate_pool = status (*)(memory_type, uint64_t, void**);
using handle_protocol = status (*)(handle, const guid*, void**);
using create_event = status (*)(evt, tpl, event_notify, void*, event*);
using exit_boot_services = status (*)(handle, size_t);
using locate_protocol = status (*)(const guid*, void*, void**);
using copy_mem = void (*)(void*, void*, size_t);
using set_mem = void (*)(void*, uint64_t, uint8_t);
}
struct boot_services {
static constexpr uint64_t signature = 0x56524553544f4f42ull;
table_header header;
// Task Priority Level management
void *raise_tpl;
void *restore_tpl;
// Memory Services
bs_impl::allocate_pages allocate_pages;
void *free_pages;
bs_impl::get_memory_map get_memory_map;
bs_impl::allocate_pool allocate_pool;
void *free_pool;
// Event & Timer Services
bs_impl::create_event create_event;
void *set_timer;
void *wait_for_event;
void *signal_event;
void *close_event;
void *check_event;
// Protocol Handler Services
void *install_protocol_interface;
void *reinstall_protocol_interface;
void *uninstall_protocol_interface;
bs_impl::handle_protocol handle_protocol;
void *_reserved;
void *register_protocol_notify;
void *locate_handle;
void *locate_device_path;
void *install_configuration_table;
// Image Services
void *load_image;
void *start_image;
void *exit;
void *unload_image;
bs_impl::exit_boot_services exit_boot_services;
// Miscellaneous Services
void *get_next_monotonic_count;
void *stall;
void *set_watchdog_timer;
// DriverSupport Services
void *connect_controller;
void *disconnect_controller;
// Open and Close Protocol Services
void *open_protocol;
void *close_protocol;
void *open_protocol_information;
// Library Services
void *protocols_per_handle;
void *locate_handle_buffer;
bs_impl::locate_protocol locate_protocol;
void *install_multiple_protocol_interfaces;
void *uninstall_multiple_protocol_interfaces;
// 32-bit CRC Services
void *calculate_crc32;
// Miscellaneous Services
bs_impl::copy_mem copy_mem;
bs_impl::set_mem set_mem;
void *create_event_ex;
};
} // namespace uefi
#endif

39
external/uefi/errors.inc vendored Normal file
View File

@@ -0,0 +1,39 @@
STATUS_WARNING( warn_unknown_glyph, 1)
STATUS_WARNING( warn_delete_failure, 2)
STATUS_WARNING( warn_write_failure, 3)
STATUS_WARNING( warn_buffer_too_small,4)
STATUS_WARNING( warn_stale_data, 5)
STATUS_WARNING( warn_file_system, 6)
STATUS_ERROR( load_error, 1)
STATUS_ERROR( invalid_parameter, 2)
STATUS_ERROR( unsupported, 3)
STATUS_ERROR( bad_buffer_size, 4)
STATUS_ERROR( buffer_too_small, 5)
STATUS_ERROR( not_ready, 6)
STATUS_ERROR( device_error, 7)
STATUS_ERROR( write_protected, 8)
STATUS_ERROR( out_of_resources, 9)
STATUS_ERROR( volume_corrupted, 10)
STATUS_ERROR( volume_full, 11)
STATUS_ERROR( no_media, 12)
STATUS_ERROR( media_changed, 13)
STATUS_ERROR( not_found, 14)
STATUS_ERROR( access_denied, 15)
STATUS_ERROR( no_response, 16)
STATUS_ERROR( no_mapping, 17)
STATUS_ERROR( timeout, 18)
STATUS_ERROR( not_started, 19)
STATUS_ERROR( already_started, 20)
STATUS_ERROR( aborted, 21)
STATUS_ERROR( icmp_error, 22)
STATUS_ERROR( tftp_error, 23)
STATUS_ERROR( protocol_error, 24)
STATUS_ERROR( incompatible_version, 25)
STATUS_ERROR( security_violation, 26)
STATUS_ERROR( crc_error, 27)
STATUS_ERROR( end_of_media, 28)
STATUS_ERROR( end_of_file, 31)
STATUS_ERROR( invalid_language, 32)
STATUS_ERROR( compromised_data, 33)
STATUS_ERROR( http_error, 35)

92
external/uefi/graphics.h vendored Normal file
View File

@@ -0,0 +1,92 @@
#pragma once
#ifndef _uefi_graphics_h_
#define _uefi_graphics_h_
// This Source Code Form is part of the j6-uefi-headers project and is subject
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#include <stdint.h>
#include <uefi/types.h>
namespace uefi {
struct text_output_mode
{
int32_t max_mode;
int32_t mode;
int32_t attribute;
int32_t cursor_column;
int32_t cursor_row;
bool cursor_visible;
};
struct pixel_bitmask
{
uint32_t red_mask;
uint32_t green_mask;
uint32_t blue_mask;
uint32_t reserved_mask;
};
enum class pixel_format
{
rgb8,
bgr8,
bitmask,
blt_only
};
struct graphics_output_mode_info
{
uint32_t version;
uint32_t horizontal_resolution;
uint32_t vertical_resolution;
pixel_format pixel_format;
pixel_bitmask pixel_information;
uint32_t pixels_per_scanline;
};
struct graphics_output_mode
{
uint32_t max_mode;
uint32_t mode;
graphics_output_mode_info *info;
uint64_t size_of_info;
uintptr_t frame_buffer_base;
uint64_t frame_buffer_size;
};
enum class attribute : uint64_t
{
black,
blue,
green,
cyan,
red,
magenta,
brown,
light_gray,
dark_gray,
light_blue,
light_green,
light_cyan,
light_red,
light_magenta,
yellow,
white,
background_black = 0x00,
background_blue = 0x10,
background_green = 0x20,
background_cyan = 0x30,
background_red = 0x40,
background_magenta = 0x50,
background_brown = 0x60,
background_light_gray = 0x70,
};
} // namespace uefi
#endif

29
external/uefi/guid.h vendored Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#ifndef _uefi_guid_h_
#define _uefi_guid_h_
// This Source Code Form is part of the j6-uefi-headers project and is subject
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#include <stdint.h>
namespace uefi {
struct guid
{
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
inline bool operator==(const guid &other) const {
return reinterpret_cast<const uint64_t*>(this)[0] == reinterpret_cast<const uint64_t*>(&other)[0]
&& reinterpret_cast<const uint64_t*>(this)[1] == reinterpret_cast<const uint64_t*>(&other)[1];
}
};
} // namespace uefi
#endif

30
external/uefi/protos/device_path.h vendored Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#ifndef _uefi_protos_device_path_h_
#define _uefi_protos_device_path_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
namespace uefi {
namespace protos {
struct device_path;
struct device_path
{
static constexpr uefi::guid guid{ 0x09576e91,0x6d3f,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
uint8_t type;
uint8_t sub_type;
uint16_t length;
protected:
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_device_path_h_

126
external/uefi/protos/file.h vendored Normal file
View File

@@ -0,0 +1,126 @@
#pragma once
#ifndef _uefi_protos_file_h_
#define _uefi_protos_file_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
namespace uefi {
namespace protos {
struct file;
struct file
{
inline uefi::status open(file ** new_handle, const wchar_t * file_name, file_mode open_mode, file_attr attributes) {
return _open(this, new_handle, file_name, open_mode, attributes);
}
inline uefi::status close() {
return _close(this);
}
inline uefi::status delete_file() {
return _delete_file(this);
}
inline uefi::status read(uint64_t * buffer_size, void * buffer) {
return _read(this, buffer_size, buffer);
}
inline uefi::status write(uint64_t * buffer_size, void * buffer) {
return _write(this, buffer_size, buffer);
}
inline uefi::status get_position(uint64_t * position) {
return _get_position(this, position);
}
inline uefi::status set_position(uint64_t position) {
return _set_position(this, position);
}
inline uefi::status get_info(const guid * info_type, uint64_t * buffer_size, void * buffer) {
return _get_info(this, info_type, buffer_size, buffer);
}
inline uefi::status set_info(const guid * info_type, uint64_t buffer_size, void * buffer) {
return _set_info(this, info_type, buffer_size, buffer);
}
inline uefi::status flush() {
return _flush(this);
}
inline uefi::status open_ex(file ** new_handle, const wchar_t * file_name, uint64_t open_mode, uint64_t attributes, file_io_token * token) {
return _open_ex(this, new_handle, file_name, open_mode, attributes, token);
}
inline uefi::status read_ex(file_io_token * token) {
return _read_ex(this, token);
}
inline uefi::status write_ex(file_io_token * token) {
return _write_ex(this, token);
}
inline uefi::status flush_ex(file_io_token * token) {
return _flush_ex(this, token);
}
uint64_t revision;
protected:
using _open_def = uefi::status (*)(uefi::protos::file *, file **, const wchar_t *, file_mode, file_attr);
_open_def _open;
using _close_def = uefi::status (*)(uefi::protos::file *);
_close_def _close;
using _delete_file_def = uefi::status (*)(uefi::protos::file *);
_delete_file_def _delete_file;
using _read_def = uefi::status (*)(uefi::protos::file *, uint64_t *, void *);
_read_def _read;
using _write_def = uefi::status (*)(uefi::protos::file *, uint64_t *, void *);
_write_def _write;
using _get_position_def = uefi::status (*)(uefi::protos::file *, uint64_t *);
_get_position_def _get_position;
using _set_position_def = uefi::status (*)(uefi::protos::file *, uint64_t);
_set_position_def _set_position;
using _get_info_def = uefi::status (*)(uefi::protos::file *, const guid *, uint64_t *, void *);
_get_info_def _get_info;
using _set_info_def = uefi::status (*)(uefi::protos::file *, const guid *, uint64_t, void *);
_set_info_def _set_info;
using _flush_def = uefi::status (*)(uefi::protos::file *);
_flush_def _flush;
using _open_ex_def = uefi::status (*)(uefi::protos::file *, file **, const wchar_t *, uint64_t, uint64_t, file_io_token *);
_open_ex_def _open_ex;
using _read_ex_def = uefi::status (*)(uefi::protos::file *, file_io_token *);
_read_ex_def _read_ex;
using _write_ex_def = uefi::status (*)(uefi::protos::file *, file_io_token *);
_write_ex_def _write_ex;
using _flush_ex_def = uefi::status (*)(uefi::protos::file *, file_io_token *);
_flush_ex_def _flush_ex;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_file_h_

35
external/uefi/protos/file_info.h vendored Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#ifndef _uefi_protos_file_info_h_
#define _uefi_protos_file_info_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
namespace uefi {
namespace protos {
struct file_info;
struct file_info
{
static constexpr uefi::guid guid{ 0x09576e92,0x6d3f,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
uint64_t size;
uint64_t file_size;
uint64_t physical_size;
time create_time;
time last_access_time;
time modification_time;
uint64_t attribute;
wchar_t file_name[];
protected:
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_file_info_h_

50
external/uefi/protos/graphics_output.h vendored Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
#ifndef _uefi_protos_graphics_output_h_
#define _uefi_protos_graphics_output_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
#include <uefi/graphics.h>
namespace uefi {
namespace protos {
struct graphics_output;
struct graphics_output
{
static constexpr uefi::guid guid{ 0x9042a9de,0x23dc,0x4a38,{0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a} };
inline uefi::status query_mode(uint32_t mode_number, uint64_t * size_of_info, uefi::graphics_output_mode_info ** info) {
return _query_mode(this, mode_number, size_of_info, info);
}
inline uefi::status set_mode(uint32_t mode_number) {
return _set_mode(this, mode_number);
}
inline uefi::status blt() {
return _blt(this);
}
protected:
using _query_mode_def = uefi::status (*)(uefi::protos::graphics_output *, uint32_t, uint64_t *, uefi::graphics_output_mode_info **);
_query_mode_def _query_mode;
using _set_mode_def = uefi::status (*)(uefi::protos::graphics_output *, uint32_t);
_set_mode_def _set_mode;
using _blt_def = uefi::status (*)(uefi::protos::graphics_output *);
_blt_def _blt;
public:
uefi::graphics_output_mode * mode;
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_graphics_output_h_

48
external/uefi/protos/loaded_image.h vendored Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#ifndef _uefi_protos_loaded_image_h_
#define _uefi_protos_loaded_image_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
#include <uefi/tables.h>
#include <uefi/protos/device_path.h>
namespace uefi {
namespace protos {
struct loaded_image;
struct loaded_image
{
static constexpr uefi::guid guid{ 0x5b1b31a1,0x9562,0x11d2,{0x8e,0x3f,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
inline uefi::status unload(uefi::handle image_handle) {
return _unload(image_handle);
}
uint32_t revision;
uefi::handle parent_handle;
uefi::system_table * system_table;
uefi::handle device_handle;
uefi::protos::device_path * file_path;
void * reserved;
uint32_t load_options_size;
void * load_options;
void * image_base;
uint64_t image_size;
uefi::memory_type image_code_type;
uefi::memory_type image_data_type;
protected:
using _unload_def = uefi::status (*)(uefi::handle);
_unload_def _unload;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_loaded_image_h_

View File

@@ -0,0 +1,36 @@
#pragma once
#ifndef _uefi_protos_simple_file_system_h_
#define _uefi_protos_simple_file_system_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
#include <uefi/protos/file.h>
namespace uefi {
namespace protos {
struct simple_file_system;
struct simple_file_system
{
static constexpr uefi::guid guid{ 0x0964e5b22,0x6459,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
inline uefi::status open_volume(uefi::protos::file ** root) {
return _open_volume(this, root);
}
uint64_t revision;
protected:
using _open_volume_def = uefi::status (*)(uefi::protos::simple_file_system *, uefi::protos::file **);
_open_volume_def _open_volume;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_simple_file_system_h_

View File

@@ -0,0 +1,92 @@
#pragma once
#ifndef _uefi_protos_simple_text_output_h_
#define _uefi_protos_simple_text_output_h_
// This file was auto generated by the j6-uefi-headers project. Please see
// https://github.com/justinian/j6-uefi-headers for more information.
#include <uefi/guid.h>
#include <uefi/types.h>
#include <uefi/graphics.h>
namespace uefi {
namespace protos {
struct simple_text_output;
struct simple_text_output
{
static constexpr uefi::guid guid{ 0x387477c2,0x69c7,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
inline uefi::status reset(bool extended_verification) {
return _reset(this, extended_verification);
}
inline uefi::status output_string(const wchar_t * string) {
return _output_string(this, string);
}
inline uefi::status test_string(const wchar_t * string) {
return _test_string(this, string);
}
inline uefi::status query_mode(uint64_t mode_number, uint64_t * columns, uint64_t * rows) {
return _query_mode(this, mode_number, columns, rows);
}
inline uefi::status set_mode(uint64_t mode_number) {
return _set_mode(this, mode_number);
}
inline uefi::status set_attribute(uefi::attribute attribute) {
return _set_attribute(this, attribute);
}
inline uefi::status clear_screen() {
return _clear_screen(this);
}
inline uefi::status set_cursor_position(uint64_t column, uint64_t row) {
return _set_cursor_position(this, column, row);
}
inline uefi::status enable_cursor(bool visible) {
return _enable_cursor(this, visible);
}
protected:
using _reset_def = uefi::status (*)(uefi::protos::simple_text_output *, bool);
_reset_def _reset;
using _output_string_def = uefi::status (*)(uefi::protos::simple_text_output *, const wchar_t *);
_output_string_def _output_string;
using _test_string_def = uefi::status (*)(uefi::protos::simple_text_output *, const wchar_t *);
_test_string_def _test_string;
using _query_mode_def = uefi::status (*)(uefi::protos::simple_text_output *, uint64_t, uint64_t *, uint64_t *);
_query_mode_def _query_mode;
using _set_mode_def = uefi::status (*)(uefi::protos::simple_text_output *, uint64_t);
_set_mode_def _set_mode;
using _set_attribute_def = uefi::status (*)(uefi::protos::simple_text_output *, uefi::attribute);
_set_attribute_def _set_attribute;
using _clear_screen_def = uefi::status (*)(uefi::protos::simple_text_output *);
_clear_screen_def _clear_screen;
using _set_cursor_position_def = uefi::status (*)(uefi::protos::simple_text_output *, uint64_t, uint64_t);
_set_cursor_position_def _set_cursor_position;
using _enable_cursor_def = uefi::status (*)(uefi::protos::simple_text_output *, bool);
_enable_cursor_def _enable_cursor;
public:
uefi::text_output_mode * mode;
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_simple_text_output_h_

52
external/uefi/runtime_services.h vendored Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#ifndef _uefi_runtime_services_h_
#define _uefi_runtime_services_h_
// This Source Code Form is part of the j6-uefi-headers project and is subject
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#include <stdint.h>
#include <uefi/tables.h>
namespace uefi {
namespace rs_impl {
using convert_pointer = uefi::status (*)(uint64_t, void **);
using set_virtual_address_map = uefi::status (*)(size_t, size_t, uint32_t, memory_descriptor *);
}
struct runtime_services {
static constexpr uint64_t signature = 0x56524553544e5552ull;
table_header header;
// Time Services
void *get_time;
void *set_time;
void *get_wakeup_time;
void *set_wakeup_time;
// Virtual Memory Services
rs_impl::set_virtual_address_map set_virtual_address_map;
rs_impl::convert_pointer convert_pointer;
// Variable Services
void *get_variable;
void *get_next_variable_name;
void *set_variable;
// Miscellaneous Services
void *get_next_high_monotonic_count;
void *reset_system;
// UEFI 2.0 Capsule Services
void *update_capsule;
void *query_capsule_capabilities;
// Miscellaneous UEFI 2.0 Service
void *query_variable_info;
};
} // namespace uefi
#endif

73
external/uefi/tables.h vendored Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#ifndef _uefi_tables_h_
#define _uefi_tables_h_
// This Source Code Form is part of the j6-uefi-headers project and is subject
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#include <stdint.h>
#include <uefi/guid.h>
#include <uefi/types.h>
namespace uefi {
struct runtime_services;
struct boot_services;
namespace protos {
struct simple_text_input;
struct simple_text_output;
}
struct table_header
{
uint64_t signature;
uint32_t revision;
uint32_t header_size;
uint32_t crc32;
uint32_t reserved;
};
struct configuration_table
{
guid vendor_guid;
void *vendor_table;
};
struct system_table
{
table_header header;
char16_t *firmware_vendor;
uint32_t firmware_revision;
handle console_in_handle;
protos::simple_text_input *con_in;
handle console_out_handle;
protos::simple_text_output *con_out;
handle standard_error_handle;
protos::simple_text_output *std_err;
runtime_services *runtime_services;
boot_services *boot_services;
unsigned int number_of_table_entries;
configuration_table *configuration_table;
};
constexpr uint32_t make_system_table_revision(int major, int minor) {
return (major << 16) | minor;
}
constexpr uint64_t system_table_signature = 0x5453595320494249ull;
constexpr uint32_t system_table_revision = make_system_table_revision(2, 70);
constexpr uint32_t specification_revision = system_table_revision;
namespace vendor_guids {
constexpr guid acpi1{ 0xeb9d2d30,0x2d88,0x11d3,{0x9a,0x16,0x00,0x90,0x27,0x3f,0xc1,0x4d} };
constexpr guid acpi2{ 0x8868e871,0xe4f1,0x11d3,{0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81} };
} // namespace vendor_guids
} // namespace uefi
#endif

157
external/uefi/types.h vendored Normal file
View File

@@ -0,0 +1,157 @@
#pragma once
#ifndef _uefi_types_h_
#define _uefi_types_h_
// This Source Code Form is part of the j6-uefi-headers project and is subject
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
#include <stdint.h>
namespace uefi {
using handle = void *;
//
// Error and status code definitions
//
constexpr uint64_t error_bit = 0x8000000000000000ull;
constexpr uint64_t make_error(uint64_t e) { return e|error_bit; }
enum class status : uint64_t
{
success = 0,
#define STATUS_WARNING(name, num) name = num,
#define STATUS_ERROR(name, num) name = make_error(num),
#include "uefi/errors.inc"
#undef STATUS_WARNING
#undef STATUS_ERROR
};
inline bool is_error(status s) { return static_cast<uint64_t>(s) & error_bit; }
inline bool is_warning(status s) { return !is_error(s) && s != status::success; }
//
// Time defitions
//
struct time
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t _pad0;
uint32_t nanosecond;
int16_t time_zone;
uint8_t daylight;
uint8_t _pad1;
};
//
// Memory and allocation defitions
//
enum class memory_type : uint32_t
{
reserved,
loader_code,
loader_data,
boot_services_code,
boot_services_data,
runtime_services_code,
runtime_services_data,
conventional_memory,
unusable_memory,
acpi_reclaim_memory,
acpi_memory_nvs,
memory_mapped_io,
memory_mapped_io_port_space,
pal_code,
persistent_memory,
max_memory_type
};
enum class allocate_type : uint32_t
{
any_pages,
max_address,
address
};
struct memory_descriptor
{
memory_type type;
uintptr_t physical_start;
uintptr_t virtual_start;
uint64_t number_of_pages;
uint64_t attribute;
};
//
// Event handling defitions
//
using event = void *;
enum class evt : uint32_t
{
notify_wait = 0x00000100,
notify_signal = 0x00000200,
signal_exit_boot_services = 0x00000201,
signal_virtual_address_change = 0x60000202,
runtime = 0x40000000,
timer = 0x80000000
};
enum class tpl : uint64_t
{
application = 4,
callback = 8,
notify = 16,
high_level = 31
};
using event_notify = void (*)(event, void*);
//
// File IO defitions
//
struct file_io_token
{
event event;
status status;
uint64_t buffer_size;
void *buffer;
};
enum class file_mode : uint64_t
{
read = 0x0000000000000001ull,
write = 0x0000000000000002ull,
create = 0x8000000000000000ull
};
enum class file_attr : uint64_t
{
none = 0,
read_only = 0x0000000000000001ull,
hidden = 0x0000000000000002ull,
system = 0x0000000000000004ull,
reserved = 0x0000000000000008ull,
directory = 0x0000000000000010ull,
archive = 0x0000000000000020ull
};
} // namespace uefi
#endif

View File

@@ -1,6 +0,0 @@
@echo off
if exist C:\Windows\Sysnative\bash.exe (
C:\Windows\Sysnative\bash.exe -c "make %*"
) else (
C:\Windows\System32\bash.exe -c "make %*"
)

View File

@@ -1,35 +0,0 @@
ifeq "$(MOD_NAME)" ""
include "you must specify a MOD_NAME"
endif
ifndef SOURCES
SOURCES := $(wildcard src/modules/$(MOD_NAME)/*.c)
SOURCES += $(wildcard src/modules/$(MOD_NAME)/*.cpp)
SOURCES += $(wildcard src/modules/$(MOD_NAME)/*.s)
endif
ifeq "$(SOURCES)" ""
include "you must specify a SOURCES list"
endif
MOD_SRC_D := src/modules/$(MOD_NAME)
MOD_BUILD_D := $(BUILD_D)/d.$(MOD_NAME)
MOD_LIBNAME := $(BUILD_D)/lib$(MOD_NAME).a
MOD_TARGETS += $(MOD_LIBNAME)
OBJS_$(MOD_NAME) := $(patsubst %,build/d.%.o,$(patsubst src/modules/%,%,$(SOURCES)))
$(MOD_LIBNAME): $(OBJS_$(MOD_NAME))
$(AR) cr $@ $(OBJS_$(MOD_NAME))
$(MOD_BUILD_D)/%.c.o: $(MOD_SRC_D)/%.c $(INIT_DEP)
$(CC) $(CFLAGS) -c -o $@ $<
$(MOD_BUILD_D)/%.cpp.o: $(MOD_SRC_D)/%.cpp $(INIT_DEP)
$(CXX) $(CXXFLAGS) -c -o $@ $<
$(MOD_BUILD_D)/%.s.o: $(MOD_SRC_D)/%.s $(BUILD_D)/versions.s $(INIT_DEP)
$(AS) $(ASFLAGS) -i $(MOD_SRC_D)/ -o $@ $<
DEPS += $(patsubst %.o,%.d,$(OBJS_$(MOD_NAME)))

137
modules.yaml Normal file
View File

@@ -0,0 +1,137 @@
name: jsix
templates: scripts/templates
modules:
kernel:
kind: exe
output: jsix.elf
target: host
deps:
- elf
- initrd
- kutil
includes:
- src/kernel
source:
- src/kernel/crti.s
- src/kernel/apic.cpp
- src/kernel/assert.cpp
- src/kernel/boot.s
- src/kernel/console.cpp
- src/kernel/cpprt.cpp
- src/kernel/cpu.cpp
- src/kernel/debug.cpp
- src/kernel/debug.s
- src/kernel/device_manager.cpp
- src/kernel/font.cpp
- src/kernel/frame_allocator.cpp
- src/kernel/fs/gpt.cpp
- src/kernel/gdt.cpp
- src/kernel/gdt.s
- src/kernel/interrupts.cpp
- src/kernel/interrupts.s
- src/kernel/io.cpp
- src/kernel/loader.s
- src/kernel/log.cpp
- src/kernel/main.cpp
- src/kernel/memory_bootstrap.cpp
- src/kernel/msr.cpp
- src/kernel/objects/handle.cpp
- src/kernel/objects/kobject.cpp
- src/kernel/page_manager.cpp
- src/kernel/pci.cpp
- src/kernel/process.cpp
- src/kernel/scheduler.cpp
- src/kernel/screen.cpp
- src/kernel/serial.cpp
- src/kernel/syscall.cpp
- src/kernel/syscall.s
- src/kernel/syscalls/object.cpp
- src/kernel/syscalls/process.cpp
- src/kernel/task.s
- src/kernel/crtn.s
boot:
kind: exe
target: boot
output: boot.efi
source:
- src/boot/main.cpp
- src/boot/console.cpp
- src/boot/error.cpp
- src/boot/fs.cpp
- src/boot/hardware.cpp
- src/boot/loader.cpp
- src/boot/memory.cpp
- src/boot/paging.cpp
- src/boot/support.cpp
nulldrv:
kind: exe
target: user
output: nulldrv
source:
- src/drivers/nulldrv/main.cpp
- src/drivers/nulldrv/main.s
elf:
kind: lib
output: libelf.a
deps:
- kutil
includes:
- src/libraries/elf/include
source:
- src/libraries/elf/elf.cpp
initrd:
kind: lib
output: libinitrd.a
deps:
- kutil
includes:
- src/libraries/initrd/include
source:
- src/libraries/initrd/initrd.cpp
kutil:
kind: lib
output: libkutil.a
includes:
- src/libraries/kutil/include
source:
- src/libraries/kutil/assert.cpp
- src/libraries/kutil/bip_buffer.cpp
- src/libraries/kutil/heap_allocator.cpp
- src/libraries/kutil/logger.cpp
- src/libraries/kutil/memory.cpp
- src/libraries/kutil/printf.c
- src/libraries/kutil/vm_space.cpp
makerd:
kind: exe
target: native
output: makerd
deps:
- initrd
source:
- src/tools/makerd/entry.cpp
- src/tools/makerd/main.cpp
tests:
kind: exe
target: native
output: tests
deps:
- kutil
source:
- src/tests/address_manager.cpp
- src/tests/constexpr_hash.cpp
- src/tests/linked_list.cpp
- src/tests/logger.cpp
- src/tests/heap_allocator.cpp
- src/tests/main.cpp
overlays:
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-llvm8-20190706.tar.bz2
path: sysroot
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-j6libc-8cb8ce7.tar.bz2
path: sysroot

89
other_software.md Normal file
View File

@@ -0,0 +1,89 @@
# Included works
jsix includes and/or is derived from a number of other works, listed here.
## Catch2
jsix uses [Catch2][] for testing. Catch2 is released under the terms of the
Boost Software license:
[Catch2]: https://github.com/catchorg/Catch2
> Boost Software License - Version 1.0 - August 17th, 2003
>
> Permission is hereby granted, free of charge, to any person or organization
> obtaining a copy of the software and accompanying documentation covered by
> this license (the "Software") to use, reproduce, display, distribute,
> execute, and transmit the Software, and to prepare derivative works of the
> Software, and to permit third-parties to whom the Software is furnished to
> do so, all subject to the following:
>
> The copyright notices in the Software and this entire statement, including
> the above license grant, this restriction and the following disclaimer,
> must be included in all copies of the Software, in whole or in part, and
> all derivative works of the Software, unless such copies or derivative
> works are solely in the form of machine-executable object code generated by
> a source language processor.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
> SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS IN THE SOFTWARE.
## cpptoml
jsix uses the [cpptoml][] library for parsing TOML configuration files. cpptoml
is released under the terms of the MIT license:
[cpptoml]: https://github.com/skystrife/cpptoml
> Copyright (c) 2014 Chase Geigle
>
> Permission is hereby granted, free of charge, to any person obtaining a copy of
> this software and associated documentation files (the "Software"), to deal in
> the Software without restriction, including without limitation the rights to
> use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
> the Software, and to permit persons to whom the Software is furnished to do so,
> subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
> FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
> IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## printf
jsix uses Marco Paland's tiny [printf][] library for its `*printf()` functions,
which is also released under the terms of the MIT license:
[printf]: https://github.com/mpaland/printf
> The MIT License (MIT)
>
> Copyright (c) 2014 Marco Paland
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env python3
#
# parse_version.py - Create a NASM version definition file given
# version inputs. Usage:
#
# parse_version.py <git-describe version> <git short sha>
def split_version(version_string):
major, minor, patch_dirty = version_string.split(".")
patch_dirty = patch_dirty.split("-")
return major, minor, patch_dirty[0], len(patch_dirty) > 1
def make_nasm(major, minor, patch, dirty, sha):
if dirty:
dirty = "1"
else:
dirty = "0"
lines = [
"%define VERSION_MAJOR {}".format(major),
"%define VERSION_MINOR {}".format(minor),
"%define VERSION_PATCH {}".format(patch),
"%define VERSION_GITSHA 0x{}{}".format(dirty, sha),
]
return "\n".join(lines)
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
print("Usage: {} <desc version> <git sha>".format(sys.argv[0]), file=sys.stderr)
sys.exit(1)
print(make_nasm(*split_version(sys.argv[1]), sys.argv[2]))

View File

@@ -1,3 +0,0 @@
call make.bat
del popcorn.log
qemu-system-x86_64.exe -bios .\assets\ovmf\x64\OVMF.fd -hda .\build\fs.img -m 512 -vga cirrus -d guest_errors,int -D popcorn.log -no-reboot

71
qemu.sh Executable file
View File

@@ -0,0 +1,71 @@
#!/usr/bin/env bash
build="$(dirname $0)/build"
assets="$(dirname $0)/assets"
debug=""
debugtarget="${build}/jsix.elf"
flash_name="ovmf_vars"
gfx="-nographic"
kvm=""
cpu="Broadwell,+pdpe1gb"
for arg in $@; do
case "${arg}" in
--debugboot)
debug="-s -S"
debugtarget="${build}/boot/boot.efi"
flash_name="ovmf_vars_d"
;;
--debug)
debug="-s -S"
flash_name="ovmf_vars_d"
;;
--gfx)
gfx=""
;;
--kvm)
kvm="-enable-kvm"
cpu="host"
;;
*)
build="${arg}"
;;
esac
done
if [[ ! -c /dev/kvm ]]; then
kvm=""
fi
if ! ninja -C "${build}"; then
exit 1
fi
if [[ -n $TMUX ]]; then
if [[ -n $debug ]]; then
tmux split-window -h "gdb ${debugtarget}" &
else
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
fi
elif [[ $DESKTOP_SESSION = "i3" ]]; then
if [[ -n $debug ]]; then
i3-msg exec i3-sensible-terminal -- -e "gdb ${PWD}/${build}/jsix.elf" &
else
i3-msg exec i3-sensible-terminal -- -e 'telnet localhost 45454' &
fi
fi
exec qemu-system-x86_64 \
-drive "if=pflash,format=raw,readonly,file=${assets}/ovmf/x64/ovmf_code.fd" \
-drive "if=pflash,format=raw,file=${build}/${flash_name}.fd" \
-drive "format=raw,file=${build}/jsix.img" \
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
-monitor telnet:localhost:45454,server,nowait \
-smp 4 \
-m 512 \
-d mmu,int,guest_errors \
-D jsix.log \
-cpu "${cpu}" \
-M q35 \
-no-reboot \
$gfx $kvm $debug

131
scripts/build_sysroot.sh Executable file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env bash
TARGET="x86_64-elf"
LLVM_BRANCH="release_80"
TOOLS="clang lld" # lld libunwind libcxxabi libcxx"
PROJECTS="compiler-rt libcxxabi libcxx libunwind"
#RUNTIMES="compiler-rt libcxxabi libcxx libunwind"
set -e
README=$(realpath "$(dirname $0)/readme_for_prebuilt_sysroots.md")
SYSROOT=$(realpath "$(dirname $0)/../sysroot")
WORK=$(realpath "$(dirname $0)/sysroot")
mkdir -p "${SYSROOT}"
mkdir -p "${WORK}"
export CC=clang
export CXX=clang++
if [[ ! -d "${WORK}/llvm" ]]; then
echo "Downloading LLVM..."
git clone -q \
--branch "${LLVM_BRANCH}" \
--depth 1 \
"https://git.llvm.org/git/llvm.git" "${WORK}/llvm"
fi
for tool in ${TOOLS}; do
if [[ ! -d "${WORK}/llvm/tools/${tool}" ]]; then
echo "Downloading ${tool}..."
git clone -q \
--branch "${LLVM_BRANCH}" \
--depth 1 \
"https://git.llvm.org/git/${tool}.git" "${WORK}/llvm/tools/${tool}"
fi
done
if [[ ! -d "${WORK}/llvm/tools/clang/tools/extra" ]]; then
echo "Downloading clang-tools-extra..."
git clone -q \
--branch "${LLVM_BRANCH}" \
--depth 1 \
"https://git.llvm.org/git/clang-tools-extra.git" "${WORK}/llvm/tools/clang/tools/extra"
fi
for proj in ${PROJECTS}; do
if [[ ! -d "${WORK}/llvm/projects/${proj}" ]]; then
echo "Downloading ${proj}..."
git clone -q \
--branch "${LLVM_BRANCH}" \
--depth 1 \
"https://git.llvm.org/git/${proj}.git" "${WORK}/llvm/projects/${proj}"
fi
done
for proj in ${RUNTIMES}; do
if [[ ! -d "${WORK}/llvm/runtimes/${proj}" ]]; then
echo "Downloading ${proj}..."
git clone -q \
--branch "${LLVM_BRANCH}" \
--depth 1 \
"https://git.llvm.org/git/${proj}.git" "${WORK}/llvm/runtime/${proj}"
fi
done
mkdir -p "${WORK}/build/llvm"
pushd "${WORK}/build/llvm"
echo "Configuring LLVM..."
cmake -G Ninja \
-DCLANG_DEFAULT_RTLIB=compiler-rt \
-DCLANG_DEFAULT_STD_C=c11 \
-DCLANG_DEFAULT_STD_CXX=cxx14 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER="clang" \
-DCMAKE_CXX_COMPILER="clang++" \
-DCMAKE_CXX_FLAGS="-Wno-unused-parameter -D_LIBCPP_HAS_NO_ALIGNED_ALLOCATION -D_LIBUNWIND_IS_BAREMETAL=1 -U_LIBUNWIND_SUPPORT_DWARF_UNWIND" \
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
-DCMAKE_MAKE_PROGRAM=`which ninja` \
-DDEFAULT_SYSROOT="${SYSROOT}" \
-DLIBCXX_CXX_ABI=libcxxabi \
-DLIBCXX_CXX_ABI_INCLUDE_PATHS="${WORK}/llvm/projects/libcxxabi/include" \
-DLIBCXX_CXX_ABI_LIBRARY_PATH=lib \
-DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF \
-DLIBCXX_ENABLE_NEW_DELETE_DEFINITIONS=ON \
-DLIBCXX_ENABLE_SHARED=OFF \
-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON \
-DLIBCXX_ENABLE_THREADS=OFF \
-DLIBCXX_INCLUDE_BENCHMARKS=OFF \
-DLIBCXX_USE_COMPILER_RT=ON \
-DLIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS=OFF \
-DLIBCXXABI_ENABLE_SHARED=OFF \
-DLIBCXXABI_ENABLE_STATIC_UNWINDER=ON \
-DLIBCXXABI_ENABLE_THREADS=OFF \
-DLIBCXXABI_LIBCXX_PATH="${WORK}/llvm/projects/libcxx" \
-DLIBCXXABI_USE_COMPILER_RT=ON \
-DLIBCXXABI_USE_LLVM_UNWINDER=ON \
-DLIBUNWIND_ENABLE_SHARED=OFF \
-DLIBUNWIND_ENABLE_THREADS=OFF \
-DLIBUNWIND_USE_COMPILER_RT=ON \
-DLLVM_CONFIG_PATH="${SYSROOT}/bin/llvm-config" \
-DLLVM_DEFAULT_TARGET_TRIPLE="x86_64-unknown-elf" \
-DLLVM_ENABLE_LIBCXX=ON \
-DLLVM_ENABLE_LLD=ON \
-DLLVM_ENABLE_PIC=OFF \
-DLLVM_ENABLE_THREADS=OFF \
-DLLVM_INSTALL_BINUTILS_SYMLINKS=ON \
-DLLVM_TARGETS_TO_BUILD="X86" \
${WORK}/llvm > cmake_configure.log
# -DCMAKE_ASM_COMPILER=nasm \
# -DCMAKE_LINKER="${SYSROOT}/bin/ld.lld" \
# -DCOMPILER_RT_ENABLE_LLD=ON \
# -DLIBCXX_ENABLE_LLD=ON \
# -DLIBCXX_ENABLE_STATIC_UNWINDER=ON \
# -DLIBCXXABI_ENABLE_LLD=ON \
# -DLIBUNWIND_ENABLE_LLD=ON \
# -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi;libunwind;compiler-rt" \
# -DCOMPILER_RT_BAREMETAL_BUILD=ON \
# -DLIBCXXABI_BAREMETAL=ON \
echo "Building LLVM..."
ninja && ninja install
ninja cxx cxxabi compiler-rt
ninja install-compiler-rt install-cxx install-cxxabi
popd
cp "${README}" "${SYSROOT}/README.md"

10
scripts/parse_syms.py Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python3
def parse_elf(filename):
import struct
with open(filename, 'rb') as elf:
if __name__ == "__main__":
import sys
for arg in sys.argv[1:]:
parse_elf(arg)

View File

@@ -0,0 +1,14 @@
# jsix OS sysroot
This is a pre-built sysroot for building the jsix operating system kernel,
bootloader, and utilities. This package is provided as a convenience, and
contains software from the following repositories.
## The LLVM toolchain
The LLVM sources as downloaded via git from [llvm.org][llvm] under the terms of
the [Apache License v2.0][apache2], modified [as described here][llvmlic].
[llvm]: https://llvm.org
[apache2]: https://www.apache.org/licenses/LICENSE-2.0
[llvmlic]: https://llvm.org/docs/DeveloperPolicy.html#new-llvm-project-license-framework

View File

@@ -0,0 +1,207 @@
ninja_required_version = 1.3
builddir = {{ buildroot }}
srcroot = {{ srcroot }}
modulefile = {{ modulefile }}
{%- for var, value in vars %}
{{ var }} = {{ value }}
{%- endfor %}
warnflags = $
-Wformat=2 $
-Winit-self $
-Wfloat-equal $
-Winline $
-Wmissing-format-attribute $
-Wmissing-include-dirs $
-Wswitch $
-Wundef $
-Wdisabled-optimization $
-Wpointer-arith $
-Wno-attributes $
-Wno-sign-compare $
-Wno-multichar $
-Wno-div-by-zero $
-Wno-endif-labels $
-Wno-pragmas $
-Wno-format-extra-args $
-Wno-unused-result $
-Wno-deprecated-declarations $
-Wno-unused-function $
-Wno-address-of-packed-member $
-Werror
ccflags = $
-I${srcroot}/src/include $
-I${srcroot}/src/include/x86_64 $
-fcolor-diagnostics $
-DVERSION_MAJOR={{ version_major }} $
-DVERSION_MINOR={{ version_minor }} $
-DVERSION_PATCH={{ version_patch }} $
-DVERSION_GITSHA=0x0{{ version_sha }} $
-DGIT_VERSION=\"{{ version }}\" $
-DGIT_VERSION_WIDE=L\"{{ version }}\" $
$warnflags
asflags = $
-DVERSION_MAJOR={{ version_major }} $
-DVERSION_MINOR={{ version_minor }} $
-DVERSION_PATCH={{ version_patch }} $
-DVERSION_GITSHA=0x{{ version_sha }}
cflags = -std=c11
cxxflags = -std=c++14
libs =
rule c
deps = gcc
depfile = $out.d
description = Compiling $name
command = $cc -MMD -MF $out.d $ccflags $cflags -o $out -c $in
rule dump_c_defs
description = Dumping C defines for $target
command = echo "" | $cc $ccflags $cflags -dM -E - > $out
rule dump_c_run
description = Dumping C arguments for $target
command = $
echo "#!/bin/bash" > $out; $
echo '$cc $ccflags $cflags $$*' > $out; $
chmod a+x $out
rule cpp
deps = gcc
depfile = $out.d
description = Compiling $name
command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in
rule dump_cpp_defs
description = Dumping C++ defines for $target
command = echo "" | $cxx -x c++ $cxxflags $ccflags -dM -E - > $out
rule dump_cpp_run
description = Dumping C++ arguments for $target
command = $
echo "#!/bin/bash" > $out; $
echo '$cc $cxxflags $ccflags $$*' > $out; $
chmod a+x $out
rule s
deps = gcc
depfile = $out.d
description = Assembling $name
command = $nasm -o $out -felf64 -MD $out.d $asflags $in
rule exe
description = Linking $name
command = $ld $ldflags -o $out $in $libs
rule lib
description = Archiving $name
command = $ar qcs $out $in
rule regen
generator = true
description = Regenrating build files
command = $
{{ generator }} $
--file $modulefile $
--dir $builddir $
generate
rule cp
description = Copying $name
command = cp $in $out
rule dump
description = Dumping decompiled $name
command = objdump -DSC -M intel $in > $out
rule makerd
description = Making init ramdisk
command = $builddir/native/makerd $in $out
rule makeefi
description = Converting $name
command = objcopy $
-j .text $
-j .sdata $
-j .data $
-j .dynamic $
-j .dynsym $
-j .rel $
-j .rela $
-j .reloc $
--target=efi-app-x86_64 $
$in $out
rule makefat
description = Creating $name
command = $
cp $srcroot/assets/diskbase.img $out; $
mcopy -s -D o -i $out@@1M $builddir/fatroot/* ::/
rule strip
description = Stripping $name
command = $
cp $in $out; $
objcopy --only-keep-debug $out $out.debug; $
strip -g $out; $
objcopy --add-gnu-debuglink=$out.debug $out
{% for target in targets %}
subninja {{ target }}/target.ninja
{% endfor %}
build $
{%- for buildfile in buildfiles %}
{{ buildfile }} $
{%- endfor %}
: regen | $
{%- for template in templates %}
{{ template }} $
{%- endfor %}
$modulefile $
{{ generator }}
build $builddir/ovmf_vars.fd : cp $srcroot/assets/ovmf/x64/ovmf_vars.fd
name = ovmf_vars.fd
build $builddir/ovmf_vars_d.fd : cp $srcroot/assets/ovmf/x64/ovmf_vars_d.fd
name = ovmf_vars_d.fd
build $builddir/jsix.elf | $builddir/jsix.elf.debug : strip $builddir/host/jsix.elf
name = kernel
build $builddir/jsix.dump : dump $builddir/host/jsix.elf
name = kernel
build $builddir/jsix.elf-gdb.py : cp ${srcroot}/assets/debugging/jsix.elf-gdb.py
name = kernel debug python scripts
build $builddir/fatroot/jsix.elf : cp $builddir/jsix.elf
name = kernel to FAT image
build $builddir/fatroot/efi/boot/bootx64.efi : cp $builddir/boot/boot.efi
name = bootloader to FAT image
build $builddir/fatroot/initrd.img : makerd ${srcroot}/assets/initrd.toml | $
${builddir}/native/makerd $
${builddir}/user/nulldrv
build $builddir/jsix.img : makefat | $
$builddir/fatroot/initrd.img $
$builddir/fatroot/jsix.elf $
$builddir/fatroot/efi/boot/bootx64.efi
name = jsix.img
default $
$builddir/ovmf_vars.fd $
$builddir/ovmf_vars_d.fd $
$builddir/jsix.dump $
$builddir/jsix.elf-gdb.py $
$builddir/jsix.img
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,14 @@
{% extends "exe.default.j2" %}
{% block variables %}
{{ super() }}
ccflags = $ccflags $
-g3 $
-DKERNEL_FILENAME=L\"jsix.elf\" $
-I${srcroot}/external/include $
-I${srcroot}/external/include/X64
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,8 @@
{% extends "module.base.j2" %}
{% block variables %}
{{ super() }}
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,12 @@
{% extends "exe.default.j2" %}
{% block variables %}
{{ super() }}
asflags = $asflags -I${srcroot}/src/kernel/
libs = $libs
ldflags = $ldflags -T ${srcroot}/src/arch/x86_64/kernel.ld
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,10 @@
{% extends "exe.default.j2" %}
{% block variables %}
{{ super() }}
ccflags = $ccflags -I${srcroot}/external/cpptoml
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,10 @@
{% extends "exe.default.j2" %}
{% block variables %}
{{ super() }}
ccflags = $ccflags -ggdb -I${srcroot}/external/catch
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,4 @@
{% extends "module.base.j2" %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,45 @@
moddir = ${builddir}/{{ name }}.dir
{% block variables %}
ccflags = $ccflags $
{%- for dep in depmods %}
{%- for inc in dep.includes %}
-I${srcroot}/{{ inc }} $
{%- endfor %}
{%- endfor %}
{%- for inc in module.includes %}
-I${srcroot}/{{ inc }} $
{%- endfor %}
{%- for define in module.defines %}
-D{{ define }} $
{%- endfor %}
{% endblock %}
{% for source in module.source %}
build ${moddir}/{{ source.output }} : {{ source.action }} ${srcroot}/{{ source.input }} || {{ buildfile }}
name = {{ source.name }}
{% endfor %}
build ${builddir}/{{ module.output }} : {{ module.kind }} $
{%- for source in module.source %}
${moddir}/{{ source.output }} $
{%- endfor -%}
{%- for dep in deplibs %}
${builddir}/{{ dep.output }} $
{%- endfor %}
| $
{%- for dep in depexes %}
${builddir}/{{ dep.output }} $
{%- endfor %}
{{ buildfile }}
name = {{ name }}
{% if module.default %}
default ${builddir}/{{ module.output }}
{% endif %}
{% block extra %}
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,38 @@
{% extends "target.default.j2" %}
{% block binaries %}
cc = clang
cxx = clang++
ld = clang++
ar = ar
nasm = nasm
objcopy = objcopy
{% endblock %}
{% block variables %}
ccflags = $ccflags $
-I $srcroot/external $
--target=x86_64-unknown-windows $
-ffreestanding $
-mno-red-zone $
-fshort-wchar $
-fno-omit-frame-pointer $
-ggdb
cxxflags = $cxxflags $
-fno-rtti $
-fno-exceptions
ldflags = $ldflags $
--target=x86_64-unknown-windows $
-nostdlib $
-Wl,-entry:efi_main $
-Wl,-subsystem:efi_application $
-fuse-ld=lld-link $
-g
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,26 @@
builddir = $builddir/{{ target }}
target = {{ target }}
{% block variables %}
{% endblock %}
{% block binaries %}
cc = clang
cxx = clang++
ld = ld
ar = ar
nasm = nasm
objcopy = objcopy
{% endblock %}
{% for module in modules %}
subninja {{ module }}.ninja
{% endfor %}
build ${builddir}/c.defs : dump_c_defs | {{ buildfile }}
build ${builddir}/cpp.defs : dump_cpp_defs | {{ buildfile }}
build ${builddir}/c.run : dump_c_run | {{ buildfile }}
build ${builddir}/cpp.run : dump_cpp_run | {{ buildfile }}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,43 @@
{% extends "target.default.j2" %}
{% block binaries %}
cc = ${srcroot}/sysroot/bin/clang
cxx = ${srcroot}/sysroot/bin/clang++
ld = ${srcroot}/sysroot/bin/ld.lld
ar = ${srcroot}/sysroot/bin/ar
nasm = nasm
objcopy = ${srcroot}/sysroot/bin/objcopy
{% endblock %}
{% block variables %}
ccflags = $ccflags $
-nostdlib $
-ffreestanding $
-nodefaultlibs $
-fno-builtin $
-mno-sse $
-fno-omit-frame-pointer $
-mno-red-zone $
-g $
-mcmodel=large $
-D__ELF__ $
-D__JSIX__ $
-isystem${srcroot}/sysroot/include $
--sysroot="${srcroot}/sysroot"
cxxflags = $cxxflags $
-fno-exceptions $
-fno-rtti $
-isystem${srcroot}/sysroot/include/c++/v1
ldflags = $ldflags $
-g $
-nostdlib $
-Bsymbolic $
-Bstatic
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,15 @@
{% extends "target.default.j2" %}
{% block binaries %}
{{ super() }}
ld = clang++
{% endblock %}
{% block variables %}
ccflags = $ccflags -g -ggdb
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -0,0 +1,47 @@
{% extends "target.default.j2" %}
{% block binaries %}
cc = ${srcroot}/sysroot/bin/clang
cxx = ${srcroot}/sysroot/bin/clang++
ld = ${srcroot}/sysroot/bin/ld.lld
ar = ${srcroot}/sysroot/bin/ar
nasm = nasm
objcopy = ${srcroot}/sysroot/bin/objcopy
{% endblock %}
{% block variables %}
ccflags = $ccflags $
-nostdlib $
-nodefaultlibs $
-fno-builtin $
-mno-sse $
-fno-omit-frame-pointer $
-mno-red-zone $
-g $
-mcmodel=large $
-D__ELF__ $
-D__JSIX__ $
-isystem${srcroot}/sysroot/include $
--sysroot="${srcroot}/sysroot"
cxxflags = $cxxflags $
-fno-exceptions $
-fno-rtti $
-isystem${srcroot}/sysroot/include/c++/v1
ldflags = $ldflags $
-g $
-nostdlib $
-Bsymbolic $
-Bstatic $
--sysroot="${srcroot}/sysroot" $
-L "${srcroot}/sysroot/lib" $
libs = $libs $
-lc
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

12
scripts/vmem_translate.py Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env python
def translate(i4 = 0, i3 = 0, i2 = 0, i1 = 0, offset = 0):
addr = (i4 << 39) + (i3 << 30) + (i2 << 21) + (i1 << 12) + offset
if addr & (1 << 47):
addr |= 0xffff000000000000
return addr
if __name__ == "__main__":
import sys
print("{:016x}".format(translate(*map(int, sys.argv[1:]))))

View File

@@ -5,29 +5,43 @@ SECTIONS
. = OFFSET + 0x100000;
.header : {
header = .;
__header_start = .;
KEEP(*(.header))
__header_end = .;
}
.text : {
code = .;
.text ALIGN(4096) : {
*(.text)
*(.isrs)
}
.data ALIGN(0x1000) : {
data = .;
.data ALIGN(4096) : {
*(.data)
*(.rodata)
}
.bss ALIGN(0x1000) : {
bss = .;
.bss ALIGN(4096) : {
__bss_start = .;
*(.bss)
__bss_end = .;
}
.note ALIGN(0x1000) : {
.note : {
*(.note.*)
}
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
__eh_frame_end = .;
}
.eh_frame_hdr : {
KEEP(*(.eh_frame_hdr))
}
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
kernel_end = ALIGN(4096);
}

View File

@@ -1,308 +0,0 @@
#include <efi.h>
#include <efilib.h>
#include <stddef.h>
#include <stdint.h>
#include "console.h"
#include "guids.h"
#include "utility.h"
size_t ROWS = 0;
size_t COLS = 0;
static EFI_SIMPLE_TEXT_OUT_PROTOCOL *con_out = 0;
const CHAR16 digits[] = {u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
EFI_STATUS
con_pick_mode(EFI_BOOT_SERVICES *bootsvc)
{
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *gfx_out_proto;
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gfx_out_proto);
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
const uint32_t modes = gfx_out_proto->Mode->MaxMode;
uint32_t best = gfx_out_proto->Mode->Mode;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info =
(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *)gfx_out_proto->Mode;
uint32_t res = info->HorizontalResolution * info->VerticalResolution;
int is_fb = info->PixelFormat != PixelBltOnly;
for (uint32_t i = 0; i < modes; ++i) {
size_t size = 0;
status = gfx_out_proto->QueryMode(gfx_out_proto, i, &size, &info);
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
#ifdef MAX_HRES
if (info->HorizontalResolution > MAX_HRES) continue;
#endif
const uint32_t new_res = info->HorizontalResolution * info->VerticalResolution;
const int new_is_fb = info->PixelFormat == PixelBltOnly;
if (new_is_fb > is_fb && new_res >= res) {
best = i;
res = new_res;
}
}
status = gfx_out_proto->SetMode(gfx_out_proto, best);
CHECK_EFI_STATUS_OR_RETURN(status, "SetMode %d/%d", best, modes);
return EFI_SUCCESS;
}
EFI_STATUS
con_initialize(EFI_SYSTEM_TABLE *system_table, const CHAR16 *version)
{
EFI_STATUS status;
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
con_out = system_table->ConOut;
// Might not find a video device at all, so ignore not found errors
status = con_pick_mode(bootsvc);
if (status != EFI_NOT_FOUND)
CHECK_EFI_STATUS_OR_RETURN(status, "con_pick_mode");
status = con_out->QueryMode(con_out, con_out->Mode->Mode, &COLS, &ROWS);
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
status = con_out->ClearScreen(con_out);
CHECK_EFI_STATUS_OR_RETURN(status, "ClearScreen");
con_out->SetAttribute(con_out, EFI_LIGHTCYAN);
con_out->OutputString(con_out, (CHAR16 *)L"Popcorn loader ");
con_out->SetAttribute(con_out, EFI_LIGHTMAGENTA);
con_out->OutputString(con_out, (CHAR16 *)version);
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L" booting...\r\n\n");
return status;
}
size_t
con_print_hex(uint32_t n)
{
CHAR16 buffer[9];
CHAR16 *p = buffer;
for (int i = 7; i >= 0; --i) {
uint8_t nibble = (n & (0xf << (i*4))) >> (i*4);
*p++ = digits[nibble];
}
*p = 0;
con_out->OutputString(con_out, buffer);
return 8;
}
size_t
con_print_long_hex(uint64_t n)
{
CHAR16 buffer[17];
CHAR16 *p = buffer;
for (int i = 15; i >= 0; --i) {
uint8_t nibble = (n & (0xf << (i*4))) >> (i*4);
*p++ = digits[nibble];
}
*p = 0;
con_out->OutputString(con_out, buffer);
return 16;
}
size_t
con_print_dec(uint32_t n)
{
CHAR16 buffer[11];
CHAR16 *p = buffer + 10;
*p-- = 0;
do {
*p-- = digits[n % 10];
n /= 10;
} while (n != 0);
con_out->OutputString(con_out, ++p);
return 10 - (p - buffer);
}
size_t
con_print_long_dec(uint64_t n)
{
CHAR16 buffer[21];
CHAR16 *p = buffer + 20;
*p-- = 0;
do {
*p-- = digits[n % 10];
n /= 10;
} while (n != 0);
con_out->OutputString(con_out, ++p);
return 20 - (p - buffer);
}
size_t
con_printf(const CHAR16 *fmt, ...)
{
CHAR16 buffer[256];
const CHAR16 *r = fmt;
CHAR16 *w = buffer;
va_list args;
size_t count = 0;
va_start(args, fmt);
while (r && *r) {
if (*r != L'%') {
count++;
*w++ = *r++;
continue;
}
*w = 0;
con_out->OutputString(con_out, buffer);
w = buffer;
r++; // chomp the %
switch (*r++) {
case L'%':
con_out->OutputString(con_out, L"%");
count++;
break;
case L'x':
count += con_print_hex(va_arg(args, uint32_t));
break;
case L'd':
case L'u':
count += con_print_dec(va_arg(args, uint32_t));
break;
case L's':
{
CHAR16 *s = va_arg(args, CHAR16*);
count += wstrlen(s);
con_out->OutputString(con_out, s);
}
break;
case L'l':
switch (*r++) {
case L'x':
count += con_print_long_hex(va_arg(args, uint64_t));
break;
case L'd':
case L'u':
count += con_print_long_dec(va_arg(args, uint64_t));
break;
default:
break;
}
break;
default:
break;
}
}
*w = 0;
con_out->OutputString(con_out, buffer);
va_end(args);
return count;
}
void
con_status_begin(const CHAR16 *message)
{
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)message);
}
void
con_status_ok()
{
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"[");
con_out->SetAttribute(con_out, EFI_GREEN);
con_out->OutputString(con_out, (CHAR16 *)L" ok ");
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"]\r\n");
}
void
con_status_fail(const CHAR16 *error)
{
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"[");
con_out->SetAttribute(con_out, EFI_LIGHTRED);
con_out->OutputString(con_out, (CHAR16 *)L"failed");
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"]\r\n");
con_out->SetAttribute(con_out, EFI_RED);
con_out->OutputString(con_out, (CHAR16 *)error);
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"\r\n");
}
EFI_STATUS
con_get_framebuffer(
EFI_BOOT_SERVICES *bootsvc,
void **buffer,
size_t *buffer_size,
uint32_t *hres,
uint32_t *vres,
uint32_t *rmask,
uint32_t *gmask,
uint32_t *bmask)
{
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gop);
if (status != EFI_NOT_FOUND) {
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
*buffer = (void *)gop->Mode->FrameBufferBase;
*buffer_size = gop->Mode->FrameBufferSize;
*hres = gop->Mode->Info->HorizontalResolution;
*vres = gop->Mode->Info->VerticalResolution;
switch (gop->Mode->Info->PixelFormat) {
case PixelRedGreenBlueReserved8BitPerColor:
*rmask = 0x0000ff;
*gmask = 0x00ff00;
*bmask = 0xff0000;
return EFI_SUCCESS;
case PixelBlueGreenRedReserved8BitPerColor:
*bmask = 0x0000ff;
*gmask = 0x00ff00;
*rmask = 0xff0000;
return EFI_SUCCESS;
case PixelBitMask:
*rmask = gop->Mode->Info->PixelInformation.RedMask;
*gmask = gop->Mode->Info->PixelInformation.GreenMask;
*bmask = gop->Mode->Info->PixelInformation.BlueMask;
return EFI_SUCCESS;
default:
// Not a framebuffer, fall through to zeroing out
// values below.
break;
}
}
*buffer = NULL;
*buffer_size = *hres = *vres = 0;
*rmask = *gmask = *bmask = 0;
return EFI_SUCCESS;
}

447
src/boot/console.cpp Normal file
View File

@@ -0,0 +1,447 @@
#include <stddef.h>
#include <stdint.h>
#include <uefi/types.h>
#include <uefi/graphics.h>
#include <uefi/protos/graphics_output.h>
#include "console.h"
#include "error.h"
#ifndef GIT_VERSION_WIDE
#define GIT_VERSION_WIDE L"no version"
#endif
namespace boot {
size_t ROWS = 0;
size_t COLS = 0;
static constexpr int level_ok = 0;
static constexpr int level_warn = 1;
static constexpr int level_fail = 2;
static const wchar_t *level_tags[] = {
L" ok ",
L" warn ",
L"failed"
};
static const uefi::attribute level_colors[] = {
uefi::attribute::green,
uefi::attribute::brown,
uefi::attribute::light_red
};
console *console::s_console = nullptr;
status_line *status_line::s_current = nullptr;
static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
static size_t
wstrlen(const wchar_t *s)
{
size_t count = 0;
while (s && *s++) count++;
return count;
}
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
m_rows(0),
m_cols(0),
m_out(out)
{
pick_mode(bs);
try_or_raise(
m_out->query_mode(m_out->mode->mode, &m_cols, &m_rows),
L"Failed to get text output mode.");
try_or_raise(
m_out->clear_screen(),
L"Failed to clear screen");
m_out->set_attribute(uefi::attribute::light_cyan);
m_out->output_string(L"jsix loader ");
m_out->set_attribute(uefi::attribute::light_magenta);
m_out->output_string(GIT_VERSION_WIDE);
m_out->set_attribute(uefi::attribute::light_gray);
m_out->output_string(L" booting...\r\n\n");
s_console = this;
}
void
console::pick_mode(uefi::boot_services *bs)
{
uefi::status status;
uefi::protos::graphics_output *gfx_out_proto;
uefi::guid guid = uefi::protos::graphics_output::guid;
try_or_raise(
bs->locate_protocol(&guid, nullptr, (void **)&gfx_out_proto),
L"Failed to find a Graphics Output Protocol handle");
const uint32_t modes = gfx_out_proto->mode->max_mode;
uint32_t best = gfx_out_proto->mode->mode;
uefi::graphics_output_mode_info *info =
(uefi::graphics_output_mode_info *)gfx_out_proto->mode;
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
int is_fb = info->pixel_format != uefi::pixel_format::blt_only;
for (uint32_t i = 0; i < modes; ++i) {
size_t size = 0;
try_or_raise(
gfx_out_proto->query_mode(i, &size, &info),
L"Failed to find a graphics mode the driver claimed to support");
#ifdef MAX_HRES
if (info->horizontal_resolution > MAX_HRES) continue;
#endif
const uint32_t new_res = info->horizontal_resolution * info->vertical_resolution;
const int new_is_fb = info->pixel_format != uefi::pixel_format::blt_only;
if (new_is_fb > is_fb && new_res >= res) {
best = i;
res = new_res;
}
}
try_or_raise(
gfx_out_proto->set_mode(best),
L"Failed to set graphics mode");
}
size_t
console::print_hex(uint32_t n) const
{
wchar_t buffer[9];
wchar_t *p = buffer;
for (int i = 7; i >= 0; --i) {
uint8_t nibble = (n >> (i*4)) & 0xf;
*p++ = digits[nibble];
}
*p = 0;
m_out->output_string(buffer);
return 8;
}
size_t
console::print_long_hex(uint64_t n) const
{
wchar_t buffer[17];
wchar_t *p = buffer;
for (int i = 15; i >= 0; --i) {
uint8_t nibble = (n >> (i*4)) & 0xf;
*p++ = digits[nibble];
}
*p = 0;
m_out->output_string(buffer);
return 16;
}
size_t
console::print_dec(uint32_t n) const
{
wchar_t buffer[11];
wchar_t *p = buffer + 10;
*p-- = 0;
do {
*p-- = digits[n % 10];
n /= 10;
} while (n != 0);
m_out->output_string(++p);
return 10 - (p - buffer);
}
size_t
console::print_long_dec(uint64_t n) const
{
wchar_t buffer[21];
wchar_t *p = buffer + 20;
*p-- = 0;
do {
*p-- = digits[n % 10];
n /= 10;
} while (n != 0);
m_out->output_string(++p);
return 20 - (p - buffer);
}
size_t
console::vprintf(const wchar_t *fmt, va_list args) const
{
wchar_t buffer[256];
const wchar_t *r = fmt;
wchar_t *w = buffer;
size_t count = 0;
while (r && *r) {
if (*r != L'%') {
count++;
*w++ = *r++;
continue;
}
*w = 0;
m_out->output_string(buffer);
w = buffer;
r++; // chomp the %
switch (*r++) {
case L'%':
m_out->output_string(const_cast<wchar_t*>(L"%"));
count++;
break;
case L'x':
count += print_hex(va_arg(args, uint32_t));
break;
case L'd':
case L'u':
count += print_dec(va_arg(args, uint32_t));
break;
case L's':
{
wchar_t *s = va_arg(args, wchar_t*);
count += wstrlen(s);
m_out->output_string(s);
}
break;
case L'l':
switch (*r++) {
case L'x':
count += print_long_hex(va_arg(args, uint64_t));
break;
case L'd':
case L'u':
count += print_long_dec(va_arg(args, uint64_t));
break;
default:
break;
}
break;
default:
break;
}
}
*w = 0;
m_out->output_string(buffer);
return count;
}
size_t
console::printf(const wchar_t *fmt, ...) const
{
va_list args;
va_start(args, fmt);
size_t result = vprintf(fmt, args);
va_end(args);
return result;
}
size_t
console::print(const wchar_t *fmt, ...)
{
va_list args;
va_start(args, fmt);
size_t result = get().vprintf(fmt, args);
va_end(args);
return result;
}
status_line::status_line(const wchar_t *message, const wchar_t *context) :
m_level(level_ok)
{
auto out = console::get().m_out;
m_line = out->mode->cursor_row;
m_depth = (s_current ? 1 + s_current->m_depth : 0);
int indent = 2 * m_depth;
out->set_cursor_position(indent, m_line);
out->set_attribute(uefi::attribute::light_gray);
out->output_string(message);
if (context) {
out->output_string(L": ");
out->output_string(context);
}
out->output_string(L"\r\n");
m_next = s_current;
s_current = this;
}
status_line::~status_line()
{
if (s_current != this)
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
finish();
if (m_next && m_level > m_next->m_level) {
m_next->m_level = m_level;
m_next->print_status_tag();
}
s_current = m_next;
}
void
status_line::print_status_tag()
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
int col = out->mode->cursor_column;
uefi::attribute color = level_colors[m_level];
const wchar_t *tag = level_tags[m_level];
out->set_cursor_position(50, m_line);
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"[");
out->set_attribute(color);
out->output_string(tag);
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"]\r\n");
out->set_cursor_position(col, row);
}
void
status_line::do_warn(const wchar_t *message, const wchar_t *error)
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
if (m_level < level_warn) {
m_level = level_warn;
print_status_tag();
}
int indent = 2 + 2 * m_depth;
out->set_cursor_position(indent, row);
out->set_attribute(uefi::attribute::yellow);
out->output_string(message);
if (error) {
out->output_string(L": ");
out->output_string(error);
}
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"\r\n");
}
void
status_line::do_fail(const wchar_t *message, const wchar_t *error)
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
if (s_current->m_level < level_fail) {
m_level = level_fail;
print_status_tag();
}
int indent = 2 + 2 * m_depth;
out->set_cursor_position(indent, row);
out->set_attribute(uefi::attribute::red);
out->output_string(message);
if (error) {
out->output_string(L": ");
out->output_string(error);
}
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"\r\n");
}
void
status_line::finish()
{
if (m_level <= level_ok)
print_status_tag();
}
/*
uefi::status
con_get_framebuffer(
EFI_BOOT_SERVICES *bootsvc,
void **buffer,
size_t *buffer_size,
uint32_t *hres,
uint32_t *vres,
uint32_t *rmask,
uint32_t *gmask,
uint32_t *bmask)
{
uefi::status status;
uefi::protos::graphics_output *gop;
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gop);
if (status != EFI_NOT_FOUND) {
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
*buffer = (void *)gop->Mode->FrameBufferBase;
*buffer_size = gop->Mode->FrameBufferSize;
*hres = gop->Mode->Info->horizontal_resolution;
*vres = gop->Mode->Info->vertical_resolution;
switch (gop->Mode->Info->PixelFormat) {
case PixelRedGreenBlueReserved8BitPerColor:
*rmask = 0x0000ff;
*gmask = 0x00ff00;
*bmask = 0xff0000;
return EFI_SUCCESS;
case PixelBlueGreenRedReserved8BitPerColor:
*bmask = 0x0000ff;
*gmask = 0x00ff00;
*rmask = 0xff0000;
return EFI_SUCCESS;
case PixelBitMask:
*rmask = gop->Mode->Info->PixelInformation.RedMask;
*gmask = gop->Mode->Info->PixelInformation.GreenMask;
*bmask = gop->Mode->Info->PixelInformation.BlueMask;
return EFI_SUCCESS;
default:
// Not a framebuffer, fall through to zeroing out
// values below.
break;
}
}
*buffer = NULL;
*buffer_size = *hres = *vres = 0;
*rmask = *gmask = *bmask = 0;
return EFI_SUCCESS;
}
*/
} // namespace boot

View File

@@ -1,20 +1,80 @@
/// \file console.h
/// Text output and status message handling
#pragma once
#include <efi.h>
#include <stdarg.h>
#include <stddef.h>
#include <uefi/boot_services.h>
#include <uefi/protos/simple_text_output.h>
EFI_STATUS con_initialize(EFI_SYSTEM_TABLE *system_table, const CHAR16 *version);
void con_status_begin(const CHAR16 *message);
void con_status_ok();
void con_status_fail(const CHAR16 *error);
size_t con_printf(const CHAR16 *fmt, ...);
namespace boot {
EFI_STATUS
con_get_framebuffer(
EFI_BOOT_SERVICES *bootsvc,
void **buffer,
size_t *buffer_size,
uint32_t *hres,
uint32_t *vres,
uint32_t *rmask,
uint32_t *gmask,
uint32_t *bmask);
/// Object providing basic output functionality to the UEFI console
class console
{
public:
console(uefi::boot_services *bs, uefi::protos::simple_text_output *out);
size_t print_hex(uint32_t n) const;
size_t print_dec(uint32_t n) const;
size_t print_long_hex(uint64_t n) const;
size_t print_long_dec(uint64_t n) const;
size_t printf(const wchar_t *fmt, ...) const;
static console & get() { return *s_console; }
static size_t print(const wchar_t *fmt, ...);
private:
friend class status_line;
void pick_mode(uefi::boot_services *bs);
size_t vprintf(const wchar_t *fmt, va_list args) const;
size_t m_rows, m_cols;
uefi::protos::simple_text_output *m_out;
static console *s_console;
};
/// Scoped status line reporter. Prints a message and an "OK" if no errors
/// or warnings were reported before destruction, otherwise reports the
/// error or warning.
class status_line
{
public:
/// Constructor.
/// \arg message Description of the operation in progress
/// \arg context If non-null, printed after `message` and a colon
status_line(const wchar_t *message, const wchar_t *context = nullptr);
~status_line();
/// Set the state to warning, and print a message. If the state is already at
/// warning or error, the state is unchanged but the message is still printed.
/// \arg message The warning message to print
/// \arg error If non-null, printed after `message`
inline static void warn(const wchar_t *message, const wchar_t *error = nullptr) {
if (s_current) s_current->do_warn(message, error);
}
/// Set the state to error, and print a message. If the state is already at
/// error, the state is unchanged but the message is still printed.
/// \arg message The error message to print
/// \arg error If non-null, printed after `message`
inline static void fail(const wchar_t *message, const wchar_t *error = nullptr) {
if (s_current) s_current->do_fail(message, error);
}
private:
void print_status_tag();
void do_warn(const wchar_t *message, const wchar_t *error);
void do_fail(const wchar_t *message, const wchar_t *error);
void finish();
size_t m_line;
int m_level;
int m_depth;
status_line *m_next;
static status_line *s_current;
};
} // namespace boot

83
src/boot/elf.h Normal file
View File

@@ -0,0 +1,83 @@
/// \file elf.h
/// Definitions and related constants for ELF64 structures
#pragma once
#include <stdint.h>
namespace boot {
namespace elf {
constexpr uint8_t version = 1;
constexpr uint8_t word_size = 2;
constexpr uint8_t endianness = 1;
constexpr uint8_t os_abi = 0;
constexpr uint16_t machine = 0x3e;
const unsigned PT_LOAD = 1;
const unsigned ST_PROGBITS = 1;
const unsigned ST_NOBITS = 8;
const unsigned long SHF_ALLOC = 0x2;
struct header
{
char magic[4];
uint8_t word_size;
uint8_t endianness;
uint8_t header_version;
uint8_t os_abi;
uint64_t reserved;
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entrypoint;
uint64_t ph_offset;
uint64_t sh_offset;
uint32_t flags;
uint16_t eh_size;
uint16_t ph_entsize;
uint16_t ph_num;
uint16_t sh_entsize;
uint16_t sh_num;
uint16_t sh_str_idx;
} __attribute__ ((packed));
struct program_header
{
uint32_t type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t file_size;
uint64_t mem_size;
uint64_t align;
} __attribute__ ((packed));
struct section_header
{
uint32_t name;
uint32_t type;
uint64_t flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t align;
uint64_t entry_size;
} __attribute__ ((packed));
} // namespace elf
} // namespace boot

94
src/boot/error.cpp Normal file
View File

@@ -0,0 +1,94 @@
#include "error.h"
#include "console.h"
namespace boot {
namespace error {
handler *handler::s_current = nullptr;
struct error_code_desc {
uefi::status code;
const wchar_t *name;
};
struct error_code_desc error_table[] = {
#define STATUS_ERROR(name, num) { uefi::status::name, L#name },
#define STATUS_WARNING(name, num) { uefi::status::name, L#name },
#include "uefi/errors.inc"
#undef STATUS_ERROR
#undef STATUS_WARNING
{ uefi::status::success, nullptr }
};
static const wchar_t *
error_message(uefi::status status)
{
int32_t i = -1;
while (error_table[++i].name != nullptr) {
if (error_table[i].code == status) return error_table[i].name;
}
if (uefi::is_error(status))
return L"Unknown Error";
else
return L"Unknown Warning";
}
[[ noreturn ]] void
raise(uefi::status status, const wchar_t *message)
{
if (handler::s_current) {
handler::s_current->handle(status, message);
}
while (1) asm("hlt");
}
handler::handler() :
m_next(s_current)
{
s_current = this;
}
handler::~handler()
{
if (s_current != this)
raise(uefi::status::warn_stale_data,
L"Non-current error handler destructing");
s_current = m_next;
}
uefi_handler::uefi_handler(console &con) :
handler(),
m_con(con)
{
}
void
uefi_handler::handle(uefi::status s, const wchar_t *message)
{
status_line::fail(message, error_message(s));
}
cpu_assert_handler::cpu_assert_handler() : handler() {}
void
cpu_assert_handler::handle(uefi::status s, const wchar_t *message)
{
asm volatile (
"movq $0xeeeeeeebadbadbad, %%r8;"
"movq %0, %%r9;"
"movq $0, %%rdx;"
"divq %%rdx;"
:
: "r"((uint64_t)s)
: "rax", "rdx", "r8", "r9");
}
} // namespace error
} // namespace boot
void debug_break()
{
volatile int go = 0;
while (!go);
}

74
src/boot/error.h Normal file
View File

@@ -0,0 +1,74 @@
/// \file error.h
/// Error handling definitions
#pragma once
#include <stddef.h>
#include <uefi/types.h>
namespace boot {
class console;
namespace error {
/// Halt or exit the program with the given error status/message
[[ noreturn ]] void raise(uefi::status status, const wchar_t *message);
/// Interface for error-handling functors
class handler
{
public:
/// Constructor must be called by implementing classes.
handler();
virtual ~handler();
/// Interface for implementations of error handling.
virtual void handle(uefi::status, const wchar_t*) = 0;
private:
friend void raise(uefi::status, const wchar_t *);
handler *m_next;
static handler *s_current;
};
/// Error handler using UEFI boot services. Integrates with `status_line`
/// to print formatted error messages to the screen.
class uefi_handler :
public handler
{
public:
uefi_handler(console &con);
virtual ~uefi_handler() {}
void handle(uefi::status, const wchar_t*) override;
private:
console &m_con;
};
/// Error handler that doesn't rely on UEFI. Sets status into CPU
/// registers and then causes a CPU #DE exception.
class cpu_assert_handler :
public handler
{
public:
cpu_assert_handler();
virtual ~cpu_assert_handler() {}
void handle(uefi::status, const wchar_t*) override;
};
} // namespace error
} // namespace boot
/// Debugging psuedo-breakpoint.
void debug_break();
/// Helper macro to raise an error if an operation fails.
/// \arg s An expression evaluating to a UEFI status
/// \arg m The error message to use on failure
#define try_or_raise(s, m) \
do { \
uefi::status _s = (s); \
if (uefi::is_error(_s)) ::boot::error::raise(_s, (m)); \
} while(0)

114
src/boot/fs.cpp Normal file
View File

@@ -0,0 +1,114 @@
#include <uefi/types.h>
#include <uefi/protos/file.h>
#include <uefi/protos/file_info.h>
#include <uefi/protos/loaded_image.h>
#include <uefi/protos/simple_file_system.h>
#include "fs.h"
#include "console.h"
#include "error.h"
#include "memory.h"
namespace boot {
namespace fs {
file::file(uefi::protos::file *f, uefi::boot_services *bs) :
m_file(f),
m_bs(bs)
{
}
file::file(file &o) :
m_file(o.m_file),
m_bs(o.m_bs)
{
o.m_file = nullptr;
}
file::file(file &&o) :
m_file(o.m_file),
m_bs(o.m_bs)
{
o.m_file = nullptr;
}
file::~file()
{
if (m_file)
m_file->close();
}
file
file::open(const wchar_t *path)
{
uefi::protos::file *fh = nullptr;
try_or_raise(
m_file->open(&fh, path, uefi::file_mode::read, uefi::file_attr::none),
L"Could not open relative path to file");
return file(fh, m_bs);
}
void *
file::load(size_t *out_size, uefi::memory_type mem_type)
{
uint8_t buffer[sizeof(uefi::protos::file_info) + 100];
size_t size = sizeof(buffer);
uefi::guid info_guid = uefi::protos::file_info::guid;
try_or_raise(
m_file->get_info(&info_guid, &size, &buffer),
L"Could not get file info");
uefi::protos::file_info *info =
reinterpret_cast<uefi::protos::file_info*>(&buffer);
size_t pages = memory::bytes_to_pages(info->file_size);
void *data = nullptr;
try_or_raise(
m_bs->allocate_pages(
uefi::allocate_type::any_pages,
mem_type, pages, &data),
L"Could not allocate pages to load file");
size = info->file_size;
try_or_raise(
m_file->read(&size, data),
L"Could not read from file");
*out_size = size;
return data;
}
file
get_boot_volume(uefi::handle image, uefi::boot_services *bs)
{
status_line status(L"Looking up boot volume");
const uefi::guid le_guid = uefi::protos::loaded_image::guid;
uefi::protos::loaded_image *loaded_image = nullptr;
try_or_raise(
bs->handle_protocol(image, &le_guid,
reinterpret_cast<void**>(&loaded_image)),
L"Could not find currently running UEFI loaded image");
const uefi::guid sfs_guid = uefi::protos::simple_file_system::guid;
uefi::protos::simple_file_system *fs;
try_or_raise(
bs->handle_protocol(loaded_image->device_handle, &sfs_guid,
reinterpret_cast<void**>(&fs)),
L"Could not find filesystem protocol for boot volume");
uefi::protos::file *f;
try_or_raise(
fs->open_volume(&f),
L"Could not open the boot volume");
return file(f, bs);
}
} // namespace fs
} // namespace boot

47
src/boot/fs.h Normal file
View File

@@ -0,0 +1,47 @@
/// \file fs.h
/// Definitions for dealing with UEFI's disk access functions
#pragma once
#include <uefi/types.h>
#include <uefi/boot_services.h>
#include <uefi/protos/file.h>
namespace boot {
namespace fs {
/// A file or directory in a filesystem.
class file
{
public:
file(file &&o);
file(file &o);
~file();
/// Open another file or directory, relative to this one.
/// \arg path Relative path to the target file from this one
file open(const wchar_t *path);
/// Load the contents of this file into memory.
/// \arg out_size _out:_ The number of bytes loaded
/// \arg mem_type The UEFI memory type to use for allocation
/// \returns A pointer to the loaded memory. Memory will be
/// page-aligned.
void * load(
size_t *out_size,
uefi::memory_type mem_type = uefi::memory_type::loader_data);
private:
friend file get_boot_volume(uefi::handle, uefi::boot_services*);
file(uefi::protos::file *f, uefi::boot_services *bs);
uefi::protos::file *m_file;
uefi::boot_services *m_bs;
};
/// Get the filesystem this loader was loaded from.
/// \returns A `file` object representing the root directory of the volume
file get_boot_volume(uefi::handle image, uefi::boot_services *bs);
} // namespace fs
} // namespace boot

View File

@@ -1,5 +1,5 @@
#pragma once
#include <efi.h>
#include <efi/efi.h>
int is_guid(EFI_GUID *a, EFI_GUID *b);

View File

@@ -3,5 +3,9 @@ GUID(0x8868e871,0xe4f1,0x11d3,0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81, guid_acpi
GUID(0x09576e92,0x6d3f,0x11d2,0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b, guid_file_info);
GUID(0x9042a9de,0x23dc,0x4a38,0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a, guid_gfx_out);
GUID(0x964e5b22,0x6459,0x11d2,0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b, guid_simple_filesystem);
GUID(0x09576e91,0x6d3f,0x11d2,0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b, guid_device_path);
GUID(0x8b843e20,0x8132,0x4852,0x90,0xcc,0x55,0x1a,0x4e,0x4a,0x7f,0x1c, guid_device_path_to_text);
GUID(0x10d0669c,0x9ec6,0x4268,0xbc,0x48,0xff,0x74,0x75,0x21,0xfe,0x07, guid_jsix_vendor);
// vim: ft=c

54
src/boot/hardware.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include "hardware.h"
#include "console.h"
#include "error.h"
namespace boot {
namespace hw {
void *
find_acpi_table(uefi::system_table *st)
{
status_line status(L"Searching for ACPI table");
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
uintptr_t acpi1_table = 0;
for (size_t i = 0; i < st->number_of_table_entries; ++i) {
uefi::configuration_table *table = &st->configuration_table[i];
// If we find an ACPI 2.0 table, return it immediately
if (table->vendor_guid == uefi::vendor_guids::acpi2)
return table->vendor_table;
if (table->vendor_guid == uefi::vendor_guids::acpi1) {
// Mark a v1 table with the LSB high
acpi1_table = reinterpret_cast<uintptr_t>(table->vendor_table);
acpi1_table |= 1;
}
}
if (!acpi1_table) {
error::raise(uefi::status::not_found, L"Could not find ACPI table");
} else if (acpi1_table & 1) {
status_line::warn(L"Only found ACPI 1.0 table");
}
return reinterpret_cast<void*>(acpi1_table);
}
void
setup_cr4()
{
uint64_t cr4 = 0;
asm volatile ( "mov %%cr4, %0" : "=r" (cr4) );
cr4 |=
0x000080 | // Enable global pages
0x000200 | // Enable FXSAVE/FXRSTOR
0x010000 | // Enable FSGSBASE
0x020000 | // Enable PCIDs
0;
asm volatile ( "mov %0, %%cr4" :: "r" (cr4) );
}
} // namespace hw
} // namespace boot

20
src/boot/hardware.h Normal file
View File

@@ -0,0 +1,20 @@
/// \file hardware.h
/// Functions and definitions for detecting and dealing with hardware
#pragma once
#include <uefi/tables.h>
namespace boot {
namespace hw {
/// Find the ACPI table in the system configuration tables
/// and return a pointer to it. If only an ACPI 1.0 table is
/// available, the returned pointer will have its least
/// significant bit set to 1.
void * find_acpi_table(uefi::system_table *st);
/// Enable CPU options in CR4 for the kernel starting state.
void setup_cr4();
} // namespace hw
} // namespace boot

View File

@@ -1,160 +0,0 @@
#include "guids.h"
#include "loader.h"
#include "memory.h"
#include "utility.h"
#define PAGE_SIZE 0x1000
static CHAR16 kernel_name[] = KERNEL_FILENAME;
static CHAR16 font_name[] = KERNEL_FONT;
EFI_STATUS
loader_alloc_pages(
EFI_BOOT_SERVICES *bootsvc,
EFI_MEMORY_TYPE mem_type,
size_t *length,
void **pages,
void **next)
{
EFI_STATUS status;
size_t page_count = ((*length - 1) / PAGE_SIZE) + 1;
EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)*pages;
status = bootsvc->AllocatePages(AllocateAddress, mem_type, page_count, &addr);
if (status == EFI_NOT_FOUND || status == EFI_OUT_OF_RESOURCES) {
// couldn't get the address we wanted, try loading the kernel anywhere
status =
bootsvc->AllocatePages(AllocateAnyPages, mem_type, page_count, &addr);
}
CHECK_EFI_STATUS_OR_RETURN(status,
L"Allocating %d kernel pages type %x",
page_count, mem_type);
*length = page_count * PAGE_SIZE;
*pages = (void *)addr;
*next = (void*)(addr + *length);
return EFI_SUCCESS;
}
EFI_STATUS
loader_load_file(
EFI_BOOT_SERVICES *bootsvc,
EFI_FILE_PROTOCOL *root,
const CHAR16 *filename,
EFI_MEMORY_TYPE mem_type,
void **data,
size_t *length,
void **next)
{
EFI_STATUS status;
EFI_FILE_PROTOCOL *file = NULL;
status = root->Open(root, &file, (CHAR16 *)filename, EFI_FILE_MODE_READ,
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
if (status == EFI_NOT_FOUND)
return status;
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", filename);
char info[sizeof(EFI_FILE_INFO) + 100];
size_t info_length = sizeof(info);
status = file->GetInfo(file, &guid_file_info, &info_length, info);
CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info");
*length = ((EFI_FILE_INFO *)info)->FileSize;
status = loader_alloc_pages(bootsvc, mem_type, length, data, next);
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
status = file->Read(file, length, *data);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
status = file->Close(file);
CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle");
return EFI_SUCCESS;
}
EFI_STATUS
loader_load_kernel(
EFI_BOOT_SERVICES *bootsvc,
struct loader_data *data)
{
if (data == NULL)
CHECK_EFI_STATUS_OR_RETURN(EFI_INVALID_PARAMETER, L"NULL loader_data");
EFI_STATUS status;
EFI_HANDLE *handles = NULL;
size_t handleCount = 0;
status = bootsvc->LocateHandleBuffer(ByProtocol, &guid_simple_filesystem, NULL, &handleCount, &handles);
CHECK_EFI_STATUS_OR_RETURN(status, L"LocateHandleBuffer");
for (unsigned i = 0; i < handleCount; ++i) {
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fileSystem = NULL;
status = bootsvc->HandleProtocol(handles[i], &guid_simple_filesystem, (void **)&fileSystem);
CHECK_EFI_STATUS_OR_RETURN(status, L"HandleProtocol");
EFI_FILE_PROTOCOL *root = NULL;
status = fileSystem->OpenVolume(fileSystem, &root);
CHECK_EFI_STATUS_OR_RETURN(status, L"OpenVolume");
void *next = NULL;
data->kernel = (void *)KERNEL_PHYS_ADDRESS;
status = loader_load_file(
bootsvc,
root,
kernel_name,
KERNEL_MEMTYPE,
&data->kernel,
&data->kernel_length,
&next);
if (status == EFI_NOT_FOUND)
continue;
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name);
data->font = next;
status = loader_load_file(
bootsvc,
root,
font_name,
KERNEL_FONT_MEMTYPE,
&data->font,
&data->font_length,
&next);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name);
data->data = next;
data->data_length += PAGE_SIZE; // extra page for map growth
status = loader_alloc_pages(
bootsvc,
KERNEL_DATA_MEMTYPE,
&data->data_length,
&data->data,
&next);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data");
data->log = next;
data->log_length = KERNEL_LOG_PAGES * PAGE_SIZE;
status = loader_alloc_pages(
bootsvc,
KERNEL_LOG_MEMTYPE,
&data->log_length,
&data->log,
&next);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel log");
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}

74
src/boot/loader.cpp Normal file
View File

@@ -0,0 +1,74 @@
#include <uefi/boot_services.h>
#include <uefi/types.h>
#include "loader.h"
#include "console.h"
#include "elf.h"
#include "error.h"
#include "memory.h"
#include "paging.h"
namespace boot {
namespace loader {
static bool
is_elfheader_valid(const elf::header *header)
{
return
header->magic[0] == 0x7f &&
header->magic[1] == 'E' &&
header->magic[2] == 'L' &&
header->magic[3] == 'F' &&
header->word_size == elf::word_size &&
header->endianness == elf::endianness &&
header->os_abi == elf::os_abi &&
header->machine == elf::machine &&
header->header_version == elf::version;
}
kernel::entrypoint
load(
const void *data, size_t size,
kernel::args::header *args,
uefi::boot_services *bs)
{
status_line status(L"Loading kernel ELF binary");
const elf::header *header = reinterpret_cast<const elf::header*>(data);
if (size < sizeof(elf::header) || !is_elfheader_valid(header))
error::raise(uefi::status::load_error, L"Kernel ELF not valid");
paging::page_table *pml4 = reinterpret_cast<paging::page_table*>(args->pml4);
for (int i = 0; i < header->ph_num; ++i) {
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
const elf::program_header *pheader =
offset_ptr<elf::program_header>(data, offset);
if (pheader->type != elf::PT_LOAD)
continue;
size_t num_pages = memory::bytes_to_pages(pheader->mem_size);
void *pages = nullptr;
try_or_raise(
bs->allocate_pages(uefi::allocate_type::any_pages,
memory::kernel_type, num_pages, &pages),
L"Failed allocating space for kernel code");
void *data_start = offset_ptr<void>(data, pheader->offset);
bs->copy_mem(pages, data_start, pheader->mem_size);
console::print(L" section %d phys: 0x%lx\r\n", i, pages);
console::print(L" section %d virt: 0x%lx\r\n", i, pheader->vaddr);
// TODO: set appropriate RWX permissions
paging::map_pages(pml4, args, reinterpret_cast<uintptr_t>(pages), pheader->vaddr, pheader->mem_size);
}
console::print(L" entrypoint: 0x%lx\r\n", header->entrypoint);
return reinterpret_cast<kernel::entrypoint>(header->entrypoint);
}
} // namespace loader
} // namespace boot

View File

@@ -1,65 +1,23 @@
/// \file loader.h
/// Definitions for loading the kernel into memory
#pragma once
#include <efi.h>
#include <stddef.h>
#define PAGE_SIZE 0x1000
#include <uefi/boot_services.h>
#ifndef KERNEL_PHYS_ADDRESS
#define KERNEL_PHYS_ADDRESS 0x100000
#endif
#include "kernel_args.h"
#ifndef KERNEL_VIRT_ADDRESS
#define KERNEL_VIRT_ADDRESS 0xFFFF800000000000
#endif
namespace boot {
namespace loader {
#ifndef VIRTUAL_OFFSET
#define VIRTUAL_OFFSET 0xf00000000
#endif
/// Parse and load an ELF file in memory into a loaded image.
/// \arg data The start of the ELF file in memory
/// \arg size The size of the ELF file in memory
/// \arg args The kernel args, used for modifying page tables
/// \returns A descriptor defining the loaded image
kernel::entrypoint load(
const void *data, size_t size,
kernel::args::header *args,
uefi::boot_services *bs);
#ifndef KERNEL_MEMTYPE
#define KERNEL_MEMTYPE 0x80000000
#endif
#ifndef KERNEL_FONT_MEMTYPE
#define KERNEL_FONT_MEMTYPE 0x80000001
#endif
#ifndef KERNEL_DATA_MEMTYPE
#define KERNEL_DATA_MEMTYPE 0x80000002
#endif
#ifndef KERNEL_LOG_MEMTYPE
#define KERNEL_LOG_MEMTYPE 0x80000003
#endif
#ifndef KERNEL_LOG_PAGES
#define KERNEL_LOG_PAGES 4
#endif
#ifndef KERNEL_PT_MEMTYPE
#define KERNEL_PT_MEMTYPE 0x80000004
#endif
#ifndef KERNEL_FILENAME
#define KERNEL_FILENAME L"kernel.bin"
#endif
#ifndef KERNEL_FONT
#define KERNEL_FONT L"screenfont.psf"
#endif
struct loader_data {
void *kernel;
size_t kernel_length;
void *font;
size_t font_length;
void *data;
size_t data_length;
void *log;
size_t log_length;
};
EFI_STATUS loader_load_kernel(EFI_BOOT_SERVICES *bootsvc, struct loader_data *data);
} // namespace loader
} // namespace boot

View File

@@ -1,180 +0,0 @@
#include <efi.h>
#include <efilib.h>
#include <stdalign.h>
#include <stddef.h>
#include "console.h"
#include "guids.h"
#include "kernel_data.h"
#include "loader.h"
#include "memory.h"
#include "utility.h"
#ifndef GIT_VERSION_WIDE
#define GIT_VERSION_WIDE L"no version"
#endif
#define KERNEL_HEADER_MAGIC 0x600db007
#define KERNEL_HEADER_VERSION 1
#pragma pack(push, 1)
struct kernel_header {
uint32_t magic;
uint16_t version;
uint16_t length;
uint8_t major;
uint8_t minor;
uint16_t patch;
uint32_t gitsha;
void *entrypoint;
};
#pragma pack(pop)
EFI_STATUS
efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
{
EFI_STATUS status;
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
EFI_RUNTIME_SERVICES *runsvc = system_table->RuntimeServices;
// When checking console initialization, use CHECK_EFI_STATUS_OR_RETURN
// because we can't be sure if the console was fully set up
status = con_initialize(system_table, GIT_VERSION_WIDE);
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
// From here on out, we can use CHECK_EFI_STATUS_OR_FAIL instead
memory_init_pointer_fixup(bootsvc, runsvc);
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
//
void *acpi_table = NULL;
for (size_t i=0; i<system_table->NumberOfTableEntries; ++i) {
EFI_CONFIGURATION_TABLE *efi_table = &system_table->ConfigurationTable[i];
if (is_guid(&efi_table->VendorGuid, &guid_acpi2)) {
acpi_table = efi_table->VendorTable;
break;
} else if (is_guid(&efi_table->VendorGuid, &guid_acpi1)) {
// Mark a v1 table with the LSB high
acpi_table = (void *)((intptr_t)efi_table->VendorTable | 0x1);
}
}
// Compute necessary number of data pages
//
size_t data_length = 0;
status = memory_get_map_length(bootsvc, &data_length);
CHECK_EFI_STATUS_OR_FAIL(status);
size_t header_size = sizeof(struct popcorn_data);
const size_t header_align = alignof(struct popcorn_data);
if (header_size % header_align)
header_size += header_align - (header_size % header_align);
data_length += header_size;
// Load the kernel image from disk and check it
//
void *kernel_image = NULL, *kernel_data = NULL;
uint64_t kernel_length = 0;
con_printf(L"Loading kernel into memory...\r\n");
struct loader_data load;
load.data_length = data_length;
status = loader_load_kernel(bootsvc, &load);
CHECK_EFI_STATUS_OR_FAIL(status);
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font);
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
con_printf(L" %u log bytes at 0x%x\r\n", load.log_length, load.log);
struct kernel_header *version = (struct kernel_header *)load.kernel;
if (version->magic != KERNEL_HEADER_MAGIC) {
con_printf(L" bad magic %x\r\n", version->magic);
CHECK_EFI_STATUS_OR_FAIL(EFI_CRC_ERROR);
}
con_printf(L" Kernel version %d.%d.%d %x%s\r\n",
version->major, version->minor, version->patch, version->gitsha & 0x0fffffff,
version->gitsha & 0xf0000000 ? "*" : "");
con_printf(L" Entrypoint 0x%x\r\n", version->entrypoint);
void (*kernel_main)() = version->entrypoint;
memory_mark_pointer_fixup((void **)&kernel_main);
// Set up the kernel data pages to pass to the kernel
//
struct popcorn_data *data_header = (struct popcorn_data *)load.data;
memory_mark_pointer_fixup((void **)&data_header);
data_header->magic = DATA_HEADER_MAGIC;
data_header->version = DATA_HEADER_VERSION;
data_header->length = sizeof(struct popcorn_data);
data_header->flags = 0;
data_header->font = load.font;
data_header->font_length = load.font_length;
memory_mark_pointer_fixup((void **)&data_header->font);
data_header->data = load.data;
data_header->data_length = load.data_length;
memory_mark_pointer_fixup((void **)&data_header->data);
data_header->log = load.log;
data_header->log_length = load.log_length;
memory_mark_pointer_fixup((void **)&data_header->log);
data_header->memory_map = (EFI_MEMORY_DESCRIPTOR *)(data_header + 1);
memory_mark_pointer_fixup((void **)&data_header->memory_map);
data_header->runtime = runsvc;
memory_mark_pointer_fixup((void **)&data_header->runtime);
data_header->acpi_table = acpi_table;
memory_mark_pointer_fixup((void **)&data_header->acpi_table);
data_header->_reserved0 = 0;
data_header->_reserved1 = 0;
// Figure out the framebuffer (if any) and add that to the data header
//
status = con_get_framebuffer(
bootsvc,
&data_header->frame_buffer,
&data_header->frame_buffer_size,
&data_header->hres,
&data_header->vres,
&data_header->rmask,
&data_header->gmask,
&data_header->bmask);
CHECK_EFI_STATUS_OR_FAIL(status);
memory_mark_pointer_fixup((void **)&data_header->frame_buffer);
// Save the memory map and tell the firmware we're taking control.
//
struct memory_map map;
map.entries = data_header->memory_map;
map.length = (load.data_length - header_size);
status = memory_get_map(bootsvc, &map);
CHECK_EFI_STATUS_OR_FAIL(status);
data_header->memory_map_length = map.length;
data_header->memory_map_desc_size = map.size;
// bootsvc->Stall(5000000);
status = bootsvc->ExitBootServices(image_handle, map.key);
CHECK_EFI_STATUS_OR_ASSERT(status, 0);
memory_virtualize(runsvc, &map);
// Hand control to the kernel
//
kernel_main(data_header);
return EFI_LOAD_ERROR;
}

165
src/boot/main.cpp Normal file
View File

@@ -0,0 +1,165 @@
#include <uefi/types.h>
#include <uefi/guid.h>
#include <uefi/tables.h>
#include <uefi/protos/simple_text_output.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#include "console.h"
#include "error.h"
#include "fs.h"
#include "hardware.h"
#include "loader.h"
#include "memory.h"
#include "paging.h"
#include "kernel_args.h"
namespace kernel {
#include "kernel_memory.h"
}
namespace boot {
constexpr int max_modules = 10; // Max modules to allocate room for
/// Change a pointer to point to the higher-half linear-offset version
/// of the address it points to.
template <typename T>
void change_pointer(T *&pointer)
{
pointer = offset_ptr<T>(pointer, kernel::memory::page_offset);
}
/// Allocate space for kernel args. Allocates enough space from pool
/// memory for the args header and `max_modules` module headers.
kernel::args::header *
allocate_args_structure(
uefi::boot_services *bs,
size_t max_modules)
{
status_line status(L"Setting up kernel args memory");
kernel::args::header *args = nullptr;
size_t args_size =
sizeof(kernel::args::header) + // The header itself
max_modules * sizeof(kernel::args::module); // The module structures
try_or_raise(
bs->allocate_pool(memory::args_type, args_size,
reinterpret_cast<void**>(&args)),
L"Could not allocate argument memory");
bs->set_mem(args, args_size, 0);
args->modules =
reinterpret_cast<kernel::args::module*>(args + 1);
args->num_modules = 0;
return args;
}
/// Load a file from disk into memory. Also adds an entry to the kernel
/// args module headers pointing at the loaded data.
/// \arg disk The opened UEFI filesystem to load from
/// \arg args The kernel args header to update with module information
/// \arg name Name of the module (informational only)
/// \arg path Path on `disk` of the file to load
/// \arg type Type specifier of this module (eg, initrd or kernel)
kernel::args::module *
load_module(
fs::file &disk,
kernel::args::header *args,
const wchar_t *name,
const wchar_t *path,
kernel::args::mod_type type)
{
status_line status(L"Loading module", name);
fs::file file = disk.open(path);
kernel::args::module &module = args->modules[args->num_modules++];
module.type = type;
module.location = file.load(&module.size, memory::module_type);
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, module.size);
return &module;
}
/// The main procedure for the portion of the loader that runs while
/// UEFI is still in control of the machine. (ie, while the loader still
/// has access to boot services.
kernel::args::header *
bootloader_main_uefi(
uefi::handle image,
uefi::system_table *st,
console &con,
kernel::entrypoint *kentry)
{
error::uefi_handler handler(con);
status_line status(L"Performing UEFI pre-boot");
uefi::boot_services *bs = st->boot_services;
uefi::runtime_services *rs = st->runtime_services;
memory::init_pointer_fixup(bs, rs);
kernel::args::header *args =
allocate_args_structure(bs, max_modules);
args->magic = kernel::args::magic;
args->version = kernel::args::version;
args->runtime_services = rs;
args->acpi_table = hw::find_acpi_table(st);
memory::mark_pointer_fixup(&args->runtime_services);
fs::file disk = fs::get_boot_volume(image, bs);
load_module(disk, args, L"initrd", L"initrd.img", kernel::args::mod_type::initrd);
kernel::args::module *kernel =
load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::mod_type::kernel);
paging::allocate_tables(args, bs);
*kentry = loader::load(kernel->location, kernel->size, args, bs);
for (unsigned i = 0; i < args->num_modules; ++i) {
kernel::args::module &mod = args->modules[i];
change_pointer(mod.location);
}
return args;
}
} // namespace boot
/// The UEFI entrypoint for the loader.
extern "C" uefi::status
efi_main(uefi::handle image_handle, uefi::system_table *st)
{
using namespace boot;
error::cpu_assert_handler handler;
console con(st->boot_services, st->con_out);
kernel::entrypoint kentry = nullptr;
kernel::args::header *args =
bootloader_main_uefi(image_handle, st, con, &kentry);
memory::efi_mem_map map =
memory::build_kernel_mem_map(args, st->boot_services);
try_or_raise(
st->boot_services->exit_boot_services(image_handle, map.key),
L"Failed to exit boot services");
memory::virtualize(args->pml4, map, st->runtime_services);
change_pointer(args->pml4);
hw::setup_cr4();
kentry(args);
debug_break();
return uefi::status::unsupported;
}

View File

@@ -1,205 +0,0 @@
#include <efi.h>
#include <efilib.h>
#include <stddef.h>
#include "loader.h"
#include "memory.h"
#include "utility.h"
#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b))
size_t fixup_pointer_index = 0;
void **fixup_pointers[64];
uint64_t *new_pml4 = 0;
const CHAR16 *memory_type_names[] = {
L"EfiReservedMemoryType",
L"EfiLoaderCode",
L"EfiLoaderData",
L"EfiBootServicesCode",
L"EfiBootServicesData",
L"EfiRuntimeServicesCode",
L"EfiRuntimeServicesData",
L"EfiConventionalMemory",
L"EfiUnusableMemory",
L"EfiACPIReclaimMemory",
L"EfiACPIMemoryNVS",
L"EfiMemoryMappedIO",
L"EfiMemoryMappedIOPortSpace",
L"EfiPalCode",
L"EfiPersistentMemory",
};
static const CHAR16 *
memory_type_name(UINT32 value)
{
if (value >= (sizeof(memory_type_names) / sizeof(CHAR16 *))) {
if (value == KERNEL_DATA_MEMTYPE) return L"Kernel Data";
else if (value == KERNEL_MEMTYPE) return L"Kernel Image";
else return L"Bad Type Value";
}
return memory_type_names[value];
}
void EFIAPI
memory_update_marked_addresses(EFI_EVENT UNUSED *event, void *context)
{
EFI_RUNTIME_SERVICES *runsvc = (EFI_RUNTIME_SERVICES*)context;
for (size_t i = 0; i < fixup_pointer_index; ++i) {
if (fixup_pointers[i])
runsvc->ConvertPointer(0, fixup_pointers[i]);
}
}
EFI_STATUS
memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc)
{
EFI_STATUS status;
EFI_EVENT event;
status = bootsvc->CreateEvent(
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
TPL_CALLBACK,
(EFI_EVENT_NOTIFY)&memory_update_marked_addresses,
runsvc,
&event);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to initialize pointer update event.");
// Reserve a page for our replacement PML4
EFI_PHYSICAL_ADDRESS addr = 0;
status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 4, &addr);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate PML4 page.");
new_pml4 = (uint64_t *)addr;
}
void
memory_mark_pointer_fixup(void **p)
{
if (fixup_pointer_index == 0) {
const size_t count = sizeof(fixup_pointers) / sizeof(void*);
for (size_t i = 0; i < count; ++i) fixup_pointers[i] = 0;
}
fixup_pointers[fixup_pointer_index++] = p;
}
void
copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len)
{
uint8_t *srcb = (uint8_t *)src;
uint8_t *dstb = (uint8_t *)dst;
uint8_t *endb = srcb + len;
while (srcb < endb)
*dstb++ = *srcb++;
}
EFI_STATUS
memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size)
{
if (size == NULL)
return EFI_INVALID_PARAMETER;
EFI_STATUS status;
size_t key, desc_size;
uint32_t desc_version;
*size = 0;
status = bootsvc->GetMemoryMap(size, 0, &key, &desc_size, &desc_version);
if (status != EFI_BUFFER_TOO_SMALL) {
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to get memory map size");
}
return EFI_SUCCESS;
}
EFI_STATUS
memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map)
{
EFI_STATUS status;
if (map == NULL)
return EFI_INVALID_PARAMETER;
size_t needs_size = 0;
status = memory_get_map_length(bootsvc, &needs_size);
if (EFI_ERROR(status)) return status;
if (map->length < needs_size)
return EFI_BUFFER_TOO_SMALL;
status = bootsvc->GetMemoryMap(&map->length, map->entries, &map->key, &map->size, &map->version);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load memory map");
return EFI_SUCCESS;
}
EFI_STATUS
memory_dump_map(struct memory_map *map)
{
if (map == NULL)
return EFI_INVALID_PARAMETER;
const size_t count = map->length / map->size;
con_printf(L"Memory map:\n");
con_printf(L"\t Descriptor Count: %d (%d bytes)\n", count, map->length);
con_printf(L"\t Descriptor Size: %d bytes\n", map->size);
con_printf(L"\t Type offset: %d\n\n", offsetof(EFI_MEMORY_DESCRIPTOR, Type));
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
EFI_MEMORY_DESCRIPTOR *d = map->entries;
while (d < end) {
int runtime = (d->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME;
con_printf(L"%s%s ", memory_type_name(d->Type), runtime ? L"*" : L" ");
con_printf(L"%lx ", d->PhysicalStart);
con_printf(L"%lx ", d->VirtualStart);
con_printf(L"[%4d]\n", d->NumberOfPages);
d = INCREMENT_DESC(d, map->size);
}
return EFI_SUCCESS;
}
void
memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
{
memory_mark_pointer_fixup((void **)&runsvc);
memory_mark_pointer_fixup((void **)&map);
// Get the pointer to the start of PML4
uint64_t* cr3 = 0;
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
// PML4 is indexed with bits 39:47 of the virtual address
uint64_t offset = (KERNEL_VIRT_ADDRESS >> 39) & 0x1ff;
// Double map the lower half pages that are present into the higher half
for (unsigned i = 0; i < offset; ++i) {
if (cr3[i] & 0x1)
new_pml4[i] = new_pml4[offset+i] = cr3[i];
else
new_pml4[i] = new_pml4[offset+i] = 0;
}
// Write our new PML4 pointer back to CR3
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (new_pml4) );
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
EFI_MEMORY_DESCRIPTOR *d = map->entries;
while (d < end) {
switch (d->Type) {
case KERNEL_MEMTYPE:
case KERNEL_FONT_MEMTYPE:
case KERNEL_DATA_MEMTYPE:
case KERNEL_LOG_MEMTYPE:
d->Attribute |= EFI_MEMORY_RUNTIME;
default:
if (d->Attribute & EFI_MEMORY_RUNTIME) {
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
}
}
d = INCREMENT_DESC(d, map->size);
}
runsvc->SetVirtualAddressMap(map->length, map->size, map->version, map->entries);
}

275
src/boot/memory.cpp Normal file
View File

@@ -0,0 +1,275 @@
#include <stddef.h>
#include <uefi/types.h>
#include "kernel_memory.h"
#include "console.h"
#include "error.h"
#include "memory.h"
#include "paging.h"
namespace boot {
namespace memory {
using mem_entry = kernel::args::mem_entry;
using mem_type = kernel::args::mem_type;
size_t fixup_pointer_index = 0;
void **fixup_pointers[64];
static const wchar_t *memory_type_names[] = {
L"reserved memory type",
L"loader code",
L"loader data",
L"boot services code",
L"boot services data",
L"runtime services code",
L"runtime services data",
L"conventional memory",
L"unusable memory",
L"acpi reclaim memory",
L"acpi memory nvs",
L"memory mapped io",
L"memory mapped io port space",
L"pal code",
L"persistent memory"
};
static const wchar_t *
memory_type_name(uefi::memory_type t)
{
if (t < uefi::memory_type::max_memory_type) {
return memory_type_names[static_cast<uint32_t>(t)];
}
switch(t) {
case args_type: return L"jsix kernel args";
case module_type: return L"jsix bootloader module";
case kernel_type: return L"jsix kernel code";
case table_type: return L"jsix page tables";
default: return L"Bad Type Value";
}
}
void
update_marked_addresses(uefi::event, void *context)
{
uefi::runtime_services *rs =
reinterpret_cast<uefi::runtime_services*>(context);
for (size_t i = 0; i < fixup_pointer_index; ++i) {
if (fixup_pointers[i])
rs->convert_pointer(0, fixup_pointers[i]);
}
}
void
init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs)
{
status_line status(L"Initializing pointer virtualization event");
uefi::event event;
bs->set_mem(&fixup_pointers, sizeof(fixup_pointers), 0);
fixup_pointer_index = 0;
try_or_raise(
bs->create_event(
uefi::evt::signal_virtual_address_change,
uefi::tpl::callback,
(uefi::event_notify)&update_marked_addresses,
rs,
&event),
L"Error creating memory virtualization event");
}
void
mark_pointer_fixup(void **p)
{
fixup_pointers[fixup_pointer_index++] = p;
}
bool
can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next)
{
return
prev.type == type &&
prev.start + (page_size * prev.pages) == next->physical_start &&
prev.attr == (next->attribute & 0xffffffff);
}
void
get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
{
status_line(L"Getting UEFI memory map");
uefi::status status = bs->get_memory_map(
&map->length, nullptr, &map->key, &map->size, &map->version);
if (status != uefi::status::buffer_too_small)
error::raise(status, L"Error getting memory map size");
if (allocate) {
map->length += 10*map->size;
try_or_raise(
bs->allocate_pool(
uefi::memory_type::loader_data, map->length,
reinterpret_cast<void**>(&map->entries)),
L"Allocating space for memory map");
try_or_raise(
bs->get_memory_map(&map->length, map->entries, &map->key, &map->size, &map->version),
L"Getting UEFI memory map");
}
}
efi_mem_map
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
{
status_line(L"Creating kernel memory map");
efi_mem_map efi_map;
get_uefi_mappings(&efi_map, false, bs);
size_t map_size = efi_map.num_entries() * sizeof(mem_entry);
kernel::args::mem_entry *kernel_map = nullptr;
try_or_raise(
bs->allocate_pages(
uefi::allocate_type::any_pages,
module_type,
bytes_to_pages(map_size),
reinterpret_cast<void**>(&kernel_map)),
L"Error allocating kernel memory map module space");
bs->set_mem(kernel_map, map_size, 0);
get_uefi_mappings(&efi_map, true, bs);
size_t i = 0;
bool first = true;
for (auto desc : efi_map) {
/*
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages);
*/
mem_type type;
switch (desc->type) {
case uefi::memory_type::reserved:
case uefi::memory_type::unusable_memory:
case uefi::memory_type::acpi_memory_nvs:
case uefi::memory_type::pal_code:
continue;
case uefi::memory_type::loader_code:
case uefi::memory_type::loader_data:
case uefi::memory_type::boot_services_code:
case uefi::memory_type::boot_services_data:
case uefi::memory_type::conventional_memory:
type = mem_type::free;
break;
case uefi::memory_type::runtime_services_code:
case uefi::memory_type::runtime_services_data:
type = mem_type::uefi_runtime;
break;
case uefi::memory_type::acpi_reclaim_memory:
type = mem_type::acpi;
break;
case uefi::memory_type::memory_mapped_io:
case uefi::memory_type::memory_mapped_io_port_space:
type = mem_type::mmio;
break;
case uefi::memory_type::persistent_memory:
type = mem_type::persistent;
break;
case args_type:
type = mem_type::args;
break;
case module_type:
type = mem_type::module;
break;
case kernel_type:
type = mem_type::kernel;
break;
case table_type:
type = mem_type::table;
break;
default:
error::raise(
uefi::status::invalid_parameter,
L"Got an unexpected memory type from UEFI memory map");
}
// TODO: validate uefi's map is sorted
if (first) {
first = false;
kernel_map[i].start = desc->physical_start;
kernel_map[i].pages = desc->number_of_pages;
kernel_map[i].type = type;
kernel_map[i].attr = (desc->attribute & 0xffffffff);
continue;
}
mem_entry &prev = kernel_map[i];
if (can_merge(prev, type, desc)) {
prev.pages += desc->number_of_pages;
} else {
mem_entry &next = kernel_map[++i];
next.start = desc->physical_start;
next.pages = desc->number_of_pages;
next.type = type;
next.attr = (desc->attribute & 0xffffffff);
}
}
// Give just the actually-set entries in the header
args->mem_map = kernel_map;
args->num_map_entries = i;
// But pass the entire allocated area in a module as well
kernel::args::module &module = args->modules[args->num_modules++];
module.location = reinterpret_cast<void*>(kernel_map);
module.size = map_size;
module.type = kernel::args::mod_type::memory_map;
/*
for (size_t i = 0; i<map.num_entries(); ++i) {
mem_entry &ent = kernel_map[i];
console::print(L" Range %lx (%x) %d [%lu]\r\n",
ent.start, ent.attr, ent.type, ent.pages);
}
*/
return efi_map;
}
void
virtualize(void *pml4, efi_mem_map &map, uefi::runtime_services *rs)
{
paging::add_current_mappings(reinterpret_cast<paging::page_table*>(pml4));
for (auto desc : map)
desc->virtual_start = desc->physical_start + ::memory::page_offset;
// Write our new PML4 pointer to CR3
asm volatile ( "mov %0, %%cr3" :: "r" (pml4) );
__sync_synchronize();
try_or_raise(
rs->set_virtual_address_map(
map.length, map.size, map.version, map.entries),
L"Error setting virtual address map");
}
} // namespace boot
} // namespace memory

View File

@@ -1,19 +1,97 @@
/// \file memory.h
/// Memory-related constants and functions.
#pragma once
#include <efi.h>
#include <uefi/boot_services.h>
#include <uefi/runtime_services.h>
#include <stdint.h>
#include "kernel_args.h"
#include "pointer_manipulation.h"
struct memory_map {
size_t length;
size_t size;
size_t key;
uint32_t version;
EFI_MEMORY_DESCRIPTOR *entries;
namespace boot {
namespace memory {
/// UEFI specifies that pages are always 4 KiB.
constexpr size_t page_size = 0x1000;
/// Get the number of pages needed to hold `bytes` bytes
inline constexpr size_t bytes_to_pages(size_t bytes) {
return ((bytes - 1) / page_size) + 1;
}
/// \defgroup memory_types
/// Custom UEFI memory type values used for data being passed to the kernel
/// @{
/// Memory containing the kernel args structure
constexpr uefi::memory_type args_type =
static_cast<uefi::memory_type>(0x80000000);
/// Memory containing any loaded modules to be passed to the kernel
constexpr uefi::memory_type module_type =
static_cast<uefi::memory_type>(0x80000001);
/// Memory containing loaded kernel code and data sections
constexpr uefi::memory_type kernel_type =
static_cast<uefi::memory_type>(0x80000002);
/// Memory containing page tables set up by the loader
constexpr uefi::memory_type table_type =
static_cast<uefi::memory_type>(0x80000003);
/// @}
/// \defgroup pointer_fixup
/// Memory virtualization pointer fixup functions. Handles changing affected pointers
/// when calling UEFI's `set_virtual_address_map` function to change the location of
/// runtime services in virtual memory.
/// @{
/// Set up the pointer fixup UEFI events. This registers the necessary callbacks for
/// runtime services to call when `set_virtual_address_map` is called.
void init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs);
/// Mark a given pointer as needing to be updated when doing pointer fixup.
void mark_pointer_fixup(void **p);
/// @}
/// Struct that represents UEFI's memory map. Contains a pointer to the map data
/// as well as the data on how to read it.
struct efi_mem_map
{
using desc = uefi::memory_descriptor;
using iterator = offset_iterator<desc>;
size_t length; ///< Total length of the map data
size_t size; ///< Size of an entry in the array
size_t key; ///< Key for detecting changes
uint32_t version; ///< Version of the `memory_descriptor` struct
desc *entries; ///< The array of UEFI descriptors
efi_mem_map() : length(0), size(0), key(0), version(0), entries(nullptr) {}
/// Get the count of entries in the array
inline size_t num_entries() const { return length / size; }
/// Return an iterator to the beginning of the array
iterator begin() { return iterator(entries, size); }
/// Return an iterator to the end of the array
iterator end() { return offset_ptr<desc>(entries, length); }
};
EFI_STATUS memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc);
void memory_mark_pointer_fixup(void **p);
/// Add the kernel's memory map as a module to the kernel args.
/// \returns The uefi memory map used to build the kernel map
efi_mem_map build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs);
EFI_STATUS memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
EFI_STATUS memory_dump_map(struct memory_map *map);
/// Activate the given memory mappings. Sets the given page tables live as well
/// as informs UEFI runtime services of the new mappings.
/// \arg pml4 The root page table for the new mappings
/// \arg map The UEFI memory map, used to update runtime services
void virtualize(
void *pml4,
efi_mem_map &map,
uefi::runtime_services *rs);
void memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map);
} // namespace boot
} // namespace memory

234
src/boot/paging.cpp Normal file
View File

@@ -0,0 +1,234 @@
#include "kernel_memory.h"
#include "console.h"
#include "error.h"
#include "loader.h"
#include "memory.h"
#include "paging.h"
#include "pointer_manipulation.h"
namespace boot {
namespace paging {
using memory::page_size;
// Flags: 0 0 0 1 0 0 0 0 0 0 1 1 = 0x0103
// IGN | | | | | | | | +- Present
// | | | | | | | +--- Writeable
// | | | | | | +----- Usermode access (supervisor only)
// | | | | | +------- PWT (determining memory type for page)
// | | | | +---------- PCD (determining memory type for page)
// | | | +------------ Accessed flag (not accessed yet)
// | | +-------------- Dirty (not dirtied yet)
// | +---------------- PAT (determining memory type for page)
// +------------------- Global
/// Page table entry flags for entries pointing at a page
constexpr uint16_t page_flags = 0x103;
// Flags: 0 0 0 0 1 1 0 0 0 0 0 1 1 = 0x0183
// | IGN | | | | | | | | +- Present
// | | | | | | | | +--- Writeable
// | | | | | | | +----- Supervisor only
// | | | | | | +------- PWT (determining memory type for page)
// | | | | | +---------- PCD (determining memory type for page)
// | | | | +------------ Accessed flag (not accessed yet)
// | | | +-------------- Dirty (not dirtied yet)
// | | +---------------- Page size (1GiB page)
// | +------------------- Global
// +---------------------------- PAT (determining memory type for page)
/// Page table entry flags for entries pointing at a huge page
constexpr uint16_t huge_page_flags = 0x183;
// Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003
// IGNORED | | | | | | | +- Present
// | | | | | | +--- Writeable
// | | | | | +----- Usermode access (Supervisor only)
// | | | | +------- PWT (determining memory type for pdpt)
// | | | +---------- PCD (determining memory type for pdpt)
// | | +------------ Accessed flag (not accessed yet)
// | +-------------- Ignored
// +---------------- Reserved 0 (Table pointer, not page)
/// Page table entry flags for entries pointing at another table
constexpr uint16_t table_flags = 0x003;
/// Iterator over page table entries.
template <unsigned D = 4>
class page_entry_iterator
{
public:
/// Constructor.
/// \arg virt Virtual address this iterator is starting at
/// \arg pml4 Root of the page tables to iterate
/// \arg page_cache Pointer to pages that can be used for page tables
/// \arg page_count Number of pages pointed to by `page_cache`
page_entry_iterator(
uintptr_t virt,
page_table *pml4,
void *&page_cache,
uint32_t &cache_count) :
m_page_cache(page_cache),
m_cache_count(cache_count)
{
m_table[0] = pml4;
for (unsigned i = 0; i < D; ++i) {
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
ensure_table(i);
}
}
uintptr_t vaddress() const {
uintptr_t address = 0;
for (unsigned i = 0; i < D; ++i)
address |= static_cast<uintptr_t>(m_index[i]) << (12 + 9*(3-i));
if (address & (1ull<<47)) // canonicalize the address
address |= (0xffffull<<48);
return address;
}
void increment()
{
for (unsigned i = D - 1; i >= 0; --i) {
if (++m_index[i] <= 511) {
for (unsigned j = i + 1; j < D; ++j)
ensure_table(j);
return;
}
m_index[i] = 0;
}
}
uint64_t & operator*() { return entry(D-1); }
private:
inline uint64_t & entry(unsigned level) { return m_table[level]->entries[m_index[level]]; }
void ensure_table(unsigned level)
{
// We're only dealing with D levels of paging, and
// there must always be a PML4.
if (level < 1 || level >= D)
return;
// Entry in the parent that points to the table we want
uint64_t & parent_ent = entry(level - 1);
if (!(parent_ent & 1)) {
if (!m_cache_count--)
error::raise(uefi::status::out_of_resources, L"Page table cache empty");
page_table *table = reinterpret_cast<page_table*>(m_page_cache);
m_page_cache = offset_ptr<void>(m_page_cache, page_size);
parent_ent = (reinterpret_cast<uintptr_t>(table) & ~0xfffull) | table_flags;
m_table[level] = table;
} else {
m_table[level] = reinterpret_cast<page_table*>(parent_ent & ~0xfffull);
}
}
void *&m_page_cache;
uint32_t &m_cache_count;
page_table *m_table[D];
uint16_t m_index[D];
};
static void
add_offset_mappings(page_table *pml4, void *&page_cache, uint32_t &num_pages)
{
uintptr_t phys = 0;
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
size_t pages = 64 * 1024; // 64 TiB of 1 GiB pages
constexpr size_t GiB = 0x40000000ull;
page_entry_iterator<2> iterator{
virt, pml4,
page_cache,
num_pages};
while (true) {
*iterator = phys | huge_page_flags;
if (--pages == 0)
break;
iterator.increment();
phys += GiB;
}
}
void
add_current_mappings(page_table *new_pml4)
{
// Get the pointer to the current PML4
page_table *old_pml4 = 0;
asm volatile ( "mov %%cr3, %0" : "=r" (old_pml4) );
// Only copy mappings in the lower half
for (int i = 0; i < ::memory::pml4e_kernel; ++i) {
uint64_t entry = old_pml4->entries[i];
if (entry & 1)
new_pml4->entries[i] = entry;
}
}
void
allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
{
status_line status(L"Allocating initial page tables");
static constexpr size_t offset_map_tables = 128 + 1;
static constexpr size_t tables_needed = offset_map_tables + 49;
void *addr = nullptr;
try_or_raise(
bs->allocate_pages(
uefi::allocate_type::any_pages,
memory::table_type,
tables_needed,
&addr),
L"Error allocating page table pages.");
bs->set_mem(addr, tables_needed*page_size, 0);
kernel::args::module &mod = args->modules[++args->num_modules];
mod.type = kernel::args::mod_type::page_tables;
mod.location = addr;
mod.size = tables_needed*page_size;
args->pml4 = addr;
args->num_free_tables = tables_needed - 1;
args->page_table_cache = offset_ptr<void>(addr, page_size);
page_table *pml4 = reinterpret_cast<page_table*>(addr);
add_offset_mappings(pml4, args->page_table_cache, args->num_free_tables);
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->num_free_tables);
}
void
map_pages(
page_table *pml4,
kernel::args::header *args,
uintptr_t phys, uintptr_t virt,
size_t size)
{
size_t pages = memory::bytes_to_pages(size);
page_entry_iterator<4> iterator{
virt, pml4,
args->page_table_cache,
args->num_free_tables};
while (true) {
*iterator = phys | page_flags;
if (--pages == 0)
break;
iterator.increment();
phys += page_size;
}
}
} // namespace paging
} // namespace boot

54
src/boot/paging.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
/// \file paging.h
/// Page table structure and related definitions
#include <stdint.h>
#include <uefi/boot_services.h>
#include "kernel_args.h"
namespace boot {
namespace paging {
/// Struct to allow easy accessing of a memory page being used as a page table.
struct page_table
{
uint64_t entries[512];
inline page_table * get(int i, uint16_t *flags = nullptr) const {
uint64_t entry = entries[i];
if ((entry & 1) == 0) return nullptr;
if (flags) *flags = entry & 0xfff;
return reinterpret_cast<page_table *>(entry & ~0xfffull);
}
inline void set(int i, void *p, uint16_t flags) {
entries[i] = reinterpret_cast<uint64_t>(p) | (flags & 0xfff);
}
};
/// Allocate memory to be used for initial page tables. Initial offset-mapped
/// page tables are pre-filled. All pages are saved as a module in kernel args
/// and kernel args' `page_table_cache` and `num_free_tables` are updated with
/// the leftover space.
void allocate_tables(
kernel::args::header *args,
uefi::boot_services *bs);
/// Copy existing page table entries to a new page table. Does not do a deep
/// copy - the new PML4 is updated to point to the existing next-level page
/// tables in the current PML4.
void add_current_mappings(page_table *new_pml4);
/// Map a physical address to a virtual address in the given page tables.
/// \arg pml4 The root of the set of page tables to be updated
/// \arg args The kernel args header, used for the page table cache
/// \arg phys The phyiscal address to map in
/// \arg virt The virtual address to map in
/// \arg size The size in bytes of the mapping
void map_pages(
page_table *pml4,
kernel::args::header *args,
uintptr_t phys, uintptr_t virt,
size_t bytes);
} // namespace paging
} // namespace boot

View File

@@ -0,0 +1,41 @@
/// \file pointer_manipulation.h
/// Helper functions and types for doing type-safe byte-wise pointer math.
#pragma once
namespace boot {
/// Return a pointer offset from `input` by `offset` bytes.
/// \tparam T Cast the return value to a pointer to `T`
/// \tparam S The type pointed to by the `input` pointer
template <typename T, typename S>
inline T* offset_ptr(S* input, ptrdiff_t offset) {
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
}
/// Iterator for an array of `T` whose size is known at runtime
/// \tparam T Type of the objects in the array, whose size might not be
/// what is returned by sizeof(T).
template <typename T>
class offset_iterator
{
public:
/// Constructor.
/// \arg t Pointer to the first item in the array
/// \arg off Offset applied to reach successive items. Default is 0,
/// which creates an effectively constant iterator.
offset_iterator(T* t, size_t off=0) : m_t(t), m_off(off) {}
T* operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
T* operator++(int) { T* tmp = m_t; operator++(); return tmp; }
bool operator==(T* p) { return p == m_t; }
T* operator*() const { return m_t; }
operator T*() const { return m_t; }
T* operator->() const { return m_t; }
private:
T* m_t;
size_t m_off;
};
} // namespace boot

39
src/boot/support.cpp Normal file
View File

@@ -0,0 +1,39 @@
#include <stdint.h>
#include "error.h"
extern "C" {
/// Basic memcpy() implementation for clang. Clang requires freestanding code
/// implement memcpy(), as it may emit references to it. This basic memcpy is
/// not the most efficient, but will get linked if no other memcpy exists.
__attribute__ ((__weak__))
void *memcpy(void *dest, const void *src, size_t n)
{
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
const uint8_t *csrc = reinterpret_cast<const uint8_t*>(src);
for (size_t i = 0; i < n; ++i)
cdest[i] = csrc[i];
return dest;
}
/// Basic memset() implementation for clang. Clang requires freestanding code
/// implement memset(), as it may emit references to it. This basic memset is
/// not the most efficient, but will get linked if no other memcpy exists.
__attribute__ ((__weak__))
void *memset(void *dest, int c, size_t n)
{
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
for (size_t i = 0; i < n; ++i)
cdest[i] = static_cast<uint8_t>(c);
return dest;
}
int _purecall()
{
::boot::error::raise(uefi::status::unsupported, L"Pure virtual call");
}
} // extern "C"
void operator delete (void *) {}

View File

@@ -1,71 +0,0 @@
#include "utility.h"
struct error_code_desc {
EFI_STATUS code;
CHAR16 *name;
};
// Based off the gnu-efi table
struct error_code_desc error_table[] = {
{ EFI_SUCCESS, L"Success" },
{ EFI_LOAD_ERROR, L"Load Error" },
{ EFI_INVALID_PARAMETER, L"Invalid Parameter" },
{ EFI_UNSUPPORTED, L"Unsupported" },
{ EFI_BAD_BUFFER_SIZE, L"Bad Buffer Size" },
{ EFI_BUFFER_TOO_SMALL, L"Buffer Too Small" },
{ EFI_NOT_READY, L"Not Ready" },
{ EFI_DEVICE_ERROR, L"Device Error" },
{ EFI_WRITE_PROTECTED, L"Write Protected" },
{ EFI_OUT_OF_RESOURCES, L"Out of Resources" },
{ EFI_VOLUME_CORRUPTED, L"Volume Corrupt" },
{ EFI_VOLUME_FULL, L"Volume Full" },
{ EFI_NO_MEDIA, L"No Media" },
{ EFI_MEDIA_CHANGED, L"Media changed" },
{ EFI_NOT_FOUND, L"Not Found" },
{ EFI_ACCESS_DENIED, L"Access Denied" },
{ EFI_NO_RESPONSE, L"No Response" },
{ EFI_NO_MAPPING, L"No mapping" },
{ EFI_TIMEOUT, L"Time out" },
{ EFI_NOT_STARTED, L"Not started" },
{ EFI_ALREADY_STARTED, L"Already started" },
{ EFI_ABORTED, L"Aborted" },
{ EFI_ICMP_ERROR, L"ICMP Error" },
{ EFI_TFTP_ERROR, L"TFTP Error" },
{ EFI_PROTOCOL_ERROR, L"Protocol Error" },
{ EFI_INCOMPATIBLE_VERSION, L"Incompatible Version" },
{ EFI_SECURITY_VIOLATION, L"Security Policy Violation" },
{ EFI_CRC_ERROR, L"CRC Error" },
{ EFI_END_OF_MEDIA, L"End of Media" },
{ EFI_END_OF_FILE, L"End of File" },
{ EFI_INVALID_LANGUAGE, L"Invalid Languages" },
{ EFI_COMPROMISED_DATA, L"Compromised Data" },
{ EFI_WARN_UNKOWN_GLYPH, L"Warning Unknown Glyph" },
{ EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure" },
{ EFI_WARN_WRITE_FAILURE, L"Warning Write Failure" },
{ EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small" },
{ 0, NULL }
};
const CHAR16 *
util_error_message(EFI_STATUS status)
{
int32_t i = -1;
while (error_table[++i].name != NULL) {
if (error_table[i].code == status) return error_table[i].name;
}
if (EFI_ERROR(status))
return L"Unknown Error";
else
return L"Unknown Warning";
}
size_t
wstrlen(const CHAR16 *s)
{
size_t count = 0;
while (s && *s++) count++;
return count;
}

View File

@@ -1,34 +0,0 @@
#include "console.h"
#include <efi.h>
#include <efilib.h>
#include <stddef.h>
#define UNUSED __attribute__((unused))
size_t wstrlen(const CHAR16 *s);
const CHAR16 *util_error_message(EFI_STATUS status);
#define CHECK_EFI_STATUS_OR_RETURN(s, msg, ...) \
if (EFI_ERROR((s))) { \
con_printf(L"ERROR: " msg L": %s\r\n", ##__VA_ARGS__, util_error_message(s)); \
return (s); \
}
#define CHECK_EFI_STATUS_OR_FAIL(s) \
if (EFI_ERROR((s))) { \
con_status_fail(util_error_message(s)); \
while (1) __asm__("hlt"); \
}
#define CHECK_EFI_STATUS_OR_ASSERT(s, d) \
if (EFI_ERROR((s))) { \
__asm__ __volatile__( \
"movq %0, %%r8;" \
"movq %1, %%r9;" \
"movq %2, %%r10;" \
"movq $0, %%rdx;" \
"divq %%rdx;" \
: \
: "r"((uint64_t)s), "r"((uint64_t)d), "r"((uint64_t)__LINE__) \
: "rax", "rdx", "r8", "r9", "r10"); \
}

67
src/drivers/ahci/ata.h Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
/// \file ata.h
/// Definitions for ATA codes
#include <stdint.h>
#include "kutil/enum_bitfields.h"
namespace ahci {
enum class ata_status : uint8_t
{
error = 0x01,
index = 0x02,
corrected = 0x04,
data_ready = 0x08,
seek_done = 0x10,
fault = 0x20,
ready = 0x40,
busy = 0x80
};
enum class ata_error : uint8_t
{
amnf = 0x01, // Address mark not found
tkznf = 0x02, // Track 0 not found
abort = 0x04, // Command abort
mcr = 0x08, // No media
idnf = 0x10, // Id not found
mc = 0x20, // No media
unc = 0x40, // Uncorrectable
bbk = 0x80, // Bad sector
};
enum class ata_cmd : uint8_t
{
read_pio = 0x20,
read_pio_ext = 0x24,
read_dma = 0xC8,
read_dma_ext = 0x25,
write_pio = 0x30,
write_pio_ext = 0x34,
write_dma = 0xCA,
write_dma_ext = 0x35,
cache_flush = 0xE7,
cache_flush_ext = 0xEA,
packet = 0xA0,
identify_packet = 0xA1,
identify = 0xEC
};
enum class sata_signature : uint32_t
{
none = 0x00000000,
sata_drive = 0x00000101,
satapi_drive = 0xeb140101,
enclosure = 0xc33c0101,
port_muxer = 0x96690101
};
} // namespace ahci
IS_BITFIELD(ahci::ata_status);
IS_BITFIELD(ahci::ata_error);

View File

@@ -0,0 +1,22 @@
#include "kutil/enum_bitfields.h"
#include "ahci/driver.h"
#include "log.h"
#include "pci.h"
namespace ahci {
driver::driver()
{
}
void
driver::register_device(pci_device *device)
{
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
device->bus(), device->device(), device->function());
ahci::hba &hba = m_devices.emplace(device);
}
} // namespace

31
src/drivers/ahci/driver.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
/// \file ahci.h
/// AHCI driver and related definitions
#include "kutil/vector.h"
#include "ahci/hba.h"
class pci_device;
namespace ahci {
/// Basic AHCI driver
class driver
{
public:
/// Constructor.
driver();
/// Register a device with the driver
/// \arg device The PCI device to handle
void register_device(pci_device *device);
/// Unregister a device from the driver
/// \arg device The PCI device to remove
void unregister_device(pci_device *device);
private:
kutil::vector<ahci::hba> m_devices;
};
} // namespace

49
src/drivers/ahci/fis.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
/// \file fis.h
/// Definitions for Frame Information Structure types. (Not for pescatarians.)
#include <stdint.h>
namespace ahci {
enum class ata_cmd : uint8_t;
enum class fis_type : uint8_t
{
register_h2d = 0x27,
register_d2h = 0x34,
dma_activate = 0x39,
dma_setup = 0x41,
data = 0x46,
bist = 0x58,
pio_setup = 0x5f,
device_bits = 0xa1
};
struct fis_register_h2d
{
fis_type type;
uint8_t pm_port; // high bit (0x80) is set for the command register flag
ata_cmd command;
uint8_t features;
uint8_t lba0;
uint8_t lba1;
uint8_t lba2;
uint8_t device;
uint8_t lba3;
uint8_t lba4;
uint8_t lba5;
uint8_t features2;
uint8_t count0;
uint8_t count1;
uint8_t icc;
uint8_t control;
uint32_t reserved;
};
} // namespace ahci

143
src/drivers/ahci/hba.cpp Normal file
View File

@@ -0,0 +1,143 @@
#include <stdint.h>
#include "ahci/ata.h"
#include "ahci/hba.h"
#include "console.h"
#include "device_manager.h"
#include "fs/gpt.h"
#include "log.h"
#include "page_manager.h"
#include "pci.h"
IS_BITFIELD(ahci::hba_cap);
IS_BITFIELD(ahci::hba_cap2);
namespace ahci {
enum class hba_cap : uint32_t
{
ccc = 0x00000080, // Command completion coalescing
ahci_only = 0x00040000, // ACHI-only mode
clo = 0x01000000, // Command list override
snotify = 0x40000000, // SNotification register
ncq = 0x40000000, // Native command queuing
addr64 = 0x80000000 // 64bit addressing
};
enum class hba_cap2 : uint32_t
{
handoff = 0x00000001 // BIOS OS hand-off
};
struct hba_data
{
hba_cap cap;
uint32_t host_control;
uint32_t int_status;
uint32_t port_impl;
uint32_t version;
uint32_t ccc_control;
uint32_t ccc_ports;
uint32_t em_location;
uint32_t em_control;
hba_cap2 cap2;
uint32_t handoff_control;
} __attribute__ ((packed));
void irq_cb(void *data)
{
hba *h = reinterpret_cast<hba *>(data);
h->handle_interrupt();
}
hba::hba(pci_device *device)
{
page_manager *pm = page_manager::get();
device_manager &dm = device_manager::get();
uint32_t bar5 = device->get_bar(5);
log::debug(logs::driver, "HBA raw BAR5 is %08lx", bar5);
void *data = reinterpret_cast<void *>(bar5 & ~0xfffull);
pm->map_offset_pointer(&data, 0x2000);
m_data = reinterpret_cast<hba_data volatile *>(data);
if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
m_data->host_control |= 0x80000000; // Enable AHCI mode
uint32_t icap = static_cast<uint32_t>(m_data->cap);
unsigned ports = (icap & 0xf) + 1;
unsigned slots = ((icap >> 8) & 0x1f) + 1;
log::debug(logs::driver, " %d ports: %08x", ports, m_data->port_impl);
log::debug(logs::driver, " %d command slots", slots);
auto *pd = reinterpret_cast<port_data volatile *>(
kutil::offset_pointer(m_data, 0x100));
bool needs_interrupt = false;
m_ports.ensure_capacity(ports);
for (unsigned i = 0; i < ports; ++i) {
bool impl = ((m_data->port_impl & (1 << i)) != 0);
port &p = m_ports.emplace(this, i, kutil::offset_pointer(pd, 0x80 * i), impl);
if (p.get_state() == port::state::active)
needs_interrupt = true;
}
if (needs_interrupt) {
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
m_data->host_control |= 0x02; // enable interrupts
}
for (auto &p : m_ports) {
if (!p.active()) continue;
if (p.get_type() == sata_signature::sata_drive) {
p.sata_reconnect();
/*
if (fs::partition::load(&p) == 0)
dm.register_block_device(&p);
*/
}
}
}
void
hba::handle_interrupt()
{
uint32_t status = m_data->int_status;
for (auto &port : m_ports) {
if (status & (1 << port.index())) {
port.handle_interrupt();
}
}
// Write 1 to the handled interrupts
m_data->int_status = status;
}
void
hba::dump()
{
console *cons = console::get();
static const char *regs[] = {
" CAP", " GHC", " IS", " PI", " VS", " C3C",
" C3P", " EML", " EMC", "CAP2", "BOHC"
};
cons->printf("HBA Registers:\n");
auto *data = reinterpret_cast<uint32_t volatile *>(m_data);
for (int i = 0; i < 11; ++i) {
cons->printf(" %s: %08x\n", regs[i], data[i]);
}
cons->putc('\n');
}
} // namespace ahci

37
src/drivers/ahci/hba.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
/// \file hba.h
/// Definition for AHCI host bus adapters
#include "kutil/vector.h"
#include "ahci/port.h"
class pci_device;
namespace ahci {
enum class hba_cap : uint32_t;
enum class hba_cap2 : uint32_t;
struct hba_data;
/// An AHCI host bus adapter
class hba
{
public:
/// Constructor.
/// \arg device The PCI device for this HBA
hba(pci_device *device);
/// Interrupt handler.
void handle_interrupt();
/// Dump the HBA registers to the console
void dump();
private:
pci_device *m_device;
hba_data volatile *m_data;
kutil::vector<port> m_ports;
};
} // namespace ahci

623
src/drivers/ahci/port.cpp Normal file
View File

@@ -0,0 +1,623 @@
#include <algorithm>
#include "kutil/assert.h"
#include "kutil/enum_bitfields.h"
#include "ahci/ata.h"
#include "ahci/hba.h"
#include "ahci/fis.h"
#include "ahci/port.h"
#include "console.h"
#include "io.h"
#include "log.h"
#include "page_manager.h"
namespace ahci {
enum class cmd_list_flags : uint16_t;
}
IS_BITFIELD(ahci::port_cmd);
IS_BITFIELD(volatile ahci::port_cmd);
IS_BITFIELD(ahci::cmd_list_flags);
namespace ahci {
const unsigned max_prd_count = 16;
enum class cmd_list_flags : uint16_t
{
atapi = 0x0020,
write = 0x0040,
prefetch = 0x0080,
reset = 0x0100,
bist = 0x0200,
clear_busy = 0x0400
};
inline cmd_list_flags
cmd_list_fis_size(uint8_t size)
{
return static_cast<cmd_list_flags>((size/4) & 0x1f);
}
struct cmd_list_entry
{
cmd_list_flags flags;
uint16_t prd_table_length;
uint32_t prd_byte_count;
uint32_t cmd_table_base_low;
uint32_t cmd_table_base_high;
uint32_t reserved[4];
} __attribute__ ((packed));
struct prdt_entry
{
uint32_t data_base_low;
uint32_t data_base_high;
uint32_t reserved;
uint32_t byte_count;
} __attribute__ ((packed));
struct cmd_table
{
uint8_t cmd_fis[64];
uint8_t atapi_cmd[16];
uint8_t reserved[48];
prdt_entry entries[0];
} __attribute__ ((packed));
enum class port_cmd : uint32_t
{
start = 0x00000001,
spinup = 0x00000002,
poweron = 0x00000004,
clo = 0x00000008,
fis_recv = 0x00000010,
fisr_running = 0x00004000,
cmds_running = 0x00008000,
none = 0x00000000
};
struct port_data
{
uint32_t cmd_base_low;
uint32_t cmd_base_high;
uint32_t fis_base_low;
uint32_t fis_base_high;
uint32_t interrupt_status;
uint32_t interrupt_enable;
port_cmd command;
uint32_t reserved0;
uint8_t task_file_status;
uint8_t task_file_error;
uint16_t reserved1;
sata_signature signature;
uint32_t serial_status;
uint32_t serial_control;
uint32_t serial_error;
uint32_t serial_active;
uint32_t cmd_issue;
uint32_t serial_notify;
uint32_t fis_switching;
uint32_t dev_sleep;
uint8_t reserved2[40];
uint8_t vendor[16];
} __attribute__ ((packed));
port::port(hba *device, uint8_t index, port_data volatile *data, bool impl) :
m_index(index),
m_type(sata_signature::none),
m_state(state::unimpl),
m_hba(device),
m_data(data),
m_fis(nullptr),
m_cmd_list(nullptr),
m_cmd_table(nullptr)
{
if (impl) {
m_state = state::inactive;
update();
}
}
port::~port()
{
if (m_cmd_list) {
page_manager *pm = page_manager::get();
pm->unmap_pages(m_cmd_list, 3);
}
}
void
port::update()
{
if (m_state == state::unimpl) return;
uint32_t detected = m_data->serial_status & 0x0f;
uint32_t power = (m_data->serial_status >> 8) & 0x0f;
if (detected == 0x3 && power == 0x1) {
m_state = state::active;
m_type = m_data->signature;
const char *name =
m_type == sata_signature::sata_drive ? "SATA" :
m_type == sata_signature::satapi_drive ? "SATAPI" :
"Other";
log::info(logs::driver, "Found device type %s at port %d", name, m_index);
rebase();
m_pending.set_size(32);
for (auto &pend : m_pending) {
pend.type = command_type::none;
}
// Clear any old pending interrupts and enable interrupts
m_data->interrupt_status = m_data->interrupt_status;
m_data->interrupt_enable = 0xffffffff;
} else {
m_state = state::inactive;
}
}
bool
port::busy()
{
return (m_data->task_file_status & 0x88) != 0;
}
void
port::start_commands()
{
while (bitfield_has(m_data->command, port_cmd::cmds_running))
io_wait();
m_data->command |= port_cmd::fis_recv;
m_data->command |= port_cmd::start;
}
void
port::stop_commands()
{
m_data->command &= ~port_cmd::start;
while (
bitfield_has(m_data->command, port_cmd::cmds_running) ||
bitfield_has(m_data->command, port_cmd::fisr_running))
io_wait();
m_data->command &= ~port_cmd::fis_recv;
}
int
port::make_command(size_t length, fis_register_h2d **fis)
{
int slot = -1;
uint32_t used_slots =
m_data->serial_active |
m_data->cmd_issue |
m_data->interrupt_status;
for (int i = 0; i < 32; ++i) {
if (used_slots & (1 << i)) continue;
if (m_pending[i].type == command_type::none) {
slot = i;
break;
} else {
log::debug(logs::driver, "Type is %d", m_pending[i].type);
}
}
if (slot < 0) {
log::error(logs::driver, "AHCI could not get a free command slot.");
return -1;
}
page_manager *pm = page_manager::get();
cmd_list_entry &ent = m_cmd_list[slot];
cmd_table &cmdt = m_cmd_table[slot];
kutil::memset(&cmdt, 0, sizeof(cmd_table) +
max_prd_count * sizeof(prdt_entry));
ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d));
fis_register_h2d *cfis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
kutil::memset(cfis, 0, sizeof(fis_register_h2d));
cfis->type = fis_type::register_h2d;
cfis->pm_port = 0x80; // set command register flag
*fis = cfis;
size_t remaining = length;
for (int i = 0; i < max_prd_count; ++i) {
size_t prd_len = std::min(remaining, 0x200000ul);
remaining -= prd_len;
void *mem = pm->map_offset_pages(page_count(prd_len));
kutil::memset(mem, 0xaf, prd_len);
uintptr_t phys = pm->offset_phys(mem);
cmdt.entries[i].data_base_low = phys & 0xffffffff;
cmdt.entries[i].data_base_high = phys >> 32;
cmdt.entries[i].byte_count = prd_len - 1;
if (remaining == 0 || i == max_prd_count - 1) {
// If this is the last one, set the interrupt flag
cmdt.entries[i].byte_count |= 0x80000000;
ent.prd_table_length = i + 1;
break;
}
}
log::debug(logs::driver, "Created command, slot %d, %d PRD entries.",
slot, ent.prd_table_length);
return slot;
}
int
port::read_async(uint64_t offset, size_t length, void *dest)
{
fis_register_h2d *fis;
int slot = make_command(length, &fis);
if (slot < 0)
return 0;
cmd_table &cmdt = m_cmd_table[slot];
fis->command = ata_cmd::read_dma_ext;
fis->device = 0x40; // ATA8-ACS p.175
uint64_t sector = offset >> 9;
fis->lba0 = (sector ) & 0xff;
fis->lba1 = (sector >> 8) & 0xff;
fis->lba2 = (sector >> 16) & 0xff;
fis->lba3 = (sector >> 24) & 0xff;
fis->lba4 = (sector >> 32) & 0xff;
fis->lba5 = (sector >> 40) & 0xff;
size_t count = ((length - 1) >> 9) + 1; // count is in sectors
fis->count0 = (count ) & 0xff;
fis->count1 = (count >> 8) & 0xff;
log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx)",
count, sector, sector*512);
m_pending[slot].type = command_type::read;
m_pending[slot].offset = offset % 512;
m_pending[slot].count = 0;
m_pending[slot].data = dest;
if(issue_command(slot))
return slot;
else
return -1;
}
size_t
port::read(uint64_t offset, size_t length, void *dest)
{
int slot = read_async(offset, length, dest);
int timeout = 0;
while (m_pending[slot].type == command_type::read) {
if (timeout++ > 5) {
return 0;
}
asm("hlt");
}
kassert(m_pending[slot].type == command_type::finished,
"Read got unexpected command type");
size_t count = m_pending[slot].count;
m_pending[slot].type = command_type::none;
m_pending[slot].count = 0;
return count;
}
int
port::identify_async()
{
fis_register_h2d *fis;
int slot = make_command(512, &fis);
if (slot < 0)
return 0;
fis->command = ata_cmd::identify;
m_pending[slot].type = command_type::identify;
m_pending[slot].offset = 0;
m_pending[slot].count = 0;
m_pending[slot].data = 0;
if(issue_command(slot))
return slot;
else
return -1;
}
void
port::sata_reconnect()
{
m_data->serial_control |= 1;
io_wait(1000); // About 1ms
m_data->serial_control &= ~1;
}
bool
port::issue_command(int slot)
{
const int max_tries = 10;
int tries = 0;
while (busy()) {
if (++tries == max_tries) {
log::warn(logs::driver, "AHCI port was busy too long");
free_command(slot);
return false;
}
io_wait();
}
// Set bit in CI. Note that only new bits should be written, not
// previous state.
m_data->cmd_issue = (1 << slot);
return true;
}
void
port::handle_interrupt()
{
log::debug(logs::driver, "AHCI port %d got an interrupt", m_index);
// TODO: handle other states in interrupt_status
uint32_t is = m_data->interrupt_status;
if (is & 0x00000040) {
// Port connect status change: For now clear the "exchange"
// bit in SERR, this should probably kick off diagnostics.
m_data->serial_error = 0x04000000;
identify_async();
}
if (is & 0x40000000) {
log::error(logs::driver, "AHCI task file error");
dump();
kassert(0, "Task file error");
}
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
m_data->interrupt_status, m_data->serial_error);
uint32_t ci = m_data->cmd_issue;
for (int i = 0; i < 32; ++i) {
// Skip commands still listed as "issued"
if (ci & (1 << i)) continue;
// Any commands not still listed as "issued" that are still pending for
// the driver are now finished, so handle them.
pending &p = m_pending[i];
switch (p.type) {
case command_type::read:
finish_read(i);
break;
case command_type::identify:
finish_identify(i);
break;
default:
break;
}
}
// Clear the whole status register to mark it as handled
m_data->interrupt_status = m_data->interrupt_status;
}
void
port::finish_read(int slot)
{
page_manager *pm = page_manager::get();
cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot];
size_t count = 0;
void *p = m_pending[slot].data;
uint8_t offset = m_pending[slot].offset;
for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = kutil::offset_pointer(pm->offset_virt(phys), offset);
log::debug(logs::driver, "Reading PRD %2d: %016lx->%016lx [%lxb]", i, mem, p, prd_len);
kutil::memcpy(p, mem, prd_len);
p = kutil::offset_pointer(p, prd_len - offset);
count += (prd_len - offset);
offset = 0;
pm->unmap_pages(mem, page_count(prd_len));
}
m_pending[slot].count = count;
m_pending[slot].type = command_type::finished;
m_pending[slot].data = nullptr;
}
static void
ident_strcpy(uint16_t *from, int words, char *dest)
{
for (int i = 0; i < words; ++i) {
*dest++ = *from >> 8;
*dest++ = *from & 0xff;
from++;
}
*dest = 0;
}
void
port::finish_identify(int slot)
{
page_manager *pm = page_manager::get();
cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot];
kassert(ent.prd_table_length == 1, "AHCI identify used multiple PRDs");
size_t prd_len = (cmdt.entries[0].byte_count & 0x7fffffff) + 1;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[0].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[0].data_base_high) << 32;
log::debug(logs::driver, "Reading ident PRD:");
uint16_t *mem = reinterpret_cast<uint16_t *>(pm->offset_virt(phys));
char string[41];
ident_strcpy(&mem[10], 10, &string[0]);
log::debug(logs::driver, " Device serial: %s", string);
ident_strcpy(&mem[23], 4, &string[0]);
log::debug(logs::driver, " Device version: %s", string);
ident_strcpy(&mem[27], 20, &string[0]);
log::debug(logs::driver, " Device model: %s", string);
uint32_t sectors = mem[60] | (mem[61] << 16);
log::debug(logs::driver, " Max sectors: %xh", sectors);
uint16_t lb_size = mem[106];
log::debug(logs::driver, " lsects per psect: %d %s %s", 1 << (lb_size & 0xf),
lb_size & 0x20 ? "multiple logical per physical" : "",
lb_size & 0x10 ? "physical > 512b" : "");
uint32_t b_per_ls = 2 * (mem[117] | (mem[118] << 16));
log::debug(logs::driver, " b per lsect: %d", b_per_ls);
/*
for (int i=0; i<256; i += 4)
log::debug(logs::driver, " %3d: %04x %3d: %04x %3d: %04x %3d: %04x",
i, mem[i], i+1, mem[i+1], i+2, mem[i+2], i+3, mem[i+3]);
*/
pm->unmap_pages(mem, page_count(prd_len));
m_pending[slot].type = command_type::none;
}
void
port::free_command(int slot)
{
page_manager *pm = page_manager::get();
cmd_table &cmdt = m_cmd_table[slot];
cmd_list_entry &ent = m_cmd_list[slot];
for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = pm->offset_virt(phys);
pm->unmap_pages(mem, page_count(prd_len));
}
ent.prd_table_length = max_prd_count;
}
void
port::rebase()
{
kassert(!m_cmd_list, "AHCI port called rebase() twice");
page_manager *pm = page_manager::get();
size_t prd_size = sizeof(cmd_table) + (max_prd_count * sizeof(prdt_entry));
// 1 for FIS + command list, N for PRD
size_t pages = 1 + page_count(prd_size * 32);
void *mem = pm->map_offset_pages(pages);
uintptr_t phys = pm->offset_phys(mem);
log::debug(logs::driver, "Rebasing address for AHCI port %d to %lx [%d]", m_index, mem, pages);
stop_commands();
// Command list
m_cmd_list = reinterpret_cast<cmd_list_entry *>(mem);
m_data->cmd_base_low = phys & 0xffffffff;
m_data->cmd_base_high = phys >> 32;
kutil::memset(mem, 0, 1024);
mem = kutil::offset_pointer(mem, 32 * sizeof(cmd_list_entry));
phys = pm->offset_phys(mem);
// FIS
m_fis = mem;
m_data->fis_base_low = phys & 0xffffffff;
m_data->fis_base_high = phys >> 32;
kutil::memset(mem, 0, 256);
mem = page_align(kutil::offset_pointer(mem, 256));
phys = pm->offset_phys(mem);
// Command table
m_cmd_table = reinterpret_cast<cmd_table *>(mem);
size_t cmdt_len = sizeof(cmd_table) +
max_prd_count * sizeof(prdt_entry);
kutil::memset(m_cmd_table, 0, cmdt_len * 32);
// set up each entry in the command list to point to the
// corresponding command table
for (int i = 0; i < 32; ++i) {
m_cmd_list[i].prd_table_length = max_prd_count;
m_cmd_list[i].cmd_table_base_low = phys & 0xffffffff;
m_cmd_list[i].cmd_table_base_high = phys >> 32;
mem = kutil::offset_pointer(mem, cmdt_len);
phys = pm->offset_phys(mem);
}
start_commands();
}
void
port::dump()
{
console *cons = console::get();
static const char *regs[] = {
" CLB", "+CLB", " FB", " +FB", " IS", " IE",
" CMD", nullptr, " TFD", " SIG", "SSTS", "SCTL", "SERR",
"SACT", " CI", "SNTF", " FBS", "DEVS"
};
cons->printf("Port Registers:\n");
auto *data = reinterpret_cast<volatile uint32_t *>(m_data);
for (int i = 0; i < 18; ++i) {
if (regs[i]) cons->printf(" %s: %08x\n", regs[i], data[i]);
}
cons->putc('\n');
}
} // namespace ahci

148
src/drivers/ahci/port.h Normal file
View File

@@ -0,0 +1,148 @@
#pragma once
/// \file port.h
/// Definition for AHCI ports
#include <stddef.h>
#include <stdint.h>
#include "kutil/vector.h"
#include "block_device.h"
namespace ahci {
struct cmd_list_entry;
struct cmd_table;
struct fis_register_h2d;
class hba;
enum class sata_signature : uint32_t;
enum class port_cmd : uint32_t;
struct port_data;
/// A port on an AHCI HBA
class port :
public block_device
{
public:
/// Constructor.
/// \arg device The HBA device this port belongs to
/// \arg index Index of the port on its HBA
/// \arg data Pointer to the device's registers for this port
/// \arg impl Whether this port is marked as implemented in the HBA
port(hba *device, uint8_t index, port_data volatile *data, bool impl);
/// Destructor
~port();
/// Get the index of this port on the HBA
/// \returns The port index
inline uint8_t index() const { return m_index; }
enum class state : uint8_t { unimpl, inactive, active };
/// Get the current state of this device
/// \returns An enum representing the state
inline state get_state() const { return m_state; }
/// Check if this device is active
/// \returns True if the device state is active
inline bool active() const { return m_state == state::active; }
/// Get the type signature of this device
/// \returns An enum representing the type of device
inline sata_signature get_type() const { return m_type; }
/// Update the state of this object from the register data
void update();
/// Return whether the port is currently busy
bool busy();
/// Start command processing from this port
void start_commands();
/// Stop command processing from this port
void stop_commands();
/// Start a read operation from the drive.
/// \arg offset Offset to start from
/// \arg length Number of bytes to read
/// \arg dest A buffer where the data will be placed
/// \returns A handle to the read operation, or -1 on error
int read_async(uint64_t offset, size_t length, void *dest);
/// Read from the drive, blocking until finished.
/// \arg offset Offset to start from
/// \arg length Number of bytes to read
/// \arg dest A buffer where the data will be placed
/// \returns The number of bytes read
virtual size_t read(uint64_t offset, size_t length, void *dest);
/// Start an identify operation for the drive.
/// \returns A handle to the read operation, or -1 on error
int identify_async();
/// Tell the HBA to reconnect to the SATA device. A successful
/// reconnect will kick off an identify command.
void sata_reconnect();
/// Handle an incoming interrupt
void handle_interrupt();
/// Dump the port registers to the console
void dump();
private:
/// Rebase the port command structures to a new location in system
/// memory, to be allocated from the page manager.
void rebase();
/// Initialize a command structure
/// \arg length The number of bytes of data needed in the PRDs
/// \arg fis [out] The FIS for this command
/// \returns The index of the command slot, or -1 if none available
int make_command(size_t length, fis_register_h2d **fis);
/// Send a constructed command to the hardware
/// \arg slot The index of the command slot used
/// \returns True if the command was successfully sent
bool issue_command(int slot);
/// Free the data structures allocated by command creation
/// \arg slot The index of the command slot used
void free_command(int slot);
/// Finish a read command started by `read()`. This will
/// free the structures allocated, so `free_command()` is
/// not necessary.
/// \arg slot The command slot that the read command used
void finish_read(int slot);
/// Finish an identify command started by `identify_async()`.
/// This will free the structures allocated, so `free_command()` is
/// not necessary.
/// \arg slot The command slot that the read command used
void finish_identify(int slot);
sata_signature m_type;
uint8_t m_index;
state m_state;
hba *m_hba;
port_data volatile *m_data;
void *m_fis;
cmd_list_entry *m_cmd_list;
cmd_table *m_cmd_table;
enum class command_type : uint8_t { none, read, write, identify, finished };
struct pending
{
command_type type;
uint8_t offset;
size_t count;
void *data;
};
kutil::vector<pending> m_pending;
};
} // namespace ahci

View File

@@ -0,0 +1,38 @@
#include <stdint.h>
#include <stdlib.h>
#include "j6/types.h"
#include "j6/errors.h"
extern "C" {
j6_status_t getpid(uint64_t *);
j6_status_t fork(uint64_t *);
j6_status_t sleep(uint64_t til);
j6_status_t debug();
j6_status_t message(const char *msg);
int main(int, const char **);
}
int
main(int argc, const char **argv)
{
uint64_t pid = 0;
uint64_t child = 0;
j6_status_t result = fork(&child);
if (result != j6_status_ok)
return result;
message("hello from nulldrv!");
result = getpid(&pid);
if (result != j6_status_ok)
return result;
for (int i = 1; i < 5; ++i)
sleep(i*10);
return pid;
}

View File

@@ -0,0 +1,81 @@
section .bss
mymessage:
resq 1024
extern main
extern exit
section .text
global getpid
getpid:
push rbp
mov rbp, rsp
; address of out var should already be in rdi
mov rax, 0x13 ; getpid syscall
syscall ; result is now already in rax, so just return
pop rbp
ret
global debug
debug:
push rbp
mov rbp, rsp
mov rax, 0x00 ; debug syscall
syscall
pop rbp
ret
global sleep
sleep:
push rbp
mov rbp, rsp
mov rax, 0x16 ; sleep syscall
syscall
pop rbp
ret
global fork
fork:
push rbp
mov rbp, rsp
; address of out var should already be in rdi
mov rax, 0x12
syscall ; result left in rax
pop rbp
ret
global message
message:
push rbp
mov rbp, rsp
; message should already be in rdi
mov rax, 0x14
syscall
pop rbp
ret
global _start
_start:
xor rbp, rbp ; Sentinel rbp
push rbp
push rbp
mov rbp, rsp
mov rdi, 0
mov rsi, 0
call main
mov rdi, rax
call exit

48
src/include/elf.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
/* elf.h - basic defines for external code written assuming <elf.h> works. Only
* Elf64 values are included.
*/
typedef uint16_t Elf64_Half;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Off;
typedef uint16_t Elf64_Section;
typedef Elf64_Half Elf64_Versym;
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
} Elf64_Rel;
typedef struct {
Elf64_Addr r_offset;
Elf64_Word r_info;
Elf64_Sword r_addend;
} Elf64_Rela;
typedef struct {
Elf64_Sxword d_tag;
union {
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
} Elf64_Dyn;
#define ELF64_R_TYPE(x) ((x) & 0xffffffff)
typedef enum {
DT_NULL = 0,
DT_RELA = 7,
DT_RELASZ = 8,
DT_RELAENT = 9
} ElfDynTag;
typedef enum {
R_X86_64_NONE = 0,
R_X86_64_RELATIVE = 8
} Elf_x86_64_RelType;

16
src/include/j6/errors.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
/// \file errors.h
/// Collection of constants for the j6_status_t type
#define j6_status_error 0x8000000000000000
#define j6_err(x) ((x) | j6_status_error)
#define j6_is_err(x) (((x) & j6_status_error) == j6_status_error)
#define j6_status_ok 0x0000
#define j6_status_destroyed 0x1001
#define j6_err_nyi j6_err(0x0001)
#define j6_err_unexpected j6_err(0x0002)
#define j6_err_invalid_arg j6_err(0x0003)

18
src/include/j6/signals.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
/// \file signals.h
/// Collection of constants for the j6_signal_t type
// Signals 0-7 are common to all types
#define j6_signal_no_handles (1 << 0)
// Signals 8-15 are user-defined signals
#define j6_signal_user0 (1 << 8)
#define j6_signal_user1 (1 << 9)
#define j6_signal_user2 (1 << 10)
#define j6_signal_user3 (1 << 11)
#define j6_signal_user4 (1 << 12)
#define j6_signal_user5 (1 << 13)
#define j6_signal_user6 (1 << 14)
#define j6_signal_user7 (1 << 15)
// All other signals are type-specific

20
src/include/j6/types.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
/// \file types.h
/// Basic kernel types exposed to userspace
#include <stdint.h>
/// All interactable kernel objects have a uniqe kernel object id
typedef uint64_t j6_koid_t;
/// Syscalls return status as this type
typedef uint64_t j6_status_t;
/// Handles are references and capabilities to other objects
typedef uint32_t j6_handle_t;
/// Some objects have signals, which are a bitmap of 64 possible signals
typedef uint64_t j6_signal_t;
/// The rights of a handle/capability are a bitmap of 64 possible rights
typedef uint64_t j6_rights_t;

96
src/include/kernel_args.h Normal file
View File

@@ -0,0 +1,96 @@
#pragma once
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
namespace kernel {
namespace args {
constexpr uint32_t magic = 0x600dda7a;
constexpr uint16_t version = 1;
enum class mod_flags : uint32_t
{
debug = 0x00000001
};
enum class mod_type : uint32_t {
unknown,
kernel,
initrd,
memory_map,
page_tables,
framebuffer,
max
};
enum class mode : uint8_t {
normal,
debug
};
struct module {
void *location;
size_t size;
mod_type type;
mod_flags flags;
}
__attribute__((packed));
enum class mem_type : uint32_t {
free,
args,
kernel,
module,
table,
acpi,
uefi_runtime,
mmio,
persistent
};
/// Structure to hold an entry in the memory map.
struct mem_entry
{
uintptr_t start;
size_t pages;
mem_type type;
uint32_t attr;
}
__attribute__((packed));
struct header {
uint32_t magic;
uint16_t version;
mode mode;
uint8_t _reserved0;
void *pml4;
void *page_table_cache;
uint32_t num_free_tables;
uint32_t num_modules;
module *modules;
mem_entry *mem_map;
size_t num_map_entries;
void *runtime_services;
void *acpi_table;
}
__attribute__((aligned(alignof(max_align_t))));
#pragma pack(pop)
} // namespace args
using entrypoint = __attribute__((sysv_abi)) void (*)(args::header *);
} // namespace kernel

View File

@@ -1,47 +0,0 @@
#pragma once
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#define DATA_HEADER_MAGIC 0x600dda7a
#define DATA_HEADER_VERSION 1
#pragma pack(push, 1)
struct popcorn_data {
uint32_t magic;
uint16_t version;
uint16_t length;
uint32_t _reserved0;
uint32_t flags;
void *font;
size_t font_length;
void *data;
size_t data_length;
void *log;
size_t log_length;
void *memory_map;
size_t memory_map_length;
size_t memory_map_desc_size;
void *runtime;
void *acpi_table;
void *frame_buffer;
size_t frame_buffer_size;
uint32_t hres;
uint32_t vres;
uint32_t rmask;
uint32_t gmask;
uint32_t bmask;
uint32_t _reserved1;
}
__attribute__((aligned(alignof(max_align_t))));
#pragma pack(pop)

Some files were not shown because too many files have changed in this diff Show More