187 Commits

Author SHA1 Message Date
Justin C. Miller
cf22ed57a2 [docs] Update the README with roadmap info 2021-02-17 00:47:12 -08:00
Justin C. Miller
b6772ac2ea [kernel] Fix #DF when building with -O3
I had failed to specify in inline asm that an input variable was the
same as the output variable.
2021-02-17 00:22:22 -08:00
Justin C. Miller
f0025dbc47 [kernel] Schedule threads on other CPUs
Now that the other CPUs have been brought up, add support for scheduling
tasks on them. The scheduler now maintains separate ready/blocked lists
per CPU, and CPUs will attempt to balance load via periodic work
stealing.

Other changes as a result of this:
- The device manager no longer creates a local APIC object, but instead
  just gathers relevant info from the APCI tables. Each CPU creates its
  own local APIC object. This also spurred the APIC timer calibration to
  become a static value, as all APICs are assumed to be symmetrical.
- Fixed a bug where the scheduler was popping the current task off of
  its ready list, however the current task is never on the ready list
  (except the idle task was first set up as both current and ready).
  This was causing the lists to get into bad states. Now a task can only
  ever be current or in a ready or blocked list.
- Got rid of the unused static process::s_processes list of all
  processes, instead of trying to synchronize it via locks.
- Added spinlocks for synchronization to the scheduler and logger
  objects.
2021-02-15 12:56:22 -08:00
Justin C. Miller
2a347942bc [kernel] Fix SMP boot on KVM
KVM didn't like setting all the CR4 bits we wanted at once. I suspect
that means real hardware won't either. Delay the setting of the rest of
CR4 until after the CPU is in long mode - only set PAE and PGE from real
mode.
2021-02-13 01:45:17 -08:00
Justin C. Miller
36da65e15b [kernel] Add index to cpu_data
Because the firmware can set the APIC ids to whatever it wants, add a
sequential index to each cpu_data structure that jsix will use for its
main identifier, or for indexing into arrays, etc.
2021-02-11 00:00:34 -08:00
Justin C. Miller
214ff3eff0 Update gitignore
Adding files that have been hanging around my deveploment environment
that should not get checked in
2021-02-10 23:59:05 -08:00
Justin C. Miller
8c0d52d0fe [kernel] Add spinlocks to vm_space, frame_allocator
Also updated spinlock interface to be an object, and added a scoped lock
object that uses it as well.
2021-02-10 23:57:51 -08:00
Justin C. Miller
793bba95b5 [boot] Do address virtualization in the bootloader
More and more places in the kernel init code are taking addresses from
the bootloader and translating them to offset-mapped addresses. The
bootloader can do this, so it should.
2021-02-10 01:23:50 -08:00
Justin C. Miller
2d4a65c654 [kernel] Pre-allocate cpu_data and pass to APs
In order to avoid cyclic dependencies in the case of page faults while
bringing up an AP, pre-allocate the cpu_data structure and related CPU
control structures, and pass them to the AP startup code.

This also changes the following:
- cpu_early_init() was split out of cpu_early_init() to allow early
  usage of current_cpu() on the BSP before we're ready for the rest of
  cpu_init(). (These functions were also renamed to follow the preferred
  area_action naming style.)
- isr_handler now zeroes out the IST entry for its vector instead of
  trying to increment the IST stack pointer
- the IST stacks are allocated outside of cpu_init, to also help reduce
  stack pressue and chance of page faults before APs are ready
- share stack areas between AP idle threads so we only waste 1K per
  additional AP for the unused idle stack
2021-02-10 15:44:07 -08:00
Justin C. Miller
872f178d94 [kernel] Update syscall MSRs for all CPUs
Since SYSCALL/SYSRET rely on MSRs to control their function, split out
syscall_enable() into syscall_initialize() and syscall_enable(), the
latter being called on all CPUs. This affects not just syscalls but also
the kernel_to_user_trampoline.

Additionally, do away with the max syscalls, and just make a single page
of syscall pointers and name pointers. Max syscalls was fragile and
needed to be kept in sync in multiple places.
2021-02-10 15:25:17 -08:00
Justin C. Miller
70d6094f46 [kernel] Add fake preludes to isr handler to trick GDB
By adding more debug information to the symbols and adding function
frame preludes to the isr handler assembly functions, GDB sees them as
valid locations for stack frames, and can display backtraces through
interrupts.
2021-02-10 01:10:26 -08:00
Justin C. Miller
31289436f5 [kernel] Use PAUSE in spinwait
Using PAUSE in a tight loop allows other logical cores on the same
physical core to make use of more of the core's resources.
2021-02-07 23:52:06 -08:00
Justin C. Miller
5e7792c11f [scripts] Add GDB j6tw page table walker
Added the command "j6tw <pml4> <addr>" which takes any arguments that
evaluate to addresses or integers. It displays the full breakdown of the
page table walk for the given address, with flags.
2021-02-07 23:50:53 -08:00
Justin C. Miller
e73064a438 [kutil] Update spinlock to an MCS-style lock
Update the existing but unused spinlock class to an MCS-style queue
spinlock. This is probably still a WIP but I expect it to see more use
with SMP getting further integrated.
2021-02-07 23:50:00 -08:00
Justin C. Miller
72787c0652 [kernel] Make sure all vma types have (virtual) dtors 2021-02-07 23:45:07 -08:00
Justin C. Miller
c88170f6e0 [kernel] Start all other processors in the system
This very large commit is mainly focused on getting the APs started and
to a state where they're waiting to have work scheduled. (Actually
scheduling on them is for another commit.)

To do this, a bunch of major changes were needed:

- Moving a lot of the CPU initialization (including for the BSP) to
  init_cpu(). This includes setting up IST stacks, writing MSRs, and
  creating the cpu_data structure. For the APs, this also creates and
  installs the GDT and TSS, and installs the global IDT.

- Creating the AP startup code, which tries to be as position
  independent as possible. It's copied from its location to 0x8000 for
  AP startup, and some of it is fixed at that address. The AP startup
  code jumps from real mode to long mode with paging in one swell foop.

- Adding limited IPI capability to the lapic class. This will need to
  improve.

- Renaming cpu/cpu.* to cpu/cpu_id.* because it was just annoying in GDB
  and really isn't anything but cpu_id anymore.

- Moved all the GDT, TSS, and IDT code into their own files and made
  them classes instead of a mess of free functions.

- Got rid of bsp_cpu_data everywhere. Now always call the new
  current_cpu() to get the current CPU's cpu_data.

- Device manager keeps a list of APIC ids now. This should go somewhere
  else eventually, device_manager needs to be refactored away.

- Moved some more things (notably the g_kernel_stacks vma) to the
  pre-constructor setup in memory_bootstrap. That whole file is in bad
  need of a refactor.
2021-02-07 23:44:28 -08:00
Justin C. Miller
a65ecb157d [fb] Fix fb log scrolling
While working on the double-buffering issue, I ripped out this feature
from scrollback and didn't put it back in. Also having main allocate
extra space for the message buffer since calling malloc/free over again
several times was causing malloc to panic. (Which should also separately
be fixed..)
2021-02-06 00:33:45 -08:00
Justin C. Miller
eb8a3c0e09 [kernel] Fix frame allocator next-block bug
The frame allocator was causing page faults when exhausting the first
(well, last, because it starts from the end) block of free pages. Turns
out it was just incrementing instead of decrementing and thus running
off the end.
2021-02-06 00:06:29 -08:00
Justin C. Miller
572fade7ff [fb] Use rep stosl for screen fill
This is mostly cleanup after fighting the double buffering bug - bring
back rep stosl for screen fill, and move draw_pixel to be an inline
function.
2021-02-05 23:55:42 -08:00
Justin C. Miller
b5885ae35f [fb] Dynamically allocate log entry buffer
Since the kernel will tell us what size of buffer we need for
j6_system_get_log(), malloc the buffer for it from the heap instead of a
fixed array on the stack.
2021-02-05 23:51:34 -08:00
Justin C. Miller
335bc01185 [kernel] Fix page_tree growth bug
The logic was inverted in contains(), meaning that new parents were
never being created, and the same level-0 block was just getting reused.
2021-02-05 23:47:29 -08:00
Justin C. Miller
c87563a520 [fb] Remove extraneous m_lines from scrollback
We have one big array of characters in scrollback, with lines of
constant width -- there's no reason to have an array of pointers when
offsets will do.
2021-02-04 20:55:56 -08:00
Justin C. Miller
e4aafca7c3 [fb] Use system_map_mmio to map framebuffer
Update fb driver to use new j6_init_desc_framebuffer format with
physical address, and system_map_mmio to map the framebuffer into its
memory.
2021-02-04 20:48:34 -08:00
Justin C. Miller
fe05d45cde [libc] Cache self handle in libc
When libc_init iterates the initv values, cache the process' self
handle.
2021-02-04 20:39:45 -08:00
Justin C. Miller
b3861decc3 [kernel] Pass the fb phys addr to userspace
Instead of always mapping the framebuffer at an arbitrary location, and
so reporting that to userspace, send the physical address so drivers can
call system_map_mmio().
2021-02-04 19:56:41 -08:00
Justin C. Miller
b3f59acf7e [kernel] Make sure to virtualize ACPI table pointers
Probably due to old UEFI page tables going away, some systems failed to
load ACPI tables at their physical location. Make sure to translate them
to kernel offset-mapped addresses.
2021-02-04 19:47:17 -08:00
Justin C. Miller
4f8e35e409 [kernel] system_get_log should take a void*
Since it's not just text that's being returned in the buffer, switch the
argument from a char* to a void*.
2021-02-04 19:44:28 -08:00
Justin C. Miller
b898949ffc [kernel] Create system_map_mmio syscall
Create a syscall for drivers to be able to ask the kernel for a VMA that
maps a MMIO area. Also expose vm_flags via j6 table style include file
and new flags.h header.
2021-02-04 19:42:45 -08:00
Justin C. Miller
2244764777 [kernel] Set process stack pointer correctly
The rsp returned by initialize_main_user_stack() needs to be put into
the cpu data area, not just put into the stack (the stack only fills in
rbp).
2021-02-03 17:01:19 -08:00
Justin C. Miller
e4c8a36577 [kernel] Fix logger::get_entry return value
logger::get_entry was returning the bytes available for reading in the
buffer instead of the bytes for a single entry.
2021-02-03 17:00:02 -08:00
Justin C. Miller
41eb45402e [kernel] Start process handles at 1
The 0 index was still sometimes not handled properly in the hash table.
Also 0 is sometimes indicative of an error. Let's just start handles
from 1 to avoid those issues.
2021-02-03 16:55:14 -08:00
Justin C. Miller
33ed95bd8e [kernel] Remove bitmask check in heap free
Since all memory regions start with a header, this check should never
pass.
2021-02-02 18:37:23 -08:00
Justin C. Miller
4985b2d1f4 [kernel] fix frame_allocator::allocate return value
frame_allocator::allocate was returning the passed-in desired amount of
pages, instead of the actual number allocated.
2021-02-02 18:36:28 -08:00
Justin C. Miller
68a2250886 [kernel] Use IST for kernel stacks for NMI, #DF, #PF
We started actually running up against the page boundary for kernel
stacks and thus double-faulting on page faults from kernel space. So I
finally added IST stacks. Note that we currently just
increment/decrement the IST entry by a page when we enter the handler to
avoid clobbering on re-entry, but this means:

* these handlers need to be able to operate with only a page of stack
* kernel stacks always have to be >1 pages
* the amount of nesting possible is tied to the kernel stack size.

These seem fine for now, but we should maybe find a way to use something
besides g_kernel_stacks to set up the IST stacks if/when this becomes an
issue.
2021-02-02 18:36:11 -08:00
Justin C. Miller
8575939b20 [boot] Fix missing last memory map entry
The bootloader was dropping the last memory map entry of the kernel map.
2021-01-31 22:54:14 -08:00
Justin C. Miller
634a1c5f6a [kernel] Implement VMA page tracking
The previous method of VMA page tracking relied on the VMA always being
mapped at least into one space and just kept track of pages in the
spaces' page tables. This had a number of drawbacks, and the mapper
system was too complex without much benefit.

Now make VMAs themselves keep track of spaces that they're a part of,
and make them responsible for knowing what page goes where. This
simplifies most types of VMA greatly. The new vm_area_open (nee
vm_area_shared, but there is now no reason for most VMAs to be
explicitly shareable) adds a 64-ary radix tree for tracking allocated
pages.

The page_tree cannot yet handle taking pages away, but this isn't
something jsix can do yet anyway.
2021-01-31 22:18:44 -08:00
Justin C. Miller
c364e30240 [kutil] Flag static allocated vectors
ktuil::vector can take a static area of memory as its initial memory,
but the case was never handled where it outgrew that memory and had to
reallocate. Steal the high bit from the capacity value to indicate the
current memory should not be kfree()'d. Also added checks in the heap
allocator to make sure pointers look valid.
2021-01-31 20:54:19 -08:00
Justin C. Miller
3595c3a440 [libj6] Create libj6
Pull syscall code out of libc and create new libj6. This should
eventually become a vDSO, but for now it can still be a static lib.
Also renames all the _syscall_* symbol names to j6_*
2021-01-30 18:00:39 -08:00
Justin C. Miller
c3dd65457d [kernel] Move 'table' includes to j6/tables
Move all table-style include files that are part of the public kernel
interface to the j6/tables include path
2021-01-28 18:42:42 -08:00
Justin C. Miller
3aa909b917 [kernel] Split loading from scheduler
In preparation for moving things to the init process, move process
loading out of the scheduler. memory_bootstrap now has a
load_simple_process function for mapping an args::program into memory,
and the stack setup has been simplified (though all the initv values are
still being added by the kernel - this needs rework) and normalized to
use the thread::add_thunk_user code path.
2021-01-28 18:26:24 -08:00
Justin C. Miller
35d8d2ab2d [kernel] Add vm_space::allocate
Refactored out vm_space::handle_fault's allocation code into a separate
vm_space::allocate function, and reimplemented handle_fault in terms of
the new function.
2021-01-28 01:08:06 -08:00
Justin C. Miller
e3ebaeb2c8 [kernel] Add new vm_area_fixed
Add a new vm_area type, vm_area_fixed, which is sharable but not
allocatable. Useful for mapping things like MMIO to process spaces.
2021-01-28 01:05:21 -08:00
Justin C. Miller
71dc332dae [kernel] Make default_priority naming consistent
The naming was default_pri in process, but default_priority in
scheduler. Normalize to the longer name.
2021-01-28 01:01:40 -08:00
Justin C. Miller
211a3c2358 [kernel] Clean up syscall code
This is a minor refactor including:
- Removing old commented-out syscall_dispatch function
- Removing IA32_EFER syscall-enable flag setting (this is done by the
  bootloader now)
- Moving much logging from inside process/thread syscalls to the 'task'
  log area, allowing for turning the 'syscall' area down to info by
  default.
2021-01-23 20:37:20 -08:00
Justin C. Miller
16b9d4fd8b [kernel] Have process_start syscall take a list of handles
This also prompted a change of the process initialization protocol to
allow handles to get typed, and changing to marking them as just
self/other handls. This also means exposing the object type enum to
userspace.
2021-01-23 20:36:27 -08:00
Justin C. Miller
c0f304559f [boot] Send module addresses as physical
This makes the job of the kernel easier when marking module pages as
used in the frame allocator. This will also help when sending modules
over to the init process.
2021-01-23 20:30:09 -08:00
Justin C. Miller
8d325184ad [docs] Add janice's jsix logo to the README 2021-01-22 00:45:14 -08:00
Justin C. Miller
0df93eaa98 [kernel] Added the process_kill syscall
Added process_kill, and also cleaned up all the disparate types being
used for thread/process exit codes. (Now all int32_t.)
2021-01-22 00:38:46 -08:00
Justin C. Miller
aae18fd035 [boot][kernel] Replace frame allocator with bitmap-based one
The previous frame allocator involved a lot of splitting and merging
linked lists and lost all information about frames while they were
allocated. The new allocator is based on an array of descriptor
structures and a bitmap. Each memory map region of allocatable memory
becomes one or more descriptors, each mapping up to 1GiB of physical
memory. The descriptors implement two levels of a bitmap tree, and have
a pointer into the large contiguous bitmap to track individual pages.
2021-01-22 00:16:01 -08:00
Justin C. Miller
fd8552ca3a [external] Update to latest j6-uefi-headers 2021-01-21 18:50:31 -08:00
Justin C. Miller
452457412b [kernel] Add process_create syscall
New syscall creates a process (and thus a new virtual address space) but
does not create any threads in it.
2021-01-20 18:39:14 -08:00
Justin C. Miller
521df1f4b7 [docs] README update
Long overdue update to the README about the project in general as well
as updating build instructions.
2021-01-20 18:31:32 -08:00
Justin C. Miller
0ae2f935af [kernel] Remove old fake stdout channel/task
This was useful for testing channels, but it just gets in the way now.
2021-01-20 01:30:33 -08:00
Justin C. Miller
3282a3ae34 [kernel] Split out sched log area
To keep the task log area useful, scheduler updates on processes now go
to the new sched log area.
2021-01-20 01:29:18 -08:00
Justin C. Miller
cb612c36ea [boot][kernel] Split programs into sections
To enable setting sections as NX or read-only, the boot program loader
now loads programs as lists of sections, and the kernel args are updated
accordingly. The kernel's loader now just takes a program pointer to
iterate the sections. Also enable NX in IA32_EFER in the bootloader.
2021-01-20 01:25:47 -08:00
Justin C. Miller
14aad62e02 [boot] Improve non-printing error handling
Add an implicit __LINE__ to the try_or_raise macro, which gets set in
r11 on cpu_assert. Also made status lines smarter about when to call
cpu_assert.
2021-01-20 01:18:31 -08:00
Justin C. Miller
847d7ab38d [kernel] Add a 'log available' signal to block on
There was previously no good way to block log-display tasks, either the
fb driver or the kernel log task. Now the system object has a signal
(j6_signal_system_has_log) that gets asserted when the log is written
to.
2021-01-18 19:12:49 -08:00
Justin C. Miller
99ef9166ae [kernel] Lower APIC calibration timer
Now that the spinwait bug is fixed, the raised time for APIC calibration
can be put back to a lower value. It was previously raised thinking more
time would get a more accurate result -- but accuracy was not the issue.
2021-01-18 18:25:44 -08:00
Justin C. Miller
0305830e32 [kernel] fix thread_create handle bug
thread_create was setting the handle it returned to be that of the
parent process, not the thread it created.
2021-01-18 18:24:18 -08:00
Justin C. Miller
9f342dff49 [kernel] fix err_insufficient bug in endpoint
The endpoint syscalls endpoint_recv and endpoint_sendrecv gained new
local stack variables for calling into possibly blocking endpoint
functions, but the len variable was being initialized to 0 instead of
the incoming buffer size.
2021-01-18 18:22:32 -08:00
Justin C. Miller
02766d82eb [kernel] Fix clock::spinwait
spinwait wasn't scaling the target to microseconds
2021-01-18 18:19:18 -08:00
Justin C. Miller
3e372faf5e [kernel] Add fake clock source if there's no HPET
If there's no HPET (or if HPET is left uninitialized for debugging)
default to a fake incrementing counter clock.
2021-01-18 13:49:59 -08:00
Justin C. Miller
786b4ea8c0 [kernel] Don't unmask IOAPIC IRQs immediately
The amount of spurious IRQ activity on real hardware severely slows down
the system (minutes per frame instead of frames per second). There's no
reason to unmask all of them from the get-go before they're set up to be
handled.
2021-01-18 13:49:59 -08:00
Justin C. Miller
20ff0ed30b [kernel] Don't panic on unknown IRQ
On real hardware, spurious IRQs came in before they were set up to be
handled. This should be logged but not fatal.
2021-01-18 13:49:59 -08:00
Justin C. Miller
2a490a1bbc [kernel] Add BGRT ACPI table struct 2021-01-18 13:49:59 -08:00
Justin C. Miller
89391e5be1 [boot] Log address of new table pages
Since it's often needed when debugging between the bootloader and
kernel, log the address of the table pages the bootloader allocated.
2021-01-18 13:49:59 -08:00
Justin C. Miller
c3cb41f78a [boot] Save commented-out mem map dumping code
This is often needed, so I'm commiting it commented out.
2021-01-18 13:49:59 -08:00
Justin C. Miller
c3a0266354 [cpu] Split cpuid validation into separate lib
In order to allow the bootloader to do preliminary CPUID validation
while UEFI is still handling displaying information to the user, split
most of the kernel's CPUID handling into a library to be used by both
kernel and boot.
2021-01-18 13:49:59 -08:00
Justin C. Miller
55a5c97034 [libc] Attempt to speed up memcpy for aligned mem
Copy long-by-long instead of byte-by-byte if both pointers are similarly
aligned.
2021-01-18 13:49:59 -08:00
Justin C. Miller
dcb8a3f3fb [fb] Use double-buffering in fb driver
Allocate and use a back buffer, so that draws to the screen are always a
single memcpy()
2021-01-18 13:49:59 -08:00
Justin C. Miller
3dffe564af [kernel] Set framebuffer to write-combining
Several changes were needed to make this work:

- Update the page_table::flags to understand memory caching types
- Set up the PAT MSR to add the WC option
- Make page-offset area mapped as WT
- Add all the MTRR and PAT MSRs, and log the MTRRs for verification
- Add a vm_area flag for write_combining
2021-01-18 13:49:59 -08:00
Justin C. Miller
1820972fb7 [kenrel] Ensure page tables are zeroed before use
I forgot to zero out pages used for page tables, which didn't come back
to bite me until testing on physical hardware..
2021-01-18 13:49:59 -08:00
Justin C. Miller
67534faa78 [boot] Add scanline size to fb boot message
Scanline size can differ from horizontal resolution in some
framebuffers. This isn't currently handled, but at least log it so
it's visible if this lack of handling is a potential error.
2021-01-18 13:49:59 -08:00
Justin C. Miller
12605843ce [boot] Don't use custom UEFI memory types
The UEFI spec specifically calls out memory types with the high bit set
as being available for OS loaders' custom use. However, it seems many
UEFI firmware implementations don't handle this well. (Virtualbox, and
the firmware on my Intel NUC and Dell XPS laptop to name a few.)

So sadly since we can't rely on this feature of UEFI in all cases, we
can't use it at all. Instead, treat _all_ memory tagged as EfiLoaderData
as possibly containing data that's been passed to the OS by the
bootloader and don't free it yet.

This will need to be followed up with a change that copies anything we
need to save and frees this memory.

See: https://github.com/kiznit/rainbow-os/blob/master/boot/machine/efi/README.md
2021-01-18 13:49:59 -08:00
Justin C. Miller
8dbdebff3f [boot] Don't use custom UEFI memory types
The UEFI spec specifically calls out memory types with the high bit set
as being available for OS loaders' custom use. However, it seems many
UEFI firmware implementations don't handle this well. (Virtualbox, and
the firmware on my Intel NUC and Dell XPS laptop to name a few.)

So sadly since we can't rely on this feature of UEFI in all cases, we
can't use it at all. Instead, treat _all_ memory tagged as EfiLoaderData
as possibly containing data that's been passed to the OS by the
bootloader and don't free it yet.

This will need to be followed up with a change that copies anything we
need to save and frees this memory.

See: https://github.com/kiznit/rainbow-os/blob/master/boot/machine/efi/README.md
2021-01-18 13:49:10 -08:00
Justin C. Miller
61845b8761 [boot] Add framebuffer progress bar
After exiting UEFI, the bootloader had no way of displaying status to
the user. Now it will display a series of small boxes as a progress bar
along the bottom of the screen if a framebuffer exists. Errors or
warnings during a step will cause that step's box to turn red or orange,
and display bars above it to signal the error code.

This caused the simplification of the error handling system (which was
mostly just calling status_line::fail) and added different types of
status objects.
2021-01-18 13:49:10 -08:00
Justin C. Miller
1ba44c99d1 [boot] List the detected framebuffer in boot log
List information about the detected framebuffer device (or lack thereof)
in the bootloader log messages.
2021-01-18 13:49:10 -08:00
Justin C. Miller
6716ab251f [kernel] Clean up process::exit
Make process::exit slightly more resilient to being called again.
2021-01-18 13:49:10 -08:00
Justin C. Miller
e3b9c0140a [fb] Simplify scrollback line counting
Using a start and a count was redundant when we know how many lines are
in the buffer already.
2021-01-18 13:49:10 -08:00
Justin C. Miller
d1c0723b44 [kernel] Fix memory clobbering from endpoint
The endpoint receive syscalls can block and then write to userspace
memory. Since the current address space may be different after blocking,
make sure to only actually write to the user memory after returning to
the syscall handler - pass values that are on the syscall handler stack
deeper into the kernel.
2021-01-18 13:49:10 -08:00
Justin C. Miller
14ed6af433 [kernel] Give processes and threads self handles
It was not consistent how processes got handles to themselves or their
threads, ending up with double entries. Now make such handles automatic
and expose them with new self_handle() methods.
2021-01-18 13:49:10 -08:00
Justin C. Miller
1325706c7c [kutil] Remove uint64_t hash_node specialization
Using a hash of zero to signal an empty slot doesn't play nice with the
hash_node specialization that uses the key for the hash, when 0 is a
common key.

I thought it would be ok, that it'd just be something to remember. But
then I used 0 as a key anyway, so clearly it was a bad idea.
2021-01-18 13:49:10 -08:00
Justin C. Miller
f5ab00a055 [boot] Reduce loader spam
Now that the ELF loader is known to be working correctly, remove its
extra print statements about section loading to keep the bootloader
output to one screen.
2021-01-18 13:49:10 -08:00
Justin C. Miller
dcdfc30c45 [build] Remove fake terminal.elf
A fake terminal.elf (copy of nulldrv.elf) was added to test the loader.
Now that there actually are multiple programs to load, remove the fake
one.
2021-01-18 13:49:10 -08:00
Justin C. Miller
a0d165c79b [scripts] Ignore demangle errors building sym table
For some reason, cxxfilt fails to demangle some names on some systems.
Instead of failing the build process, just skip those symbols.
2021-01-18 13:49:10 -08:00
Justin C. Miller
7b23310d8b [scripts] Allow qemu.sh to not remove VGA device
Added --vga option to qemu.sh to stop it from passing "-vga none" to
qemu. This allows the console version to act like it has a video device.
2021-01-18 13:48:11 -08:00
Justin C. Miller
e477dea5c7 [fb] Output klog to fb if video exists
If there's no video, do as we did before, otherwise route logs to the fb
driver instead. (Need to clean this up to just have a log consumer
general interface?) Also added a "scrollback" class to fb driver and
updated the system_get_log syscall.
2021-01-18 13:48:11 -08:00
Justin C. Miller
dccb136c99 [fb] Change to embedding PSF file
Moved old PSF parsing code from kernel, and switched to embedding whole
PSF instead of just glyph data to make font class the same code paths
for both cases.
2021-01-18 13:48:11 -08:00
Justin C. Miller
6af29a7181 [fb] Add default hard-coded font
For the fb driver to have a font before loading from disk is available,
create a hard-coded font as a byte array.

To create this, added a new scripts/psf_to_cpp.py which also refactored
out much of scripts/parse_font.py into a new shared module
scripts/fontpsf.py.
2021-01-18 13:48:11 -08:00
7ca3a19eed [kernel] Fix vm_space extra deletion
vm_space::clear() was freeing pages on process exit even when free was
false, and potentially double-freeing some pages.
2021-01-18 13:48:11 -08:00
7fcb4efab6 [kernel] Improve process init
Move process init from each process needing a main.s with _start to
crt0.s in libc. Also change to a sysv-like initial stack with a
j6-specific array of initialization values after the program arguments.
2021-01-18 13:48:11 -08:00
a8024d3dd3 [kernel] Rename kernel entrypoint
The kernel entrypoint being named _start conflicts with userspace
program entrypoints and makes debugging more difficult. Rename it to
_kernel_start.
2021-01-18 13:48:11 -08:00
8bb78c95a8 [tools] Improve j6stack GDB command
Improve the j6stack command in two ways: first, swap the order of the
arguments, as depth is much more likely to be changed. Second, on any
exception accessing memory in the stack, print the exception and
continue instead of failing the whole command.
2021-01-18 13:48:11 -08:00
19cbf1ca67 [fb] Create fb driver
Create a new framebuffer driver. Also hackily passing frame buffer size
in the list of init handles to all processes and mapping the framebuffer
into all processes. Changed bootloader passing frame buffer as a module
to its own struct.
2021-01-18 13:48:11 -08:00
Justin C. Miller
e70eb5a926 Merge branch 'master' of github.com:justinian/jsix 2021-01-06 23:30:53 -08:00
7bdde2d359 [kernel] Fix console line endings
The console's putc() was looking for CRs and if it saw one, appending an
LF. The output was only writing LFs, though, so instead what's needed is
to look for LFs, and if it sees one, insert a CR first.
2020-12-23 12:36:43 -08:00
Justin C. Miller
3daa07e311 [scripts] Fix demangle error
On my laptop, a few symbol names raise errors as invalid when attempting
to demangle them. This should not stop the rest of the symbol table from
building.
2020-11-10 01:31:23 -08:00
Justin C. Miller
47fab631d0 [boot] Fix compile warning about struct init
The buffer structure was getting initialized with out of order members.
2020-11-10 01:29:38 -08:00
2e3d7b1656 [kernel] Minor cleanups that have been sitting
Removal of an unused header and fixing a lint warning that a define
could be unset.
2020-11-10 01:17:58 -08:00
bf600a7608 [kutil] Add djb hash as 32 bit constexpr hash
This didn't end up getting used, but I'm adding it for later use.
2020-11-10 01:15:37 -08:00
6b00805d04 [kutil] Make vector size type templateable
Previously kutil::vector used size_t as its size type. Since most uses
in the kernel will never approach 4 billion items, default the size type
to uint32_t but make it an optional template argument. This saves 8
bytes per vector, which can be non-trivial with lots of vectors.
2020-10-18 20:50:31 -07:00
82b7082fc5 [kernel] Make koid generation per-type
Previously koids were a single global counter tagged with the type in
the top bits. Now there are per-type counters that each increment
separately.
2020-10-18 20:48:09 -07:00
8bb9e22218 [kernel] Move bind_irq syscall to new system object
In order to implement capabilities on system resources like IRQs so that
they may be restricted to drivers only, add a new 'system' kobject type,
and move the bind_irq functionality from endpoint to system.

Also fix some stack bugs passing the initial handles to a program.
2020-10-18 20:45:06 -07:00
2ad90dcb5c [kernel] Remove old unused crti/crtn
These were never used because clang generates .ctors and .dtors instead
of .init and .fini
2020-10-07 20:18:49 -07:00
97ea77bd27 [kernel] Consolodate koid and close syscalls
A number of object types had _close or _koid syscalls. Moved those to be
generic for kobject.
2020-10-05 21:51:42 -07:00
1904e240cf [kernel] Let endpoints get interrupt notifications
- Add a tag field to all endpoint messages, which doubles as a
  notification field
- Add a endpoint_bind_irq syscall to enable an endpoint to listen for
  interrupt notifications. This mechanism needs to change.
- Add a temporary copy of the serial port code to nulldrv, and let it
  take responsibility for COM2
2020-10-05 01:06:49 -07:00
4ccaa2dfea [boot] Load programs in boot, not kernel
Remove ELF and initrd loading from the kernel. The bootloader now loads
the initial programs, as it does with the kernel. Other files that were
in the initrd are now on the ESP, and non-program files are just passed
as modules.
2020-10-04 17:11:03 -07:00
da38006f44 [kernel] Remove obsolete 'mappings' list from VMAs
The vm_area_shared type of VMA used to track mappings in a separate
array, which doubled information and wasted space. This was no longer
used, and is now removed.
2020-09-27 21:47:35 -07:00
87b0a93d32 [kernel] Have thread call scheduler on blocking
Instead of making every callsite that may make a thread do a blocking
operation also invoke the scheduler, move that logic into thread
implementation - if the thread is blocking and is the current thread,
call schedule().

Related changes in this commit:

- Also make exiting threads and processes call the scheduler when
  blocking.
- Threads start blocked, and get automatically added to the scheduler's
  blocked list.
2020-09-27 21:35:15 -07:00
ff78c951f0 [libc] Implement sbrk to allow malloc() to work
Userspace can now allocte via malloc. This is slightly janky because it
relies on a single static handle in the library code.
2020-09-27 17:31:23 -07:00
2d44e8112b [kernel] Remove 'allowed' page table flag
The allowed flag was janky and easy to get lost when doing page table
manipulation. All allocation goes throug vm_area now, so 'allowed' can
be dropped.
2020-09-27 16:06:25 -07:00
f7f8bb3f45 [kernel] Replace buffer_cache with vm_area_buffers
In order to reduce the amount of tracked state, now use the
vm_area_buffers instead of a VMA with buffer_cache on top.
2020-09-27 15:34:24 -07:00
67ebc58812 [kernel] Allow for more than three syscall args
The rcx register is used by the function call ABI for the 4th argument,
but is also clobbered by SYSCALL to hold the IP. The r10 register is
caller-saved but not part of the ABI, so stash rcx there when crossing
the syscall boundary.
2020-09-26 22:01:21 -07:00
13aee1755e [kernel] Spit out vm_area types
The vm_space allow() functionality was a bit janky; using VMAs for all
regions would be a lot cleaner. To that end, this change:

- Adds a "static array" ctor to kutil::vector for setting the kernel
  address space's VMA list. This way a kernel heap VMA can be created
  without the heap already existing.
- Splits vm_area into different subclasses depending on desired behavior
- Splits out the concept of vm_mapper which maps vm_areas to vm_spaces,
  so that some kinds of VMA can be inherently single-space
- Implements VMA resizing so that userspace can grow allocations.
- Obsolete page_table_indices is removed

Also, the following bugs were fixed:

- kutil::map iterators on empty maps no longer break
- memory::page_count was doing page-align, not page-count

See: Github bug #242
See: [frobozz blog post](https://jsix.dev/posts/frobozz/)

Tags:
2020-09-26 21:47:15 -07:00
0e0975e5f6 [kernel] Add VMA interface
Finished the VMA kobject and added the related syscalls. Processes can
now allocate memory! Other changes in this commit:

- stop using g_frame_allocator and add frame_allocator::get()
- make sure to release all handles in the process dtor
- fix kutil::map::iterator never comparing to end()
2020-09-23 00:29:05 -07:00
d4283731e4 [kernel] Add syscall helpers
Added the syscalls/helpers.h file to templatize common kobject syscall
operations. Also moved most syscall implementations to using
process::current() and thread::current() instead of asking the
scheduler.
2020-09-23 00:22:15 -07:00
6780ab1b67 [abi] Add j6_err_exists
This error was used by code that didn't end up getting used, but it's a
useful error to keep around.
2020-09-23 00:18:06 -07:00
41eac2764a [kernel] Fix threads and procs never deleting
A check was added in scheduler::prune() which defers deleting threads
and processes if they're the current ones. However, they were still
getting removed from the block list, so they were being leaked.
2020-09-23 00:15:23 -07:00
113d14c440 [kernel] Get rid of page_manager
page_manager is dead - final uses replaced in vm_space (page_in and
clear). Removed the header and cpp, and other lingering references.
2020-09-20 16:16:23 -07:00
abe523be77 [kernel] Remove page_manager::map_pages
The last use of page_manager::map_pages was during loading of ELF
binaries. Switched to vm_space::allow instead.
2020-09-20 14:06:06 -07:00
1b392a0551 [kernel] Move page mapping into vm_space
vm_space no longer relies on page_manager to map pages during a page
fault. Other changes that come with this commit:

- C++ standard has been changed to C++17
- enum bitfield operators became constexpr
- enum bifrield operators can take a mix of ints and enum arguments
- added page table flags enum instead of relying on ints
- remove page_table::unmap_table and page_table::unmap_pages
2020-09-20 10:52:57 -07:00
deb2fa0a09 [kernel] Use kernel proc space as kernel space
As mentioned in the last commit, with processes owning spaces, there was
a weird extra space in the "kernel" process that owns the kernel
threads. Now we use that space as the global kernel space, and don't
create a separate one.
2020-09-18 01:58:46 -07:00
671a0ce0fb [kernel] Move pml4 create/delete into vm_space
vm_space and page_table continue to take over duties from
page_manager:

- creation and deletion of address spaces / pml4s
- cross-address-space copies for endpoints
- taking over pml4 ownership from process

Also fixed the bug where the wrong process was being set in the cpu
data.

To solve: now the kernel process has its own vm_space which is not
g_kernel_space.
2020-09-18 01:22:49 -07:00
ac67111b83 [kernel] Move the page table cache into page_table
Further chipping away at page_manager: the cache of pages to be used as
page tables gets moved to a static in page_table.
2020-09-17 21:30:05 -07:00
09575370ce [kernel] Remove unecessary functions from page manager
In preparation for removing more from page manager, removed several
unecessary functions and all their callsites.
2020-09-17 01:33:10 -07:00
9aa08a70cf [kernel] Begin replacing page_manager with vm_space
This is the first commit of several reworking the VM system. The main
focus is replacing page_manager's global functionality with objects
representing individual VM spaces. The main changes in this commit were:

- Adding the (as yet unused) vm_area object, which will be the main
  point of control for programs to allocate or share memory.
- Replace the old vm_space with a new one based on state in its page
  tables. They will also be containers for vm_areas.
- vm_space takes over from page_manager as the page fault handler
- Commented out the page walking in memory_bootstrap; I'll probably need
  to recreate this functionality, but it was broken as it was.
- Split out the page_table.h implementations from page_manager.cpp into
  the new page_table.cpp, updated it, and added page_table::iterator as
  well.
2020-09-17 00:48:17 -07:00
ca7f78565d [tools] Add the j6bt command to gdb functions
Added the command j6bt (and renamed popc_stack to j6stack) to make
tracking kernel callstacks across interrupts easier.
2020-09-16 23:58:28 -07:00
dcb4d1823f [kutil] Update map iteration
Add an iterator type to kutil::map, and allow for each loops. Also
unify the compare() signature expected by sorting containers, and fixes
to adding and sorting in kutil::vector.
2020-09-15 00:21:28 -07:00
e8564c755b [kernel] Move vm_space into kernel
The vm_space code should not have been in kutil, moving it to kernel.
2020-09-13 16:11:24 -07:00
9dee5e4138 [kernel] Use map for process handles
Replace linearly-indexed vector of handles with new kutil::map. Also
provide thread::current() and process::current() accessors so that every
syscall doesn't need to include the scheduler to deduce the current
process.
2020-09-13 15:54:47 -07:00
245f260d67 [kutil] Allow for specialization in kutil::map
Restructure kutil::map to allow specialization to alter storage as well
as the public API.
2020-09-13 15:53:28 -07:00
5f27727e52 [tests] Upgrade Catch2 to version 2.13.1 2020-09-13 02:19:57 -07:00
1238608430 [kutil] Add kutil::map hash map
Added the kutil::map collection, an open addressing, robinhood hash map
with backwards shift deletes. Also added hash.h with templated
implementations of the FNV-1a 64 bit hash function, and pulled the log2
function out of the heap_allocator code into the new util.h.
2020-09-12 00:31:38 -07:00
71cd7af17b [kutil] Add sorted insert to kutil::vector
Added a sorted insert to vector, as well as cleaning up and extending
the removal functions.
2020-09-07 16:59:21 -07:00
8490472581 [kutil] Fix failing heap allocator tests
The tests clearly haven't even been built in a while. I've added a
helper script to the project root. Also added a kassert() handler that
will allow tests to catch or fail on asserts.
2020-09-07 16:56:07 -07:00
8534d8d3c5 [kernel] Add endpoint object and related syscalls
The endpoint object adds synchronous IPC. Also added the wait-type of
'object' to threads.
2020-09-07 01:09:56 -07:00
53d260cc6e [kernel] Allow unmapping non-fully-mapped areas
The buffer cache will try to clean up a returned buffer by unmapping it,
but it may have only been committed without ever getting mapped. Allow
for page_out to handle non-fully mapped regions.
2020-09-06 16:31:02 -07:00
53a4682418 [kernel] Fix current thread deletion bug
Defer from calling process::thread_exited() in scheduler::prune() if the
thread in question is the currently-executing thread, so that we don't
blow away the stack we're executing on. The next call to prune will pick
up the exited thread.
2020-09-06 15:01:24 -07:00
42455873ff [kernel] Make stdout channel available to processes
The "fake" stdout channel is now being passed in the new j6_process_init
structure to processes, and nulldrv now uses it to print a message to
the console.
2020-08-30 18:47:14 -07:00
724b846ee4 [kernel] Make channels stream based
Multiple changes regarding channels. Mainly channels are now stream
based and can handle partial reads or writes. Channels now use the
kernel buffers area with the related buffer_cache. Added a fake stdout
stream channel and kernel task to read its contents to the screen in
preparation for handing channels as stdin/stdout to processes.
2020-08-30 18:04:19 -07:00
f27b133089 [kernel] Rename stack_cache to buffer_cache
Renamed and genericized the stack_cache class to manage any address
range area of buffers or memory regions. Removed singleton and created
some globals that map to different address regions (kernel stacks,
kernel buffers).

Tags: vmem virtual memeory
2020-08-30 17:59:13 -07:00
773617cbf3 [libc] Created syscall trampolines in libc
Using syscalls.inc (moved to src/include) generate trampoline functions
for directly calling syscalls with libc functions.
2020-08-23 18:14:45 -07:00
44e29b3c4a [build] Fix dump_cc_run not creating bash scripts
It was the difference between > and >>
2020-08-23 17:43:18 -07:00
95a35cd0bf [libc] Bring libc in-tree
Moving libc from its separate repo into this one, minor resulting build
fixes, and a hacky way to add -I for libc headers in builds.
2020-08-23 17:21:08 -07:00
28b800a497 [kernel] Initialize logger task after symbols init
In case something blows up when initializing kernel tasks, make sure
symbol table init is done beforehand.
2020-08-23 17:18:45 -07:00
838776d7df [kernel] Fix bug in vmem commit
When committing an area of vmem and splitting from a larger block, the
block that is returned was set to the unknown state, and the leading
block was incorrectly set to the desired state.

Also remove extra unused thread ctor.
2020-08-23 17:11:46 -07:00
e19fa377d7 [kernel] Remove old unused process.*
These files are not included in the build, but had not been removed.
2020-08-23 17:10:14 -07:00
d63a30728c [kernel] Add get_caller function
Added a function to get the callsite of the current function invocation.
2020-08-22 16:34:58 -07:00
4819920b4e [kutil] Add unreserve/uncommit to vm_space
Added the functionality for unreseve and uncommit to vm_space. Also
added the documented but not implemented ability to pass a 0 address to
get any address region with the given size.
2020-08-16 17:19:17 -07:00
bbb9aae198 [kernel] Use symbol table in stack traces
Now using the symbol table built by build_symbol_table.py in the kernel
when printing stack traces.
2020-08-09 17:27:51 -07:00
0d94776c46 [build] Remove makest from build script
Removing the outdated reference to makest from the build scripts, which
should have been removed as part of the build_symbol_table.py change.
2020-08-09 17:23:14 -07:00
565a5ee960 [makerd] Fix argc bug
Makerd was improperly checking argc for its usage.
2020-08-06 21:12:36 -07:00
e7f9d8f1d7 [scripts] Add symbol table building script
Create a script that builds a simple-to-read symbol table from the
output of `nm`. Include running that script over the kernel in the
build, and including that output in the initrd.

Tags: callstack debugging
2020-08-06 21:11:19 -07:00
55bc49598e [kernel] Assert all PDs exist in kernel space
The bootloader now creates all PD tables in kernel space, so remove
memory_bootstrap.cpp code that dealt with cases where there was no PD
for a given range, and kassert that all PDs exist.

Also deal with the case where the final PD exists, which never committed
the last address range.
2020-08-06 19:07:51 -07:00
579eaaf4a0 [kernel] Move kernel stacks out of the heap
We were previously allocating kernel stacks as large objects on the
heap. Now keep track of areas of the kernel stack area that are in use,
and allocate them from there. Also required actually implementing
vm_space::commit(). This still needs more work.
2020-08-02 18:15:28 -07:00
b98334db28 [kernel] Don't keep creating new processes for kernel tasks
The scheduler was making new process objects every time it made kernel
task threads. Don't do that.
2020-08-02 18:13:09 -07:00
e1b1b5d357 [kernel] Don't double-construct the scheduler
The scheduler singleton was getting constructed twice, once at static
time and then again in main(). Make the singleton a pointer so we only
construct it once.
2020-08-02 18:11:09 -07:00
cf582c4ce4 [boot] Add PD tables for all kernel PML4 entries
Process PML4s all point their high (kernelspace) entries at the same set
of PDs, but that copying only happens on process creation. New PDs added
to the kernel PML4 won't get shared among other PML4s. This change
instantiates empty PDs for all PML4 entries in the higher half to make
sure this can't happen.
2020-08-02 18:06:41 -07:00
911b4c0b10 [kutil] Fix a bug with order ignoring header size
When moving heap allocation order calculation to use __builtin_clz, we
based that calculation off of the length requested, not the totla length
including the memory header size.
2020-08-02 16:54:06 -07:00
94c1f0d3fc [kutil] Calculate block-size order with clz
Previously we were using a less efficient loop method of finding the
appropriate block size order, now we use __builtin_clz, which should use
the CPU's clz instruction if it's available.
2020-07-30 19:54:25 -07:00
73221dfe34 [kutil] Rename 'size' to 'order' when meaning 2^N
Heap allocator is a buddy allocator that deals with power-of-two block
sizes. Previously it referred to both a number of bytes and an order of
magnitude as a 'size'. Rename functions and variables referring to
orders of magnitude to 'order'.

Tags: pedantry
2020-07-30 19:41:41 -07:00
4ffd4949ca [kernel] Clean up threads' kernel stacks on exit
Add a destructor to threads in order to deallocate their kernel stacks.
2020-07-30 19:32:31 -07:00
3f137805bc [kutil] Tracks allocated size in heap allocator
Add a member that keeps track of allocated size to the heap allocator.
This isn't exposed, but is handy for debugging.
2020-07-30 19:30:53 -07:00
6a00057817 [kernel] Remove process startup_bonus to timeslice
Previously we added startup_bonus to work around a segfault happening
when we preemted a newly created process instead of letting it give up
the CPU. Bug is not longer occuring, though that makes me nervous.
2020-07-30 19:11:51 -07:00
58bc5acb1e [kernel] Add object_signal system call
Add a system call to assert signals on a given object, only within the
range of user-settable signals. Also made object_wait return
immediately if any of the given signals are already set.
2020-07-26 18:03:30 -07:00
d3e9d92466 [kernel] Add channel objects
Add the channel object for sending messages between threads. Currently
no good of passing channels to other threads, but global variables in a
single process work. Currently channels are slow and do double copies,
need to refine more.

Tags: ipc
2020-07-26 17:29:11 -07:00
ae3290c53d [kernel] Add userspace threading
Implement the syscalls necessary for threads to create other threads in
their same process. This involved rearranging a number of syscalls, as
well as implementing object_wait and a basic implementation of a
process' list of handles.
2020-07-26 16:02:38 -07:00
4cf222a5bb [kernel] Remove getpid and fork system calls
The getpid and fork system calls were stubbed out previously, this
commit removes them and adds process_koid as a getpid replacement.
2020-07-19 17:15:36 -07:00
c3abe035c8 [kernel] Remove thread_data pointer from TCB
The TCB is always stored at a constant offset within the thread object.
So instead of carrying an extra pointer, just implement thread::from_tcb
to get the thread.
2020-07-19 17:01:15 -07:00
ef5c333030 [kernel] Create process kernel object
Re-implent the concept of processes as separate from threads, and as a
kobject API object. Also improve scheduler::prune which was doing some
unnecessary iterations.
2020-07-19 16:47:18 -07:00
f4cbb9498f [kernel] Fix clock period vs frequency error
Calling `spinwait()` was hanging due to improper computation of the
clock rate because justin did a dumb at math. Also the period can be
greater than 1ns, so the clock's units were updated to microseconds.
2020-07-12 17:43:37 -07:00
794c86f9b4 [kernel] Add thead kobject class
Add the thread kernel API object and move the scheduler to use threads
instead of processes for scheduling and task switching.
2020-07-12 16:07:20 -07:00
Justin C. Miller
8687fe3786 [boot] Zero extra memory in loaded sections
When loading ELF headers (as opposed to sections), the `file_size` of
the data may be smaller than the `mem_size` of the section to be loaded
in memory. Don't blindly copy `mem_size` bytes from the ELF file, but
instead only `file_size`, then zero the rest.

Tags: elf loader
2020-07-04 18:20:49 -07:00
Justin C. Miller
0a28d2db07 [kernel] Update scheduler policies
A few updates to scheduler policies:
* Grant processes a startup timeslice bonus for time spent loading the
  process
* Grant processes a small fraction of a timeslice for yielding the CPU
  with time left
2020-07-03 15:21:03 -07:00
Justin C. Miller
6c468a134b [kernel] Add HPET support, create clock class
Create a clock class which can be queried for current timestamp in
nanoseconds. Also implements a simple HPET class as one possible clock
source.

Tags: time
2020-06-28 17:49:31 -07:00
Justin C. Miller
9b67f87062 [kernel] Add external/ to kernel includes
In order to support future changes, the kernel should also be able to
include from the external/ tree.
2020-06-28 11:51:51 -07:00
Justin C. Miller
b4adc29d7f [kernel] Give scheduler better history tracking
The scheduler again tracks remaining timeslice. Timeslices are bigger,
but once a process uses all of its timeslice, it's demoted and
replenished at the next priority. The scheduler also tracks the last
time a process ran, and promotes it if it's been starved for twice its
full timeslice.

TODO: replenish a small amount of timeslice each time a process is run,
so that more interactive processes keep their priorities.
2020-06-05 00:15:03 -07:00
Justin C. Miller
a10aca573d [kernel] Change to one-shot timer scheduler
Instead of many timer interrupts and decrementing a process' remaining
quanta, change to setting a single timer for when a process should be
preempted. If it uses its whole timeslice, demote it. If it uses less
than half before blocking, promote it. Determine timeslice based on
priority as well.

This change also required changing the apic timer interface to be purely
interval (in microseconds) based instead of its previous interval/tick
hybrid.
2020-06-03 20:56:59 -07:00
Justin C. Miller
ea1224e213 [kernel] Fix scheduler clock bug
The fake clock in the scheduler wasn't getting initialized, so sleeps in
the test userspace programs were returning immediately.
2020-06-02 23:46:03 -07:00
Justin C. Miller
64ad65fa1b [kernel] Fix logger task's bip buffer
The bip buffer implementation was not initializing it's write-state
member, which was causing logs to always fail when the logger was not in
immediate mode.
2020-06-02 20:30:07 -07:00
Justin C. Miller
b881b2639d [kernel] Remove last of old allocator interface
Removing the `allocator.h` file defining the `kutil::allocator`
interface, now that explicit allocators are not being passed around.
Also removed the unused `frame_allocator::raw_allocator` class and
`kutil::invalid_allocator` object.

Tags: memory
2020-06-01 23:40:19 -07:00
Justin C. Miller
a5f72edf82 [kernel] Add in_hv cpu "feature" flag
Reformat the cpu_features.inc file and add the `in_hv` feature that is
supposedly set by hypervisors when running in emulation. QEMU does not
set it.

Tags: cpuid
2020-06-01 23:33:30 -07:00
Justin C. Miller
ea2a3e6f16 [kutil] Remove ctor workaround in slab_allocated
Before global constructors were working, I had added a hack to zero out
the static vector member of in `slab_allocated`. Now that they are
working, this can be removed.
2020-06-01 00:36:34 -07:00
Justin C. Miller
2125f043b6 [kernel] Remove global constructor test class
Removing a test class that wasn't meant to be checked in.
2020-06-01 00:34:37 -07:00
Justin C. Miller
655b5c88d4 [kernel] Remove obsolete 1MiB offset
Back when the bootloader could only mirror higher half memory linearly
to the lower half, the kernel couldn't be loaded at the beginning of
kernel space because it was unlikely to find enough pages there, so it
was loaded at +1MiB in kernel space. Now the bootloader can map any
pages to the necessary locations, the offset can be removed.

Tags: elf linker virtual memory
2020-06-01 00:01:07 -07:00
Justin C. Miller
88b090fe94 [kernel] Run global constructors
Look up the global constructor list that the linker outputs, and run
them all. Required creation of the `kutil::no_construct` template for
objects that are constructed before the global constructors are run.

Also split the `memory_initialize` function into two - one for just
those objects that need to happen before the global ctors, and one
after.

Tags: memory c++
2020-05-31 23:58:01 -07:00
Justin C. Miller
c6c3a556b3 [kernel] Remove explicit allocator passing
Many kernel objects had to keep a hold of refrences to allocators in
order to pass them on down the call chain. Remove those explicit
refrences and use `operator new`, `operator delete`, and define new
`kalloc` and `kfree`.

Also remove `slab_allocator` and replace it with a new mixin for slab
allocation, `slab_allocated`, that overrides `operator new` and
`operator free` for its subclass.

Remove some no longer used related headers, `buddy_allocator.h` and
`address_manager.h`

Tags: memory
2020-05-31 18:22:23 -07:00
423 changed files with 33393 additions and 6030 deletions

2
.gitignore vendored
View File

@@ -3,8 +3,10 @@
*.bak
tags
jsix.log
*.out
*.o
*.a
sysroot
.gdb_history
.peru
__pycache__

View File

@@ -1,15 +1,18 @@
# jsix: A toy OS kernel
![jsix](assets/jsix.svg)
**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 jsix operating system
**jsix** is a custom multi-core x64 operating system that I am building from
scratch. It's far from finished, or even being usable - see the *Status and
Roadmap* section, below.
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,
CPUs and UEFI firmware. (See [this list][cpu_features] for the currently
required CPU features.) 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.
@@ -17,13 +20,11 @@ The design goals of the project are:
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.
by the traditional microkernel problems.
* 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.
modern OS development. Initial feature implementations may temporarily throw
away modular design to allow for exploration of 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
@@ -31,6 +32,69 @@ 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.
[cpu_features]: https://github.com/justinian/jsix/blob/master/src/libraries/cpu/include/cpu/features.inc
## Status and Roadmap
The following major feature areas are targets for jsix development:
#### UEFI boot loader
_Done._ The bootloader loads the kernel and initial userspace programs, and
sets up necessary kernel arguments about the memory map and EFI GOP
framebuffer. Possible future ideas:
- take over more init-time functions from the kernel
- rewrite it in Zig
#### Memory
_Virtual memory: Sufficient._ The kernel manages virtual memory with a number
of kinds of `vm_area` objects representing mapped areas, which can belong to
one or more `vm_space` objects which represent a whole virtual memory space.
(Each process has a `vm_space`, and so does the kernel itself.)
Remaining to do:
- TLB shootdowns
- Page swapping
_Physical page allocation: Sufficient._ The current physical page allocator
implementation suses a group of block representing up-to-1GiB areas of usable
memory as defined by the bootloader. Each block has a three-level bitmap
denoting free/used pages.
#### Multitasking
_Sufficient._ The global scheduler object keeps separate ready/blocked lists
per core. Cores periodically attempt to balance load via work stealing.
User-space tasks are able to create threads as well as other processes.
Several kernel-only tasks exist, though I'm trying to reduce that. Eventually
only the timekeeping task should be a separate kernel-only thread.
#### API
_In progress._ User-space tasks are able to make syscalls to the kernel via
fast SYSCALL/SYSRET instructions.
Major tasks still to do:
- The process initialization protocol needs to be re-built entirely.
- Processes' handles to kernel objects need the ability to check capabilities
#### Hardware Support
* Framebuffer driver: _In progress._ Currently on machines with a video
device accessible by UEFI, jsix starts a user-space framebuffer driver that
only prints out kernel logs.
* Serial driver: _To do._ Machines without a video device should have a
user-space log output task like the framebuffer driver, but currently this
is done inside the kernel.
* USB driver: _To do_
* AHCI (SATA) driver: _To do_
## Building
jsix uses the [Ninja][] build tool, and generates the build files for it with a
@@ -38,7 +102,7 @@ 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
[Bonnibel]: https://github.com/justinian/bonnibel_rs
[Cargo]: https://crates.io/crates/bonnibel
Requrirements:
@@ -71,8 +135,8 @@ 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
sudo apt install qemu-system-x86 nasm clang-10 mtools curl ninja-build
sudo update-alternatives /usr/bin/clang clang /usr/bin/clang-10 1000
sudo update-alternatives /usr/bin/clang++ clang++ /usr/bin/clang++-10 1000
curl -L -o pb https://github.com/justinian/bonnibel_rs/releases/download/v2.3.0/pb-linux-amd64 && chmod a+x pb

View File

@@ -2,7 +2,7 @@ import gdb
class PrintStackCommand(gdb.Command):
def __init__(self):
super().__init__("popc_stack", gdb.COMMAND_DATA)
super().__init__("j6stack", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
args = gdb.string_to_argv(arg)
@@ -16,13 +16,117 @@ class PrintStackCommand(gdb.Command):
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)))
try:
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)))
except Exception as e:
print(e)
continue
class PrintBacktraceCommand(gdb.Command):
def __init__(self):
super().__init__("j6bt", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
args = gdb.string_to_argv(arg)
depth = 30
if len(args) > 0:
depth = int(args[0])
frame = "$rbp"
if len(args) > 1:
frame = args[1]
for i in range(depth-1, -1, -1):
ret = gdb.parse_and_eval(f"*(uint64_t*)({frame} + 8)")
frame = gdb.parse_and_eval(f"*(uint64_t*)({frame})")
name = ""
block = gdb.block_for_pc(int(ret))
if block:
name = block.function or ""
print("{:016x} {}".format(int(ret), name))
if frame == 0 or ret == 0:
return
class TableWalkCommand(gdb.Command):
def __init__(self):
super().__init__("j6tw", gdb.COMMAND_DATA)
def invoke(self, arg, from_tty):
args = gdb.string_to_argv(arg)
if len(args) < 2:
raise Exception("Must be: j6tw <pml4> <addr>")
pml4 = int(gdb.parse_and_eval(args[0]))
addr = int(gdb.parse_and_eval(args[1]))
indices = [
(addr >> 39) & 0x1ff,
(addr >> 30) & 0x1ff,
(addr >> 21) & 0x1ff,
(addr >> 12) & 0x1ff,
]
names = ["PML4", "PDP", "PD", "PT"]
table_flags = [
(0x0001, "present"),
(0x0002, "write"),
(0x0004, "user"),
(0x0008, "pwt"),
(0x0010, "pcd"),
(0x0020, "accessed"),
(0x0040, "dirty"),
(0x0080, "largepage"),
(0x0100, "global"),
(0x1080, "pat"),
((1<<63), "xd"),
]
page_flags = [
(0x0001, "present"),
(0x0002, "write"),
(0x0004, "user"),
(0x0008, "pwt"),
(0x0010, "pcd"),
(0x0020, "accessed"),
(0x0040, "dirty"),
(0x0080, "pat"),
(0x0100, "global"),
((1<<63), "xd"),
]
flagsets = [table_flags, table_flags, table_flags, page_flags]
table = pml4
entry = 0
for i in range(len(indices)):
entry = int(gdb.parse_and_eval(f'((uint64_t*){table})[{indices[i]}]'))
flagset = flagsets[i]
flag_names = " | ".join([f[1] for f in flagset if (entry & f[0]) == f[0]])
print(f"{names[i]:>4}: {table:016x}")
print(f" index: {indices[i]:3} {entry:016x}")
print(f" flags: {flag_names}")
if (entry & 1) == 0 or (i < 3 and (entry & 0x80)):
break
table = (entry & 0x7ffffffffffffe00) | 0xffffc00000000000
PrintStackCommand()
PrintBacktraceCommand()
TableWalkCommand()
gdb.execute("target remote :1234")
gdb.execute("display/i $rip")

View File

@@ -12,6 +12,11 @@
dest = "screenfont.psf"
source = "../assets/fonts/tamsyn8x16r.psf"
[[files]]
dest = "symbol_table.dat"
source = "symbol_table.dat"
symbols = true
[[files]]
dest = "nulldrv1"
source = "user/nulldrv"

1
assets/jsix.svg Executable file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="176.562 356.069 211.11 113" width="211.11pt" height="113pt"><g><g><rect x="176.562" y="356.069" width="211.11" height="113" transform="matrix(1,0,0,1,0,0)" fill="rgb(255,255,255)"/><g><path d=" M 212.981 372.36 L 219.564 376.16 L 226.147 379.961 L 226.147 387.563 L 226.147 395.164 L 219.564 398.965 L 212.981 402.766 L 206.398 398.965 L 199.815 395.164 L 199.815 387.563 L 199.815 379.961 L 206.398 376.16 L 212.981 372.36 L 212.981 372.36 L 212.981 372.36 Z M 256.292 397.366 L 262.875 401.166 L 269.458 404.967 L 269.458 412.569 L 269.458 420.17 L 262.875 423.971 L 256.292 427.772 L 249.709 423.971 L 243.126 420.17 L 243.126 412.569 L 243.126 404.967 L 249.709 401.166 L 256.292 397.366 L 256.292 397.366 Z M 183.622 387.283 L 205.52 374.64 L 227.418 361.997 L 249.316 374.64 L 271.214 387.283 L 271.214 412.569 L 271.214 437.854 L 249.316 450.497 L 227.418 463.14 L 205.52 450.497 L 183.622 437.854 L 183.622 412.569 L 183.622 387.283 L 183.622 387.283 L 183.622 387.283 Z M 241.855 372.36 L 248.438 376.16 L 255.021 379.961 L 255.021 387.563 L 255.021 395.164 L 248.438 398.965 L 241.855 402.766 L 235.272 398.965 L 228.689 395.164 L 228.689 387.563 L 228.689 379.961 L 235.272 376.16 L 241.855 372.36 Z " fill-rule="evenodd" fill="rgb(49,79,128)"/><path d=" M 298.642 379.579 L 291.621 379.579 L 291.621 372.558 L 298.642 372.558 L 298.642 379.579 Z M 285.214 446.718 L 285.214 441.452 L 287.32 441.452 L 287.32 441.452 Q 289.339 441.452 290.524 440.092 L 290.524 440.092 L 290.524 440.092 Q 291.708 438.731 291.708 436.625 L 291.708 436.625 L 291.708 387.039 L 298.729 387.039 L 298.729 436.011 L 298.729 436.011 Q 298.729 440.925 295.921 443.822 L 295.921 443.822 L 295.921 443.822 Q 293.113 446.718 288.286 446.718 L 288.286 446.718 L 285.214 446.718 Z M 306.628 432.676 L 306.628 427.41 L 314.088 427.41 L 314.088 427.41 Q 317.862 427.41 319.573 425.347 L 319.573 425.347 L 319.573 425.347 Q 321.285 423.285 321.285 419.95 L 321.285 419.95 L 321.285 419.95 Q 321.285 417.317 319.705 415.474 L 319.705 415.474 L 319.705 415.474 Q 318.125 413.631 314.966 411.174 L 314.966 411.174 L 314.966 411.174 Q 312.245 408.98 310.621 407.356 L 310.621 407.356 L 310.621 407.356 Q 308.998 405.732 307.813 403.319 L 307.813 403.319 L 307.813 403.319 Q 306.628 400.905 306.628 397.746 L 306.628 397.746 L 306.628 397.746 Q 306.628 393.095 309.744 390.067 L 309.744 390.067 L 309.744 390.067 Q 312.859 387.039 318.125 387.039 L 318.125 387.039 L 325.76 387.039 L 325.76 392.305 L 319.441 392.305 L 319.441 392.305 Q 313.21 392.305 313.21 398.185 L 313.21 398.185 L 313.21 398.185 Q 313.21 400.467 314.615 402.134 L 314.615 402.134 L 314.615 402.134 Q 316.019 403.802 319.003 406.083 L 319.003 406.083 L 319.003 406.083 Q 321.723 408.19 323.479 409.901 L 323.479 409.901 L 323.479 409.901 Q 325.234 411.613 326.463 414.202 L 326.463 414.202 L 326.463 414.202 Q 327.691 416.791 327.691 420.301 L 327.691 420.301 L 327.691 420.301 Q 327.691 426.532 324.4 429.604 L 324.4 429.604 L 324.4 429.604 Q 321.109 432.676 315.141 432.676 L 315.141 432.676 L 306.628 432.676 Z M 342.611 379.579 L 335.59 379.579 L 335.59 372.558 L 342.611 372.558 L 342.611 379.579 Z M 342.611 432.676 L 335.59 432.676 L 335.59 387.039 L 342.611 387.039 L 342.611 432.676 Z M 356.126 432.676 L 348.754 432.676 L 361.392 409.77 L 349.632 387.039 L 356.39 387.039 L 364.639 403.187 L 372.977 387.039 L 379.735 387.039 L 367.974 409.77 L 380.612 432.676 L 373.24 432.676 L 364.639 416.001 L 356.126 432.676 Z " fill="rgb(49,79,128)"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

2385
external/catch/catch.hpp vendored

File diff suppressed because it is too large Load Diff

View File

@@ -14,8 +14,10 @@
namespace uefi {
namespace bs_impl {
using allocate_pages = status (*)(allocate_type, memory_type, size_t, void**);
using free_pages = status (*)(void*, size_t);
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 free_pool = status (*)(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);
@@ -35,10 +37,10 @@ struct boot_services {
// Memory Services
bs_impl::allocate_pages allocate_pages;
void *free_pages;
bs_impl::free_pages free_pages;
bs_impl::get_memory_map get_memory_map;
bs_impl::allocate_pool allocate_pool;
void *free_pool;
bs_impl::free_pool free_pool;
// Event & Timer Services
bs_impl::create_event create_event;

390
external/uefi/networking.h vendored Normal file
View File

@@ -0,0 +1,390 @@
#pragma once
#ifndef _uefi_networking_h_
#define _uefi_networking_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 {
//
// IPv4 definitions
//
struct ipv4_address
{
uint8_t addr[4];
};
//
// IPv6 definitions
//
struct ipv6_address
{
uint8_t addr[16];
};
struct ip6_address_info
{
ipv6_address address;
uint8_t prefix_length;
};
struct ip6_route_table
{
ipv6_address gateway;
ipv6_address destination;
uint8_t prefix_length;
};
enum class ip6_neighbor_state : int {
incomplete,
reachable,
stale,
delay,
probe,
}
struct ip6_neighbor_cache
{
ipv6_address neighbor;
mac_address link_address;
ip6_neighbor_state state;
};
enum class icmpv6_type : uint8_t
{
dest_unreachable = 0x1,
packet_too_big = 0x2,
time_exceeded = 0x3,
parameter_problem = 0x4,
echo_request = 0x80,
echo_reply = 0x81,
listener_query = 0x82,
listener_report = 0x83,
listener_done = 0x84,
router_solicit = 0x85,
router_advertise = 0x86,
neighbor_solicit = 0x87,
neighbor_advertise = 0x88,
redirect = 0x89,
listener_report_2 = 0x8f,
};
enum class icmpv6_code : uint8_t
{
// codes for icmpv6_type::dest_unreachable
no_route_to_dest = 0x0,
comm_prohibited = 0x1,
beyond_scope = 0x2,
addr_unreachable = 0x3,
port_unreachable = 0x4,
source_addr_failed = 0x5,
route_rejected = 0x6,
// codes for icmpv6_type::time_exceeded
timeout_hop_limit = 0x0,
timeout_reassemble = 0x1,
// codes for icmpv6_type::parameter_problem
erroneous_header = 0x0,
unrecognize_next_hdr = 0x1,
unrecognize_option = 0x2,
};
struct ip6_icmp_type
{
icmpv6_type type;
icmpv6_code code;
};
struct ip6_config_data
{
uint8_t default_protocol;
bool accept_any_protocol;
bool accept_icmp_errors;
bool accept_promiscuous;
ipv6_address destination_address;
ipv6_address station_address;
uint8_t traffic_class;
uint8_t hop_limit;
uint32_t flow_label;
uint32_t receive_timeout;
uint32_t transmit_timeout;
};
struct ip6_mode_data
{
bool is_started;
uint32_t max_packet_size;
ip6_config_data config_data;
bool is_configured;
uint32_t address_count;
ip6_address_info * address_list;
uint32_t group_count;
ipv6_address * group_table;
uint32_t route_count;
ip6_route_table * route_table;
uint32_t neighbor_count;
ip6_neighbor_cache * neighbor_cache;
uint32_t prefix_count;
ip6_address_info * prefix_table;
uint32_t icmp_type_count;
* icmp_type_list;
};
struct ip6_header
{
uint8_t traffic_class_h : 4;
uint8_t version : 4;
uint8_t flow_label_h : 4;
uint8_t traffic_class_l : 4;
uint16_t flow_label_l;
uint16_t payload_length;
uint8_t next_header;
uint8_t hop_limit;
ipv6_address source_address;
ipv6_address destination_address;
} __attribute__ ((packed));
struct ip6_fragment_data
{
uint32_t fragment_length;
void *fragment_buffer;
};
struct ip6_override_data
{
uint8_t protocol;
uint8_t hop_limit;
uint32_t flow_label;
};
struct ip6_receive_data
{
time time_stamp;
event recycle_signal;
uint32_t header_length;
ip6_header *header;
uint32_t data_length;
uint32_t fragment_count;
ip6_fragment_data fragment_table[1];
};
struct ip6_transmit_data
{
ipv6_address destination_address;
ip6_override_data *override_data;
uint32_t ext_hdrs_length;
void *ext_hdrs;
uint8_t next_header;
uint32_t data_length;
uint32_t fragment_count;
ip6_fragment_data fragment_table[1];
};
struct ip6_completion_token
{
event event;
status status;
union {
ip6_receive_data *rx_data;
ip6_transmit_data *tx_data;
} packet;
};
enum class ip6_config_data_type : int
{
interface_info,
alt_interface_id,
policy,
dup_addr_detect_transmits,
manual_address,
gateway,
dns_server,
maximum
};
struct ip6_config_interface_info
{
wchar_t name[32];
uint8_t if_type;
uint32_t hw_address_size;
mac_address hw_address;
uint32_t address_info_count;
ip6_address_info *address_info;
uint32_t route_count;
ip6_route_table *route_table;
};
struct ip6_config_interface_id
{
uint8_t id[8];
};
enum class ip6_config_policy : int
{
manual,
automatic
};
struct ip6_config_dup_addr_detect_transmits
{
uint32_t dup_addr_detect_transmits;
};
struct ip6_config_manual_address
{
ipv6_address address;
bool is_anycast;
uint8_t prefix_length;
};
//
// IP definitions
//
union ip_address
{
uint8_t addr[4];
ipv4_address v4;
ipv6_address v6;
};
//
// HTTP definitions
//
struct httpv4_access_point
{
bool use_default_address;
ipv4_address local_address;
ipv4_address local_subnet;
uint16_t local_port;
};
struct httpv6_access_point
{
ipv6_address local_address;
uint16_t local_port;
};
enum class http_version : int {
v10,
v11,
unsupported,
};
struct http_config_data
{
http_version http_version;
uint32_t time_out_millisec;
bool local_address_is_ipv6;
union {
httpv4_access_point *ipv4_node;
httpv6_access_point *ipv6_node;
} access_point;
};
enum class http_method : int {
get,
post,
patch,
options,
connect,
head,
put,
delete_,
trace,
};
struct http_request_data
{
http_method method;
wchar_t *url;
};
enum class http_status_code : int {
unsupported,
continue_,
switching_protocols,
ok,
created,
accepted,
non_authoritative_information,
no_content,
reset_content,
partial_content,
multiple_choices,
moved_permanently,
found,
see_other,
not_modified,
use_proxy,
temporary_redirect,
bad_request,
unauthorized,
payment_required,
forbidden,
not_found,
method_not_allowed,
not_acceptable,
proxy_authentication_required,
request_time_out,
conflict,
gone,
length_required,
precondition_failed,
request_entity_too_large,
request_uri_too_large,
unsupported_media_type,
requested_range_not_satisfied,
expectation_failed,
internal_server_error,
not_implemented,
bad_gateway,
service_unavailable,
gateway_timeout,
http_version_not_supported,
permanent_redirect, // I hate your decisions, uefi.
};
struct http_response_data
{
http_status_code status_code;
};
struct http_header
{
char *field_name;
char *field_value;
};
struct http_message
{
union {
http_request_data *request;
http_response_data *response;
} data;
size_t header_count;
http_header *headers;
size_t body_length;
void *body;
};
struct http_token
{
event event;
status status;
http_message *message;
};
} // namespace uefi
#endif

View File

@@ -16,6 +16,7 @@ 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;
@@ -27,4 +28,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_device_path_h_
#endif // _uefi_protos_device_path_h_

View File

@@ -14,7 +14,7 @@ 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);
@@ -123,4 +123,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_file_h_
#endif // _uefi_protos_file_h_

View File

@@ -16,6 +16,7 @@ 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;
@@ -32,4 +33,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_file_info_h_
#endif // _uefi_protos_file_info_h_

View File

@@ -17,6 +17,7 @@ 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);
}
@@ -47,4 +48,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_graphics_output_h_
#endif // _uefi_protos_graphics_output_h_

72
external/uefi/protos/http.h vendored Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#ifndef _uefi_protos_http_h_
#define _uefi_protos_http_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/networking.h>
namespace uefi {
namespace protos {
struct http;
struct http
{
static constexpr uefi::guid guid{ 0x7a59b29b,0x910b,0x4171,{0x82,0x42,0xa8,0x5a,0x0d,0xf2,0x5b,0x5b} };
static constexpr uefi::guid service_binding{ 0xbdc8e6af,0xd9bc,0x4379,{0xa7,0x2a,0xe0,0xc4,0xe7,0x5d,0xae,0x1c} };
inline uefi::status get_mode_data(uefi::http_config_data * http_config_data) {
return _get_mode_data(this, http_config_data);
}
inline uefi::status configure(uefi::http_config_data * http_config_data) {
return _configure(this, http_config_data);
}
inline uefi::status request(uefi::http_token * token) {
return _request(this, token);
}
inline uefi::status cancel(uefi::http_token * token) {
return _cancel(this, token);
}
inline uefi::status response(uefi::http_token * token) {
return _response(this, token);
}
inline uefi::status poll() {
return _poll(this);
}
protected:
using _get_mode_data_def = uefi::status (*)(uefi::protos::http *, uefi::http_config_data *);
_get_mode_data_def _get_mode_data;
using _configure_def = uefi::status (*)(uefi::protos::http *, uefi::http_config_data *);
_configure_def _configure;
using _request_def = uefi::status (*)(uefi::protos::http *, uefi::http_token *);
_request_def _request;
using _cancel_def = uefi::status (*)(uefi::protos::http *, uefi::http_token *);
_cancel_def _cancel;
using _response_def = uefi::status (*)(uefi::protos::http *, uefi::http_token *);
_response_def _response;
using _poll_def = uefi::status (*)(uefi::protos::http *);
_poll_def _poll;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_http_h_

93
external/uefi/protos/ip6.h vendored Normal file
View File

@@ -0,0 +1,93 @@
#pragma once
#ifndef _uefi_protos_ip6_h_
#define _uefi_protos_ip6_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/networking.h>
namespace uefi {
namespace protos {
struct ip6;
struct ip6
{
static constexpr uefi::guid guid{ 0x2c8759d5,0x5c2d,0x66ef,{0x92,0x5f,0xb6,0x6c,0x10,0x19,0x57,0xe2} };
static constexpr uefi::guid service_binding{ 0xec835dd3,0xfe0f,0x617b,{0xa6,0x21,0xb3,0x50,0xc3,0xe1,0x33,0x88} };
inline uefi::status get_mode_data(uefi::ip6_mode_data * ip6_mode_data, uefi::managed_network_config_data * mnp_config_data, uefi::simple_network_mode * snp_config_data) {
return _get_mode_data(this, ip6_mode_data, mnp_config_data, snp_config_data);
}
inline uefi::status configure(uefi::ip6_config_data * ip6_config_data) {
return _configure(this, ip6_config_data);
}
inline uefi::status groups(bool join_flag, uefi::ipv6_address * group_address) {
return _groups(this, join_flag, group_address);
}
inline uefi::status routes(bool delete_route, uefi::ipv6_address * destination, uint8_t prefix_length, uefi::ipv6_address * gateway_address) {
return _routes(this, delete_route, destination, prefix_length, gateway_address);
}
inline uefi::status neighbors(bool delete_flag, uefi::ipv6_address * target_ip6_address, uefi::mac_address * target_link_address, uint32_t timeout, bool override) {
return _neighbors(this, delete_flag, target_ip6_address, target_link_address, timeout, override);
}
inline uefi::status transmit(uefi::ip6_completion_token * token) {
return _transmit(this, token);
}
inline uefi::status receive(uefi::ip6_completion_token * token) {
return _receive(this, token);
}
inline uefi::status cancel(uefi::ip6_completion_token * token) {
return _cancel(this, token);
}
inline uefi::status poll() {
return _poll(this);
}
protected:
using _get_mode_data_def = uefi::status (*)(uefi::protos::ip6 *, uefi::ip6_mode_data *, uefi::managed_network_config_data *, uefi::simple_network_mode *);
_get_mode_data_def _get_mode_data;
using _configure_def = uefi::status (*)(uefi::protos::ip6 *, uefi::ip6_config_data *);
_configure_def _configure;
using _groups_def = uefi::status (*)(uefi::protos::ip6 *, bool, uefi::ipv6_address *);
_groups_def _groups;
using _routes_def = uefi::status (*)(uefi::protos::ip6 *, bool, uefi::ipv6_address *, uint8_t, uefi::ipv6_address *);
_routes_def _routes;
using _neighbors_def = uefi::status (*)(uefi::protos::ip6 *, bool, uefi::ipv6_address *, uefi::mac_address *, uint32_t, bool);
_neighbors_def _neighbors;
using _transmit_def = uefi::status (*)(uefi::protos::ip6 *, uefi::ip6_completion_token *);
_transmit_def _transmit;
using _receive_def = uefi::status (*)(uefi::protos::ip6 *, uefi::ip6_completion_token *);
_receive_def _receive;
using _cancel_def = uefi::status (*)(uefi::protos::ip6 *, uefi::ip6_completion_token *);
_cancel_def _cancel;
using _poll_def = uefi::status (*)(uefi::protos::ip6 *);
_poll_def _poll;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_ip6_h_

57
external/uefi/protos/ip6_config.h vendored Normal file
View File

@@ -0,0 +1,57 @@
#pragma once
#ifndef _uefi_protos_ip6_config_h_
#define _uefi_protos_ip6_config_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/networking.h>
namespace uefi {
namespace protos {
struct ip6_config;
struct ip6_config
{
static constexpr uefi::guid guid{ 0x937fe521,0x95ae,0x4d1a,{0x89,0x29,0x48,0xbc,0xd9,0x0a,0xd3,0x1a} };
inline uefi::status set_data(uefi::ip6_config_data_type data_type, size_t data_size, void * data) {
return _set_data(this, data_type, data_size, data);
}
inline uefi::status get_data(uefi::ip6_config_data_type data_type, size_t data_size, void * data) {
return _get_data(this, data_type, data_size, data);
}
inline uefi::status register_data_notify(uefi::ip6_config_data_type data_type, uefi::event event) {
return _register_data_notify(this, data_type, event);
}
inline uefi::status unregister_data_notify(uefi::ip6_config_data_type data_type, uefi::event event) {
return _unregister_data_notify(this, data_type, event);
}
protected:
using _set_data_def = uefi::status (*)(uefi::protos::ip6_config *, uefi::ip6_config_data_type, size_t, void *);
_set_data_def _set_data;
using _get_data_def = uefi::status (*)(uefi::protos::ip6_config *, uefi::ip6_config_data_type, size_t, void *);
_get_data_def _get_data;
using _register_data_notify_def = uefi::status (*)(uefi::protos::ip6_config *, uefi::ip6_config_data_type, uefi::event);
_register_data_notify_def _register_data_notify;
using _unregister_data_notify_def = uefi::status (*)(uefi::protos::ip6_config *, uefi::ip6_config_data_type, uefi::event);
_unregister_data_notify_def _unregister_data_notify;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_ip6_config_h_

36
external/uefi/protos/load_file.h vendored Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#ifndef _uefi_protos_load_file_h_
#define _uefi_protos_load_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>
#include <uefi/protos/device_path.h>
namespace uefi {
namespace protos {
struct load_file;
struct load_file
{
static constexpr uefi::guid guid{ {0x56ec3091,0x954c,0x11d2,{0x8e,0x3f,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
inline uefi::status load_file(uefi::protos::device_path * file_path, bool boot_policy, size_t * buffer_size, void * buffer) {
return _load_file(this, file_path, boot_policy, buffer_size, buffer);
}
protected:
using _load_file_def = uefi::status (*)(uefi::protos::load_file *, uefi::protos::device_path *, bool, size_t *, void *);
_load_file_def _load_file;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_load_file_h_

View File

@@ -18,6 +18,7 @@ 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);
}
@@ -45,4 +46,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_loaded_image_h_
#endif // _uefi_protos_loaded_image_h_

41
external/uefi/protos/service_binding.h vendored Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#ifndef _uefi_protos_service_binding_h_
#define _uefi_protos_service_binding_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 service_binding;
struct service_binding
{
inline uefi::status create_child(uefi::handle * child_handle) {
return _create_child(this, child_handle);
}
inline uefi::status destroy_child(uefi::handle child_handle) {
return _destroy_child(this, child_handle);
}
protected:
using _create_child_def = uefi::status (*)(uefi::protos::service_binding *, uefi::handle *);
_create_child_def _create_child;
using _destroy_child_def = uefi::status (*)(uefi::protos::service_binding *, uefi::handle);
_destroy_child_def _destroy_child;
public:
};
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_service_binding_h_

View File

@@ -17,6 +17,7 @@ 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);
}
@@ -33,4 +34,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_simple_file_system_h_
#endif // _uefi_protos_simple_file_system_h_

View File

@@ -17,6 +17,7 @@ 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);
}
@@ -89,4 +90,4 @@ public:
} // namespace protos
} // namespace uefi
#endif // _uefi_protos_simple_text_output_h_
#endif // _uefi_protos_simple_text_output_h_

View File

@@ -6,54 +6,67 @@ modules:
output: jsix.elf
target: host
deps:
- elf
- initrd
- cpu
- kutil
includes:
- src/kernel
source:
- src/kernel/crti.s
- src/kernel/apic.cpp
- src/kernel/ap_startup.s
- src/kernel/assert.cpp
- src/kernel/boot.s
- src/kernel/clock.cpp
- 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/gdtidt.s
- src/kernel/hpet.cpp
- src/kernel/idt.cpp
- 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/channel.cpp
- src/kernel/objects/endpoint.cpp
- src/kernel/objects/kobject.cpp
- src/kernel/page_manager.cpp
- src/kernel/objects/thread.cpp
- src/kernel/objects/process.cpp
- src/kernel/objects/system.cpp
- src/kernel/objects/vm_area.cpp
- src/kernel/page_table.cpp
- src/kernel/page_tree.cpp
- src/kernel/pci.cpp
- src/kernel/process.cpp
- src/kernel/scheduler.cpp
- src/kernel/screen.cpp
- src/kernel/serial.cpp
- src/kernel/symbol_table.cpp
- src/kernel/syscall.cpp
- src/kernel/syscall.s
- src/kernel/syscalls/channel.cpp
- src/kernel/syscalls/endpoint.cpp
- src/kernel/syscalls/object.cpp
- src/kernel/syscalls/process.cpp
- src/kernel/syscalls/system.cpp
- src/kernel/syscalls/thread.cpp
- src/kernel/syscalls/vm_area.cpp
- src/kernel/task.s
- src/kernel/crtn.s
- src/kernel/tss.cpp
- src/kernel/vm_space.cpp
boot:
kind: exe
target: boot
output: boot.efi
deps:
- cpu
source:
- src/boot/main.cpp
- src/boot/console.cpp
@@ -63,35 +76,31 @@ modules:
- src/boot/loader.cpp
- src/boot/memory.cpp
- src/boot/paging.cpp
- src/boot/status.cpp
- src/boot/support.cpp
nulldrv:
kind: exe
target: user
output: nulldrv
output: nulldrv.elf
deps:
- libc
source:
- src/drivers/nulldrv/io.cpp
- src/drivers/nulldrv/main.cpp
- src/drivers/nulldrv/main.s
- src/drivers/nulldrv/serial.cpp
elf:
kind: lib
output: libelf.a
fb:
kind: exe
target: user
output: fb.elf
deps:
- kutil
includes:
- src/libraries/elf/include
- libc
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
- src/drivers/fb/font.cpp
- src/drivers/fb/main.cpp
- src/drivers/fb/screen.cpp
- src/drivers/fb/scrollback.cpp
kutil:
kind: lib
@@ -105,17 +114,203 @@ modules:
- src/libraries/kutil/logger.cpp
- src/libraries/kutil/memory.cpp
- src/libraries/kutil/printf.c
- src/libraries/kutil/vm_space.cpp
- src/libraries/kutil/spinlock.cpp
makerd:
kind: exe
target: native
output: makerd
deps:
- initrd
cpu:
kind: lib
output: libcpu.a
includes:
- src/libraries/cpu/include
source:
- src/tools/makerd/entry.cpp
- src/tools/makerd/main.cpp
- src/libraries/cpu/cpu_id.cpp
j6:
kind: lib
output: libj6.a
includes:
- src/libraries/j6/include
target: user
source:
- src/libraries/j6/syscalls.s
libc:
kind: lib
output: libc.a
includes:
- src/libraries/libc/include
deps:
- j6
target: user
defines:
- DISABLE_SSE
- LACKS_UNISTD_H
- LACKS_FCNTL_H
- LACKS_SYS_PARAM_H
- LACKS_SYS_MMAN_H
- LACKS_SCHED_H
- LACKS_STRINGS_H
- HAVE_MMAP=0
#- LACKS_STRING_H
#- LACKS_ERRNO_H
#- LACKS_STDLIB_H
#- LACKS_TIME_H
source:
- src/libraries/libc/arch/x86_64/_Exit.s
- src/libraries/libc/arch/x86_64/crt0.s
- src/libraries/libc/arch/x86_64/init_libc.c
- src/libraries/libc/ctype/isalnum.c
- src/libraries/libc/ctype/isalpha.c
- src/libraries/libc/ctype/isblank.c
- src/libraries/libc/ctype/iscntrl.c
- src/libraries/libc/ctype/isdigit.c
- src/libraries/libc/ctype/isgraph.c
- src/libraries/libc/ctype/islower.c
- src/libraries/libc/ctype/isprint.c
- src/libraries/libc/ctype/ispunct.c
- src/libraries/libc/ctype/isspace.c
- src/libraries/libc/ctype/isupper.c
- src/libraries/libc/ctype/isxdigit.c
- src/libraries/libc/ctype/tolower.c
- src/libraries/libc/ctype/toupper.c
- src/libraries/libc/inttypes/imaxabs.c
- src/libraries/libc/inttypes/imaxdiv.c
- src/libraries/libc/inttypes/strtoimax.c
- src/libraries/libc/inttypes/strtoumax.c
- src/libraries/libc/locale/localeconv.c
- src/libraries/libc/locale/setlocale.c
- src/libraries/libc/j6libc/assert.c
- src/libraries/libc/j6libc/errno.c
- src/libraries/libc/j6libc/allocpages.c
- src/libraries/libc/j6libc/atomax.c
- src/libraries/libc/j6libc/closeall.c
- src/libraries/libc/j6libc/close.c
- src/libraries/libc/j6libc/digits.c
- src/libraries/libc/j6libc/filemode.c
- src/libraries/libc/j6libc/fillbuffer.c
- src/libraries/libc/j6libc/flushbuffer.c
- src/libraries/libc/j6libc/is_leap.c
- src/libraries/libc/j6libc/load_lc_collate.c
- src/libraries/libc/j6libc/load_lc_ctype.c
- src/libraries/libc/j6libc/load_lc_messages.c
- src/libraries/libc/j6libc/load_lc_monetary.c
- src/libraries/libc/j6libc/load_lc_numeric.c
- src/libraries/libc/j6libc/load_lc_time.c
- src/libraries/libc/j6libc/load_lines.c
- src/libraries/libc/j6libc/open.c
- src/libraries/libc/j6libc/prepread.c
- src/libraries/libc/j6libc/prepwrite.c
- src/libraries/libc/j6libc/print.c
- src/libraries/libc/j6libc/rename.c
- src/libraries/libc/j6libc/scan.c
- src/libraries/libc/j6libc/seed.c
- src/libraries/libc/j6libc/seek.c
- src/libraries/libc/j6libc/stdinit.c
- src/libraries/libc/j6libc/strtox_main.c
- src/libraries/libc/j6libc/strtox_prelim.c
- src/libraries/libc/j6libc/sbrk.c
- src/libraries/libc/signal/raise.c
- src/libraries/libc/signal/signal.c
- src/libraries/libc/stdio/clearerr.c
- src/libraries/libc/stdio/fclose.c
- src/libraries/libc/stdio/feof.c
- src/libraries/libc/stdio/ferror.c
- src/libraries/libc/stdio/fflush.c
- src/libraries/libc/stdio/fgetc.c
- src/libraries/libc/stdio/fgetpos.c
- src/libraries/libc/stdio/fgets.c
- src/libraries/libc/stdio/fopen.c
- src/libraries/libc/stdio/fprintf.c
- src/libraries/libc/stdio/fputc.c
- src/libraries/libc/stdio/fputs.c
- src/libraries/libc/stdio/fread.c
- src/libraries/libc/stdio/freopen.c
- src/libraries/libc/stdio/fscanf.c
- src/libraries/libc/stdio/fseek.c
- src/libraries/libc/stdio/fsetpos.c
- src/libraries/libc/stdio/ftell.c
- src/libraries/libc/stdio/fwrite.c
- src/libraries/libc/stdio/getc.c
- src/libraries/libc/stdio/getchar.c
- src/libraries/libc/stdio/perror.c
- src/libraries/libc/stdio/printf.c
- src/libraries/libc/stdio/putc.c
- src/libraries/libc/stdio/putchar.c
- src/libraries/libc/stdio/puts.c
- src/libraries/libc/stdio/remove.c
- src/libraries/libc/stdio/rename.c
- src/libraries/libc/stdio/rewind.c
- src/libraries/libc/stdio/scanf.c
- src/libraries/libc/stdio/setbuf.c
- src/libraries/libc/stdio/setvbuf.c
- src/libraries/libc/stdio/snprintf.c
- src/libraries/libc/stdio/sprintf.c
- src/libraries/libc/stdio/sscanf.c
- src/libraries/libc/stdio/tmpfile.c
- src/libraries/libc/stdio/tmpnam.c
- src/libraries/libc/stdio/ungetc.c
- src/libraries/libc/stdio/vfprintf.c
- src/libraries/libc/stdio/vfscanf.c
- src/libraries/libc/stdio/vprintf.c
- src/libraries/libc/stdio/vscanf.c
- src/libraries/libc/stdio/vsnprintf.c
- src/libraries/libc/stdio/vsprintf.c
- src/libraries/libc/stdio/vsscanf.c
- src/libraries/libc/stdlib/abort.c
- src/libraries/libc/stdlib/abs.c
- src/libraries/libc/stdlib/atexit.c
- src/libraries/libc/stdlib/atoi.c
- src/libraries/libc/stdlib/atol.c
- src/libraries/libc/stdlib/atoll.c
- src/libraries/libc/stdlib/bsearch.c
- src/libraries/libc/stdlib/div.c
- src/libraries/libc/stdlib/exit.c
- src/libraries/libc/stdlib/_Exit.c
- src/libraries/libc/stdlib/getenv.c
- src/libraries/libc/stdlib/labs.c
- src/libraries/libc/stdlib/ldiv.c
- src/libraries/libc/stdlib/llabs.c
- src/libraries/libc/stdlib/lldiv.c
- src/libraries/libc/stdlib/malloc.c
- src/libraries/libc/stdlib/qsort.c
- src/libraries/libc/stdlib/rand.c
- src/libraries/libc/stdlib/srand.c
- src/libraries/libc/stdlib/strtol.c
- src/libraries/libc/stdlib/strtoll.c
- src/libraries/libc/stdlib/strtoul.c
- src/libraries/libc/stdlib/strtoull.c
- src/libraries/libc/stdlib/system.c
- src/libraries/libc/string/memchr.c
- src/libraries/libc/string/memcmp.c
- src/libraries/libc/string/memcpy.c
- src/libraries/libc/string/memmove.c
- src/libraries/libc/string/memset.c
- src/libraries/libc/string/strcat.c
- src/libraries/libc/string/strchr.c
- src/libraries/libc/string/strcmp.c
- src/libraries/libc/string/strcoll.c
- src/libraries/libc/string/strcpy.c
- src/libraries/libc/string/strcspn.c
- src/libraries/libc/string/strerror.c
- src/libraries/libc/string/strlen.c
- src/libraries/libc/string/strncat.c
- src/libraries/libc/string/strncmp.c
- src/libraries/libc/string/strncpy.c
- src/libraries/libc/string/strpbrk.c
- src/libraries/libc/string/strrchr.c
- src/libraries/libc/string/strspn.c
- src/libraries/libc/string/strstr.c
- src/libraries/libc/string/strtok.c
- src/libraries/libc/string/strxfrm.c
- src/libraries/libc/time/asctime.c
- src/libraries/libc/time/clock.c
- src/libraries/libc/time/ctime.c
- src/libraries/libc/time/difftime.c
- src/libraries/libc/time/gmtime.c
- src/libraries/libc/time/localtime.c
- src/libraries/libc/time/mktime.c
- src/libraries/libc/time/strftime.c
- src/libraries/libc/time/time.c
- src/libraries/libc/time/timespec_get.c
tests:
kind: exe
@@ -124,12 +319,14 @@ modules:
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
- src/tests/map.cpp
- src/tests/vector.cpp
overlays:
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-llvm8-20190706.tar.bz2
path: sysroot

11
qemu.sh
View File

@@ -6,6 +6,7 @@ debug=""
debugtarget="${build}/jsix.elf"
flash_name="ovmf_vars"
gfx="-nographic"
vga="-vga none"
kvm=""
cpu="Broadwell,+pdpe1gb"
@@ -22,6 +23,10 @@ for arg in $@; do
;;
--gfx)
gfx=""
vga=""
;;
--vga)
vga=""
;;
--kvm)
kvm="-enable-kvm"
@@ -45,6 +50,8 @@ if [[ -n $TMUX ]]; then
if [[ -n $debug ]]; then
tmux split-window -h "gdb ${debugtarget}" &
else
tmux split-window -h -l 80 "sleep 1; telnet localhost 45455" &
tmux last-pane
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
fi
elif [[ $DESKTOP_SESSION = "i3" ]]; then
@@ -61,6 +68,8 @@ exec qemu-system-x86_64 \
-drive "format=raw,file=${build}/jsix.img" \
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
-monitor telnet:localhost:45454,server,nowait \
-serial stdio \
-serial telnet:localhost:45455,server,nowait \
-smp 4 \
-m 512 \
-d mmu,int,guest_errors \
@@ -68,4 +77,4 @@ exec qemu-system-x86_64 \
-cpu "${cpu}" \
-M q35 \
-no-reboot \
$gfx $kvm $debug
$gfx $vga $kvm $debug

75
scripts/build_symbol_table.py Executable file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python3
#
# Generate the jsix style symbol table. The format in memory of this table
# is as follows:
#
# <num_entires> : 8 bytes
# <index> : 16 * N bytes
# <name data> : variable
#
# Each index entry has the format
# <symbol address> : 8 bytes
# <offset of name> : 8 bytes
#
# Name offsets are from the start of the symbol table as a whole. (ie,
# where <num_entries> is located.)
def parse_syms(infile):
"""Take the output of the `nm` command, and parse it into a tuple
representing the symbols in the text segment of the binary. Returns
a list of (address, symbol_name)."""
from cxxfilt import demangle, InvalidName
syms = []
for line in sys.stdin:
addr, t, mangled = line.split()
if t not in "tTvVwW": continue
try:
name = demangle(mangled)
except InvalidName:
continue
addr = int(addr, base=16)
syms.append((addr, name))
return sorted(syms)
def write_table(syms, outfile):
"""Write the given symbol table as generated by parse_syms()
to the outfile, index first, and then name character data."""
import struct
outfile.write(struct.pack("@Q", len(syms)))
index_pos = outfile.tell()
outfile.seek(struct.calcsize("@QQ") * len(syms), 1)
nul = b'\0'
positions = {}
for s in syms:
addr, name = s
positions[addr] = outfile.tell()
data = name.encode('utf-8')
outfile.write(name.encode('utf-8'))
outfile.write(nul)
outfile.seek(index_pos)
for s in syms:
addr = s[0]
pos = positions[addr]
outfile.write(struct.pack("@QQ", addr, pos))
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <output>")
sys.exit(1)
outfile = open(sys.argv[1], "wb")
write_table(parse_syms(sys.stdin), outfile)

75
scripts/fontpsf.py Normal file
View File

@@ -0,0 +1,75 @@
_MAGIC = (0x72, 0xb5, 0x4a, 0x86)
_VERSION = 0
class PSF2:
from collections import namedtuple
Header = namedtuple("PSF2Header",
["version", "offset", "flags", "count", "charsize", "height", "width"])
def __init__(self, filename, header, data):
self.__filename = filename
self.__header = header
self.__data = data
data = property(lambda self: self.__data)
header = property(lambda self: self.__header)
count = property(lambda self: self.__header.count)
charsize = property(lambda self: self.__header.charsize)
dimension = property(lambda self: (self.__header.width, self.__header.height))
filename = property(lambda self: self.__filename)
@classmethod
def load(cls, filename):
from os.path import basename
from struct import unpack_from
data = open(filename, 'rb').read()
fmt = "BBBBIIIIIII"
values = unpack_from(fmt, data)
if values[:len(_MAGIC)] != _MAGIC:
raise Exception("Bad magic number in header")
header = PSF2.Header(*values[len(_MAGIC):])
if header.version != _VERSION:
raise Exception(f"Bad version {header.version} in header")
return cls(basename(filename), header, data)
class Glyph:
__slots__ = ['index', 'data']
def __init__(self, i, data):
self.index = i
self.data = data
def __index__(self):
return self.index
def empty(self):
return not bool([b for b in self.data if b != 0])
def description(self):
c = chr(self.index)
if c.isprintable():
return "Glyph {:02x}: '{}'".format(self.index, c)
else:
return "Glyph {:02x}".format(self.index)
def __getitem__(self, i):
c = self.__header.charsize
n = i * c + self.__header.offset
return PSF2.Glyph(i, self.__data[n:n+c])
class __iter:
__slots__ = ['font', 'n']
def __init__(self, font):
self.font = font
self.n = 0
def __next__(self):
if self.n < self.font.count:
glyph = self.font[self.n]
self.n += 1
return glyph
else:
raise StopIteration
def __iter__(self):
return PSF2.__iter(self)

34
scripts/parse_font.py Normal file → Executable file
View File

@@ -1,21 +1,6 @@
#!/usr/bin/env python3
MAGIC = (0x72, 0xb5, 0x4a, 0x86)
from collections import namedtuple
PSF2 = namedtuple("PSF2", ["version", "offset", "flags", "count", "charsize", "height", "width"])
def read_header(data):
from struct import unpack_from, calcsize
fmt = "BBBBIIIIIII"
values = unpack_from(fmt, data)
if values[:len(MAGIC)] != MAGIC:
raise Exception("Bad magic number in header")
return PSF2(*values[len(MAGIC):])
from fontpsf import PSF2
def print_glyph(header, data):
bw = (header.width + 7) // 8
@@ -28,16 +13,15 @@ def print_glyph(header, data):
def display_font(filename):
data = open(filename, 'rb').read()
font = PSF2.load(filename)
print(font.header)
header = read_header(data)
print(header)
c = header.charsize
for i in range(0, header.count):
n = i * c + header.offset
print("Glyph {}:".format(i))
print_glyph(header, data[n:n+c])
for glyph in font:
if glyph.empty():
print("{}: BLANK".format(glyph.description()))
else:
print("{}:".format(glyph.description()))
print_glyph(font.header, glyph.data)
if __name__ == "__main__":

27
scripts/psf_to_cpp.py Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env python3
from fontpsf import PSF2
def print_header(filename):
font = PSF2.load(filename)
print("#pragma once")
print(f"// This file was autogenerated by psf_to_cpp.py from {font.filename}\n")
print(f"const uint8_t font_glyph_size = {font.charsize};")
print(f"const uint8_t font_glyph_width = {font.dimension[0]};")
print(f"const uint8_t font_glyph_height = {font.dimension[1]};")
print(f"const uint16_t font_glyph_count = {font.count};\n")
print('const uint8_t font_glyph_data[] = {')
for glyph in font:
print(" ", "".join([f"0x{b:02x}," for b in glyph.data]), end="")
print(" // {}".format(glyph.description()))
print("};")
if __name__ == "__main__":
import sys
for filename in sys.argv[1:]:
print_header(filename)

View File

@@ -47,10 +47,11 @@ asflags = $
-DVERSION_MAJOR={{ version_major }} $
-DVERSION_MINOR={{ version_minor }} $
-DVERSION_PATCH={{ version_patch }} $
-DVERSION_GITSHA=0x{{ version_sha }}
-DVERSION_GITSHA=0x{{ version_sha }} $
-I${srcroot}/src/include
cflags = -std=c11
cxxflags = -std=c++14
cxxflags = -std=c++17
libs =
rule c
@@ -67,7 +68,7 @@ rule dump_c_run
description = Dumping C arguments for $target
command = $
echo "#!/bin/bash" > $out; $
echo '$cc $ccflags $cflags $$*' > $out; $
echo '$cc $ccflags $cflags $$*' >> $out; $
chmod a+x $out
rule cpp
@@ -84,7 +85,7 @@ rule dump_cpp_run
description = Dumping C++ arguments for $target
command = $
echo "#!/bin/bash" > $out; $
echo '$cc $cxxflags $ccflags $$*' > $out; $
echo '$cc $cxxflags $ccflags $$*' >> $out; $
chmod a+x $out
rule s
@@ -118,9 +119,9 @@ 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 makest
description = Making symbol table
command = nm $in | ${srcroot}/scripts/build_symbol_table.py $out
rule makeefi
description = Converting $name
@@ -186,12 +187,18 @@ build $builddir/fatroot/jsix.elf : cp $builddir/jsix.elf
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/fatroot/nulldrv.elf : cp $builddir/user/nulldrv.elf
name = null driver to FAT image
build $builddir/fatroot/fb.elf : cp $builddir/user/fb.elf
name = fb driver to FAT image
build ${builddir}/fatroot/symbol_table.dat : makest ${builddir}/jsix.elf
build $builddir/jsix.img : makefat | $
$builddir/fatroot/initrd.img $
$builddir/fatroot/symbol_table.dat $
$builddir/fatroot/nulldrv.elf $
$builddir/fatroot/fb.elf $
$builddir/fatroot/jsix.elf $
$builddir/fatroot/efi/boot/bootx64.efi
name = jsix.img

View File

@@ -5,6 +5,7 @@
asflags = $asflags -I${srcroot}/src/kernel/
libs = $libs
ldflags = $ldflags -T ${srcroot}/src/arch/x86_64/kernel.ld
ccflags = $ccflags -I${srcroot}/external
{% endblock %}

View File

@@ -22,5 +22,10 @@ build ${builddir}/cpp.defs : dump_cpp_defs | {{ buildfile }}
build ${builddir}/c.run : dump_c_run | {{ buildfile }}
build ${builddir}/cpp.run : dump_cpp_run | {{ buildfile }}
default ${builddir}/c.defs
default ${builddir}/cpp.defs
default ${builddir}/c.run
default ${builddir}/cpp.run
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -24,6 +24,7 @@ ccflags = $ccflags $
-D__ELF__ $
-D__JSIX__ $
-isystem${srcroot}/sysroot/include $
-isystem${srcroot}/src/libraries/libc/include $
--sysroot="${srcroot}/sysroot"
cxxflags = $cxxflags $

View File

@@ -23,6 +23,7 @@ ccflags = $ccflags $
-D__ELF__ $
-D__JSIX__ $
-isystem${srcroot}/sysroot/include $
-isystem${srcroot}/src/libraries/libc/include $
--sysroot="${srcroot}/sysroot"
cxxflags = $cxxflags $
@@ -38,9 +39,6 @@ ldflags = $ldflags $
--sysroot="${srcroot}/sysroot" $
-L "${srcroot}/sysroot/lib" $
libs = $libs $
-lc
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -1,8 +1,7 @@
ENTRY(_start)
ENTRY(_kernel_start)
SECTIONS
{
OFFSET = 0xFFFF800000000000;
. = OFFSET + 0x100000;
. = 0xFFFF800000000000;
.header : {
__header_start = .;
@@ -20,6 +19,12 @@ SECTIONS
*(.rodata)
}
.ctors : ALIGN(8) {
__ctors = .;
KEEP(*(.ctors))
__ctors_end = .;
}
.bss ALIGN(4096) : {
__bss_start = .;
*(.bss)

View File

@@ -17,23 +17,7 @@ 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'};
@@ -49,9 +33,10 @@ wstrlen(const wchar_t *s)
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
m_rows(0),
m_cols(0),
m_out(out)
m_rows {0},
m_cols {0},
m_out {out},
m_fb {0, 0}
{
pick_mode(bs);
@@ -70,7 +55,26 @@ console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out)
m_out->output_string(GIT_VERSION_WIDE);
m_out->set_attribute(uefi::attribute::light_gray);
m_out->output_string(L" booting...\r\n\n");
m_out->output_string(L" booting...\r\n");
if (m_fb.type != kernel::args::fb_type::none) {
wchar_t const * type = nullptr;
switch (m_fb.type) {
case kernel::args::fb_type::rgb8:
type = L"rgb8";
break;
case kernel::args::fb_type::bgr8:
type = L"bgr8";
break;
default:
type = L"unknown";
}
printf(L"Found framebuffer: %dx%d[%d] type %s @0x%x\r\n",
m_fb.horizontal, m_fb.vertical, m_fb.scanline, type, m_fb.phys_addr);
} else {
printf(L"No framebuffer found.\r\n");
}
s_console = this;
}
@@ -82,9 +86,14 @@ console::pick_mode(uefi::boot_services *bs)
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");
m_fb.type = kernel::args::fb_type::none;
uefi::status has_gop = bs->locate_protocol(&guid, nullptr,
(void **)&gfx_out_proto);
if (has_gop != uefi::status::success)
// No video output found, skip it
return;
const uint32_t modes = gfx_out_proto->mode->max_mode;
uint32_t best = gfx_out_proto->mode->mode;
@@ -93,7 +102,7 @@ console::pick_mode(uefi::boot_services *bs)
(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;
int pixmode = static_cast<int>(info->pixel_format);
for (uint32_t i = 0; i < modes; ++i) {
size_t size = 0;
@@ -107,17 +116,37 @@ console::pick_mode(uefi::boot_services *bs)
#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;
int new_pixmode = static_cast<int>(info->pixel_format);
if (new_is_fb > is_fb && new_res >= res) {
if (new_pixmode <= pixmode && new_res >= res) {
best = i;
res = new_res;
pixmode = new_pixmode;
}
}
try_or_raise(
gfx_out_proto->set_mode(best),
L"Failed to set graphics mode");
if (pixmode <= static_cast<int>(uefi::pixel_format::bgr8)) {
m_fb.phys_addr = gfx_out_proto->mode->frame_buffer_base;
m_fb.size = gfx_out_proto->mode->frame_buffer_size;
m_fb.vertical = gfx_out_proto->mode->info->vertical_resolution;
m_fb.horizontal = gfx_out_proto->mode->info->horizontal_resolution;
m_fb.scanline = gfx_out_proto->mode->info->pixels_per_scanline;
switch (gfx_out_proto->mode->info->pixel_format) {
case uefi::pixel_format::rgb8:
m_fb.type = kernel::args::fb_type::rgb8;
break;
case uefi::pixel_format::bgr8:
m_fb.type = kernel::args::fb_type::bgr8;
break;
default:
m_fb.type = kernel::args::fb_type::none;
}
}
}
size_t
@@ -272,176 +301,4 @@ console::print(const wchar_t *fmt, ...)
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,10 +1,12 @@
/// \file console.h
/// Text output and status message handling
/// Text output handler
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <uefi/boot_services.h>
#include <uefi/protos/simple_text_output.h>
#include "kernel_args.h"
#include "types.h"
namespace boot {
@@ -12,6 +14,8 @@ namespace boot {
class console
{
public:
using framebuffer = kernel::args::framebuffer;
console(uefi::boot_services *bs, uefi::protos::simple_text_output *out);
size_t print_hex(uint32_t n) const;
@@ -20,6 +24,8 @@ public:
size_t print_long_dec(uint64_t n) const;
size_t printf(const wchar_t *fmt, ...) const;
const framebuffer & fb() const { return m_fb; };
static console & get() { return *s_console; }
static size_t print(const wchar_t *fmt, ...);
@@ -31,50 +37,9 @@ private:
size_t m_rows, m_cols;
uefi::protos::simple_text_output *m_out;
framebuffer m_fb;
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

View File

@@ -1,11 +1,11 @@
#include "error.h"
#include "console.h"
#include "kernel_args.h"
#include "status.h"
namespace boot {
namespace error {
handler *handler::s_current = nullptr;
struct error_code_desc {
uefi::status code;
const wchar_t *name;
@@ -20,8 +20,8 @@ struct error_code_desc error_table[] = {
{ uefi::status::success, nullptr }
};
static const wchar_t *
error_message(uefi::status status)
const wchar_t *
message(uefi::status status)
{
int32_t i = -1;
while (error_table[++i].name != nullptr) {
@@ -34,56 +34,32 @@ error_message(uefi::status status)
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)
[[ noreturn ]] static void
cpu_assert(uefi::status s, const wchar_t *message, size_t line)
{
asm volatile (
"movq $0xeeeeeeebadbadbad, %%r8;"
"movq %0, %%r9;"
"movq %1, %%r10;"
"movq %2, %%r11;"
"movq $0, %%rdx;"
"divq %%rdx;"
:
: "r"((uint64_t)s)
: "rax", "rdx", "r8", "r9");
: "r"((uint64_t)s), "r"(message), "r"(line)
: "rax", "rdx", "r8", "r9", "r10");
while (1) asm("hlt");
}
[[ noreturn ]] void
raise(uefi::status status, const wchar_t *message, size_t line)
{
if(status_line::fail(message, status))
while (1) asm("hlt");
else
cpu_assert(status, message, line);
}
} // namespace error
} // namespace boot

View File

@@ -12,50 +12,9 @@ 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);
[[ noreturn ]] void raise(uefi::status status, const wchar_t *message, size_t line = 0);
/// 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;
};
const wchar_t * message(uefi::status status);
} // namespace error
} // namespace boot
@@ -69,6 +28,6 @@ void debug_break();
#define try_or_raise(s, m) \
do { \
uefi::status _s = (s); \
if (uefi::is_error(_s)) ::boot::error::raise(_s, (m)); \
if (uefi::is_error(_s)) ::boot::error::raise(_s, (m), __LINE__); \
} while(0)

View File

@@ -8,6 +8,7 @@
#include "console.h"
#include "error.h"
#include "memory.h"
#include "status.h"
namespace boot {
namespace fs {
@@ -50,19 +51,19 @@ file::open(const wchar_t *path)
return file(fh, m_bs);
}
void *
file::load(size_t *out_size, uefi::memory_type mem_type)
buffer
file::load(uefi::memory_type mem_type)
{
uint8_t buffer[sizeof(uefi::protos::file_info) + 100];
size_t size = sizeof(buffer);
uint8_t info_buf[sizeof(uefi::protos::file_info) + 100];
size_t size = sizeof(info_buf);
uefi::guid info_guid = uefi::protos::file_info::guid;
try_or_raise(
m_file->get_info(&info_guid, &size, &buffer),
m_file->get_info(&info_guid, &size, &info_buf),
L"Could not get file info");
uefi::protos::file_info *info =
reinterpret_cast<uefi::protos::file_info*>(&buffer);
reinterpret_cast<uefi::protos::file_info*>(&info_buf);
size_t pages = memory::bytes_to_pages(info->file_size);
void *data = nullptr;
@@ -77,8 +78,7 @@ file::load(size_t *out_size, uefi::memory_type mem_type)
m_file->read(&size, data),
L"Could not read from file");
*out_size = size;
return data;
return { .size = size, .data = data };
}
file

View File

@@ -5,6 +5,7 @@
#include <uefi/types.h>
#include <uefi/boot_services.h>
#include <uefi/protos/file.h>
#include "types.h"
namespace boot {
namespace fs {
@@ -22,13 +23,10 @@ public:
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);
/// \returns A buffer describing the loaded memory. The
/// memory will be page-aligned.
buffer load(uefi::memory_type mem_type = uefi::memory_type::loader_data);
private:
friend file get_boot_volume(uefi::handle, uefi::boot_services*);

View File

@@ -1,13 +0,0 @@
#include "guids.h"
#define GUID(dw, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, name) \
EFI_GUID name __attribute__((section(".rodata"))) = {dw, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
#include "guids.inc"
#undef GUID
int is_guid(EFI_GUID *a, EFI_GUID *b)
{
uint64_t *ai = (uint64_t *)a;
uint64_t *bi = (uint64_t *)b;
return ai[0] == bi[0] && ai[1] == bi[1];
}

View File

@@ -1,8 +0,0 @@
#pragma once
#include <efi/efi.h>
int is_guid(EFI_GUID *a, EFI_GUID *b);
#define GUID(dw, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, name) extern EFI_GUID name
#include "guids.inc"
#undef GUID

View File

@@ -1,11 +0,0 @@
GUID(0xeb9d2d30,0x2d88,0x11d3,0x9a,0x16,0x00,0x90,0x27,0x3f,0xc1,0x4d, guid_acpi1);
GUID(0x8868e871,0xe4f1,0x11d3,0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81, guid_acpi2);
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

View File

@@ -1,6 +1,7 @@
#include "hardware.h"
#include "console.h"
#include "error.h"
#include "status.h"
namespace boot {
namespace hw {
@@ -36,8 +37,25 @@ find_acpi_table(uefi::system_table *st)
return reinterpret_cast<void*>(acpi1_table);
}
static uint64_t
rdmsr(uint32_t addr)
{
uint32_t low, high;
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
return (static_cast<uint64_t>(high) << 32) | low;
}
static void
wrmsr(uint32_t addr, uint64_t value)
{
uint32_t low = value & 0xffffffff;
uint32_t high = value >> 32;
__asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high));
}
void
setup_cr4()
setup_control_regs()
{
uint64_t cr4 = 0;
asm volatile ( "mov %%cr4, %0" : "=r" (cr4) );
@@ -48,6 +66,15 @@ setup_cr4()
0x020000 | // Enable PCIDs
0;
asm volatile ( "mov %0, %%cr4" :: "r" (cr4) );
// Set up IA32_EFER
constexpr uint32_t IA32_EFER = 0xC0000080;
uint64_t efer = rdmsr(IA32_EFER);
efer |=
0x0001 | // Enable SYSCALL
0x0800 | // Enable NX bit
0;
wrmsr(IA32_EFER, efer);
}
} // namespace hw

View File

@@ -13,8 +13,8 @@ namespace hw {
/// 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();
/// Enable CPU options in CR4 etc for the kernel starting state.
void setup_control_regs();
} // namespace hw
} // namespace boot

View File

@@ -5,12 +5,33 @@
#include "console.h"
#include "elf.h"
#include "error.h"
#include "fs.h"
#include "memory.h"
#include "paging.h"
#include "status.h"
namespace args = kernel::args;
namespace boot {
namespace loader {
buffer
load_file(
fs::file &disk,
const wchar_t *name,
const wchar_t *path,
uefi::memory_type type)
{
status_line status(L"Loading file", name);
fs::file file = disk.open(path);
buffer b = file.load(type);
//console::print(L" Loaded at: 0x%lx, %d bytes\r\n", b.data, b.size);
return b;
}
static bool
is_elfheader_valid(const elf::header *header)
{
@@ -26,48 +47,70 @@ is_elfheader_valid(const elf::header *header)
header->header_version == elf::version;
}
kernel::entrypoint
load(
const void *data, size_t size,
kernel::args::header *args,
void
load_program(
args::program &program,
const wchar_t *name,
buffer data,
uefi::boot_services *bs)
{
status_line status(L"Loading kernel ELF binary");
const elf::header *header = reinterpret_cast<const elf::header*>(data);
status_line status(L"Loading program:", name);
const elf::header *header = reinterpret_cast<const elf::header*>(data.data);
if (size < sizeof(elf::header) || !is_elfheader_valid(header))
error::raise(uefi::status::load_error, L"Kernel ELF not valid");
if (data.size < sizeof(elf::header) || !is_elfheader_valid(header))
error::raise(uefi::status::load_error, L"ELF file not valid");
paging::page_table *pml4 = reinterpret_cast<paging::page_table*>(args->pml4);
uintptr_t prog_base = uintptr_t(-1);
uintptr_t prog_end = 0;
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);
offset_ptr<elf::program_header>(data.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);
uintptr_t end = pheader->vaddr + pheader->mem_size;
if (pheader->vaddr < prog_base) prog_base = pheader->vaddr;
if (end > prog_end) prog_end = end;
}
console::print(L" entrypoint: 0x%lx\r\n", header->entrypoint);
return reinterpret_cast<kernel::entrypoint>(header->entrypoint);
size_t total_size = prog_end - prog_base;
size_t num_pages = memory::bytes_to_pages(total_size);
void *pages = nullptr;
try_or_raise(
bs->allocate_pages(uefi::allocate_type::any_pages,
uefi::memory_type::loader_data, num_pages, &pages),
L"Failed allocating space for program");
bs->set_mem(pages, total_size, 0);
program.base = prog_base;
program.total_size = total_size;
program.num_sections = 0;
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.data, offset);
if (pheader->type != elf::PT_LOAD)
continue;
args::program_section &section = program.sections[program.num_sections++];
void *src_start = offset_ptr<void>(data.data, pheader->offset);
void *dest_start = offset_ptr<void>(pages, pheader->vaddr - prog_base);
bs->copy_mem(dest_start, src_start, pheader->file_size);
section.phys_addr = reinterpret_cast<uintptr_t>(dest_start);
section.virt_addr = pheader->vaddr;
section.size = pheader->mem_size;
section.type = static_cast<args::section_flags>(pheader->flags);
}
program.entrypoint = header->entrypoint;
}
} // namespace loader

View File

@@ -5,18 +5,36 @@
#include <uefi/boot_services.h>
#include "kernel_args.h"
#include "memory.h"
#include "types.h"
namespace boot {
namespace fs { class file; }
namespace loader {
/// Load a file from disk into memory.
/// \arg disk The opened UEFI filesystem to load from
/// \arg name Name of the module (informational only)
/// \arg path Path on `disk` of the file to load
/// \arg type Memory type to use for allocation
buffer
load_file(
fs::file &disk,
const wchar_t *name,
const wchar_t *path,
uefi::memory_type type = uefi::memory_type::loader_data);
/// 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,
/// \arg program The program structure to fill
/// \arg data Buffer of the ELF file in memory
/// \arg bs Boot services
void
load_program(
kernel::args::program &program,
const wchar_t *name,
buffer data,
uefi::boot_services *bs);
} // namespace loader

View File

@@ -8,12 +8,14 @@
#include <stdint.h>
#include "console.h"
#include "cpu/cpu_id.h"
#include "error.h"
#include "fs.h"
#include "hardware.h"
#include "loader.h"
#include "memory.h"
#include "paging.h"
#include "status.h"
#include "kernel_args.h"
@@ -21,9 +23,24 @@ namespace kernel {
#include "kernel_memory.h"
}
namespace args = kernel::args;
namespace boot {
constexpr int max_modules = 10; // Max modules to allocate room for
constexpr int max_modules = 5; // Max modules to allocate room for
constexpr int max_programs = 5; // Max programs to allocate room for
struct program_desc
{
const wchar_t *name;
const wchar_t *path;
};
const program_desc program_list[] = {
{L"kernel", L"jsix.elf"},
{L"null driver", L"nulldrv.elf"},
{L"fb driver", L"fb.elf"},
};
/// Change a pointer to point to the higher-half linear-offset version
/// of the address it points to.
@@ -34,129 +51,165 @@ void change_pointer(T *&pointer)
}
/// Allocate space for kernel args. Allocates enough space from pool
/// memory for the args header and `max_modules` module headers.
kernel::args::header *
/// memory for the args header and the module and program headers.
args::header *
allocate_args_structure(
uefi::boot_services *bs,
size_t max_modules)
size_t max_modules,
size_t max_programs)
{
status_line status(L"Setting up kernel args memory");
status_line status {L"Setting up kernel args memory"};
kernel::args::header *args = nullptr;
args::header *args = nullptr;
size_t args_size =
sizeof(kernel::args::header) + // The header itself
max_modules * sizeof(kernel::args::module); // The module structures
sizeof(args::header) + // The header itself
max_modules * sizeof(args::module) + // The module structures
max_programs * sizeof(args::program); // The program structures
try_or_raise(
bs->allocate_pool(memory::args_type, args_size,
bs->allocate_pool(uefi::memory_type::loader_data, 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);
reinterpret_cast<args::module*>(args + 1);
args->num_modules = 0;
args->programs =
reinterpret_cast<args::program*>(args->modules + max_modules);
args->num_programs = 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)
/// Add a module to the kernel args list
inline void
add_module(args::header *args, args::mod_type type, buffer &data)
{
status_line status(L"Loading module", name);
args::module &m = args->modules[args->num_modules++];
m.type = type;
m.location = data.data;
m.size = data.size;
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);
change_pointer(m.location);
}
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, module.size);
return &module;
/// Check that all required cpu features are supported
void
check_cpu_supported()
{
status_line status {L"Checking CPU features"};
cpu::cpu_id cpu;
uint64_t missing = cpu.missing();
if (missing) {
#define CPU_FEATURE_OPT(...)
#define CPU_FEATURE_REQ(name, ...) \
if (!cpu.has_feature(cpu::feature::name)) { \
status::fail(L"CPU required feature " L ## #name, uefi::status::unsupported); \
}
#include "cpu/features.inc"
#undef CPU_FEATURE_REQ
#undef CPU_FEATURE_OPT
error::raise(uefi::status::unsupported, L"CPU not supported");
}
}
/// 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)
args::header *
uefi_preboot(uefi::handle image, uefi::system_table *st)
{
error::uefi_handler handler(con);
status_line status(L"Performing UEFI pre-boot");
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::header *args =
allocate_args_structure(bs, max_modules, max_programs);
args->magic = kernel::args::magic;
args->version = kernel::args::version;
args->magic = args::magic;
args->version = args::version;
args->runtime_services = rs;
args->acpi_table = hw::find_acpi_table(st);
paging::allocate_tables(args, bs);
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);
buffer symbols = loader::load_file(disk, L"symbol table", L"symbol_table.dat",
uefi::memory_type::loader_data);
add_module(args, args::mod_type::symbol_table, symbols);
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);
for (auto &desc : program_list) {
buffer buf = loader::load_file(disk, desc.name, desc.path);
args::program &program = args->programs[args->num_programs++];
loader::load_program(program, desc.name, buf, bs);
}
return args;
}
memory::efi_mem_map
uefi_exit(args::header *args, uefi::handle image, uefi::boot_services *bs)
{
status_line status {L"Exiting UEFI", nullptr, false};
memory::efi_mem_map map =
memory::build_kernel_mem_map(args, bs);
try_or_raise(
bs->exit_boot_services(image, map.key),
L"Failed to exit boot services");
return map;
}
} // namespace boot
/// The UEFI entrypoint for the loader.
extern "C" uefi::status
efi_main(uefi::handle image_handle, uefi::system_table *st)
efi_main(uefi::handle image, uefi::system_table *st)
{
using namespace boot;
error::cpu_assert_handler handler;
console con(st->boot_services, st->con_out);
check_cpu_supported();
kernel::entrypoint kentry = nullptr;
kernel::args::header *args =
bootloader_main_uefi(image_handle, st, con, &kentry);
args::header *args = uefi_preboot(image, st);
memory::efi_mem_map map = uefi_exit(args, image, st->boot_services);
memory::efi_mem_map map =
memory::build_kernel_mem_map(args, st->boot_services);
args->video = con.fb();
status_bar status {con.fb()}; // Switch to fb status display
try_or_raise(
st->boot_services->exit_boot_services(image_handle, map.key),
L"Failed to exit boot services");
// Map the kernel to the appropriate address
args::program &kernel = args->programs[0];
for (auto &section : kernel.sections)
if (section.size)
paging::map_section(args, section);
memory::fix_frame_blocks(args);
kernel::entrypoint kentry =
reinterpret_cast<kernel::entrypoint>(kernel.entrypoint);
status.next();
hw::setup_control_regs();
memory::virtualize(args->pml4, map, st->runtime_services);
status.next();
change_pointer(args);
change_pointer(args->pml4);
hw::setup_cr4();
change_pointer(args->modules);
change_pointer(args->programs);
status.next();
kentry(args);
debug_break();

View File

@@ -7,12 +7,15 @@
#include "error.h"
#include "memory.h"
#include "paging.h"
#include "status.h"
namespace boot {
namespace memory {
using mem_entry = kernel::args::mem_entry;
using mem_type = kernel::args::mem_type;
using frame_block = kernel::args::frame_block;
using kernel::args::frames_per_block;
size_t fixup_pointer_index = 0;
void **fixup_pointers[64];
@@ -35,20 +38,28 @@ static const wchar_t *memory_type_names[] = {
L"persistent memory"
};
static const wchar_t *kernel_memory_type_names[] = {
L"free",
L"pending",
L"acpi",
L"uefi_runtime",
L"mmio",
L"persistent"
};
static const wchar_t *
memory_type_name(uefi::memory_type t)
{
if (t < uefi::memory_type::max_memory_type) {
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";
}
return L"Bad Type Value";
}
static const wchar_t *
kernel_memory_type_name(kernel::args::mem_type t)
{
return kernel_memory_type_names[static_cast<uint32_t>(t)];
}
void
@@ -98,58 +109,174 @@ can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next)
}
void
get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
get_uefi_mappings(efi_mem_map &map, uefi::boot_services *bs)
{
status_line(L"Getting UEFI memory map");
size_t length = map.total;
uefi::status status = bs->get_memory_map(
&map->length, nullptr, &map->key, &map->size, &map->version);
&length, map.entries, &map.key, &map.size, &map.version);
map.length = length;
if (status == uefi::status::success)
return;
if (status != uefi::status::buffer_too_small)
error::raise(status, L"Error getting memory map size");
if (allocate) {
map->length += 10*map->size;
if (map.entries) {
try_or_raise(
bs->allocate_pool(
uefi::memory_type::loader_data, map->length,
reinterpret_cast<void**>(&map->entries)),
L"Allocating space for memory map");
bs->free_pool(reinterpret_cast<void*>(map.entries)),
L"Freeing previous memory map space");
}
try_or_raise(
bs->get_memory_map(&map->length, map->entries, &map->key, &map->size, &map->version),
L"Getting UEFI memory map");
map.total = length + 10*map.size;
try_or_raise(
bs->allocate_pool(
uefi::memory_type::loader_data, map.total,
reinterpret_cast<void**>(&map.entries)),
L"Allocating space for memory map");
map.length = map.total;
try_or_raise(
bs->get_memory_map(&map.length, map.entries, &map.key, &map.size, &map.version),
L"Getting UEFI memory map");
}
inline size_t bitmap_size(size_t frames) { return (frames + 63) / 64; }
inline size_t num_blocks(size_t frames) { return (frames + (frames_per_block-1)) / frames_per_block; }
void
build_kernel_frame_blocks(const mem_entry *map, size_t nent, kernel::args::header *args, uefi::boot_services *bs)
{
status_line status {L"Creating kernel frame accounting map"};
size_t block_count = 0;
size_t total_bitmap_size = 0;
for (size_t i = 0; i < nent; ++i) {
const mem_entry &ent = map[i];
if (ent.type != mem_type::free)
continue;
block_count += num_blocks(ent.pages);
total_bitmap_size += bitmap_size(ent.pages) * sizeof(uint64_t);
}
size_t total_size = block_count * sizeof(frame_block) + total_bitmap_size;
frame_block *blocks = nullptr;
try_or_raise(
bs->allocate_pages(
uefi::allocate_type::any_pages,
uefi::memory_type::loader_data,
bytes_to_pages(total_size),
reinterpret_cast<void**>(&blocks)),
L"Error allocating kernel frame block space");
frame_block *next_block = blocks;
for (size_t i = 0; i < nent; ++i) {
const mem_entry &ent = map[i];
if (ent.type != mem_type::free)
continue;
size_t page_count = ent.pages;
uintptr_t base_addr = ent.start;
while (page_count) {
frame_block *blk = next_block++;
bs->set_mem(blk, sizeof(frame_block), 0);
blk->attrs = ent.attr;
blk->base = base_addr;
base_addr += frames_per_block * page_size;
if (page_count >= frames_per_block) {
page_count -= frames_per_block;
blk->count = frames_per_block;
blk->map1 = ~0ull;
bs->set_mem(blk->map2, sizeof(blk->map2), 0xff);
} else {
blk->count = page_count;
unsigned i = 0;
uint64_t b1 = (page_count + 4095) / 4096;
blk->map1 = (1 << b1) - 1;
uint64_t b2 = (page_count + 63) / 64;
uint64_t b2q = b2 / 64;
uint64_t b2r = b2 % 64;
bs->set_mem(blk->map2, b2q, 0xff);
blk->map2[b2q] = (1 << b2r) - 1;
break;
}
}
}
uint64_t *bitmap = reinterpret_cast<uint64_t*>(next_block);
bs->set_mem(bitmap, total_bitmap_size, 0);
for (unsigned i = 0; i < block_count; ++i) {
frame_block &blk = blocks[i];
blk.bitmap = bitmap;
size_t b = blk.count / 64;
size_t r = blk.count % 64;
bs->set_mem(blk.bitmap, b*8, 0xff);
blk.bitmap[b] = (1 << r) - 1;
bitmap += bitmap_size(blk.count);
}
args->frame_block_count = block_count;
args->frame_block_pages = bytes_to_pages(total_size);
args->frame_blocks = blocks;
}
void
fix_frame_blocks(kernel::args::header *args)
{
// Map the frame blocks to the appropriate address
paging::map_pages(args,
reinterpret_cast<uintptr_t>(args->frame_blocks),
::memory::bitmap_start,
args->frame_block_pages,
true, false);
uintptr_t offset = ::memory::bitmap_start -
reinterpret_cast<uintptr_t>(args->frame_blocks);
for (unsigned i = 0; i < args->frame_block_count; ++i) {
frame_block &blk = args->frame_blocks[i];
blk.bitmap = reinterpret_cast<uint64_t*>(
reinterpret_cast<uintptr_t>(blk.bitmap) + offset);
}
}
efi_mem_map
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
{
status_line(L"Creating kernel memory map");
status_line status {L"Creating kernel memory map"};
efi_mem_map efi_map;
get_uefi_mappings(&efi_map, false, bs);
efi_mem_map map;
get_uefi_mappings(map, bs);
size_t map_size = efi_map.num_entries() * sizeof(mem_entry);
size_t map_size = 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,
uefi::memory_type::loader_data,
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);
get_uefi_mappings(map, bs);
size_t i = 0;
size_t nent = 0;
bool first = true;
for (auto desc : efi_map) {
for (auto desc : map) {
/*
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
// EFI map dump
console::print(L" eRange %lx (%lx) %x(%s) [%lu]\r\n",
desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages);
*/
@@ -162,10 +289,10 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
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:
case uefi::memory_type::loader_data:
type = mem_type::free;
break;
@@ -187,22 +314,6 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
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,
@@ -212,18 +323,19 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
// 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);
mem_entry &ent = kernel_map[nent++];
ent.start = desc->physical_start;
ent.pages = desc->number_of_pages;
ent.type = type;
ent.attr = (desc->attribute & 0xffffffff);
continue;
}
mem_entry &prev = kernel_map[i];
mem_entry &prev = kernel_map[nent - 1];
if (can_merge(prev, type, desc)) {
prev.pages += desc->number_of_pages;
} else {
mem_entry &next = kernel_map[++i];
mem_entry &next = kernel_map[nent++];
next.start = desc->physical_start;
next.pages = desc->number_of_pages;
next.type = type;
@@ -233,23 +345,20 @@ build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
// 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;
args->map_count = nent;
/*
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);
// kernel map dump
for (unsigned i = 0; i < nent; ++i) {
const kernel::args::mem_entry &e = kernel_map[i];
console::print(L" kRange %lx (%lx) %x(%s) [%lu]\r\n",
e.start, e.attr, e.type, kernel_memory_type_name(e.type), e.pages);
}
*/
return efi_map;
build_kernel_frame_blocks(kernel_map, nent, args, bs);
get_uefi_mappings(map, bs);
return map;
}
void

View File

@@ -18,28 +18,6 @@ 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
@@ -63,12 +41,13 @@ struct efi_mem_map
using iterator = offset_iterator<desc>;
size_t length; ///< Total length of the map data
size_t total; ///< Total allocated space for 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) {}
efi_mem_map() : length(0), total(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; }
@@ -84,6 +63,14 @@ struct efi_mem_map
/// \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);
/// Create the kernel frame allocation maps
void build_kernel_frame_blocks(
const kernel::args::mem_entry *map, size_t nent,
kernel::args::header *args, uefi::boot_services *bs);
/// Map the frame allocation maps to the right spot and fix up pointers
void fix_frame_blocks(kernel::args::header *args);
/// 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

View File

@@ -6,13 +6,16 @@
#include "memory.h"
#include "paging.h"
#include "pointer_manipulation.h"
#include "status.h"
namespace boot {
namespace paging {
using memory::page_size;
using ::memory::pml4e_kernel;
using ::memory::table_entries;
// Flags: 0 0 0 1 0 0 0 0 0 0 1 1 = 0x0103
// Flags: 0 0 0 1 0 0 0 0 0 0 0 1 = 0x0101
// IGN | | | | | | | | +- Present
// | | | | | | | +--- Writeable
// | | | | | | +----- Usermode access (supervisor only)
@@ -23,9 +26,9 @@ using memory::page_size;
// | +---------------- PAT (determining memory type for page)
// +------------------- Global
/// Page table entry flags for entries pointing at a page
constexpr uint16_t page_flags = 0x103;
constexpr uint64_t page_flags = 0x101;
// Flags: 0 0 0 0 1 1 0 0 0 0 0 1 1 = 0x0183
// Flags: 0 0 0 0 1 1 0 0 0 1 0 1 1 = 0x018b
// | IGN | | | | | | | | +- Present
// | | | | | | | | +--- Writeable
// | | | | | | | +----- Supervisor only
@@ -37,7 +40,7 @@ constexpr uint16_t page_flags = 0x103;
// | +------------------- 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;
constexpr uint64_t huge_page_flags = 0x18b;
// Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003
// IGNORED | | | | | | | +- Present
@@ -49,7 +52,7 @@ constexpr uint16_t huge_page_flags = 0x183;
// | +-------------- Ignored
// +---------------- Reserved 0 (Table pointer, not page)
/// Page table entry flags for entries pointing at another table
constexpr uint16_t table_flags = 0x003;
constexpr uint64_t table_flags = 0x003;
/// Iterator over page table entries.
template <unsigned D = 4>
@@ -65,7 +68,7 @@ public:
uintptr_t virt,
page_table *pml4,
void *&page_cache,
uint32_t &cache_count) :
size_t &cache_count) :
m_page_cache(page_cache),
m_cache_count(cache_count)
{
@@ -115,7 +118,7 @@ private:
if (!(parent_ent & 1)) {
if (!m_cache_count--)
error::raise(uefi::status::out_of_resources, L"Page table cache empty");
error::raise(uefi::status::out_of_resources, L"Page table cache empty", 0x7ab1e5);
page_table *table = reinterpret_cast<page_table*>(m_page_cache);
m_page_cache = offset_ptr<void>(m_page_cache, page_size);
@@ -128,14 +131,14 @@ private:
}
void *&m_page_cache;
uint32_t &m_cache_count;
size_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)
add_offset_mappings(page_table *pml4, void *&page_cache, size_t &num_pages)
{
uintptr_t phys = 0;
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
@@ -157,6 +160,16 @@ add_offset_mappings(page_table *pml4, void *&page_cache, uint32_t &num_pages)
}
}
static void
add_kernel_pds(page_table *pml4, void *&page_cache, size_t &num_pages)
{
for (unsigned i = pml4e_kernel; i < table_entries; ++i) {
pml4->set(i, page_cache, table_flags);
page_cache = offset_ptr<void>(page_cache, page_size);
num_pages--;
}
}
void
add_current_mappings(page_table *new_pml4)
{
@@ -177,51 +190,73 @@ 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;
static constexpr size_t pd_tables = 256; // number of pages for kernelspace PDs
static constexpr size_t extra_tables = 64; // number of extra pages
// number of pages for kernelspace PDs + PML4
static constexpr size_t kernel_tables = pd_tables + 1;
static constexpr size_t tables_needed = kernel_tables + extra_tables;
void *addr = nullptr;
try_or_raise(
bs->allocate_pages(
uefi::allocate_type::any_pages,
memory::table_type,
uefi::memory_type::loader_data,
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);
args->pml4 = pml4;
args->table_pages = tables_needed;
args->table_count = tables_needed - 1;
args->page_tables = offset_ptr<void>(addr, page_size);
console::print(L" First page (pml4) at: 0x%lx\r\n", pml4);
add_kernel_pds(pml4, args->page_tables, args->table_count);
add_offset_mappings(pml4, args->page_tables, args->table_count);
//console::print(L" Set up initial mappings, %d spare tables.\r\n", args->table_count);
}
template <typename E>
constexpr bool has_flag(E set, E flag) {
return
(static_cast<uint64_t>(set) & static_cast<uint64_t>(flag)) ==
static_cast<uint64_t>(flag);
}
void
map_pages(
page_table *pml4,
kernel::args::header *args,
uintptr_t phys, uintptr_t virt,
size_t size)
size_t count, bool write_flag, bool exe_flag)
{
size_t pages = memory::bytes_to_pages(size);
if (!count)
return;
paging::page_table *pml4 =
reinterpret_cast<paging::page_table*>(args->pml4);
page_entry_iterator<4> iterator{
virt, pml4,
args->page_table_cache,
args->num_free_tables};
args->page_tables,
args->table_count};
uint64_t flags = page_flags;
if (!exe_flag)
flags |= (1ull << 63); // set NX bit
if (write_flag)
flags |= 2;
while (true) {
*iterator = phys | page_flags;
if (--pages == 0)
*iterator = phys | flags;
if (--count == 0)
break;
iterator.increment();
@@ -229,6 +264,24 @@ map_pages(
}
}
void
map_section(
kernel::args::header *args,
const kernel::args::program_section &section)
{
using kernel::args::section_flags;
size_t pages = memory::bytes_to_pages(section.size);
map_pages(
args,
section.phys_addr,
section.virt_addr,
pages,
has_flag(section.type, section_flags::write),
has_flag(section.type, section_flags::execute));
}
} // namespace paging
} // namespace boot

View File

@@ -38,17 +38,22 @@ void allocate_tables(
/// 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
/// Map physical memory pages to virtual addresses in the given page tables.
/// \arg args The kernel args header, used for the page table cache and pml4
/// \arg section The program section to load
void map_pages(
page_table *pml4,
kernel::args::header *args,
uintptr_t phys, uintptr_t virt,
size_t bytes);
size_t count, bool write_flag, bool exe_flag);
/// Map a program section in physical memory to its virtual address in the
/// given page tables.
/// \arg args The kernel args header, used for the page table cache and pml4
/// \arg section The program section to load
void map_section(
kernel::args::header *args,
const kernel::args::program_section &section);
} // namespace paging
} // namespace boot

265
src/boot/status.cpp Normal file
View File

@@ -0,0 +1,265 @@
#include <uefi/types.h>
#include <uefi/graphics.h>
#include "console.h"
#include "error.h"
#include "kernel_args.h"
#include "status.h"
constexpr int num_boxes = 30;
namespace boot {
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
};
status *status::s_current = nullptr;
unsigned status::s_current_type = 0;
unsigned status_bar::s_count = 0;
status_line::status_line(const wchar_t *message, const wchar_t *context, bool fails_clean) :
status(fails_clean),
m_level(level_ok),
m_depth(0),
m_outer(nullptr)
{
if (status::s_current_type == status_line::type) {
m_outer = static_cast<status_line*>(s_current);
m_depth = (m_outer ? 1 + m_outer->m_depth : 0);
}
s_current = this;
s_current_type = status_line::type;
auto out = console::get().m_out;
m_line = out->mode->cursor_row;
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");
print_status_tag();
}
status_line::~status_line()
{
if (s_current != this)
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
if (m_outer && m_level > m_outer->m_level) {
m_outer->m_level = m_level;
m_outer->print_status_tag();
}
s_current = m_outer;
}
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, uefi::status status)
{
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);
const wchar_t *error = error::message(status);
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, uefi::status status)
{
auto out = console::get().m_out;
int row = out->mode->cursor_row;
if (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);
const wchar_t *error = error::message(status);
if (error) {
out->output_string(L": ");
out->output_string(error);
}
out->set_attribute(uefi::attribute::light_gray);
out->output_string(L"\r\n");
}
status_bar::status_bar(kernel::args::framebuffer const &fb) :
status(fb.size),
m_outer(nullptr)
{
m_size = (fb.vertical / num_boxes) - 1;
m_top = fb.vertical - m_size;
m_horiz = fb.horizontal;
m_fb = reinterpret_cast<uint32_t*>(fb.phys_addr);
m_type = static_cast<uint16_t>(fb.type);
next();
if (status::s_current_type == status_bar::type)
m_outer = static_cast<status_bar*>(s_current);
s_current = this;
s_current_type = status_bar::type;
}
status_bar::~status_bar()
{
if (s_current != this)
error::raise(uefi::status::unsupported, L"Destroying non-current status_bar");
draw_box();
s_current = m_outer;
}
void
status_bar::do_warn(const wchar_t *message, uefi::status status)
{
m_status = status;
if (m_level < level_warn) {
m_level = level_warn;
draw_box();
}
}
void
status_bar::do_fail(const wchar_t *message, uefi::status status)
{
m_status = status;
if (m_level < level_fail) {
m_level = level_fail;
draw_box();
}
}
static uint32_t
make_color(uint8_t r, uint8_t g, uint8_t b, uint16_t type)
{
switch (static_cast<kernel::args::fb_type>(type)) {
case kernel::args::fb_type::bgr8:
return
(static_cast<uint32_t>(b) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(r) << 16);
case kernel::args::fb_type::rgb8:
return
(static_cast<uint32_t>(r) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(b) << 16);
default:
return 0;
}
}
void
status_bar::draw_box()
{
static const uint32_t colors[] = {0x909090, 0xf0f0f0};
constexpr unsigned ncolors = sizeof(colors) / sizeof(uint32_t);
if (m_fb == nullptr)
return;
unsigned x0 = m_current * m_size;
unsigned x1 = x0 + m_size - 3;
unsigned y0 = m_top;
unsigned y1 = m_top + m_size - 3;
uint32_t color = 0;
switch (m_level) {
case level_ok:
color = colors[m_current % ncolors];
break;
case level_warn:
color = make_color(0xff, 0xb2, 0x34, m_type);
break;
case level_fail:
color = make_color(0xfb, 0x0a, 0x1e, m_type);
break;
default:
color = 0;
}
for (unsigned y = y0; y < y1; ++y)
for (unsigned x = x0; x < x1; ++x)
m_fb[y * m_horiz + x] = color;
if (m_level > level_ok) {
unsigned nbars = static_cast<uint64_t>(m_status) & 0xffff;
constexpr unsigned bar_height = 4;
for (unsigned i = 1; i <= nbars; ++i) {
y0 = m_top - 2 * i * bar_height;
y1 = y0 + bar_height;
for (unsigned y = y0; y < y1; ++y)
for (unsigned x = x0; x < x1; ++x)
m_fb[y * m_horiz + x] = color;
}
}
}
} // namespace boot

131
src/boot/status.h Normal file
View File

@@ -0,0 +1,131 @@
/// \file status.h
/// Status message and indicator handling
#pragma once
#include <stdint.h>
#include <uefi/types.h>
namespace kernel {
namespace args {
class framebuffer;
}
}
namespace boot {
// Abstract base class for status reporters.
class status
{
public:
status(bool fails_clean = true) : m_fails_clean(fails_clean) {}
virtual void do_warn(const wchar_t *message, uefi::status status) = 0;
virtual void do_fail(const wchar_t *message, uefi::status status) = 0;
/// 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, if text is supported
/// \arg status If set, the error or warning code that should be represented
/// \returns True if there was a status handler to display the warning
inline static bool warn(const wchar_t *message, uefi::status status = uefi::status::success) {
if (!s_current) return false;
s_current->do_warn(message, status);
return s_current->m_fails_clean;
}
/// 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, if text is supported
/// \arg status The error or warning code that should be represented
/// \returns True if there was a status handler to display the failure
inline static bool fail(const wchar_t *message, uefi::status status) {
if (!s_current) return false;
s_current->do_fail(message, status);
return s_current->m_fails_clean;
}
protected:
static status *s_current;
static unsigned s_current_type;
private:
bool m_fails_clean;
};
/// 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 status
{
public:
constexpr static unsigned type = 1;
/// Constructor.
/// \arg message Description of the operation in progress
/// \arg context If non-null, printed after `message` and a colon
/// \arg fails_clean If true, this object can handle printing failure
status_line(
const wchar_t *message,
const wchar_t *context = nullptr,
bool fails_clean = true);
~status_line();
virtual void do_warn(const wchar_t *message, uefi::status status) override;
virtual void do_fail(const wchar_t *message, uefi::status status) override;
private:
void print_status_tag();
size_t m_line;
int m_level;
int m_depth;
status_line *m_outer;
};
/// Scoped status bar reporter. Draws a row of boxes along the bottom of
/// the screen, turning one red if there's an error in that step.
class status_bar :
public status
{
public:
constexpr static unsigned type = 2;
using framebuffer = kernel::args::framebuffer;
/// Constructor.
/// \arg fb The framebuffer descriptor to draw to
status_bar(kernel::args::framebuffer const &fb);
~status_bar();
virtual void do_warn(const wchar_t *message, uefi::status status) override;
virtual void do_fail(const wchar_t *message, uefi::status status) override;
inline void next() {
m_current = s_count++;
m_level = 0;
m_status = uefi::status::success;
draw_box();
}
private:
void draw_box();
uint32_t *m_fb;
uint32_t m_size;
uint32_t m_top;
uint32_t m_horiz;
int m_level;
uefi::status m_status;
uint16_t m_type;
uint16_t m_current;
status_bar *m_outer;
static unsigned s_count;
};
} // namespace boot

13
src/boot/types.h Normal file
View File

@@ -0,0 +1,13 @@
/// \file types.h
/// Definitions of shared types used throughout the bootloader
#pragma once
namespace boot {
struct buffer
{
size_t size;
void *data;
};
} // namespace boot

View File

@@ -0,0 +1,390 @@
0x72, 0xb5, 0x4a, 0x86, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0x22, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x72, 0x8c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x7e, 0x24, 0x24,
0x24, 0x7e, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
0x1e, 0x20, 0x20, 0x1c, 0x02, 0x02, 0x3c, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0x49, 0x4a, 0x34, 0x08, 0x16, 0x29, 0x49, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x30, 0x31,
0x49, 0x46, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10,
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08,
0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x24, 0x18, 0x7e, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x18, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08,
0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x02, 0x04,
0x08, 0x10, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
0x04, 0x08, 0x1c, 0x02, 0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x0c, 0x14, 0x24, 0x44, 0x7e, 0x04, 0x04, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x7c, 0x02,
0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0x20, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c,
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x04, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x18, 0x18, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04,
0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02,
0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42,
0x02, 0x04, 0x08, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1c, 0x22, 0x41, 0x4f, 0x51, 0x51, 0x51, 0x53, 0x4d, 0x40,
0x20, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42,
0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
0x42, 0x42, 0x42, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x1e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x42,
0x42, 0x42, 0x44, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x46,
0x42, 0x42, 0x22, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x55, 0x49, 0x49,
0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
0x62, 0x52, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c,
0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x04, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x48, 0x44, 0x42, 0x42,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x40, 0x20, 0x18,
0x04, 0x02, 0x02, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42,
0x24, 0x24, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
0x41, 0x41, 0x41, 0x49, 0x49, 0x49, 0x55, 0x63, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x41,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08,
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10,
0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x78, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, 0x00,
0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x20, 0x10, 0x08,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7c, 0x42, 0x42,
0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42,
0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x10,
0x10, 0x7e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x7c, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x04, 0x00, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x38, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x44, 0x48, 0x50,
0x70, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7c,
0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x42, 0x42,
0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x2e, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x20, 0x18, 0x04, 0x02, 0x7c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x7e, 0x10, 0x10,
0x10, 0x10, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41,
0x49, 0x49, 0x55, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x04, 0x08,
0x10, 0x20, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x10, 0x10,
0x10, 0x10, 0x10, 0xe0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e, 0x00, 0x00,
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x00, 0x00, 0x00, 0x70, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07,
0x08, 0x08, 0x08, 0x08, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x31, 0x49,
0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,
0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x08, 0x08,
0x1c, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1c, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1c, 0x22, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x72, 0x8c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x24, 0x24,
0x24, 0x3c, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
0x22, 0x14, 0x08, 0x3e, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08,
0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x4d, 0x51, 0x51, 0x4d, 0x41, 0x22,
0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x09, 0x12, 0x24, 0x48, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x08, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x24, 0x12, 0x09, 0x12, 0x24, 0x48,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00,
0x00, 0x08, 0x10, 0x20, 0x40, 0x42, 0x3c, 0x00, 0x20, 0x10, 0x00, 0x18,
0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
0x04, 0x08, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42,
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24,
0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x18,
0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00,
0x24, 0x24, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42,
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x24,
0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
0x14, 0x14, 0x24, 0x27, 0x3c, 0x44, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x1e,
0x08, 0x08, 0x30, 0x00, 0x20, 0x10, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c,
0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x7e,
0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00,
0x18, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e,
0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c,
0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x3e,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x04, 0x08, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x00, 0x3e,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0x22, 0x21, 0x21, 0x79, 0x21, 0x21, 0x22, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x42, 0x62, 0x52, 0x4a, 0x46,
0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x1c,
0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00,
0x04, 0x08, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c,
0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41,
0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x1c,
0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00,
0x24, 0x24, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x14,
0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c,
0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x42, 0x3c, 0x40, 0x00, 0x00, 0x00,
0x20, 0x10, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x24, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08,
0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x40, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0x42, 0x44, 0x4c, 0x42, 0x42, 0x42, 0x44, 0x58,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x3c, 0x02, 0x02,
0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08,
0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x02, 0x02,
0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24,
0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x18, 0x24, 0x24, 0x18, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x09, 0x39,
0x4f, 0x48, 0x48, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3c, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3c, 0x08, 0x08, 0x30, 0x00,
0x00, 0x00, 0x20, 0x10, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x3c, 0x42, 0x42,
0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00,
0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x24, 0x24, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x38, 0x08, 0x08,
0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08,
0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x24, 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x38, 0x08, 0x08,
0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x06, 0x1a,
0x01, 0x1d, 0x23, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x32, 0x4c, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x3c, 0x42, 0x42,
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08,
0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x42, 0x42,
0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24,
0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c, 0x46, 0x4a,
0x52, 0x62, 0x42, 0x3c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10,
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x00, 0x00, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24,
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e,
0x02, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x5c, 0x62, 0x41,
0x41, 0x41, 0x62, 0x5c, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x24, 0x24,
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00,
0xff, 0xff, 0xe2, 0x96, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xb0,
0xff, 0xff, 0xff, 0xff, 0xe2, 0x94, 0x98, 0xff, 0xe2, 0x94, 0x90, 0xff,
0xe2, 0x94, 0x8c, 0xff, 0xe2, 0x94, 0x94, 0xff, 0xe2, 0x94, 0xbc, 0xff,
0xff, 0xff, 0xe2, 0x94, 0x80, 0xff, 0xff, 0xff, 0xe2, 0x94, 0x9c, 0xff,
0xe2, 0x94, 0xa4, 0xff, 0xe2, 0x94, 0xb4, 0xff, 0xe2, 0x94, 0xac, 0xff,
0xe2, 0x94, 0x82, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xa3, 0xff, 0xff,
0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, 0x24, 0xff, 0x25, 0xff,
0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff, 0x2a, 0xff, 0x2b, 0xff,
0x2c, 0xff, 0x2d, 0xff, 0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff,
0x32, 0xff, 0x33, 0xff, 0x34, 0xff, 0x35, 0xff, 0x36, 0xff, 0x37, 0xff,
0x38, 0xff, 0x39, 0xff, 0x3a, 0xff, 0x3b, 0xff, 0x3c, 0xff, 0x3d, 0xff,
0x3e, 0xff, 0x3f, 0xff, 0x40, 0xff, 0x41, 0xff, 0x42, 0xff, 0x43, 0xff,
0x44, 0xff, 0x45, 0xff, 0x46, 0xff, 0x47, 0xff, 0x48, 0xff, 0x49, 0xff,
0x4a, 0xff, 0x4b, 0xff, 0x4c, 0xff, 0x4d, 0xff, 0x4e, 0xff, 0x4f, 0xff,
0x50, 0xff, 0x51, 0xff, 0x52, 0xff, 0x53, 0xff, 0x54, 0xff, 0x55, 0xff,
0x56, 0xff, 0x57, 0xff, 0x58, 0xff, 0x59, 0xff, 0x5a, 0xff, 0x5b, 0xff,
0x5c, 0xff, 0x5d, 0xff, 0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff,
0x62, 0xff, 0x63, 0xff, 0x64, 0xff, 0x65, 0xff, 0x66, 0xff, 0x67, 0xff,
0x68, 0xff, 0x69, 0xff, 0x6a, 0xff, 0x6b, 0xff, 0x6c, 0xff, 0x6d, 0xff,
0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff, 0x72, 0xff, 0x73, 0xff,
0x74, 0xff, 0x75, 0xff, 0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff,
0x7a, 0xff, 0x7b, 0xff, 0x7c, 0xff, 0x7d, 0xff, 0x7e, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xa0, 0xff, 0xc2, 0xa1,
0xff, 0xc2, 0xa2, 0xff, 0xc2, 0xa3, 0xff, 0xc2, 0xa4, 0xff, 0xc2, 0xa5,
0xff, 0xc2, 0xa6, 0xff, 0xff, 0xc2, 0xa8, 0xff, 0xc2, 0xa9, 0xff, 0xff,
0xc2, 0xab, 0xff, 0xff, 0xc2, 0xad, 0xff, 0xff, 0xff, 0xc2, 0xb0, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xb8, 0xff, 0xff, 0xff,
0xc2, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xbf, 0xff, 0xc3, 0x80, 0xff,
0xc3, 0x81, 0xff, 0xc3, 0x82, 0xff, 0xc3, 0x83, 0xff, 0xc3, 0x84, 0xff,
0xc3, 0x85, 0xff, 0xc3, 0x86, 0xff, 0xc3, 0x87, 0xff, 0xc3, 0x88, 0xff,
0xc3, 0x89, 0xff, 0xc3, 0x8a, 0xff, 0xc3, 0x8b, 0xff, 0xc3, 0x8c, 0xff,
0xc3, 0x8d, 0xff, 0xc3, 0x8e, 0xff, 0xc3, 0x8f, 0xff, 0xc3, 0x90, 0xff,
0xc3, 0x91, 0xff, 0xc3, 0x92, 0xff, 0xc3, 0x93, 0xff, 0xc3, 0x94, 0xff,
0xc3, 0x95, 0xff, 0xc3, 0x96, 0xff, 0xc3, 0x97, 0xff, 0xc3, 0x98, 0xff,
0xc3, 0x99, 0xff, 0xc3, 0x9a, 0xff, 0xc3, 0x9b, 0xff, 0xc3, 0x9c, 0xff,
0xc3, 0x9d, 0xff, 0xc3, 0x9e, 0xff, 0xc3, 0x9f, 0xff, 0xc3, 0xa0, 0xff,
0xc3, 0xa1, 0xff, 0xc3, 0xa2, 0xff, 0xc3, 0xa3, 0xff, 0xc3, 0xa4, 0xff,
0xc3, 0xa5, 0xff, 0xc3, 0xa6, 0xff, 0xc3, 0xa7, 0xff, 0xc3, 0xa8, 0xff,
0xc3, 0xa9, 0xff, 0xc3, 0xaa, 0xff, 0xc3, 0xab, 0xff, 0xc3, 0xac, 0xff,
0xc3, 0xad, 0xff, 0xc3, 0xae, 0xff, 0xc3, 0xaf, 0xff, 0xc3, 0xb0, 0xff,
0xc3, 0xb1, 0xff, 0xc3, 0xb2, 0xff, 0xc3, 0xb3, 0xff, 0xc3, 0xb4, 0xff,
0xc3, 0xb5, 0xff, 0xc3, 0xb6, 0xff, 0xc3, 0xb7, 0xff, 0xc3, 0xb8, 0xff,
0xc3, 0xb9, 0xff, 0xc3, 0xba, 0xff, 0xc3, 0xbb, 0xff, 0xc3, 0xbc, 0xff,
0xc3, 0xbd, 0xff, 0xc3, 0xbe, 0xff, 0xc3, 0xbf, 0xff

View File

@@ -1,6 +1,7 @@
#include "kutil/assert.h"
#include <assert.h>
#include "font.h"
/* PSF2 header format
* Taken from the Linux KBD documentation
* http://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
@@ -27,43 +28,51 @@ struct psf2_header {
uint32_t height, width; // max dimensions of glyphs
};
const uint8_t default_font[] = {
// xxd -i < font_file.psf > default_font.inc
#include "default_font.inc"
};
font::font(void const *data) :
m_size(0, 0),
m_count(0),
m_data(nullptr)
m_sizex {0},
m_sizey {0},
m_count {0},
m_data {nullptr}
{
if (!data)
data = default_font;
psf2_header const *psf2 = static_cast<psf2_header const *>(data);
for (int i = 0; i < sizeof(magic); ++i) {
kassert(psf2->magic[i] == magic[i], "Bad font magic number.");
assert(psf2->magic[i] == magic[i] && "Bad font magic number.");
}
m_data = static_cast<uint8_t const *>(data) + psf2->header_size;
m_size.x = psf2->width;
m_size.y = psf2->height;
m_sizex = psf2->width;
m_sizey = psf2->height;
m_count = psf2->length;
}
void
font::draw_glyph(
screen *s,
screen &s,
uint32_t glyph,
screen::pixel_t fg,
screen::pixel_t bg,
unsigned x,
unsigned y) const
{
unsigned bwidth = (m_size.x+7)/8;
unsigned bwidth = (m_sizex+7)/8;
uint8_t const *data = m_data + (glyph * glyph_bytes());
for (int dy = 0; dy < m_size.y; ++dy) {
for (int dy = 0; dy < m_sizey; ++dy) {
for (int dx = 0; dx < bwidth; ++dx) {
uint8_t byte = data[dy * bwidth + dx];
for (int i = 0; i < 8; ++i) {
if (dx*8 + i >= m_size.x) continue;
if (dx*8 + i >= m_sizex) break;
const uint8_t mask = 1 << (7-i);
uint32_t c = (byte & mask) ? fg : bg;
s->draw_pixel(x + dx*8 + i, y + dy, c);
s.draw_pixel(x + dx*8 + i, y + dy, c);
}
}
}

33
src/drivers/fb/font.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <stdint.h>
#include "screen.h"
class font
{
public:
/// Constructor.
/// \arg data The font data to load. If null, will load the default
/// built-in font.
font(void const *data = nullptr);
unsigned glyph_bytes() const { return m_sizey * ((m_sizex + 7) / 8); }
unsigned count() const { return m_count; }
unsigned width() const { return m_sizex; }
unsigned height() const { return m_sizey; }
bool valid() const { return m_count > 0; }
void draw_glyph(
screen &s,
uint32_t glyph,
screen::pixel_t fg,
screen::pixel_t bg,
unsigned x,
unsigned y) const;
private:
unsigned m_sizex, m_sizey;
unsigned m_count;
uint8_t const *m_data;
};

139
src/drivers/fb/main.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "j6/init.h"
#include "j6/errors.h"
#include "j6/flags.h"
#include "j6/signals.h"
#include "j6/syscalls.h"
#include "j6/types.h"
#include "font.h"
#include "screen.h"
#include "scrollback.h"
extern "C" {
int main(int, const char **);
void _get_init(size_t *initc, struct j6_init_value **initv);
}
extern j6_handle_t __handle_sys;
extern j6_handle_t __handle_self;
struct entry
{
uint8_t bytes;
uint8_t area;
uint8_t severity;
uint8_t sequence;
char message[0];
};
int
main(int argc, const char **argv)
{
j6_system_log("fb driver starting");
size_t initc = 0;
j6_init_value *initv = nullptr;
_get_init(&initc, &initv);
j6_init_framebuffer *fb = nullptr;
for (unsigned i = 0; i < initc; ++i) {
if (initv[i].type == j6_init_desc_framebuffer) {
fb = reinterpret_cast<j6_init_framebuffer*>(initv[i].data);
break;
}
}
if (!fb || fb->addr == 0) {
j6_system_log("fb driver didn't find a framebuffer, exiting");
return 1;
}
j6_handle_t fb_handle = j6_handle_invalid;
uint32_t flags =
j6_vm_flag_write |
j6_vm_flag_write_combine;
j6_status_t s = j6_system_map_mmio(__handle_sys, &fb_handle, fb->addr, fb->size, flags);
if (s != j6_status_ok) {
return s;
}
s = j6_vma_map(fb_handle, __handle_self, fb->addr);
if (s != j6_status_ok) {
return s;
}
const screen::pixel_order order = (fb->flags & 1) ?
screen::pixel_order::bgr8 : screen::pixel_order::rgb8;
screen scr(
reinterpret_cast<void*>(fb->addr),
fb->horizontal,
fb->vertical,
fb->scanline,
order);
font fnt;
screen::pixel_t fg = scr.color(0xb0, 0xb0, 0xb0);
screen::pixel_t bg = scr.color(49, 79, 128);
scr.fill(bg);
scr.update();
constexpr int margin = 2;
const unsigned xstride = (margin + fnt.width());
const unsigned ystride = (margin + fnt.height());
const unsigned rows = (scr.height() - margin) / ystride;
const unsigned cols = (scr.width() - margin) / xstride;
scrollback scroll(rows, cols);
int pending = 0;
constexpr int pending_threshold = 5;
j6_handle_t sys = __handle_sys;
size_t buffer_size = 0;
void *message_buffer = nullptr;
while (true) {
size_t size = buffer_size;
j6_status_t s = j6_system_get_log(sys, message_buffer, &size);
if (s == j6_err_insufficient) {
free(message_buffer);
message_buffer = malloc(size * 2);
buffer_size = size;
continue;
} else if (s != j6_status_ok) {
j6_system_log("fb driver got error from get_log, quitting");
return s;
}
if (size > 0) {
entry *e = reinterpret_cast<entry*>(message_buffer);
size_t eom = e->bytes - sizeof(entry);
e->message[eom] = 0;
scroll.add_line(e->message, eom);
if (++pending > pending_threshold) {
scroll.render(scr, fnt);
scr.update();
pending = 0;
}
} else {
if (pending) {
scroll.render(scr, fnt);
scr.update();
pending = 0;
}
}
}
j6_system_log("fb driver done, exiting");
return 0;
}

48
src/drivers/fb/screen.cpp Normal file
View File

@@ -0,0 +1,48 @@
#include <stdlib.h>
#include <string.h>
#include "screen.h"
screen::screen(volatile void *addr, unsigned hres, unsigned vres, unsigned scanline, pixel_order order) :
m_fb(static_cast<volatile pixel_t *>(addr)),
m_order(order),
m_scanline(scanline),
m_resx(hres),
m_resy(vres)
{
const size_t size = scanline * vres;
m_back = reinterpret_cast<pixel_t*>(malloc(size * sizeof(pixel_t)));
}
screen::pixel_t
screen::color(uint8_t r, uint8_t g, uint8_t b) const
{
switch (m_order) {
case pixel_order::bgr8:
return
(static_cast<uint32_t>(b) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(r) << 16);
case pixel_order::rgb8:
return
(static_cast<uint32_t>(r) << 0) |
(static_cast<uint32_t>(g) << 8) |
(static_cast<uint32_t>(b) << 16);
}
}
void
screen::fill(pixel_t color)
{
const size_t len = m_scanline * m_resy;
asm volatile ( "rep stosl" : :
"a"(color), "c"(len), "D"(m_back) );
}
void
screen::update()
{
const size_t len = m_scanline * m_resy * sizeof(pixel_t);
asm volatile ( "rep movsb" : :
"c"(len), "S"(m_back), "D"(m_fb) );
}

36
src/drivers/fb/screen.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
class screen
{
public:
using pixel_t = uint32_t;
enum class pixel_order : uint8_t { bgr8, rgb8, };
screen(volatile void *addr, unsigned hres, unsigned vres, unsigned scanline, pixel_order order);
unsigned width() const { return m_resx; }
unsigned height() const { return m_resy; }
pixel_t color(uint8_t r, uint8_t g, uint8_t b) const;
void fill(pixel_t color);
inline void draw_pixel(unsigned x, unsigned y, pixel_t color) {
const size_t index = x + y * m_scanline;
m_back[index] = color;
}
void update();
private:
volatile pixel_t *m_fb;
pixel_t *m_back;
pixel_order m_order;
unsigned m_scanline;
unsigned m_resx, m_resy;
screen() = delete;
};

View File

@@ -0,0 +1,58 @@
#include <stdlib.h>
#include <string.h>
#include "font.h"
#include "screen.h"
#include "scrollback.h"
scrollback::scrollback(unsigned lines, unsigned cols, unsigned margin) :
m_rows {lines},
m_cols {cols},
m_count {0},
m_margin {margin}
{
m_data = reinterpret_cast<char*>(malloc(lines*cols));
memset(m_data, ' ', lines*cols);
}
void
scrollback::add_line(const char *line, size_t len)
{
unsigned i = m_count++ % m_rows;
if (len > m_cols)
len = m_cols;
char *start = m_data + (i * m_cols);
memcpy(start, line, len);
if (len < m_cols)
memset(start + len, ' ', m_cols - len);
}
char *
scrollback::get_line(unsigned i)
{
unsigned line = (i + m_count) % m_rows;
return &m_data[line*m_cols];
}
void
scrollback::render(screen &scr, font &fnt)
{
screen::pixel_t fg = scr.color(0xb0, 0xb0, 0xb0);
screen::pixel_t bg = scr.color(49, 79, 128);
const unsigned xstride = (m_margin + fnt.width());
const unsigned ystride = (m_margin + fnt.height());
unsigned start = m_count <= m_rows ? 0 :
m_count % m_rows;
for (unsigned y = 0; y < m_rows; ++y) {
unsigned i = (start + y) % m_rows;
char *line = &m_data[i*m_cols];
for (unsigned x = 0; x < m_cols; ++x) {
fnt.draw_glyph(scr, line[x], fg, bg, m_margin+x*xstride, m_margin+y*ystride);
}
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
/// \file scrollback.h
class screen;
class font;
class scrollback
{
public:
scrollback(unsigned lines, unsigned cols, unsigned margin = 2);
void add_line(const char *line, size_t len);
char * get_line(unsigned i);
void render(screen &scr, font &fnt);
private:
char *m_data;
unsigned m_rows, m_cols;
unsigned m_start;
unsigned m_count;
unsigned m_margin;
};

View File

@@ -0,0 +1,22 @@
#include "io.h"
uint8_t
inb(uint16_t port)
{
uint8_t val;
__asm__ __volatile__ ( "inb %1, %0" : "=a"(val) : "Nd"(port) );
return val;
}
void
outb(uint16_t port, uint8_t val)
{
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
}
void
io_wait(unsigned times)
{
for (unsigned i = 0; i < times; ++i)
outb(0x80, 0);
}

24
src/drivers/nulldrv/io.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
extern "C" {
/// Read a byte from an IO port.
/// \arg port The address of the IO port
/// \returns One byte read from the port
uint8_t inb(uint16_t port);
/// Write a byte to an IO port.
/// \arg port The addres of the IO port
/// \arg val The byte to write
void outb(uint16_t port, uint8_t val);
/// Pause briefly by doing IO to port 0x80
/// \arg times Number of times to delay by writing
void io_wait(unsigned times = 1);
}
constexpr uint16_t COM1 = 0x03f8;
constexpr uint16_t COM2 = 0x02f8;

View File

@@ -3,36 +3,141 @@
#include "j6/types.h"
#include "j6/errors.h"
#include "j6/signals.h"
#include "j6/syscalls.h"
#include "io.h"
#include "serial.h"
char inbuf[1024];
extern j6_handle_t __handle_sys;
j6_handle_t endp = j6_handle_invalid;
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 **);
}
void
thread_proc()
{
j6_system_log("sub thread starting");
char buffer[512];
size_t len = sizeof(buffer);
j6_tag_t tag = 0;
j6_status_t result = j6_endpoint_receive(endp, &tag, &len, (void*)buffer);
if (result != j6_status_ok)
j6_thread_exit(result);
j6_system_log("sub thread received message");
for (int i = 0; i < len; ++i)
if (buffer[i] >= 'A' && buffer[i] <= 'Z')
buffer[i] += 0x20;
tag++;
result = j6_endpoint_send(endp, tag, len, (void*)buffer);
if (result != j6_status_ok)
j6_thread_exit(result);
j6_system_log("sub thread sent message");
for (int i = 1; i < 5; ++i)
j6_thread_sleep(i*10);
j6_system_log("sub thread exiting");
j6_thread_exit(0);
}
int
main(int argc, const char **argv)
{
uint64_t pid = 0;
uint64_t child = 0;
j6_handle_t child = j6_handle_invalid;
j6_signal_t out = 0;
j6_status_t result = fork(&child);
j6_system_log("main thread starting");
for (int i = 0; i < argc; ++i)
j6_system_log(argv[i]);
void *base = malloc(0x1000);
if (!base)
return 1;
uint64_t *vma_ptr = reinterpret_cast<uint64_t*>(base);
for (int i = 0; i < 3; ++i)
vma_ptr[i*100] = uint64_t(i);
j6_system_log("main thread wrote to memory area");
j6_status_t result = j6_endpoint_create(&endp);
if (result != j6_status_ok)
return result;
message("hello from nulldrv!");
j6_system_log("main thread created endpoint");
result = getpid(&pid);
result = j6_thread_create(reinterpret_cast<void*>(&thread_proc), &child);
if (result != j6_status_ok)
return result;
for (int i = 1; i < 5; ++i)
sleep(i*10);
j6_system_log("main thread created sub thread");
return pid;
char message[] = "MAIN THREAD SUCCESSFULLY CALLED SENDRECV IF THIS IS LOWERCASE";
size_t size = sizeof(message);
j6_tag_t tag = 16;
result = j6_endpoint_sendrecv(endp, &tag, &size, (void*)message);
if (result != j6_status_ok)
return result;
if (tag != 17)
j6_system_log("GOT WRONG TAG FROM SENDRECV");
result = j6_system_bind_irq(__handle_sys, endp, 3);
if (result != j6_status_ok)
return result;
j6_system_log(message);
j6_system_log("main thread waiting on child");
result = j6_object_wait(child, -1ull, &out);
if (result != j6_status_ok)
return result;
j6_system_log("main thread creating a new process");
j6_handle_t child_proc = j6_handle_invalid;
result = j6_process_create(&child_proc);
if (result != j6_status_ok)
return result;
j6_system_log("main testing irqs");
serial_port com2(COM2);
const char *fgseq = "\x1b[2J";
while (*fgseq)
com2.write(*fgseq++);
for (int i = 0; i < 10; ++i)
com2.write('%');
size_t len = 0;
while (true) {
result = j6_endpoint_receive(endp, &tag, &len, nullptr);
if (result != j6_status_ok)
return result;
if (j6_tag_is_irq(tag))
j6_system_log("main thread got irq!");
}
j6_system_log("main thread closing endpoint");
result = j6_object_close(endp);
if (result != j6_status_ok)
return result;
j6_system_log("main thread done, exiting");
return 0;
}

View File

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

View File

@@ -0,0 +1,41 @@
#include "io.h"
#include "serial.h"
serial_port::serial_port() :
m_port(0)
{
}
serial_port::serial_port(uint16_t port) :
m_port(port)
{
outb(port + 1, 0x00); // Disable all interrupts
outb(port + 3, 0x80); // Enable the Divisor Latch Access Bit
outb(port + 0, 0x01); // Divisor low bit: 1 (115200 baud)
outb(port + 1, 0x00); // Divisor high bit
outb(port + 3, 0x03); // 8-N-1
outb(port + 2, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
outb(port + 4, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable)
outb(port + 1, 0x03); // Enable interrupts
}
bool serial_port::read_ready() { return (inb(m_port + 5) & 0x01) != 0; }
bool serial_port::write_ready() {
uint8_t lsr = inb(m_port + 5);
return (lsr & 0x20) != 0;
}
char
serial_port::read() {
while (!read_ready());
return inb(m_port);
}
void
serial_port::write(char c) {
while (!write_ready());
outb(m_port, c);
}

View File

@@ -0,0 +1,26 @@
#pragma once
/// \file serial.h
/// Declarations related to serial ports.
#include <stdint.h>
#define serial_port nulldrv_serial_port
class serial_port
{
public:
/// Constructor.
/// \arg port The IO address of the serial port
serial_port(uint16_t port);
serial_port();
void write(char c);
char read();
private:
uint16_t m_port;
bool read_ready();
bool write_ready();
};

View File

@@ -8,9 +8,13 @@
#define j6_status_ok 0x0000
#define j6_status_closed 0x1000
#define j6_status_destroyed 0x1001
#define j6_status_exists 0x1002
#define j6_err_nyi j6_err(0x0001)
#define j6_err_unexpected j6_err(0x0002)
#define j6_err_invalid_arg j6_err(0x0003)
#define j6_err_not_ready j6_err(0x0004)
#define j6_err_insufficient j6_err(0x0005)

10
src/include/j6/flags.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
/// \file flags.h
/// Enums used as flags for syscalls
enum j6_vm_flags {
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v,
#include "j6/tables/vm_flags.inc"
#undef VM_FLAG
j6_vm_flags_MAX
};

37
src/include/j6/init.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
/// \file init.h
/// Types used in process and thread initialization
#include <stdint.h>
#include "j6/types.h"
enum j6_init_type { // `value` is a:
j6_init_handle_self, // Handle to the system
j6_init_handle_other, // Handle to this process
j6_init_desc_framebuffer // Pointer to a j6_init_framebuffer descriptor
};
struct j6_typed_handle {
enum j6_object_type type;
j6_handle_t handle;
};
struct j6_init_value {
enum j6_init_type type;
union {
struct j6_typed_handle handle;
void *data;
};
};
/// Structure defining a framebuffer.
/// `flags` has the following bits:
/// 0-3: Pixel layout. 0000: rgb8, 0001: bgr8
struct j6_init_framebuffer {
uintptr_t addr;
size_t size;
uint32_t vertical;
uint32_t horizontal;
uint32_t scanline;
uint32_t flags;
};

View File

@@ -2,17 +2,73 @@
/// \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 0-15 are common to all types
#define j6_signal_no_handles (1ull << 0)
#define j6_signal_closed (1ull << 1)
// 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)
// Signals 16-47 are defined per-object-type
// All other signals are type-specific
// Event signals
#define j7_signal_event00 (1ull << 16)
#define j7_signal_event01 (1ull << 17)
#define j7_signal_event02 (1ull << 18)
#define j7_signal_event03 (1ull << 19)
#define j7_signal_event04 (1ull << 20)
#define j7_signal_event05 (1ull << 21)
#define j7_signal_event06 (1ull << 22)
#define j7_signal_event07 (1ull << 23)
#define j7_signal_event08 (1ull << 24)
#define j7_signal_event09 (1ull << 25)
#define j7_signal_event10 (1ull << 26)
#define j7_signal_event11 (1ull << 27)
#define j7_signal_event12 (1ull << 28)
#define j7_signal_event13 (1ull << 29)
#define j7_signal_event14 (1ull << 30)
#define j7_signal_event15 (1ull << 31)
#define j7_signal_event16 (1ull << 32)
#define j7_signal_event17 (1ull << 33)
#define j7_signal_event18 (1ull << 34)
#define j7_signal_event19 (1ull << 35)
#define j7_signal_event20 (1ull << 36)
#define j7_signal_event21 (1ull << 37)
#define j7_signal_event22 (1ull << 38)
#define j7_signal_event23 (1ull << 39)
#define j7_signal_event24 (1ull << 40)
#define j7_signal_event25 (1ull << 41)
#define j7_signal_event26 (1ull << 42)
#define j7_signal_event27 (1ull << 43)
#define j7_signal_event28 (1ull << 44)
#define j7_signal_event29 (1ull << 45)
#define j7_signal_event30 (1ull << 46)
#define j7_signal_event31 (1ull << 47)
// System signals
#define j6_signal_system_has_log (1ull << 16)
// Channel signals
#define j6_signal_channel_can_send (1ull << 16)
#define j6_signal_channel_can_recv (1ull << 17)
// Endpoint signals
#define j6_signal_endpoint_can_send (1ull << 16)
#define j6_signal_endpoint_can_recv (1ull << 17)
// Signals 48-63 are user-defined signals
#define j6_signal_user0 (1ull << 48)
#define j6_signal_user1 (1ull << 49)
#define j6_signal_user2 (1ull << 50)
#define j6_signal_user3 (1ull << 51)
#define j6_signal_user4 (1ull << 52)
#define j6_signal_user5 (1ull << 53)
#define j6_signal_user6 (1ull << 54)
#define j6_signal_user7 (1ull << 55)
#define j6_signal_user8 (1ull << 56)
#define j6_signal_user9 (1ull << 57)
#define j6_signal_user10 (1ull << 58)
#define j6_signal_user11 (1ull << 59)
#define j6_signal_user12 (1ull << 60)
#define j6_signal_user13 (1ull << 61)
#define j6_signal_user14 (1ull << 62)
#define j6_signal_user15 (1ull << 63)
#define j6_signal_user_mask (0xffffull << 48)

View File

@@ -0,0 +1,15 @@
LOG(apic, info);
LOG(device, debug);
LOG(paging, info);
LOG(driver, info);
LOG(memory, debug);
LOG(fs, info);
LOG(task, info);
LOG(sched, info);
LOG(loader, debug);
LOG(boot, debug);
LOG(syscall,info);
LOG(vmem, debug);
LOG(objs, debug);
LOG(timer, debug);
LOG(clock, debug);

View File

@@ -0,0 +1,12 @@
OBJECT_TYPE( none, 0x00 )
OBJECT_TYPE( system, 0x01 )
OBJECT_TYPE( event, 0x02 )
OBJECT_TYPE( channel, 0x03 )
OBJECT_TYPE( endpoint, 0x04 )
OBJECT_TYPE( vma, 0x05 )
OBJECT_TYPE( process, 0x06 )
OBJECT_TYPE( thread, 0x07 )

View File

@@ -0,0 +1,35 @@
SYSCALL(0x00, system_log, const char *)
SYSCALL(0x01, system_noop, void)
SYSCALL(0x02, system_get_log, j6_handle_t, void *, size_t *)
SYSCALL(0x03, system_bind_irq, j6_handle_t, j6_handle_t, unsigned)
SYSCALL(0x04, system_map_mmio, j6_handle_t, j6_handle_t *, uintptr_t, size_t, uint32_t)
SYSCALL(0x08, object_koid, j6_handle_t, j6_koid_t *)
SYSCALL(0x09, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *)
SYSCALL(0x0a, object_signal, j6_handle_t, j6_signal_t)
SYSCALL(0x0b, object_close, j6_handle_t)
SYSCALL(0x10, process_create, j6_handle_t *)
SYSCALL(0x11, process_start, j6_handle_t, uintptr_t, j6_handle_t *, size_t)
SYSCALL(0x11, process_kill, j6_handle_t)
SYSCALL(0x17, process_exit, int32_t)
SYSCALL(0x18, thread_create, void *, j6_handle_t *)
SYSCALL(0x19, thread_exit, int32_t)
SYSCALL(0x1a, thread_pause, void)
SYSCALL(0x1b, thread_sleep, uint64_t)
SYSCALL(0x20, channel_create, j6_handle_t *)
SYSCALL(0x21, channel_send, j6_handle_t, size_t *, void *)
SYSCALL(0x22, channel_receive, j6_handle_t, size_t *, void *)
SYSCALL(0x28, endpoint_create, j6_handle_t *)
SYSCALL(0x29, endpoint_send, j6_handle_t, j6_tag_t, size_t, void *)
SYSCALL(0x2a, endpoint_receive, j6_handle_t, j6_tag_t *, size_t *, void *)
SYSCALL(0x2b, endpoint_sendrecv, j6_handle_t, j6_tag_t *, size_t *, void *)
SYSCALL(0x30, vma_create, j6_handle_t *, size_t, uint32_t)
SYSCALL(0x31, vma_create_map, j6_handle_t *, size_t, uintptr_t, uint32_t)
SYSCALL(0x32, vma_map, j6_handle_t, j6_handle_t, uintptr_t)
SYSCALL(0x33, vma_unmap, j6_handle_t, j6_handle_t)
SYSCALL(0x34, vma_resize, j6_handle_t, size_t *)

View File

@@ -0,0 +1,9 @@
VM_FLAG( none, 0x00000000)
VM_FLAG( write, 0x00000001)
VM_FLAG( exec, 0x00000002)
VM_FLAG( zero, 0x00000010)
VM_FLAG( contiguous, 0x00000020)
VM_FLAG( large_pages, 0x00000100)
VM_FLAG( huge_pages, 0x00000200)
VM_FLAG( write_combine, 0x00001000)
VM_FLAG( mmio, 0x00010000)

View File

@@ -10,11 +10,36 @@ 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;
/// The first word of IPC messages are the tag. Tags with the high bit
/// set are reserved for the system.
typedef uint64_t j6_tag_t;
#define j6_tag_system_flag 0x8000000000000000
#define j6_tag_invalid 0x0000000000000000
/// If all high bits except the last 16 are set, then the tag represents
/// an IRQ.
#define j6_tag_irq_base 0xffffffffffff0000
#define j6_tag_is_irq(x) (((x) & j6_tag_irq_base) == j6_tag_irq_base)
#define j6_tag_from_irq(x) ((x) | j6_tag_irq_base)
#define j6_tag_to_irq(x) ((x) & ~j6_tag_irq_base)
/// Handles are references and capabilities to other objects. The least
/// significant 32 bits are an identifier, and the most significant 32
/// bits are a bitmask of capabilities this handle has on that object.
typedef uint64_t j6_handle_t;
#define j6_handle_rights_shift 4
#define j6_handle_id_mask 0xffffffffull
#define j6_handle_invalid 0xffffffffull
enum j6_object_type {
#define OBJECT_TYPE( name, val ) j6_object_type_ ## name = val,
#include "j6/tables/object_types.inc"
#undef OBJECT_TYPE
j6_object_type_max
};

View File

@@ -10,44 +10,56 @@ 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
symbol_table
};
struct module {
void *location;
size_t size;
mod_type type;
mod_flags flags;
}
__attribute__((packed));
};
enum class fb_type : uint16_t {
none,
rgb8,
bgr8
};
struct framebuffer {
uintptr_t phys_addr;
size_t size;
uint32_t vertical;
uint32_t horizontal;
uint16_t scanline;
fb_type type;
};
enum class section_flags : uint32_t {
none = 0,
execute = 1,
write = 2,
read = 4,
};
struct program_section {
uintptr_t phys_addr;
uintptr_t virt_addr;
uint32_t size;
section_flags type;
};
struct program {
uintptr_t entrypoint;
uintptr_t base;
size_t total_size;
size_t num_sections;
program_section sections[3];
};
enum class mem_type : uint32_t {
free,
args,
kernel,
module,
table,
pending,
acpi,
uefi_runtime,
mmio,
@@ -61,33 +73,54 @@ struct mem_entry
size_t pages;
mem_type type;
uint32_t attr;
}
__attribute__((packed));
};
constexpr size_t frames_per_block = 64 * 64 * 64;
struct frame_block
{
uintptr_t base;
uint32_t count;
uint32_t attrs;
uint64_t map1;
uint64_t map2[64];
uint64_t *bitmap;
};
enum class boot_flags : uint16_t {
none = 0x0000,
debug = 0x0001
};
struct header {
uint32_t magic;
uint16_t version;
mode mode;
uint8_t _reserved0;
boot_flags flags;
void *pml4;
void *page_table_cache;
uint32_t num_free_tables;
void *page_tables;
size_t table_count;
size_t table_pages;
program *programs;
size_t num_programs;
uint32_t num_modules;
module *modules;
size_t num_modules;
mem_entry *mem_map;
size_t num_map_entries;
size_t map_count;
frame_block *frame_blocks;
size_t frame_block_count;
size_t frame_block_pages;
void *runtime_services;
void *acpi_table;
framebuffer video;
}
__attribute__((aligned(alignof(max_align_t))));
#pragma pack(pop)
} // namespace args

View File

@@ -11,23 +11,41 @@ namespace memory {
constexpr size_t frame_size = 0x1000;
/// Start of kernel memory.
constexpr uintptr_t kernel_offset = 0xffff800000000000;
constexpr uintptr_t kernel_offset = 0xffff800000000000ull;
/// Offset from physical where page tables are mapped.
constexpr uintptr_t page_offset = 0xffffc00000000000;
constexpr uintptr_t page_offset = 0xffffc00000000000ull;
/// Initial process thread's stack address
constexpr uintptr_t initial_stack = 0x0000800000000000;
/// Max number of pages for a kernel stack
constexpr unsigned kernel_stack_pages = 2;
/// Initial process thread's stack size, in pages
constexpr unsigned initial_stack_pages = 1;
/// Max number of pages for a kernel buffer
constexpr unsigned kernel_buffer_pages = 16;
/// Max size of the kernel heap
constexpr size_t kernel_max_heap = 0x8000000000; // 512GiB
constexpr size_t kernel_max_heap = 0x8000000000ull; // 512GiB
/// Start of the kernel heap
constexpr uintptr_t heap_start = page_offset - kernel_max_heap;
/// Max size of the kernel stacks area
constexpr size_t kernel_max_stacks = 0x8000000000ull; // 512GiB
/// Start of the kernel stacks
constexpr uintptr_t stacks_start = heap_start - kernel_max_stacks;
/// Max size of kernel buffers area
constexpr size_t kernel_max_buffers = 0x8000000000ull; // 512GiB
/// Start of kernel buffers
constexpr uintptr_t buffers_start = stacks_start - kernel_max_buffers;
/// Max size of kernel bitmap area
constexpr size_t kernel_max_bitmap = 0x8000000000ull; // 512GiB
/// Start of kernel bitmap
constexpr uintptr_t bitmap_start = buffers_start - kernel_max_bitmap;
/// First kernel space PML4 entry
constexpr unsigned pml4e_kernel = 256;
@@ -41,4 +59,27 @@ namespace memory {
/// through the page_offset area.
inline bool page_mappable(uintptr_t a) { return (a & page_offset) == 0; }
/// Convert a physical address to a virtual one (in the offset-mapped area)
template <typename T> T * to_virtual(uintptr_t a) {
return reinterpret_cast<T*>(a|page_offset);
}
/// Convert a physical address to a virtual one (in the offset-mapped area)
template <typename T> T * to_virtual(T *p) {
return to_virtual<T>(reinterpret_cast<uintptr_t>(p));
}
/// Get the number of pages needed for a given number of bytes.
/// \arg bytes The number of bytes desired
/// \returns The number of pages needed to contain the desired bytes
inline size_t page_count(size_t bytes) {
return ((bytes - 1) >> 12) + 1;
}
/// Get the given address, aligned to the next lowest page
inline uintptr_t page_align_down(uintptr_t a) { return a & ~(frame_size-1); }
/// Get the given address, aligned to the next page
inline uintptr_t page_align_up(uintptr_t a) { return page_align_down(a-1) + frame_size; }
} // namespace memory

View File

@@ -1,11 +0,0 @@
LOG(apic, info);
LOG(device, info);
LOG(paging, warn);
LOG(driver, info);
LOG(memory, info);
LOG(fs, info);
LOG(task, info);
LOG(boot, debug);
LOG(syscall,debug);
LOG(vmem, debug);
LOG(objs, debug);

View File

@@ -23,7 +23,7 @@ struct acpi_table_header
} __attribute__ ((packed));
#define TABLE_HEADER(signature) \
static const uint32_t type_id = kutil::byteswap(signature); \
static constexpr uint32_t type_id = kutil::byteswap(signature); \
acpi_table_header header;
@@ -188,3 +188,24 @@ struct acpi_mcfg
acpi_mcfg_entry entries[0];
} __attribute__ ((packed));
struct acpi_hpet
{
TABLE_HEADER('HPET');
uint32_t hardware_id;
acpi_gas base_address;
uint8_t index;
uint16_t periodic_min;
uint8_t attributes;
} __attribute__ ((packed));
struct acpi_bgrt
{
TABLE_HEADER('BGRT');
uint16_t version;
uint8_t status;
uint8_t type;
uintptr_t address;
uint32_t offset_x;
uint32_t offset_y;
} __attribute__ ((packed));

149
src/kernel/ap_startup.s Normal file
View File

@@ -0,0 +1,149 @@
%include "tasking.inc"
section .ap_startup
BASE equ 0x8000 ; Where the kernel will map this at runtime
CR0_PE equ (1 << 0)
CR0_MP equ (1 << 1)
CR0_ET equ (1 << 4)
CR0_NE equ (1 << 5)
CR0_WP equ (1 << 16)
CR0_PG equ (1 << 31)
CR0_VAL equ CR0_PE|CR0_MP|CR0_ET|CR0_NE|CR0_WP|CR0_PG
CR4_DE equ (1 << 3)
CR4_PAE equ (1 << 5)
CR4_MCE equ (1 << 6)
CR4_PGE equ (1 << 7)
CR4_OSFXSR equ (1 << 9)
CR4_OSCMMEXCPT equ (1 << 10)
CR4_FSGSBASE equ (1 << 16)
CR4_PCIDE equ (1 << 17)
CR4_INIT equ CR4_PAE|CR4_PGE
CR4_VAL equ CR4_DE|CR4_PAE|CR4_MCE|CR4_PGE|CR4_OSFXSR|CR4_OSCMMEXCPT|CR4_FSGSBASE|CR4_PCIDE
EFER_MSR equ 0xC0000080
EFER_SCE equ (1 << 0)
EFER_LME equ (1 << 8)
EFER_NXE equ (1 << 11)
EFER_VAL equ EFER_SCE|EFER_LME|EFER_NXE
bits 16
default rel
align 8
global ap_startup
ap_startup:
jmp .start_real
align 8
.pml4: dq 0
.cpu: dq 0
.ret: dq 0
align 16
.gdt:
dq 0x0 ; Null GDT entry
dq 0x00209A0000000000 ; Code
dq 0x0000920000000000 ; Data
align 4
.gdtd:
dw ($ - .gdt)
dd BASE + (.gdt - ap_startup)
align 4
.idtd:
dw 0 ; zero-length IDT descriptor
dd 0
.start_real:
cli
cld
xor ax, ax
mov ds, ax
; set the temporary null IDT
lidt [BASE + (.idtd - ap_startup)]
; Enter long mode
mov eax, cr4
or eax, CR4_INIT
mov cr4, eax
mov eax, [BASE + (.pml4 - ap_startup)]
mov cr3, eax
mov ecx, EFER_MSR
rdmsr
or eax, EFER_VAL
wrmsr
mov eax, CR0_VAL
mov cr0, eax
; Set the temporary minimal GDT
lgdt [BASE + (.gdtd - ap_startup)]
jmp (1 << 3):(BASE + (.start_long - ap_startup))
bits 64
default abs
align 8
.start_long:
; set data segments
mov ax, (2 << 3)
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, CR4_VAL
mov rdi, [BASE + (.cpu - ap_startup)]
mov rax, [rdi + CPU_DATA.rsp0]
mov rsp, rax
mov rax, [BASE + (.ret - ap_startup)]
jmp rax
global ap_startup_code_size
ap_startup_code_size:
dq ($ - ap_startup)
section .text
global init_ap_trampoline
init_ap_trampoline:
push rbp
mov rbp, rsp
; rdi is the kernel pml4
mov [BASE + (ap_startup.pml4 - ap_startup)], rdi
; rsi is the cpu data for this AP
mov [BASE + (ap_startup.cpu - ap_startup)], rsi
; rdx is the address to jump to
mov [BASE + (ap_startup.ret - ap_startup)], rdx
; rcx is the processor id
mov rdi, rdx
pop rbp
ret
extern long_ap_startup
global ap_idle
ap_idle:
call long_ap_startup
sti
.hang:
hlt
jmp .hang

View File

@@ -1,10 +1,27 @@
#include "kutil/assert.h"
#include "apic.h"
#include "clock.h"
#include "interrupts.h"
#include "io.h"
#include "kernel_memory.h"
#include "log.h"
#include "page_manager.h"
uint64_t lapic::s_ticks_per_us = 0;
static constexpr uint16_t lapic_id = 0x0020;
static constexpr uint16_t lapic_spurious = 0x00f0;
static constexpr uint16_t lapic_icr_low = 0x0300;
static constexpr uint16_t lapic_icr_high = 0x0310;
static constexpr uint16_t lapic_lvt_timer = 0x0320;
static constexpr uint16_t lapic_lvt_lint0 = 0x0350;
static constexpr uint16_t lapic_lvt_lint1 = 0x0360;
static constexpr uint16_t lapic_lvt_error = 0x0370;
static constexpr uint16_t lapic_timer_init = 0x0380;
static constexpr uint16_t lapic_timer_cur = 0x0390;
static constexpr uint16_t lapic_timer_div = 0x03e0;
static uint32_t
apic_read(uint32_t volatile *apic, uint16_t offset)
@@ -15,6 +32,7 @@ apic_read(uint32_t volatile *apic, uint16_t offset)
static void
apic_write(uint32_t volatile *apic, uint16_t offset, uint32_t value)
{
log::debug(logs::apic, "LAPIC write: %x = %08lx", offset, value);
*(apic + offset/sizeof(uint32_t)) = value;
}
@@ -32,24 +50,64 @@ ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value)
*(base + 4) = value;
}
apic::apic(uint32_t *base) :
m_base(base)
apic::apic(uintptr_t base) :
m_base(memory::to_virtual<uint32_t>(base))
{
// Map 1MiB of space for the APIC registers and
// MSI area
page_manager::get()->map_offset_pointer(
reinterpret_cast<void **>(&m_base),
0x100000);
}
lapic::lapic(uint32_t *base, isr spurious) :
apic(base)
lapic::lapic(uintptr_t base) :
apic(base),
m_divisor(0)
{
apic_write(m_base, 0xf0, static_cast<uint32_t>(spurious));
apic_write(m_base, lapic_lvt_error, static_cast<uint32_t>(isr::isrAPICError));
apic_write(m_base, lapic_spurious, static_cast<uint32_t>(isr::isrSpurious));
log::info(logs::apic, "LAPIC created, base %lx", m_base);
}
uint8_t
lapic::get_id()
{
return static_cast<uint8_t>(apic_read(m_base, lapic_id) >> 24);
}
void
lapic::send_ipi(ipi mode, uint8_t vector, uint8_t dest)
{
// Wait until the APIC is ready to send
ipi_wait();
uint32_t command =
static_cast<uint32_t>(vector) |
static_cast<uint32_t>(mode);
apic_write(m_base, lapic_icr_high, static_cast<uint32_t>(dest) << 24);
apic_write(m_base, lapic_icr_low, command);
}
void
lapic::send_ipi_broadcast(ipi mode, bool self, uint8_t vector)
{
// Wait until the APIC is ready to send
ipi_wait();
uint32_t command =
static_cast<uint32_t>(vector) |
static_cast<uint32_t>(mode) |
(self ? 0 : (1 << 18)) |
(1 << 19);
apic_write(m_base, lapic_icr_high, 0);
apic_write(m_base, lapic_icr_low, command);
}
void
lapic::ipi_wait()
{
while (apic_read(m_base, lapic_icr_low) & (1<<12))
asm volatile ("pause" : : : "memory");
}
void
lapic::calibrate_timer()
{
@@ -57,40 +115,25 @@ lapic::calibrate_timer()
log::info(logs::apic, "Calibrating APIC timer...");
// Set up PIT sleep
uint8_t command = 0x30; // channel 0, loybyte/highbyte, mode 0
outb(0x43, command);
const uint32_t initial = -1u;
enable_timer_internal(isr::isrSpurious, 1, initial, false);
enable_timer(isr::isrSpurious);
set_divisor(1);
apic_write(m_base, lapic_timer_init, initial);
const int iterations = 5;
for (int i=0; i<iterations; ++i) {
const uint16_t pit_33ms = 39375;
uint16_t pit_count = pit_33ms;
outb(0x40, pit_count & 0xff);
io_wait();
outb(0x40, (pit_count >> 8) & 0xff);
uint64_t us = 20000;
clock::get().spinwait(us);
uint32_t remaining = apic_read(m_base, lapic_timer_cur);
uint64_t ticks_total = initial - remaining;
s_ticks_per_us = ticks_total / us;
while (pit_count <= pit_33ms) {
outb(0x43, 0); // latch counter values
pit_count =
static_cast<uint16_t>(inb(0x40)) |
static_cast<uint16_t>(inb(0x40)) << 8;
}
}
uint32_t remain = stop_timer();
uint32_t ticks_total = initial - remain;
m_ticks_per_us = ticks_total / (iterations * 33000);
log::info(logs::apic, "APIC timer ticks %d times per nanosecond.", m_ticks_per_us);
log::info(logs::apic, "APIC timer ticks %d times per microsecond.", s_ticks_per_us);
interrupts_enable();
}
uint32_t
lapic::enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat)
void
lapic::set_divisor(uint8_t divisor)
{
uint32_t divbits = 0;
@@ -104,42 +147,40 @@ lapic::enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool r
case 64: divbits = 0x9; break;
case 128: divbits = 0xa; break;
default:
kassert(0, "Invalid divisor passed to lapic::enable_timer");
kassert(0, "Invalid divisor passed to lapic::set_divisor");
}
apic_write(m_base, lapic_timer_div, divbits);
m_divisor = divisor;
}
void
lapic::enable_timer(isr vector, bool repeat)
{
uint32_t lvte = static_cast<uint8_t>(vector);
if (repeat)
lvte |= 0x20000;
apic_write(m_base, lapic_lvt_timer, lvte);
log::debug(logs::apic, "Enabling APIC timer count %ld, divisor %d, isr %02x",
count, divisor, vector);
apic_write(m_base, 0x320, lvte);
apic_write(m_base, 0x3e0, divbits);
reset_timer(count);
return count;
log::debug(logs::apic, "Enabling APIC timer at isr %02x", vector);
}
uint32_t
lapic::enable_timer(isr vector, uint64_t interval, bool repeat)
lapic::reset_timer(uint64_t interval)
{
uint64_t ticks = interval * m_ticks_per_us;
uint64_t remaining = ticks_to_us(apic_read(m_base, lapic_timer_cur));
uint64_t ticks = us_to_ticks(interval);
int divisor = 1;
while (ticks > -1u) {
ticks /= 2;
divisor *= 2;
while (ticks > 0xffffffffull) {
ticks >>= 1;
divisor <<= 1;
}
return enable_timer_internal(vector, divisor, static_cast<uint32_t>(ticks), repeat);
}
if (divisor != m_divisor)
set_divisor(divisor);
uint32_t
lapic::reset_timer(uint32_t count)
{
uint32_t remaining = apic_read(m_base, 0x390);
apic_write(m_base, 0x380, count);
apic_write(m_base, lapic_timer_init, ticks);
return remaining;
}
@@ -148,7 +189,7 @@ lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
{
kassert(num == 0 || num == 1, "Invalid LINT passed to lapic::enable_lint.");
uint16_t off = num ? 0x360 : 0x350;
uint16_t off = num ? lapic_lvt_lint1 : lapic_lvt_lint0;
uint32_t lvte = static_cast<uint8_t>(vector);
uint16_t polarity = flags & 0x3;
@@ -169,21 +210,21 @@ lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags)
void
lapic::enable()
{
apic_write(m_base, 0xf0,
apic_read(m_base, 0xf0) | 0x100);
apic_write(m_base, lapic_spurious,
apic_read(m_base, lapic_spurious) | 0x100);
log::debug(logs::apic, "LAPIC enabled!");
}
void
lapic::disable()
{
apic_write(m_base, 0xf0,
apic_read(m_base, 0xf0) & ~0x100);
apic_write(m_base, lapic_spurious,
apic_read(m_base, lapic_spurious) & ~0x100);
log::debug(logs::apic, "LAPIC disabled.");
}
ioapic::ioapic(uint32_t *base, uint32_t base_gsi) :
ioapic::ioapic(uintptr_t base, uint32_t base_gsi) :
apic(base),
m_base_gsi(base_gsi)
{
@@ -206,6 +247,9 @@ ioapic::ioapic(uint32_t *base, uint32_t base_gsi) :
void
ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
{
log::debug(logs::apic, "IOAPIC %d redirecting irq %3d to vector %3d [%04x]%s",
m_id, irq, vector, flags, masked ? " (masked)" : "");
uint64_t entry = static_cast<uint64_t>(vector);
uint16_t polarity = flags & 0x3;
@@ -226,6 +270,9 @@ ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked)
void
ioapic::mask(uint8_t irq, bool masked)
{
log::debug(logs::apic, "IOAPIC %d %smasking irq %3d",
m_id, masked ? "" : "un", irq);
uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10);
if (masked)
entry |= (1 << 16);

View File

@@ -3,22 +3,38 @@
/// Classes to control both local and I/O APICs.
#include <stdint.h>
#include "kutil/enum_bitfields.h"
enum class isr : uint8_t;
/// Base class for other APIC types
class apic
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
apic(uint32_t *base);
/// \arg base Physical base address of the APIC's MMIO registers
apic(uintptr_t base);
protected:
uint32_t *m_base;
};
enum class ipi : uint32_t
{
// Delivery modes
fixed = 0x0000,
smi = 0x0200,
nmi = 0x0400,
init = 0x0500,
startup = 0x0600,
// Flags
deassert = 0x0000,
assert = 0x4000,
edge = 0x0000, ///< edge-triggered
level = 0x8000, ///< level-triggered
};
IS_BITFIELD(ipi);
/// Controller for processor-local APICs
class lapic :
@@ -26,24 +42,40 @@ class lapic :
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
/// \arg spurious Vector of the spurious interrupt handler
lapic(uint32_t *base, isr spurious);
/// \arg base Physicl base address of the APIC's MMIO registers
lapic(uintptr_t base);
/// Get the local APIC's ID
uint8_t get_id();
/// Send an inter-processor interrupt.
/// \arg mode The sending mode
/// \arg vector The interrupt vector
/// \arg dest The APIC ID of the destination
void send_ipi(ipi mode, uint8_t vector, uint8_t dest);
/// Send an inter-processor broadcast interrupt to all other CPUs
/// \arg mode The sending mode
/// \arg self If true, include this CPU in the broadcast
/// \arg vector The interrupt vector
void send_ipi_broadcast(ipi mode, bool self, uint8_t vector);
/// Wait for an IPI to finish sending. This is done automatically
/// before sending another IPI with send_ipi().
void ipi_wait();
/// Enable interrupts for the LAPIC timer.
/// \arg vector Interrupt vector the timer should use
/// \arg interval The timer interval, in microseconds
/// \arg repeat If false, this timer is one-off, otherwise repeating
/// \returns The count of ticks the timer is set for
uint32_t enable_timer(isr vector, uint64_t interval, bool repeat = true);
void enable_timer(isr vector, bool repeat = true);
/// Reset the timer countdown.
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
/// \returns The count of ticks that were remaining before reset
uint32_t reset_timer(uint32_t count);
/// \arg interval The interval in us before an interrupt, or 0 to stop the timer
/// \returns The interval in us that was remaining before reset
uint32_t reset_timer(uint64_t interval);
/// Stop the timer.
/// \returns The count of ticks remaining before an interrupt was to happen
/// \returns The interval in us remaining before an interrupt was to happen
inline uint32_t stop_timer() { return reset_timer(0); }
/// Enable interrupts for the LAPIC LINT0 pin.
@@ -56,13 +88,18 @@ public:
void enable(); ///< Enable servicing of interrupts
void disable(); ///< Disable (temporarily) servicing of interrupts
/// Calibrate the timer speed against the PIT
/// Calibrate the timer speed against the clock
void calibrate_timer();
private:
uint32_t enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat);
inline static uint64_t ticks_to_us(uint64_t ticks) { return ticks / s_ticks_per_us; }
inline static uint64_t us_to_ticks(uint64_t interval) { return interval * s_ticks_per_us; }
uint32_t m_ticks_per_us;
void set_divisor(uint8_t divisor);
void set_repeat(bool repeat);
uint32_t m_divisor;
static uint64_t s_ticks_per_us;
};
@@ -72,9 +109,9 @@ class ioapic :
{
public:
/// Constructor
/// \arg base Base virtual address of the APIC's MMIO registers
/// \arg base Physical base address of the APIC's MMIO registers
/// \arg base_gsi Starting global system interrupt number of this IOAPIC
ioapic(uint32_t *base, uint32_t base_gsi);
ioapic(uintptr_t base, uint32_t base_gsi);
uint32_t get_base_gsi() const { return m_base_gsi; }
uint32_t get_num_gsi() const { return m_num_gsi; }

View File

@@ -14,8 +14,8 @@ _header:
section .text
align 16
global _start:function (_start.end - _start)
_start:
global _kernel_start:function (_kernel_start.end - _kernel_start)
_kernel_start:
cli
mov rsp, idle_stack_end

22
src/kernel/clock.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "clock.h"
clock * clock::s_instance = nullptr;
clock::clock(uint64_t rate, clock::source source_func, void *data) :
m_rate(rate),
m_data(data),
m_source(source_func)
{
// TODO: make this atomic
if (s_instance == nullptr)
s_instance = this;
update();
}
void
clock::spinwait(uint64_t us) const
{
uint64_t when = value() + us;
while (value() < when) asm ("pause");
}

44
src/kernel/clock.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
/// \file clock.h
/// The kernel time keeping interface
#include <stdint.h>
class clock
{
public:
/// A source is a function that returns the current
/// value of some clock source.
using source = uint64_t (*)(void*);
/// Constructor.
/// \arg rate Number of source ticks per us
/// \arg source Function for the clock source
/// \arg data Data to pass to the source function
clock(uint64_t rate, source source_func, void *data);
/// Get the current value of the clock.
/// \returns Current value of the source, in us
/// TODO: optimize divison by finding a multiply and
/// shift value instead
inline uint64_t value() const { return m_source(m_data) / m_rate; }
/// Update the internal state via the source
/// \returns Current value of the clock
inline void update() { m_current = value(); }
/// Wait in a tight loop
/// \arg interval Time to wait, in us
void spinwait(uint64_t us) const;
/// Get the master clock
static clock & get() { return *s_instance; }
private:
uint64_t m_current; ///< current us count
uint64_t m_rate; ///< source ticks per us
void *m_data;
source m_source;
static clock *s_instance;
};

View File

@@ -1,223 +1,24 @@
#include "kutil/coord.h"
#include "kutil/guid.h"
#include "kutil/memory.h"
#include "kutil/no_construct.h"
#include "kutil/printf.h"
#include "console.h"
#include "font.h"
#include "screen.h"
#include "serial.h"
const char digits[] = "0123456789abcdef";
console g_console;
class console::screen_out
{
public:
screen_out(screen *s, font *f) :
m_font(f),
m_screen(s),
m_size(s->width() / f->width(), s->height() / f->height()),
m_fg(0xffffff),
m_bg(0),
m_first(0),
m_data(nullptr),
m_attrs(nullptr),
m_palette(nullptr)
{
const unsigned count = m_size.size();
const size_t attrs_size = 2 * count;
m_data = new char[count];
kutil::memset(m_data, 0, count);
m_palette = new screen::pixel_t[256];
fill_palette();
m_attrs = new uint16_t[count];
set_color(7, 0); // Grey on black default
for (unsigned i = 0; i < count; ++i) m_attrs[i] = m_attr;
repaint();
}
~screen_out()
{
delete [] m_data;
delete [] m_palette;
delete [] m_attrs;
}
void fill_palette()
{
unsigned index = 0;
// Manually add the 16 basic ANSI colors
m_palette[index++] = m_screen->color(0x00, 0x00, 0x00);
m_palette[index++] = m_screen->color(0xcd, 0x00, 0x00);
m_palette[index++] = m_screen->color(0x00, 0xcd, 0x00);
m_palette[index++] = m_screen->color(0xcd, 0xcd, 0x00);
m_palette[index++] = m_screen->color(0x00, 0x00, 0xee);
m_palette[index++] = m_screen->color(0xcd, 0x00, 0xcd);
m_palette[index++] = m_screen->color(0x00, 0xcd, 0xcd);
m_palette[index++] = m_screen->color(0xe5, 0xe5, 0xe5);
m_palette[index++] = m_screen->color(0x7f, 0x7f, 0x7f);
m_palette[index++] = m_screen->color(0xff, 0x00, 0x00);
m_palette[index++] = m_screen->color(0x00, 0xff, 0x00);
m_palette[index++] = m_screen->color(0xff, 0xff, 0x00);
m_palette[index++] = m_screen->color(0x00, 0x50, 0xff);
m_palette[index++] = m_screen->color(0xff, 0x00, 0xff);
m_palette[index++] = m_screen->color(0x00, 0xff, 0xff);
m_palette[index++] = m_screen->color(0xff, 0xff, 0xff);
// Build the high-color portion of the table
const uint32_t intensity[] = {0, 0x5f, 0x87, 0xaf, 0xd7, 0xff};
const uint32_t intensities = sizeof(intensity) / sizeof(uint32_t);
for (uint32_t r = 0; r < intensities; ++r) {
for (uint32_t g = 0; g < intensities; ++g) {
for (uint32_t b = 0; b < intensities; ++b) {
m_palette[index++] = m_screen->color(
intensity[r], intensity[g], intensity[b]);
}
}
}
// Build the greyscale portion of the table
for (uint8_t i = 0x08; i <= 0xee; i += 10)
m_palette[index++] = m_screen->color(i, i, i);
}
void repaint()
{
m_screen->fill(m_bg);
if (!m_data) return;
for (unsigned y = 0; y < m_size.y; ++y) {
const char *line = line_pointer(y);
const uint16_t *attrs = attr_pointer(y);
for (unsigned x = 0; x < m_size.x; ++x) {
const uint16_t attr = attrs[x];
set_color(static_cast<uint8_t>(attr),
static_cast<uint8_t>(attr >> 8));
m_font->draw_glyph(
m_screen,
line[x] ? line[x] : ' ',
m_fg,
m_bg,
x * m_font->width(),
y * m_font->height());
}
}
}
void scroll(unsigned lines)
{
if (!m_data) {
m_pos.x = 0;
m_pos.y = 0;
} else {
unsigned bytes = lines * m_size.x;
char *line = line_pointer(0);
for (unsigned i = 0; i < bytes; ++i)
*line++ = 0;
m_first = (m_first + lines) % m_size.y;
m_pos.y -= lines;
}
repaint();
}
void set_color(uint8_t fg, uint8_t bg)
{
m_bg = m_palette[bg];
m_fg = m_palette[fg];
m_attr = (bg << 8) | fg;
}
void putc(char c)
{
char *line = line_pointer(m_pos.y);
uint16_t *attrs = attr_pointer(m_pos.y);
switch (c) {
case '\t':
m_pos.x = (m_pos.x + 4) / 4 * 4;
break;
case '\r':
m_pos.x = 0;
break;
case '\n':
m_pos.x = 0;
m_pos.y++;
break;
default: {
if (line) line[m_pos.x] = c;
if (attrs) attrs[m_pos.x] = m_attr;
const unsigned x = m_pos.x * m_font->width();
const unsigned y = m_pos.y * m_font->height();
m_font->draw_glyph(m_screen, c, m_fg, m_bg, x, y);
m_pos.x++;
}
}
if (m_pos.x >= m_size.x) {
m_pos.x = m_pos.x % m_size.x;
m_pos.y++;
}
if (m_pos.y >= m_size.y) {
scroll(1);
line = line_pointer(m_pos.y);
}
}
private:
char * line_pointer(unsigned line)
{
if (!m_data) return nullptr;
return m_data + ((m_first + line) % m_size.y) * m_size.x;
}
uint16_t * attr_pointer(unsigned line)
{
if (!m_attrs) return nullptr;
return m_attrs + ((m_first + line) % m_size.y) * m_size.x;
}
font *m_font;
screen *m_screen;
kutil::coord<unsigned> m_size;
kutil::coord<unsigned> m_pos;
screen::pixel_t m_fg, m_bg;
uint16_t m_attr;
size_t m_first;
char *m_data;
uint16_t *m_attrs;
screen::pixel_t *m_palette;
};
static kutil::no_construct<console> __g_console_storage;
console &g_console = __g_console_storage.value;
console::console() :
m_screen(nullptr),
m_serial(nullptr)
{
}
console::console(serial_port *serial) :
m_screen(nullptr),
m_serial(serial)
{
if (m_serial) {
@@ -236,9 +37,6 @@ console::echo()
void
console::set_color(uint8_t fg, uint8_t bg)
{
if (m_screen)
m_screen->set_color(fg, bg);
if (m_serial) {
const char *fgseq = "\x1b[38;5;";
while (*fgseq)
@@ -258,21 +56,24 @@ console::set_color(uint8_t fg, uint8_t bg)
}
}
void
size_t
console::puts(const char *message)
{
while (message && *message)
size_t n = 0;
while (message && *message) {
n++;
putc(*message++);
}
return n;
}
void
console::putc(char c)
{
if (m_screen) m_screen->putc(c);
if (m_serial) {
if (c == '\n') m_serial->write('\r');
m_serial->write(c);
if (c == '\r') m_serial->write('\n');
}
}
@@ -283,9 +84,3 @@ void console::vprintf(const char *fmt, va_list args)
vsnprintf_(buffer, buf_size, fmt, args);
puts(buffer);
}
void
console::init_screen(screen *s, font *f)
{
m_screen = new screen_out(s, f);
}

View File

@@ -1,11 +1,8 @@
#pragma once
#include <algorithm>
#include <stdarg.h>
#include <stdint.h>
class font;
struct kernel_data;
class screen;
class serial_port;
class console
@@ -17,7 +14,7 @@ public:
void set_color(uint8_t fg = 7, uint8_t bg = 0);
void putc(char c);
void puts(const char *message);
size_t puts(const char *message);
void vprintf(const char *fmt, va_list args);
inline void printf(const char *fmt, ...)
@@ -36,17 +33,13 @@ public:
void echo();
void init_screen(screen *s, font *f);
static console * get();
private:
class screen_out;
screen_out *m_screen;
serial_port *m_serial;
};
extern console g_console;
extern console &g_console;
inline console * console::get() { return &g_console; }
@@ -61,7 +54,7 @@ void console::put_hex(T x, int width, char pad)
int len = 1;
for (int i = chars - 1; i >= 0; --i) {
uint8_t nibble = (x >> (i*4)) & 0xf;
if (nibble) len = std::max(i + 1, len);
if (nibble) len = len > i + 1 ? len : i + 1;
message[chars - i - 1] = digits[nibble];
}
message[chars] = 0;

View File

@@ -1,116 +1,66 @@
#include <stdint.h>
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "cpu.h"
#include "cpu/cpu_id.h"
#include "device_manager.h"
#include "gdt.h"
#include "idt.h"
#include "kernel_memory.h"
#include "log.h"
#include "msr.h"
#include "objects/vm_area.h"
#include "syscall.h"
#include "tss.h"
cpu_data bsp_cpu_data;
static constexpr uint32_t cpuid_extended = 0x80000000;
inline static void
__cpuid(
uint32_t leaf,
uint32_t subleaf,
uint32_t *eax,
uint32_t *ebx = nullptr,
uint32_t *ecx = nullptr,
uint32_t *edx = nullptr)
{
uint32_t a, b, c, d;
__asm__ __volatile__ ( "cpuid"
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
: "a"(leaf), "c"(subleaf)
);
if (eax) *eax = a;
if (ebx) *ebx = b;
if (ecx) *ecx = c;
if (edx) *edx = d;
}
cpu_id::cpu_id() :
m_features(0)
{
__cpuid(0, 0,
&m_high_basic,
reinterpret_cast<uint32_t *>(&m_vendor_id[0]),
reinterpret_cast<uint32_t *>(&m_vendor_id[8]),
reinterpret_cast<uint32_t *>(&m_vendor_id[4]));
__cpuid(cpuid_extended, 0, &m_high_ext);
if (m_high_ext >= cpuid_extended + 4) {
__cpuid(cpuid_extended + 2, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[0]),
reinterpret_cast<uint32_t *>(&m_brand_name[4]),
reinterpret_cast<uint32_t *>(&m_brand_name[8]),
reinterpret_cast<uint32_t *>(&m_brand_name[12]));
__cpuid(cpuid_extended + 3, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[16]),
reinterpret_cast<uint32_t *>(&m_brand_name[20]),
reinterpret_cast<uint32_t *>(&m_brand_name[24]),
reinterpret_cast<uint32_t *>(&m_brand_name[28]));
__cpuid(cpuid_extended + 4, 0,
reinterpret_cast<uint32_t *>(&m_brand_name[32]),
reinterpret_cast<uint32_t *>(&m_brand_name[36]),
reinterpret_cast<uint32_t *>(&m_brand_name[40]),
reinterpret_cast<uint32_t *>(&m_brand_name[44]));
} else {
m_brand_name[0] = 0;
}
}
cpu_id::regs
cpu_id::get(uint32_t leaf, uint32_t sub) const
{
regs ret {0, 0, 0, 0};
if ((leaf & cpuid_extended) == 0 && leaf > m_high_basic) return ret;
if ((leaf & cpuid_extended) != 0 && leaf > m_high_ext) return ret;
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
return ret;
}
cpu_data g_bsp_cpu_data;
void
cpu_id::validate()
cpu_validate()
{
bool fail = false;
uint32_t leaf = 0;
uint32_t sub = 0;
regs r;
cpu::cpu_id cpu;
log::info(logs::boot, "CPU: %s", brand_name());
log::debug(logs::boot, " Vendor is %s", vendor_id());
log::info(logs::boot, "CPU: %s", cpu.brand_name());
log::debug(logs::boot, " Vendor is %s", cpu.vendor_id());
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", highest_basic());
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", highest_ext() & ~cpuid_extended);
log::debug(logs::boot, " Higest basic CPUID: 0x%02x", cpu.highest_basic());
log::debug(logs::boot, " Higest ext CPUID: 0x%02x", cpu.highest_ext() & ~cpu::cpu_id::cpuid_extended);
#define CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit) \
if (leaf != feat_leaf || sub != feat_sub) { \
leaf = feat_leaf; sub = feat_sub; r = get(leaf, sub); \
} \
if (r.regname & (1 << bit)) \
m_features |= (1 << static_cast<uint64_t>(cpu_feature::name)); \
log::debug(logs::boot, " Supports %9s: %s", #name, (r.regname & (1<<bit)) ? "yes" : "no");
#define CPU_FEATURE_OPT(name, ...) \
log::debug(logs::boot, " Supports %9s: %s", #name, cpu.has_feature(cpu::feature::name) ? "yes" : "no");
#define CPU_FEATURE_REQ(name, feat_leaf, feat_sub, regname, bit) \
CPU_FEATURE_OPT(name, feat_leaf, feat_sub, regname, bit); \
if ((r.regname & (1 << bit)) == 0) { \
log::error(logs::boot, "CPU missing required feature " #name); \
fail = true; \
}
kassert(cpu.has_feature(cpu::feature::name), "Missing required CPU feature " #name );
#include "cpu_features.inc"
#include "cpu/features.inc"
#undef CPU_FEATURE_OPT
#undef CPU_FEATURE_REQ
if (fail)
log::fatal(logs::boot, "CPU not supported.");
}
bool
cpu_id::has_feature(cpu_feature feat)
void
cpu_early_init(cpu_data *cpu)
{
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
IDT::get().install();
cpu->gdt->install();
// Install the GS base pointint to the cpu_data
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(cpu));
}
void
cpu_init(cpu_data *cpu, bool bsp)
{
if (!bsp) {
// The BSP already called cpu_early_init
cpu_early_init(cpu);
}
// Set up the syscall MSRs
syscall_enable();
// Set up the page attributes table
uint64_t pat = rdmsr(msr::ia32_pat);
pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC
wrmsr(msr::ia32_pat, pat);
}

View File

@@ -2,7 +2,12 @@
#include <stdint.h>
struct process;
class GDT;
class lapic;
class process;
struct TCB;
class thread;
class TSS;
struct cpu_state
{
@@ -16,76 +21,39 @@ struct cpu_state
/// version in 'tasking.inc'
struct cpu_data
{
cpu_data *self;
uint16_t id;
uint16_t index;
uint32_t reserved;
uintptr_t rsp0;
uintptr_t rsp3;
process *tcb;
TCB *tcb;
thread *thread;
process *process;
TSS *tss;
GDT *gdt;
// Members beyond this point do not appear in
// the assembly version
lapic *apic;
};
extern cpu_data bsp_cpu_data;
extern "C" cpu_data * _current_gsbase();
/// Enum of the cpu features jsix cares about
enum class cpu_feature {
#define CPU_FEATURE_REQ(name, ...) name,
#define CPU_FEATURE_OPT(name, ...) name,
#include "cpu_features.inc"
#undef CPU_FEATURE_OPT
#undef CPU_FEATURE_REQ
max
};
/// Set up the running CPU. This sets GDT, IDT, and necessary MSRs as well as creating
/// the cpu_data structure for this processor.
/// \arg cpu The cpu_data structure for this CPU
/// \arg bsp True if this CPU is the BSP
void cpu_init(cpu_data *cpu, bool bsp);
class cpu_id
{
public:
/// CPUID result register values
struct regs {
union {
uint32_t reg[4];
uint32_t eax, ebx, ecx, edx;
};
/// Do early (before cpu_init) initialization work. Only needs to be called manually for
/// the BSP, otherwise cpu_init will call it.
/// \arg cpu The cpu_data structure for this CPU
void cpu_early_init(cpu_data *cpu);
/// Return true if bit |bit| of EAX is set
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
/// Get the cpu_data struct for the current executing CPU
inline cpu_data & current_cpu() { return *_current_gsbase(); }
/// Return true if bit |bit| of EBX is set
bool ebx_bit(unsigned bit) { return (ebx >> bit) & 0x1; }
/// Return true if bit |bit| of ECX is set
bool ecx_bit(unsigned bit) { return (ecx >> bit) & 0x1; }
/// Return true if bit |bit| of EDX is set
bool edx_bit(unsigned bit) { return (edx >> bit) & 0x1; }
};
cpu_id();
/// The the result of a given CPUID leaf/subleaf
/// \arg leaf The leaf selector (initial EAX)
/// \arg subleaf The subleaf selector (initial ECX)
/// \returns A |regs| struct of the values retuned
regs get(uint32_t leaf, uint32_t sub = 0) const;
/// Get the name of the cpu vendor (eg, "GenuineIntel")
inline const char * vendor_id() const { return m_vendor_id; }
/// Get the brand name of this processor model
inline const char * brand_name() const { return m_brand_name; }
/// Get the highest basic CPUID leaf supported
inline uint32_t highest_basic() const { return m_high_basic; }
/// Get the highest extended CPUID leaf supported
inline uint32_t highest_ext() const { return m_high_ext; }
/// Validate the CPU supports the necessary options for jsix
void validate();
/// Return true if the CPU claims to support the given feature
bool has_feature(cpu_feature feat);
private:
uint32_t m_high_basic;
uint32_t m_high_ext;
char m_vendor_id[13];
char m_brand_name[48];
uint64_t m_features;
};
/// Validate the required CPU features are present. Really, the bootloader already
/// validated the required features, but still iterate the options and log about them.
void cpu_validate();

View File

@@ -1,16 +0,0 @@
CPU_FEATURE_OPT(pcid, 0x00000001, 0, ecx, 17)
CPU_FEATURE_OPT(x2apic, 0x00000001, 0, ecx, 21)
CPU_FEATURE_REQ(fpu, 0x00000001, 0, edx, 0)
CPU_FEATURE_REQ(pse, 0x00000001, 0, edx, 3)
CPU_FEATURE_OPT(tsc, 0x00000001, 0, edx, 4)
CPU_FEATURE_REQ(msr, 0x00000001, 0, edx, 5)
CPU_FEATURE_REQ(apic, 0x00000001, 0, edx, 9)
CPU_FEATURE_REQ(pge, 0x00000001, 0, edx, 13)
CPU_FEATURE_OPT(pat, 0x00000001, 0, edx, 16)
CPU_FEATURE_REQ(fxsr, 0x00000001, 0, edx, 24)
CPU_FEATURE_OPT(fsgsbase, 0x00000007, 0, ebx, 0)
CPU_FEATURE_OPT(invpcid, 0x00000007, 0, ebx, 10)
CPU_FEATURE_OPT(pku, 0x00000007, 0, ecx, 3)
CPU_FEATURE_REQ(syscall, 0x80000001, 0, edx, 11)
CPU_FEATURE_REQ(pdpe1gb, 0x80000001, 0, edx, 26)
CPU_FEATURE_OPT(extapic, 0x80000001, 0, ecx, 3)

View File

@@ -1,14 +0,0 @@
section .init
global _init:function
_init:
push rbp
mov rbp, rsp
; Control flow falls through to other .init sections
section .fini
global _fini:function
_fini:
push rbp
mov rbp, rsp
; Control flow falls through to other .fini sections

View File

@@ -1,10 +0,0 @@
section .init
; Control flow falls through to here from other .init sections
pop rbp
ret
section .fini
; Control flow falls through to here from other .fini sections
pop rbp
ret

View File

@@ -2,7 +2,9 @@
#include "cpu.h"
#include "debug.h"
#include "gdt.h"
#include "page_manager.h"
#include "objects/process.h"
#include "objects/thread.h"
#include "symbol_table.h"
size_t __counter_syscall_enter = 0;
size_t __counter_syscall_sysret = 0;
@@ -11,10 +13,17 @@ void
print_regs(const cpu_state &regs)
{
console *cons = console::get();
cpu_data &cpu = current_cpu();
uint64_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
uintptr_t cr3 = 0;
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
cons->printf(" process: %llx", cpu.process->koid());
cons->printf(" thread: %llx\n", cpu.thread->koid());
print_regL("rax", regs.rax);
print_regM("rbx", regs.rbx);
print_regR("rcx", regs.rcx);
@@ -35,10 +44,10 @@ print_regs(const cpu_state &regs)
cons->puts("\n\n");
print_regL("rbp", regs.rbp);
print_regM("rsp", regs.user_rsp);
print_regR("sp0", bsp_cpu_data.rsp0);
print_regR("sp0", cpu.rsp0);
print_regL("rip", regs.rip);
print_regM("cr3", page_manager::get()->get_pml4());
print_regM("cr3", cr3);
print_regR("cr2", cr2);
cons->puts("\n");
@@ -54,14 +63,26 @@ void
print_stacktrace(int skip)
{
console *cons = console::get();
symbol_table *syms = symbol_table::get();
frame *fp = nullptr;
int fi = -skip;
__asm__ __volatile__ ( "mov %%rbp, %0" : "=r" (fp) );
while (fp && fp->return_addr) {
if (fi++ >= 0)
cons->printf(" frame %2d: %lx\n", fi-1, fp->return_addr);
if (fi++ >= 0) {
const symbol_table::entry *e = syms ? syms->find_symbol(fp->return_addr) : nullptr;
const char *name = e ? e->name : "";
cons->printf(" frame %2d:", fi-1);
cons->set_color(5);
cons->printf(" %016llx", fp->return_addr);
cons->set_color();
cons->set_color(6);
cons->printf(" %s\n", name);
cons->set_color();
}
fp = fp->prev;
}
}

View File

@@ -4,9 +4,12 @@
#include <stdint.h>
struct cpu_state;
extern "C" {
uintptr_t get_rsp();
uintptr_t get_rip();
uintptr_t get_caller();
uintptr_t get_frame(int frame);
uintptr_t get_gsbase();
void _halt();

View File

@@ -8,6 +8,12 @@ get_rip:
pop rax ; do the same thing as 'ret', except with 'jmp'
jmp rax ; with the return address still in rax
global get_caller
get_caller:
; No prelude - don't touch rsp or rbp
mov rax, [rbp+8]
ret
global get_gsbase
get_gsbase:
rdgsbase rax

View File

@@ -5,16 +5,20 @@
#include "kutil/memory.h"
#include "acpi_tables.h"
#include "apic.h"
#include "clock.h"
#include "console.h"
#include "device_manager.h"
#include "interrupts.h"
#include "kernel_memory.h"
#include "log.h"
#include "page_manager.h"
#include "objects/endpoint.h"
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
static const char expected_signature[] = "RSD PTR ";
device_manager device_manager::s_instance(nullptr, kutil::allocator::invalid);
device_manager device_manager::s_instance;
struct acpi1_rsdp
{
@@ -34,7 +38,7 @@ struct acpi2_rsdp
uint32_t rsdt_address;
uint32_t length;
uint64_t xsdt_address;
acpi_table_header *xsdt_address;
uint8_t checksum20;
uint8_t reserved[3];
} __attribute__ ((packed));
@@ -46,7 +50,6 @@ acpi_table_header::validate(uint32_t expected_type) const
return !expected_type || (expected_type == type);
}
void irq2_callback(void *)
{
}
@@ -59,18 +62,31 @@ void irq4_callback(void *)
}
device_manager::device_manager(const void *root_table, kutil::allocator &alloc) :
m_lapic(nullptr),
m_ioapics(alloc),
m_pci(alloc),
m_devices(alloc),
m_irqs(alloc),
m_blockdevs(alloc)
device_manager::device_manager() :
m_lapic_base(0)
{
m_irqs.ensure_capacity(32);
m_irqs.set_size(16);
for (int i = 0; i < 16; ++i)
m_irqs[i] = nullptr;
m_irqs[2] = ignore_endpoint;
}
template <typename T> static const T *
check_get_table(const acpi_table_header *header)
{
kassert(header && header->validate(T::type_id), "Invalid ACPI table.");
return reinterpret_cast<const T *>(header);
}
void
device_manager::parse_acpi(const void *root_table)
{
kassert(root_table != 0, "ACPI root table pointer is null.");
const acpi1_rsdp *acpi1 =
reinterpret_cast<const acpi1_rsdp *>(root_table);
const acpi1_rsdp *acpi1 = memory::to_virtual(
reinterpret_cast<const acpi1_rsdp *>(root_table));
for (int i = 0; i < sizeof(acpi1->signature); ++i)
kassert(acpi1->signature[i] == expected_signature[i],
@@ -87,12 +103,27 @@ device_manager::device_manager(const void *root_table, kutil::allocator &alloc)
sum = kutil::checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
kassert(sum == 0, "ACPI 2.0 RSDP checksum mismatch.");
load_xsdt(reinterpret_cast<const acpi_xsdt *>(acpi2->xsdt_address));
load_xsdt(memory::to_virtual(acpi2->xsdt_address));
}
m_irqs.ensure_capacity(32);
m_irqs.set_size(16);
m_irqs[2] = {"Clock interrupt", irq2_callback, nullptr};
m_irqs[4] = {"Serial interrupt", irq4_callback, nullptr};
const device_manager::apic_nmi *
device_manager::get_lapic_nmi(uint8_t id) const
{
for (const auto &nmi : m_nmis) {
if (nmi.cpu == 0xff || nmi.cpu == id)
return &nmi;
}
return nullptr;
}
const device_manager::irq_override *
device_manager::get_irq_override(uint8_t irq) const
{
for (const auto &o : m_overrides)
if (o.source == irq) return &o;
return nullptr;
}
ioapic *
@@ -108,9 +139,9 @@ put_sig(char *into, uint32_t type)
}
void
device_manager::load_xsdt(const acpi_xsdt *xsdt)
device_manager::load_xsdt(const acpi_table_header *header)
{
kassert(xsdt && acpi_validate(xsdt), "Invalid ACPI XSDT.");
const auto *xsdt = check_get_table<acpi_xsdt>(header);
char sig[5] = {0,0,0,0,0};
log::info(logs::device, "ACPI 2.0+ tables loading");
@@ -120,7 +151,8 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
size_t num_tables = acpi_table_entries(xsdt, sizeof(void*));
for (size_t i = 0; i < num_tables; ++i) {
const acpi_table_header *header = xsdt->headers[i];
const acpi_table_header *header =
memory::to_virtual(xsdt->headers[i]);
put_sig(sig, header->type);
log::debug(logs::device, " Found table %s", sig);
@@ -129,11 +161,15 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
switch (header->type) {
case acpi_apic::type_id:
load_apic(reinterpret_cast<const acpi_apic *>(header));
load_apic(header);
break;
case acpi_mcfg::type_id:
load_mcfg(reinterpret_cast<const acpi_mcfg *>(header));
load_mcfg(header);
break;
case acpi_hpet::type_id:
load_hpet(header);
break;
default:
@@ -143,41 +179,42 @@ device_manager::load_xsdt(const acpi_xsdt *xsdt)
}
void
device_manager::load_apic(const acpi_apic *apic)
device_manager::load_apic(const acpi_table_header *header)
{
uint32_t *local = reinterpret_cast<uint32_t *>(apic->local_address);
const auto *apic = check_get_table<acpi_apic>(header);
m_lapic = new lapic(local, isr::isrSpurious);
m_lapic_base = apic->local_address;
size_t count = acpi_table_entries(apic, 1);
uint8_t const *p = apic->controller_data;
uint8_t const *end = p + count;
// Pass one: count IOAPIC objcts
int num_ioapics = 0;
// Pass one: count objcts
unsigned num_lapics = 0;
unsigned num_ioapics = 0;
unsigned num_overrides = 0;
unsigned num_nmis = 0;
while (p < end) {
const uint8_t type = p[0];
const uint8_t length = p[1];
if (type == 1) num_ioapics++;
p += length;
}
m_ioapics.set_capacity(num_ioapics);
// Pass two: set up IOAPIC objcts
p = apic->controller_data;
while (p < end) {
const uint8_t type = p[0];
const uint8_t length = p[1];
if (type == 1) {
uint32_t *base = reinterpret_cast<uint32_t *>(kutil::read_from<uint32_t>(p+4));
uint32_t base_gsr = kutil::read_from<uint32_t>(p+8);
m_ioapics.emplace(base, base_gsr);
switch (type) {
case 0: ++num_lapics; break;
case 1: ++num_ioapics; break;
case 2: ++num_overrides; break;
case 4: ++num_nmis; break;
default: break;
}
p += length;
}
// Pass three: configure APIC objects
m_apic_ids.set_capacity(num_lapics);
m_ioapics.set_capacity(num_ioapics);
m_overrides.set_capacity(num_overrides);
m_nmis.set_capacity(num_nmis);
// Pass two: configure objects
p = apic->controller_data;
while (p < end) {
const uint8_t type = p[0];
@@ -187,38 +224,42 @@ device_manager::load_apic(const acpi_apic *apic)
case 0: { // Local APIC
uint8_t uid = kutil::read_from<uint8_t>(p+2);
uint8_t id = kutil::read_from<uint8_t>(p+3);
log::debug(logs::device, " Local APIC uid %x id %x", id);
m_apic_ids.append(id);
log::debug(logs::device, " Local APIC uid %x id %x", uid, id);
}
break;
case 1: // I/O APIC
case 1: { // I/O APIC
uintptr_t base = kutil::read_from<uint32_t>(p+4);
uint32_t base_gsi = kutil::read_from<uint32_t>(p+8);
m_ioapics.emplace(base, base_gsi);
log::debug(logs::device, " IO APIC gsi %x base %x", base_gsi, base);
}
break;
case 2: { // Interrupt source override
uint8_t source = kutil::read_from<uint8_t>(p+3);
isr gsi = isr::irq00 + kutil::read_from<uint32_t>(p+4);
uint16_t flags = kutil::read_from<uint16_t>(p+8);
irq_override o;
o.source = kutil::read_from<uint8_t>(p+3);
o.gsi = kutil::read_from<uint32_t>(p+4);
o.flags = kutil::read_from<uint16_t>(p+8);
m_overrides.append(o);
log::debug(logs::device, " Intr source override IRQ %d -> %d Pol %d Tri %d",
source, gsi, (flags & 0x3), ((flags >> 2) & 0x3));
// TODO: in a multiple-IOAPIC system this might be elsewhere
m_ioapics[0].redirect(source, static_cast<isr>(gsi), flags, true);
o.source, o.gsi, (o.flags & 0x3), ((o.flags >> 2) & 0x3));
}
break;
case 4: {// LAPIC NMI
uint8_t cpu = kutil::read_from<uint8_t>(p + 2);
uint8_t num = kutil::read_from<uint8_t>(p + 5);
uint16_t flags = kutil::read_from<uint16_t>(p + 3);
apic_nmi nmi;
nmi.cpu = kutil::read_from<uint8_t>(p + 2);
nmi.lint = kutil::read_from<uint8_t>(p + 5);
nmi.flags = kutil::read_from<uint16_t>(p + 3);
m_nmis.append(nmi);
log::debug(logs::device, " LAPIC NMI Proc %d LINT%d Pol %d Tri %d",
kutil::read_from<uint8_t>(p+2),
kutil::read_from<uint8_t>(p+5),
kutil::read_from<uint16_t>(p+3) & 0x3,
(kutil::read_from<uint16_t>(p+3) >> 2) & 0x3);
m_lapic->enable_lint(num, num == 0 ? isr::isrLINT0 : isr::isrLINT1, true, flags);
log::debug(logs::device, " LAPIC NMI Proc %02x LINT%d Pol %d Tri %d",
nmi.cpu, nmi.lint, nmi.flags & 0x3, (nmi.flags >> 2) & 0x3);
}
break;
@@ -228,39 +269,24 @@ device_manager::load_apic(const acpi_apic *apic)
p += length;
}
for (uint8_t i = 0; i < m_ioapics[0].get_num_gsi(); ++i) {
switch (i) {
case 2: break;
default: m_ioapics[0].mask(i, false);
}
}
m_lapic->enable();
}
void
device_manager::load_mcfg(const acpi_mcfg *mcfg)
device_manager::load_mcfg(const acpi_table_header *header)
{
const auto *mcfg = check_get_table<acpi_mcfg>(header);
size_t count = acpi_table_entries(mcfg, sizeof(acpi_mcfg_entry));
m_pci.set_size(count);
m_devices.set_capacity(16);
page_manager *pm = page_manager::get();
for (unsigned i = 0; i < count; ++i) {
const acpi_mcfg_entry &mcfge = mcfg->entries[i];
m_pci[i].group = mcfge.group;
m_pci[i].bus_start = mcfge.bus_start;
m_pci[i].bus_end = mcfge.bus_end;
m_pci[i].base = reinterpret_cast<uint32_t *>(mcfge.base);
int num_busses = m_pci[i].bus_end - m_pci[i].bus_start + 1;
/// Map the MMIO space into memory
pm->map_offset_pointer(reinterpret_cast<void **>(&m_pci[i].base),
(num_busses << 20));
m_pci[i].base = memory::to_virtual<uint32_t>(mcfge.base);
log::debug(logs::device, " Found MCFG entry: base %lx group %d bus %d-%d",
mcfge.base, mcfge.group, mcfge.bus_start, mcfge.bus_end);
@@ -269,6 +295,29 @@ device_manager::load_mcfg(const acpi_mcfg *mcfg)
probe_pci();
}
void
device_manager::load_hpet(const acpi_table_header *header)
{
const auto *hpet = check_get_table<acpi_hpet>(header);
log::debug(logs::device, " Found HPET device #%3d: base %016lx pmin %d attr %02x",
hpet->index, hpet->base_address.address, hpet->periodic_min, hpet->attributes);
uint32_t hwid = hpet->hardware_id;
uint8_t rev_id = hwid & 0xff;
uint8_t comparators = (hwid >> 8) & 0x1f;
uint8_t count_size_cap = (hwid >> 13) & 1;
uint8_t legacy_replacement = (hwid >> 15) & 1;
uint32_t pci_vendor_id = (hwid >> 16);
log::debug(logs::device, " rev:%02d comparators:%02d count_size_cap:%1d legacy_repl:%1d",
rev_id, comparators, count_size_cap, legacy_replacement);
log::debug(logs::device, " pci vendor id: %04x", pci_vendor_id);
m_hpets.emplace(hpet->index,
reinterpret_cast<uint64_t*>(hpet->base_address.address + ::memory::page_offset));
}
void
device_manager::probe_pci()
{
@@ -291,6 +340,13 @@ device_manager::probe_pci()
}
}
static uint64_t
fake_clock_source(void*)
{
static uint64_t value = 0;
return value++;
}
void
device_manager::init_drivers()
{
@@ -309,11 +365,61 @@ device_manager::init_drivers()
ahcid.register_device(&device);
}
*/
clock *master_clock = nullptr;
if (m_hpets.count() > 0) {
hpet &h = m_hpets[0];
h.enable();
// becomes the singleton
master_clock = new clock(h.rate(), hpet_clock_source, &h);
log::info(logs::clock, "Created master clock using HPET 0: Rate %d", h.rate());
} else {
//TODO: Other clocks, APIC clock?
master_clock = new clock(5000, fake_clock_source, nullptr);
}
kassert(master_clock, "Failed to allocate master clock");
}
bool
device_manager::dispatch_irq(unsigned irq)
{
if (irq >= m_irqs.count())
return false;
endpoint *e = m_irqs[irq];
if (!e || e == ignore_endpoint)
return e == ignore_endpoint;
e->signal_irq(irq);
return true;
}
bool
device_manager::bind_irq(unsigned irq, endpoint *target)
{
// TODO: grow if under max size
if (irq >= m_irqs.count())
return false;
m_irqs[irq]= target;
return true;
}
void
device_manager::unbind_irqs(endpoint *target)
{
const size_t count = m_irqs.count();
for (size_t i = 0; i < count; ++i) {
if (m_irqs[i] == target)
m_irqs[i] = nullptr;
}
}
bool
device_manager::allocate_msi(const char *name, pci_device &device, irq_callback cb, void *data)
{
/*
// TODO: find gaps to fill
uint8_t irq = m_irqs.count();
isr vector = isr::irq00 + irq;
@@ -324,6 +430,7 @@ device_manager::allocate_msi(const char *name, pci_device &device, irq_callback
device.write_msi_regs(
0xFEE00000,
static_cast<uint16_t>(vector));
*/
return true;
}

View File

@@ -3,12 +3,12 @@
/// The device manager definition
#include "kutil/vector.h"
#include "apic.h"
#include "hpet.h"
#include "pci.h"
struct acpi_xsdt;
struct acpi_apic;
struct acpi_mcfg;
struct acpi_table_header;
class block_device;
class endpoint;
using irq_callback = void (*)(void *);
@@ -18,27 +18,35 @@ class device_manager
{
public:
/// Constructor.
/// \arg root_table Pointer to the ACPI RSDP
/// \arg alloc Allocator for device arrays
device_manager(const void *root_table, kutil::allocator &alloc);
device_manager();
/// Get the system global device manager.
/// \returns A reference to the system device manager
static device_manager & get() { return s_instance; }
/// Get the LAPIC
/// \returns An object representing the local APIC
lapic * get_lapic() { return m_lapic; }
/// Get an IOAPIC
/// \arg i Index of the requested IOAPIC
/// \returns An object representing the given IOAPIC if it exists,
/// otherwise nullptr.
ioapic * get_ioapic(int i);
/// Parse ACPI tables.
/// \arg root_table Pointer to the ACPI RSDP
void parse_acpi(const void *root_table);
/// Intialize drivers for the current device list.
void init_drivers();
/// Bind an IRQ to an endpoint
/// \arg irq The IRQ number to bind
/// \arg target The endpoint to recieve messages when the IRQ is signalled
/// \returns True on success
bool bind_irq(unsigned irq, endpoint *target);
/// Remove IRQ bindings for an endpoint
/// \arg target The endpoint to remove
void unbind_irqs(endpoint *target);
/// Allocate an MSI IRQ for a device
/// \arg name Name of the interrupt, for display to user
/// \arg device Device this MSI is being allocated for
@@ -54,17 +62,40 @@ public:
/// Dispatch an IRQ interrupt
/// \arg irq The irq number of the interrupt
/// \returns True if the interrupt was handled
inline bool dispatch_irq(uint8_t irq)
bool dispatch_irq(unsigned irq);
struct apic_nmi
{
if (irq < m_irqs.count()) {
irq_allocation &cba = m_irqs[irq];
if (cba.callback) {
cba.callback(cba.data);
return true;
}
}
return false;
}
uint8_t cpu;
uint8_t lint;
uint16_t flags;
};
struct irq_override
{
uint8_t source;
uint16_t flags;
uint32_t gsi;
};
/// Get the list of APIC ids for other CPUs
inline const kutil::vector<uint8_t> & get_apic_ids() const { return m_apic_ids; }
/// Get the LAPIC base address
/// \returns The physical base address of the local apic registers
uintptr_t get_lapic_base() const { return m_lapic_base; }
/// Get the NMI mapping for the given local APIC
/// \arg id ID of the local APIC
/// \returns apic_nmi structure describing the NMI configuration,
/// or null if no configuration was provided
const apic_nmi * get_lapic_nmi(uint8_t id) const;
/// Get the IRQ source override for the given IRQ
/// \arg irq IRQ number (not isr vector)
/// \returns irq_override structure describing that IRQ's
/// configuration, or null if no configuration was provided
const irq_override * get_irq_override(uint8_t irq) const;
/// Register the existance of a block device.
/// \arg blockdev Pointer to the block device
@@ -83,18 +114,31 @@ public:
m_blockdevs[i] : nullptr;
}
/// Get an HPET device
/// \arg i Index of the device to get
/// \returns A pointer to the requested device, or nullptr
inline hpet * get_hpet(unsigned i)
{
return i < m_hpets.count() ?
&m_hpets[i] : nullptr;
}
private:
/// Parse the ACPI XSDT and load relevant sub-tables.
/// \arg xsdt Pointer to the XSDT from the firmware
void load_xsdt(const acpi_xsdt *xsdt);
void load_xsdt(const acpi_table_header *xsdt);
/// Parse the ACPI MADT and initialize APICs from it.
/// \arg apic Pointer to the MADT from the XSDT
void load_apic(const acpi_apic *apic);
void load_apic(const acpi_table_header *apic);
/// Parse the ACPI MCFG and initialize PCIe from it.
/// \arg mcfg Pointer to the MCFG from the XSDT
void load_mcfg(const acpi_mcfg *mcfg);
void load_mcfg(const acpi_table_header *mcfg);
/// Parse the ACPI HPET and initialize an HPET from it.
/// \arg hpet Pointer to the HPET from the XSDT
void load_hpet(const acpi_table_header *hpet);
/// Probe the PCIe busses and add found devices to our
/// device list. The device list is destroyed and rebuilt.
@@ -104,25 +148,23 @@ private:
/// that has no callback.
void bad_irq(uint8_t irq);
lapic *m_lapic;
uintptr_t m_lapic_base;
kutil::vector<ioapic> m_ioapics;
kutil::vector<hpet> m_hpets;
kutil::vector<uint8_t> m_apic_ids;
kutil::vector<apic_nmi> m_nmis;
kutil::vector<irq_override> m_overrides;
kutil::vector<pci_group> m_pci;
kutil::vector<pci_device> m_devices;
struct irq_allocation
{
const char *name;
irq_callback callback;
void *data;
};
kutil::vector<irq_allocation> m_irqs;
kutil::vector<endpoint*> m_irqs;
kutil::vector<block_device *> m_blockdevs;
static device_manager s_instance;
device_manager() = delete;
device_manager(const device_manager &) = delete;
device_manager operator=(const device_manager &) = delete;
};

View File

@@ -1,33 +0,0 @@
#pragma once
#include <stdint.h>
#include "kutil/coord.h"
#include "screen.h"
class font
{
public:
font(void const *data);
unsigned glyph_bytes() const { return m_size.y * ((m_size.x + 7) / 8); }
unsigned count() const { return m_count; }
unsigned width() const { return m_size.x; }
unsigned height() const { return m_size.y; }
bool valid() const { return m_count > 0; }
void draw_glyph(
screen *s,
uint32_t glyph,
screen::pixel_t fg,
screen::pixel_t bg,
unsigned x,
unsigned y) const;
private:
kutil::coord<unsigned> m_size;
unsigned m_count;
uint8_t const *m_data;
font() = delete;
};

View File

@@ -1,93 +1,163 @@
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "frame_allocator.h"
#include "kernel_args.h"
#include "kernel_memory.h"
#include "log.h"
using memory::frame_size;
using memory::page_offset;
using frame_block_node = kutil::list_node<frame_block>;
frame_allocator g_frame_allocator;
int
frame_block::compare(const frame_block *rhs) const
frame_allocator &
frame_allocator::get()
{
if (address < rhs->address)
return -1;
else if (address > rhs->address)
return 1;
return 0;
extern frame_allocator &g_frame_allocator;
return g_frame_allocator;
}
frame_allocator::raw_alloc::raw_alloc(frame_allocator &fa) : m_fa(fa) {}
void *
frame_allocator::raw_alloc::allocate(size_t size)
frame_allocator::frame_allocator(kernel::args::frame_block *frames, size_t count) :
m_blocks {frames},
m_count {count}
{
kassert(size <= frame_size, "Raw allocator only allocates a single page");
uintptr_t addr = 0;
if (size <= frame_size)
m_fa.allocate(1, &addr);
return reinterpret_cast<void*>(addr + page_offset);
}
void
frame_allocator::raw_alloc::free(void *p)
{
m_fa.free(reinterpret_cast<uintptr_t>(p), 1);
}
frame_allocator::frame_allocator() :
m_raw_alloc(*this)
inline unsigned
bsf(uint64_t v)
{
asm ("tzcntq %q0, %q1" : "=r"(v) : "0"(v) : "cc");
return v;
}
size_t
frame_allocator::allocate(size_t count, uintptr_t *address)
{
kassert(!m_free.empty(), "frame_allocator::pop_frames ran out of free frames!");
if (m_free.empty())
return 0;
kutil::scoped_lock lock {m_lock};
auto *first = m_free.front();
for (long i = m_count - 1; i >= 0; --i) {
frame_block &block = m_blocks[i];
if (count >= first->count) {
*address = first->address;
m_free.remove(first);
return first->count;
} else {
first->count -= count;
*address = first->address + (first->count * frame_size);
return count;
if (!block.map1)
continue;
// Tree walk to find the first available page
unsigned o1 = bsf(block.map1);
uint64_t m2 = block.map2[o1];
unsigned o2 = bsf(m2);
uint64_t m3 = block.bitmap[(o1 << 6) + o2];
unsigned o3 = bsf(m3);
unsigned frame = (o1 << 12) + (o2 << 6) + o3;
// See how many contiguous pages are here
unsigned n = bsf(~m3 >> o3);
if (n > count)
n = count;
*address = block.base + frame * frame_size;
// Clear the bits to mark these pages allocated
m3 &= ~(((1 << n) - 1) << o3);
block.bitmap[(o1 << 6) + o2] = m3;
if (!m3) {
// if that was it for this group, clear the next level bit
m2 &= ~(1 << o2);
block.map2[o1] = m2;
if (!m2) {
// if that was cleared too, update the top level
block.map1 &= ~(1 << o1);
}
}
return n;
}
}
inline uintptr_t end(frame_block *node) { return node->address + node->count * frame_size; }
kassert(false, "frame_allocator ran out of free frames!");
return 0;
}
void
frame_allocator::free(uintptr_t address, size_t count)
{
frame_block_node *node =
reinterpret_cast<frame_block_node*>(address + page_offset);
kutil::scoped_lock lock {m_lock};
kutil::memset(node, 0, sizeof(frame_block_node));
node->address = address;
node->count = count;
kassert(address % frame_size == 0, "Trying to free a non page-aligned frame!");
m_free.sorted_insert(node);
if (!count)
return;
frame_block_node *next = node->next();
if (next && end(node) == next->address) {
node->count += next->count;
m_free.remove(next);
}
for (long i = 0; i < m_count; ++i) {
frame_block &block = m_blocks[i];
uintptr_t end = block.base + block.count * frame_size;
frame_block_node *prev = node->prev();
if (prev && end(prev) == address) {
prev->count += node->count;
m_free.remove(node);
if (address < block.base || address >= end)
continue;
uint64_t frame = (address - block.base) >> 12;
unsigned o1 = (frame >> 12) & 0x3f;
unsigned o2 = (frame >> 6) & 0x3f;
unsigned o3 = frame & 0x3f;
while (count--) {
block.map1 |= (1 << o1);
block.map2[o1] |= (1 << o2);
block.bitmap[o2] |= (1 << o3);
if (++o3 == 64) {
o3 = 0;
if (++o2 == 64) {
o2 = 0;
++o1;
kassert(o1 < 64, "Tried to free pages past the end of a block");
}
}
}
}
}
void
frame_allocator::used(uintptr_t address, size_t count)
{
kutil::scoped_lock lock {m_lock};
kassert(address % frame_size == 0, "Trying to mark a non page-aligned frame!");
if (!count)
return;
for (long i = 0; i < m_count; ++i) {
frame_block &block = m_blocks[i];
uintptr_t end = block.base + block.count * frame_size;
if (address < block.base || address >= end)
continue;
uint64_t frame = (address - block.base) >> 12;
unsigned o1 = (frame >> 12) & 0x3f;
unsigned o2 = (frame >> 6) & 0x3f;
unsigned o3 = frame & 0x3f;
while (count--) {
block.bitmap[o2] &= ~(1 << o3);
if (!block.bitmap[o2]) {
block.map2[o1] &= ~(1 << o2);
if (!block.map2[o1]) {
block.map1 &= ~(1 << o1);
}
}
if (++o3 == 64) {
o3 = 0;
if (++o2 == 64) {
o2 = 0;
++o1;
kassert(o1 < 64, "Tried to mark pages past the end of a block");
}
}
}
}
}

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