153 Commits
v0.2.0 ... vdso

Author SHA1 Message Date
Justin C. Miller
dd73cf833f Change initrd 'executable' bool to type enum. 2019-10-10 23:09:40 -07:00
Justin C. Miller
71fe3d733b Add the VDSO to the initrd 2019-10-10 00:14:46 -07:00
Justin C. Miller
991b13424e Initial building of the vdso.
Not actually integrating with the kernel yet.
2019-10-09 22:29:45 -07:00
Justin C. Miller
6963304c01 Changing jsix license
jsix is now licensed under the MPL2.
2019-10-06 01:37:46 -07:00
Justin C. Miller
ae651a4fcd Move cpptoml.h to external directory 2019-10-06 00:46:30 -07:00
Justin C. Miller
17c2fe6e4e Add debig-exit device to qemu.sh for future tests 2019-10-06 00:29:10 -07:00
Justin C. Miller
f066ac3ffd Move catch.hpp to external directory 2019-10-06 00:01:27 -07:00
Justin C. Miller
edcf633e84 Remove cargo-culted znocombreloc ld flag 2019-08-10 13:49:57 -07:00
Justin C. Miller
d09a454b8b Normalize bootloader's con_debug line endings 2019-07-21 22:29:19 -07:00
Justin C. Miller
d6329ea9bf Update to bonnibel 2.1 2019-07-21 21:10:43 -07:00
Justin C. Miller
83897048ab Update for bonnibel 2.0 2019-07-20 23:19:21 -07:00
Justin C. Miller
7cc59770b8 Update libc for new sysall values via peru 2019-07-07 10:07:00 -07:00
Justin C. Miller
b056d95920 Organize system calls
* syscalls should all return j6_status_t now
* syscalls are grouped by category in name as well as in files
2019-07-07 09:54:29 -07:00
Justin C. Miller
19cd01ef8d Add initial pass of syscall API kobjects 2019-07-07 09:54:29 -07:00
Justin C. Miller
b3f88bbe02 Further refine sysroot.
* remove need for NASM from sysroot
* have peru sync libc separately
2019-07-07 09:52:06 -07:00
Justin C. Miller
f57f38edbd Remove old ident_page_flags 2019-07-05 20:52:04 -07:00
Justin C. Miller
12377ae730 Ignore .peru 2019-07-05 17:29:56 -07:00
Justin C. Miller
bb93dcef44 Remove sysroot binutils dependency
* Link host-targeted binaries with lld
 * Add peru script for getting prebuilt sysroot
 * Add readme for prebuilt sysroots
 * Remove non-working build_sysroot_gcc.sh, rename clang version to just
   build_sysroot.sh
2019-07-05 17:26:24 -07:00
Justin C. Miller
678a12dc90 Ignore .gdb_history 2019-07-04 17:43:42 -07:00
Justin C. Miller
c3dacb2906 Fix mis-flagged page table entries 2019-07-04 17:43:08 -07:00
Justin C. Miller
3a68ec439d Add CPU feature checking.
Introduces the cpu_features.inc table to enumerate the CPU features that
j6 cares about. Features in this table marked CPU_FEATURE_REQ are
considered required, and the boot process will log an error and halt
when any of these features are not supported. This should save me from
banging my head against the wall like I did last night with the missing
pdpe1gb feature.
2019-07-04 16:41:25 -07:00
Justin C. Miller
7ce418aabc Fix KVM page faults from lack of 1GB page support 2019-07-04 02:51:50 -07:00
Justin C. Miller
8ee5091f41 update gitignore 2019-07-03 21:55:23 -07:00
Justin C. Miller
6285517ef7 Rename Popcorn to jsix.
See README.md for more information.
2019-05-27 14:07:29 -07:00
Justin C. Miller
2b0cd6f2f2 Make qemu.sh work with i3 as well 2019-05-26 10:35:22 -07:00
Justin C. Miller
a653c55941 Use 0 instead of syscall_invalid in syscall jump list 2019-05-18 18:11:08 -07:00
Justin C. Miller
ce035d2a43 Finish address_manager to vm_space transition 2019-05-18 18:06:57 -07:00
Justin C. Miller
2d54eb5143 Add vmem log area 2019-05-11 11:32:22 -07:00
Justin C. Miller
b9c8edb657 Allow clang to colorize output in ninja 2019-04-18 00:28:23 -07:00
Justin C. Miller
88315c25a5 Allow vm_space to merge ranges 2019-04-18 00:22:01 -07:00
Justin C. Miller
806bfd1fbf First pass at vm_space (address_manager replacement) 2019-04-17 19:16:54 -07:00
Justin C. Miller
910b5116f4 Fix stack overruns 2019-04-16 23:39:52 -07:00
Justin C. Miller
6302e8b73a Overhaul memory allocation model
This commit makes several fundamental changes to memory handling:

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

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

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

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

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

11
.gitignore vendored
View File

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

View File

@@ -1,132 +1,354 @@
# Popcorn
# License
Popcorn itself is released under the terms of the MIT license:
jsix is (c) 2017-2019 Justin C Miller, and distributed under the terms of the
Mozilla Public License 2.0.
> Copyright © 2018 Justin C. Miller, http://devjustinian.com <justin@devjustinian.com>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the “Software”), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
---
# Included works
## Mozilla Public License Version 2.0
Popcorn includes and/or is derived from a number of other works, listed here.
### 1. Definitions
## Catch2
#### 1.1. "Contributor"
Popcorn uses [Catch2](http://github.com/catchorg/Catch2) for testing. Catch2 is
released under the terms of the Boost Software license:
means each individual or legal entity that creates, contributes to the creation
of, or owns Covered Software.
> Boost Software License - Version 1.0 - August 17th, 2003
>
> Permission is hereby granted, free of charge, to any person or organization
> obtaining a copy of the software and accompanying documentation covered by
> this license (the "Software") to use, reproduce, display, distribute,
> execute, and transmit the Software, and to prepare derivative works of the
> Software, and to permit third-parties to whom the Software is furnished to
> do so, all subject to the following:
>
> The copyright notices in the Software and this entire statement, including
> the above license grant, this restriction and the following disclaimer,
> must be included in all copies of the Software, in whole or in part, and
> all derivative works of the Software, unless such copies or derivative
> works are solely in the form of machine-executable object code generated by
> a source language processor.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
> SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS IN THE SOFTWARE.
#### 1.2. "Contributor Version"
## Intel EFI Application Toolkit
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
Popcorn's UEFI loader uses code from Intel's EFI Application toolkit. Relevant
code includes license statements at the top of each file.
#### 1.3. "Contribution
## Waf
means Covered Software of a particular Contributor.
Popcorn's build system uses [Waf](https://waf.io/), which claims to be released
under the BSD license. I could not find its specific license file, so I am
reproducing a generic 3-clause BSD license (the most restrictive, so as not to
assume any extra rights that may not actually be granted) for it here:
#### 1.4. "Covered Software"
> Copyright © 2005-2018 Thomas Nagy
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions are met:
>
> 1. Redistributions of source code must retain the above copyright notice, this
> list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above copyright notice,
> this list of conditions and the following disclaimer in the documentation
> and/or other materials provided with the distribution.
>
> 3. Neither the name of the copyright holder nor the names of its contributors
> may be used to endorse or promote products derived from this software
> without specific prior written permission.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
> ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
means Source Code Form to which the initial Contributor has attached the notice
in Exhibit A, the Executable Form of such Source Code Form, and Modifications
of such Source Code Form, in each case including portions thereof.
## GNU-EFI
#### 1.5. "Incompatible With Secondary Licenses"
Popcorn's UEFI bootloader initially used
[GNU-EFI](https://gnu-efi.sourceforge.net), and still uses one file (the linker
script for the bootloader) from that project. GNU-EFI claims to be released
under the BSD license. Again, I could not find its specific license file, so I
am reproducing a generic 3-clause BSD license (the most restrictive, so as not
to assume any extra rights that may not actually be granted) for it here:
means
> Copyright © Nigel Croxon
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions are met:
>
> 1. Redistributions of source code must retain the above copyright notice, this
> list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above copyright notice,
> this list of conditions and the following disclaimer in the documentation
> and/or other materials provided with the distribution.
>
> 3. Neither the name of the copyright holder nor the names of its contributors
> may be used to endorse or promote products derived from this software
> without specific prior written permission.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
> ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* **(a)** that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
* **(b)** that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of a
Secondary License.
#### 1.6. "Executable Form"
means any form of the work other than Source Code Form.
#### 1.7. "Larger Work"
means a work that combines Covered Software with other material, in a separate
file or files, that is not Covered Software.
#### 1.8. "License"
means this document.
#### 1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether at the
time of the initial grant or subsequently, any and all of the rights conveyed
by this License.
#### 1.10. "Modifications"
means any of the following:
* **(a)** any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
* **(b)** any new file in Source Code Form that contains any Covered Software.
#### 1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method, process, and
apparatus claims, in any patent Licensable by such Contributor that would be
infringed, but for the grant of the License, by the making, using, selling,
offering for sale, having made, import, or transfer of either its Contributions
or its Contributor Version.
#### 1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public License,
Version 3.0, or any later versions of those licenses.
#### 1.13. "Source Code Form"
means the form of the work preferred for making modifications.
#### 1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this License. For
legal entities, "You" includes any entity that controls, is controlled by, or
is under common control with You. For purposes of this definition, "control"
means **(a)** the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or **(b)**
ownership of more than fifty percent (50%) of the outstanding shares or
beneficial ownership of such entity.
### 2. License Grants and Conditions
#### 2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive
license:
* **(a)** under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available, modify,
display, perform, distribute, and otherwise exploit its Contributions, either
on an unmodified basis, with Modifications, or as part of a Larger Work; and
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its Contributions or
its Contributor Version.
#### 2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution become
effective for each Contribution on the date the Contributor first distributes
such Contribution.
#### 2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under this
License. No additional rights or licenses will be implied from the distribution
or licensing of Covered Software under this License. Notwithstanding Section
2.1(b) above, no patent license is granted by a Contributor:
* **(a)** for any code that a Contributor has removed from Covered Software; or
* **(b)** for infringements caused by: **(i)** Your and any other third party's
modifications of Covered Software, or **(ii)** the combination of its
Contributions with other software (except as part of its Contributor
Version); or
* **(c)** under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks, or
logos of any Contributor (except as may be necessary to comply with the notice
requirements in Section 3.4).
#### 2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to distribute
the Covered Software under a subsequent version of this License (see Section
10.2) or under the terms of a Secondary License (if permitted under the terms
of Section 3.3).
#### 2.5. Representation
Each Contributor represents that the Contributor believes its Contributions are
its original creation(s) or it has sufficient rights to grant the rights to its
Contributions conveyed by this License.
#### 2.6. Fair Use
This License is not intended to limit any rights You have under applicable
copyright doctrines of fair use, fair dealing, or other equivalents.
#### 2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
### 3. Responsibilities
#### 3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under the
terms of this License. You must inform recipients that the Source Code Form of
the Covered Software is governed by the terms of this License, and how they can
obtain a copy of this License. You may not attempt to alter or restrict the
recipients' rights in the Source Code Form.
#### 3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
* **(a)** such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost of
distribution to the recipient; and
* **(b)** You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the license
for the Executable Form does not attempt to limit or alter the recipients'
rights in the Source Code Form under this License.
#### 3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for the
Covered Software. If the Larger Work is a combination of Covered Software with
a work governed by one or more Secondary Licenses, and the Covered Software is
not Incompatible With Secondary Licenses, this License permits You to
additionally distribute such Covered Software under the terms of such Secondary
License(s), so that the recipient of the Larger Work may, at their option,
further distribute the Covered Software under the terms of either this License
or such Secondary License(s).
#### 3.4. Notices
You may not remove or alter the substance of any license notices (including
copyright notices, patent notices, disclaimers of warranty, or limitations of
liability) contained within the Source Code Form of the Covered Software,
except that You may alter any license notices to the extent required to remedy
known factual inaccuracies.
#### 3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support, indemnity
or liability obligations to one or more recipients of Covered Software.
However, You may do so only on Your own behalf, and not on behalf of any
Contributor. You must make it absolutely clear that any such warranty, support,
indemnity, or liability obligation is offered by You alone, and You hereby
agree to indemnify every Contributor for any liability incurred by such
Contributor as a result of warranty, support, indemnity or liability terms You
offer. You may include additional disclaimers of warranty and limitations of
liability specific to any jurisdiction.
### 4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute, judicial
order, or regulation then You must: **(a)** comply with the terms of this
License to the maximum extent possible; and **(b)** describe the limitations
and the code they affect. Such description must be placed in a text file
included with all distributions of the Covered Software under this License.
Except to the extent prohibited by statute or regulation, such description must
be sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
### 5. Termination
**5.1.** The rights granted under this License will terminate automatically if
You fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor are
reinstated **(a)** provisionally, unless and until such Contributor explicitly
and finally terminates Your grants, and **(b)** on an ongoing basis, if such
Contributor fails to notify You of the non-compliance by some reasonable means
prior to 60 days after You have come back into compliance. Moreover, Your
grants from a particular Contributor are reinstated on an ongoing basis if such
Contributor notifies You of the non-compliance by some reasonable means, this
is the first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after Your
receipt of the notice.
**5.2.** If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions, counter-claims, and
cross-claims) alleging that a Contributor Version directly or indirectly
infringes any patent, then the rights granted to You by any and all
Contributors for the Covered Software under Section 2.1 of this License shall
terminate.
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all end
user license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
### 6. Disclaimer of Warranty
> Covered Software is provided under this License on an "as is" basis, without
> warranty of any kind, either expressed, implied, or statutory, including,
> without limitation, warranties that the Covered Software is free of defects,
> merchantable, fit for a particular purpose or non-infringing. The entire risk
> as to the quality and performance of the Covered Software is with You.
> Should any Covered Software prove defective in any respect, You (not any
> Contributor) assume the cost of any necessary servicing, repair, or
> correction. This disclaimer of warranty constitutes an essential part of this
> License. No use of any Covered Software is authorized under this License
> except under this disclaimer.
### 7. Limitation of Liability
> Under no circumstances and under no legal theory, whether tort (including
> negligence), contract, or otherwise, shall any Contributor, or anyone who
> distributes Covered Software as permitted above, be liable to You for any
> direct, indirect, special, incidental, or consequential damages of any
> character including, without limitation, damages for lost profits, loss of
> goodwill, work stoppage, computer failure or malfunction, or any and all
> other commercial damages or losses, even if such party shall have been
> informed of the possibility of such damages. This limitation of liability
> shall not apply to liability for death or personal injury resulting from such
> party's negligence to the extent applicable law prohibits such limitation.
> Some jurisdictions do not allow the exclusion or limitation of incidental or
> consequential damages, so this exclusion and limitation may not apply to You.
### 8. Litigation
Any litigation relating to this License may be brought only in the courts of a
jurisdiction where the defendant maintains its principal place of business and
such litigation shall be governed by laws of that jurisdiction, without
reference to its conflict-of-law provisions. Nothing in this Section shall
prevent a party's ability to bring cross-claims or counter-claims.
### 9. Miscellaneous
This License represents the complete agreement concerning the subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. Any law or regulation which provides that the language of a
contract shall be construed against the drafter shall not be used to construe
this License against a Contributor.
### 10. Versions of the License
#### 10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section 10.3,
no one other than the license steward has the right to modify or publish new
versions of this License. Each version will be given a distinguishing version
number.
#### 10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version of the
License under which You originally received the Covered Software, or under the
terms of any subsequent version published by the license steward.
#### 10.3. Modified Versions
If you create software not governed by this License, and you want to create a
new license for such software, you may create and use a modified version of
this License if you rename the license and remove any references to the name of
the license steward (except to note that such modified license differs from
this License).
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the notice
described in Exhibit B of this License must be attached.
### Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this file,
You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file, then
You may include the notice in a location (such as a LICENSE file in a relevant
directory) where a recipient would be likely to look for such a notice.
You may add additional accurate notices of copyright ownership.
### Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible With Secondary Licenses", as defined
by the Mozilla Public License, v. 2.0.

View File

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

View File

@@ -1,6 +1,6 @@
# popcorn: A toy OS kernel
# jsix: A toy OS kernel
**popcorn** is the kernel for the hobby OS that I am currently building. It's
**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.
@@ -25,16 +25,54 @@ The design goals of the project are:
temporarily) in some places to allow me to play around with the related
hardware.
A note on the name: This kernel was originally named Popcorn, but I have since
discovered that the Popcorn Linux project is also developing a kernel with that
name, started around the same time as this project. So I've renamed this kernel
jsix (Always styled _jsix_ or `j6`, never capitalized) as an homage to L4, xv6,
and my wonderful wife.
## Building
Popcorn uses the `waf` build tool, which is included in the repo. The other
requirements are:
jsix uses the [Ninja][] build tool, and generates the build files for it with a
custom tool called [Bonnibel][]. Bonnibel can be installed with [Cargo][], or
downloaded as a prebuilt binary from its Github repository.
* python (to run waf)
[Ninja]: https://ninja-build.org
[Bonnibel]: https://github.com/justinian/bonnibel
[Cargo]: https://crates.io/crates/bonnibel
Requrirements:
* bonnibel
* ninja
* clang
* nasm
* mtools
* curl for downloading the toolchain
### Setting up the cross toolchain
Running `pb sync` will download and unpack the toolchain into `sysroot`.
#### Compiling the toolchain yourself
If you have `clang` and `curl` installed, runing the `scripts/build_sysroot.sh`
script will download and build a LLVM toolchain configured for building jsix
host binaries.
### Building and running jsix
Once the toolchain has been set up, running Bonnibel's `pb init` command will
set up the build configuration, and `pb build` will actually run the build. If
you have `qemu-system-x86_64` installed, the `qemu.sh` script will to run jsix
in QEMU `-nographic` mode.
I personally run this either from a real debian amd64 testing/buster machine or
a windows WSL debian testing/buster installation. The following should be
enough to set up such a system to build the kernel:
sudo apt install qemu-system-x86 nasm clang-6.0 mtools curl
sudo update-alternatives /usr/bin/clang clang /usr/bin/clang-6.0 1000
sudo update-alternatives /usr/bin/clang++ clang++ /usr/bin/clang++-6.0 1000
curl -L -o pb https://github.com/justinian/bonnibel/releases/download/2.0.0/pb_linux_amd64 && chmod a+x pb
After cloning, run `waf configure`. Then you can run `waf build` to build the
project, and `waf test` to run the tests. A floppy disk image will be built in
`build/popcorn.img`. If you have `qemu-system-x86_64` installed, then you can
run `waf qemu` to run it in `-nographic` mode.

View File

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

Binary file not shown.

28
assets/initrd.toml Normal file
View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

3669
external/cpptoml/cpptoml.h vendored Normal file

File diff suppressed because it is too large Load Diff

147
modules.yaml Normal file
View File

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

132
other_software.md Normal file
View File

@@ -0,0 +1,132 @@
# Included works
jsix includes and/or is derived from a number of other works, listed here.
## Catch2
jsix uses [Catch2][] for testing. Catch2 is released under the terms of the
Boost Software license:
[Catch2]: https://github.com/catchorg/Catch2
> Boost Software License - Version 1.0 - August 17th, 2003
>
> Permission is hereby granted, free of charge, to any person or organization
> obtaining a copy of the software and accompanying documentation covered by
> this license (the "Software") to use, reproduce, display, distribute,
> execute, and transmit the Software, and to prepare derivative works of the
> Software, and to permit third-parties to whom the Software is furnished to
> do so, all subject to the following:
>
> The copyright notices in the Software and this entire statement, including
> the above license grant, this restriction and the following disclaimer,
> must be included in all copies of the Software, in whole or in part, and
> all derivative works of the Software, unless such copies or derivative
> works are solely in the form of machine-executable object code generated by
> a source language processor.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
> SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> DEALINGS IN THE SOFTWARE.
## cpptoml
jsix uses the [cpptoml][] library for parsing TOML configuration files. cpptoml
is released under the terms of the MIT license:
[cpptoml]: https://github.com/skystrife/cpptoml
> Copyright (c) 2014 Chase Geigle
>
> Permission is hereby granted, free of charge, to any person obtaining a copy of
> this software and associated documentation files (the "Software"), to deal in
> the Software without restriction, including without limitation the rights to
> use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
> the Software, and to permit persons to whom the Software is furnished to do so,
> subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
> FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
> IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
> CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## printf
jsix uses Marco Paland's tiny [printf][] library for its `*printf()` functions,
which is also released under the terms of the MIT license:
[printf]: https://github.com/mpaland/printf
> The MIT License (MIT)
>
> Copyright (c) 2014 Marco Paland
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all
> copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
## GNU-EFI
jsix's UEFI bootloader initially used [GNU-EFI][], and still uses one file (the
linker script for the bootloader) from that project. GNU-EFI claims to be
released under the BSD license. Again, I could not find its specific license
file, so I am reproducing a generic 3-clause BSD license (the most restrictive,
so as not to assume any extra rights that may not actually be granted) for it
here:
[GNU-EFI]: https://gnu-efi.sourceforge.net
> Copyright (c) Nigel Croxon
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions are met:
>
> 1. Redistributions of source code must retain the above copyright notice, this
> list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above copyright notice,
> this list of conditions and the following disclaimer in the documentation
> and/or other materials provided with the distribution.
>
> 3. Neither the name of the copyright holder nor the names of its contributors
> may be used to endorse or promote products derived from this software
> without specific prior written permission.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
> ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
> WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## Intel EFI Application Toolkit
jsix's UEFI loader uses code from Intel's EFI Application toolkit. Relevant
code includes license statements at the top of each file.

65
qemu.sh Executable file
View File

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

131
scripts/build_sysroot.sh Executable file
View File

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

10
scripts/parse_syms.py Executable file
View File

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

View File

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

View File

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

View File

@@ -0,0 +1,27 @@
{% extends "exe.default.j2" %}
{% block variables %}
{{ super() }}
ccflags = $ccflags $
-DKERNEL_FILENAME=L\"jsix.elf\" $
-DGNU_EFI_USE_MS_ABI $
-DHAVE_USE_MS_ABI $
-DEFI_DEBUG=0 $
-DEFI_DEBUG_CLEAR_MEMORY=0 $
-DBOOTLOADER_DEBUG
ldflags = $ldflags $
-T ${srcroot}/src/arch/x86_64/boot.ld $
-shared
{% endblock %}
{% block extra %}
build $builddir/boot.efi : makeefi ${builddir}/{{ module.output }}
name = boot.efi
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
{% extends "exe.default.j2" %}
{% block variables %}
{{ super() }}
ccflags = $ccflags -fPIC -mcmodel=large
ldflags = $ldflags -shared -znotext -T ${srcroot}/src/arch/x86_64/vdso.ld
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
{% extends "target.default.j2" %}
{% block binaries %}
cc = clang
cxx = clang++
ld = ld
ar = ar
nasm = nasm
objcopy = objcopy
{% endblock %}
{% block variables %}
ccflags = $ccflags $
-ggdb $
-nostdlib $
-ffreestanding $
-nodefaultlibs $
-fno-builtin $
-mno-sse $
-fno-omit-frame-pointer $
-mno-red-zone $
-fshort-wchar $
-D__ELF__ $
-fPIC
cxxflags = $cxxflags $
-fno-exceptions $
-fno-rtti $
ldflags = $ldflags $
-g $
-nostdlib $
-Bsymbolic $
-nostartfiles
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,28 +10,38 @@ SECTIONS
__header_end = .;
}
.text : {
.text ALIGN(4096) : {
*(.text)
*(.isrs)
}
.data : {
.data ALIGN(4096) : {
*(.data)
*(.rodata)
}
.isrs : {
*(.isrs)
.bss ALIGN(4096) : {
__bss_start = .;
*(.bss)
__bss_end = .;
}
.note : {
*(.note.*)
}
.bss ALIGN(0x1000) : {
__bss_start = .;
*(.bss)
__bss_end = .;
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
__eh_frame_end = .;
}
.eh_frame_hdr : {
KEEP(*(.eh_frame_hdr))
}
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
kernel_end = ALIGN(4096);
}

73
src/arch/x86_64/vdso.ld Normal file
View File

@@ -0,0 +1,73 @@
PHDRS {
headers PT_PHDR FLAGS(4) FILEHDR PHDRS ;
rodata PT_LOAD FLAGS(4) ;
text PT_LOAD FLAGS(5) ;
dynamic PT_DYNAMIC FLAGS(4) ;
note PT_NOTE ;
eh_frame PT_GNU_EH_FRAME ;
}
SECTIONS {
. = SIZEOF_HEADERS;
/DISCARD/ : {
*(.got*)
*(.plt*)
*(.note.*)
*(.hash*)
*(.debug*)
}
.illegal.relocations : {
*(.rel*)
}
.illegal.writeable : {
*(.data*)
*(.bss*)
}
.rodata : {
*(.rodata*)
} :rodata
.dynamic : {
*(.dynamic)
} :dynamic
.dynsym : {
*(.dynsym*)
} :rodata :dynamic
.dynstr : {
*(.dynstr*)
} :rodata :dynamic
.gnu.hash : {
*(.gnu.hash*)
} :rodata
.eh_frame_hdr : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
__eh_frame_end = .;
KEEP(*(.eh_frame_hdr))
} :eh_frame
.shstrtab : {
*(.shstrtab)
}
.text ALIGN(0x1000) : {
*(.text*)
*(.init*)
*(.fini*)
. = ALIGN(0x1000);
} :text
ASSERT(SIZEOF(.illegal.relocations) == 0,
"Code has introduced relocations into the VDSO")
ASSERT(SIZEOF(.illegal.writeable) == 0,
"Code has introduced writeable data into the VDSO")
}

View File

@@ -1,4 +1,3 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
@@ -9,16 +8,55 @@
size_t ROWS = 0;
size_t COLS = 0;
static EFI_SIMPLE_TEXT_OUT_PROTOCOL *con_out = 0;
static EFI_SIMPLE_TEXT_OUT_PROTOCOL *m_out = 0;
const CHAR16 digits[] = {u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
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'};
console::console(EFI_SYSTEM_TABLE *system_table) :
m_rows(0),
m_cols(0),
m_out(nullptr)
{
s_console = this;
m_boot = system_table->BootServices;
m_out = system_table->ConOut;
}
EFI_STATUS
con_pick_mode(EFI_BOOT_SERVICES *bootsvc)
console::initialize(const wchar_t *version)
{
EFI_STATUS status;
// Might not find a video device at all, so ignore not found errors
status = pick_mode();
if (status != EFI_NOT_FOUND)
CHECK_EFI_STATUS_OR_FAIL(status);
status = m_out->QueryMode(m_out, m_out->Mode->Mode, &m_cols, &m_rows);
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
status = m_out->ClearScreen(m_out);
CHECK_EFI_STATUS_OR_RETURN(status, "ClearScreen");
m_out->SetAttribute(m_out, EFI_LIGHTCYAN);
m_out->OutputString(m_out, (wchar_t *)L"jsix loader ");
m_out->SetAttribute(m_out, EFI_LIGHTMAGENTA);
m_out->OutputString(m_out, (wchar_t *)version);
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)L" booting...\r\n\n");
return status;
}
EFI_STATUS
console::pick_mode()
{
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *gfx_out_proto;
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gfx_out_proto);
status = m_boot->LocateProtocol(&guid_gfx_out, NULL, (void **)&gfx_out_proto);
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
const uint32_t modes = gfx_out_proto->Mode->MaxMode;
@@ -53,106 +91,72 @@ con_pick_mode(EFI_BOOT_SERVICES *bootsvc)
return EFI_SUCCESS;
}
EFI_STATUS
con_initialize(EFI_SYSTEM_TABLE *system_table, const CHAR16 *version)
{
EFI_STATUS status;
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
con_out = system_table->ConOut;
// Might not find a video device at all, so ignore not found errors
status = con_pick_mode(bootsvc);
if (status != EFI_NOT_FOUND)
CHECK_EFI_STATUS_OR_RETURN(status, "con_pick_mode");
status = con_out->QueryMode(con_out, con_out->Mode->Mode, &COLS, &ROWS);
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
status = con_out->ClearScreen(con_out);
CHECK_EFI_STATUS_OR_RETURN(status, "ClearScreen");
con_out->SetAttribute(con_out, EFI_LIGHTCYAN);
con_out->OutputString(con_out, (CHAR16 *)L"Popcorn loader ");
con_out->SetAttribute(con_out, EFI_LIGHTMAGENTA);
con_out->OutputString(con_out, (CHAR16 *)version);
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L" booting...\r\n\n");
return status;
}
size_t
con_print_hex(uint32_t n)
console::print_hex(uint32_t n) const
{
CHAR16 buffer[9];
CHAR16 *p = buffer;
wchar_t buffer[9];
wchar_t *p = buffer;
for (int i = 7; i >= 0; --i) {
uint8_t nibble = (n & (0xf << (i*4))) >> (i*4);
*p++ = digits[nibble];
}
*p = 0;
con_out->OutputString(con_out, buffer);
m_out->OutputString(m_out, buffer);
return 8;
}
size_t
con_print_long_hex(uint64_t n)
console::print_long_hex(uint64_t n) const
{
CHAR16 buffer[17];
CHAR16 *p = buffer;
wchar_t buffer[17];
wchar_t *p = buffer;
for (int i = 15; i >= 0; --i) {
uint8_t nibble = (n & (0xf << (i*4))) >> (i*4);
*p++ = digits[nibble];
}
*p = 0;
con_out->OutputString(con_out, buffer);
m_out->OutputString(m_out, buffer);
return 16;
}
size_t
con_print_dec(uint32_t n)
console::print_dec(uint32_t n) const
{
CHAR16 buffer[11];
CHAR16 *p = buffer + 10;
wchar_t buffer[11];
wchar_t *p = buffer + 10;
*p-- = 0;
do {
*p-- = digits[n % 10];
n /= 10;
} while (n != 0);
con_out->OutputString(con_out, ++p);
m_out->OutputString(m_out, ++p);
return 10 - (p - buffer);
}
size_t
con_print_long_dec(uint64_t n)
console::print_long_dec(uint64_t n) const
{
CHAR16 buffer[21];
CHAR16 *p = buffer + 20;
wchar_t buffer[21];
wchar_t *p = buffer + 20;
*p-- = 0;
do {
*p-- = digits[n % 10];
n /= 10;
} while (n != 0);
con_out->OutputString(con_out, ++p);
m_out->OutputString(m_out, ++p);
return 20 - (p - buffer);
}
size_t
con_printf(const CHAR16 *fmt, ...)
console::vprintf(const wchar_t *fmt, va_list args) const
{
CHAR16 buffer[256];
const CHAR16 *r = fmt;
CHAR16 *w = buffer;
va_list args;
wchar_t buffer[256];
const wchar_t *r = fmt;
wchar_t *w = buffer;
size_t count = 0;
va_start(args, fmt);
while (r && *r) {
if (*r != L'%') {
count++;
@@ -161,43 +165,43 @@ con_printf(const CHAR16 *fmt, ...)
}
*w = 0;
con_out->OutputString(con_out, buffer);
m_out->OutputString(m_out, buffer);
w = buffer;
r++; // chomp the %
switch (*r++) {
case L'%':
con_out->OutputString(con_out, L"%");
m_out->OutputString(m_out, const_cast<wchar_t*>(L"%"));
count++;
break;
case L'x':
count += con_print_hex(va_arg(args, uint32_t));
count += print_hex(va_arg(args, uint32_t));
break;
case L'd':
case L'u':
count += con_print_dec(va_arg(args, uint32_t));
count += print_dec(va_arg(args, uint32_t));
break;
case L's':
{
CHAR16 *s = va_arg(args, CHAR16*);
wchar_t *s = va_arg(args, wchar_t*);
count += wstrlen(s);
con_out->OutputString(con_out, s);
m_out->OutputString(m_out, s);
}
break;
case L'l':
switch (*r++) {
case L'x':
count += con_print_long_hex(va_arg(args, uint64_t));
count += print_long_hex(va_arg(args, uint64_t));
break;
case L'd':
case L'u':
count += con_print_long_dec(va_arg(args, uint64_t));
count += print_long_dec(va_arg(args, uint64_t));
break;
default:
@@ -211,44 +215,66 @@ con_printf(const CHAR16 *fmt, ...)
}
*w = 0;
con_out->OutputString(con_out, buffer);
va_end(args);
m_out->OutputString(m_out, buffer);
return count;
}
void
con_status_begin(const CHAR16 *message)
size_t
console::printf(const wchar_t *fmt, ...) const
{
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)message);
va_list args;
va_start(args, fmt);
size_t result = vprintf(fmt, args);
va_end(args);
return result;
}
size_t
console::print(const wchar_t *fmt, ...)
{
va_list args;
va_start(args, fmt);
size_t result = get().vprintf(fmt, args);
va_end(args);
return result;
}
void
con_status_ok()
console::status_begin(const wchar_t *message) const
{
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"[");
con_out->SetAttribute(con_out, EFI_GREEN);
con_out->OutputString(con_out, (CHAR16 *)L" ok ");
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"]\r\n");
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)message);
}
void
con_status_fail(const CHAR16 *error)
console::status_ok() const
{
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"[");
con_out->SetAttribute(con_out, EFI_LIGHTRED);
con_out->OutputString(con_out, (CHAR16 *)L"failed");
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"]\r\n");
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)L"[");
m_out->SetAttribute(m_out, EFI_GREEN);
m_out->OutputString(m_out, (wchar_t *)L" ok ");
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)L"]\r\n");
}
con_out->SetAttribute(con_out, EFI_RED);
con_out->OutputString(con_out, (CHAR16 *)error);
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
con_out->OutputString(con_out, (CHAR16 *)L"\r\n");
void
console::status_fail(const wchar_t *error) const
{
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)L"[");
m_out->SetAttribute(m_out, EFI_LIGHTRED);
m_out->OutputString(m_out, (wchar_t *)L"failed");
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)L"]\r\n");
m_out->SetAttribute(m_out, EFI_RED);
m_out->OutputString(m_out, (wchar_t *)error);
m_out->SetAttribute(m_out, EFI_LIGHTGRAY);
m_out->OutputString(m_out, (wchar_t *)L"\r\n");
}
EFI_STATUS

View File

@@ -1,12 +1,38 @@
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <efi/efi.h>
EFI_STATUS con_initialize(EFI_SYSTEM_TABLE *system_table, const CHAR16 *version);
void con_status_begin(const CHAR16 *message);
void con_status_ok();
void con_status_fail(const CHAR16 *error);
size_t con_printf(const CHAR16 *fmt, ...);
class console
{
public:
console(EFI_SYSTEM_TABLE *system_table);
EFI_STATUS initialize(const wchar_t *version);
void status_begin(const wchar_t *message) const;
void status_fail(const wchar_t *error) const;
void status_ok() const;
size_t print_hex(uint32_t n) const;
size_t print_dec(uint32_t n) const;
size_t print_long_hex(uint64_t n) const;
size_t print_long_dec(uint64_t n) const;
size_t printf(const wchar_t *fmt, ...) const;
static const console & get() { return *s_console; }
static size_t print(const wchar_t *fmt, ...);
private:
EFI_STATUS pick_mode();
size_t vprintf(const wchar_t *fmt, va_list args) const;
size_t m_rows, m_cols;
EFI_BOOT_SERVICES *m_boot;
EFI_SIMPLE_TEXT_OUT_PROTOCOL *m_out;
static console *s_console;
};
EFI_STATUS
con_get_framebuffer(

View File

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

View File

@@ -6,8 +6,59 @@
#define PAGE_SIZE 0x1000
static CHAR16 kernel_name[] = KERNEL_FILENAME;
static CHAR16 font_name[] = KERNEL_FONT;
static wchar_t kernel_name[] = KERNEL_FILENAME;
static wchar_t initrd_name[] = INITRD_FILENAME;
EFI_STATUS
loader_alloc_aligned(
EFI_BOOT_SERVICES *bootsvc,
EFI_MEMORY_TYPE mem_type,
size_t *length,
void **pages)
{
EFI_STATUS status;
EFI_PHYSICAL_ADDRESS addr;
size_t alignment = PAGE_SIZE;
while (alignment < *length)
alignment *= 2;
size_t page_count = alignment / PAGE_SIZE;
*length = alignment;
con_debug(L"Trying to find %d aligned pages for %x", page_count, mem_type);
status = bootsvc->AllocatePages(AllocateAnyPages, mem_type, page_count * 2, &addr);
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating %d pages for alignment", page_count * 2);
con_debug(L" Found %d pages at %lx", page_count * 2, addr);
EFI_PHYSICAL_ADDRESS aligned = addr;
aligned = ((aligned - 1) & ~(alignment - 1)) + alignment;
con_debug(L" Aligning %lx to %lx", addr, aligned);
size_t before =
(reinterpret_cast<uint64_t>(aligned) -
reinterpret_cast<uint64_t>(addr)) /
PAGE_SIZE;
if (before) {
con_debug(L" Freeing %d initial pages", before);
bootsvc->FreePages(addr, before);
}
size_t after = page_count - before;
if (after) {
EFI_PHYSICAL_ADDRESS end =
reinterpret_cast<EFI_PHYSICAL_ADDRESS>(
reinterpret_cast<uint64_t>(aligned) +
page_count * PAGE_SIZE);
con_debug(L" Freeing %d remaining pages", after);
bootsvc->FreePages(end, after);
}
*pages = (void *)aligned;
return EFI_SUCCESS;
}
EFI_STATUS
loader_alloc_pages(
@@ -21,12 +72,10 @@ loader_alloc_pages(
size_t page_count = ((*length - 1) / PAGE_SIZE) + 1;
EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)*pages;
con_debug(L"Trying to find %d non-aligned pages for %x at %lx",
page_count, mem_type, addr);
status = bootsvc->AllocatePages(AllocateAddress, mem_type, page_count, &addr);
if (status == EFI_NOT_FOUND || status == EFI_OUT_OF_RESOURCES) {
// couldn't get the address we wanted, try loading the kernel anywhere
status =
bootsvc->AllocatePages(AllocateAnyPages, mem_type, page_count, &addr);
}
CHECK_EFI_STATUS_OR_RETURN(status,
L"Allocating %d kernel pages type %x",
page_count, mem_type);
@@ -38,7 +87,7 @@ loader_alloc_pages(
}
EFI_STATUS
loader_load_font(
loader_load_initrd(
EFI_BOOT_SERVICES *bootsvc,
EFI_FILE_PROTOCOL *root,
struct loader_data *data)
@@ -46,13 +95,13 @@ loader_load_font(
EFI_STATUS status;
EFI_FILE_PROTOCOL *file = NULL;
status = root->Open(root, &file, (CHAR16 *)font_name, EFI_FILE_MODE_READ,
status = root->Open(root, &file, (wchar_t *)initrd_name, EFI_FILE_MODE_READ,
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
if (status == EFI_NOT_FOUND)
return status;
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", font_name);
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", initrd_name);
char info[sizeof(EFI_FILE_INFO) + 100];
size_t info_length = sizeof(info);
@@ -60,16 +109,16 @@ loader_load_font(
status = file->GetInfo(file, &guid_file_info, &info_length, info);
CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info");
data->font_length = ((EFI_FILE_INFO *)info)->FileSize;
data->initrd_length = ((EFI_FILE_INFO *)info)->FileSize;
status = loader_alloc_pages(
status = loader_alloc_aligned(
bootsvc,
KERNEL_FONT_MEMTYPE,
&data->font_length,
&data->font);
memtype_initrd,
&data->initrd_length,
&data->initrd);
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
status = file->Read(file, &data->font_length, data->font);
status = file->Read(file, &data->initrd_length, data->initrd);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
status = file->Close(file);
@@ -87,8 +136,10 @@ loader_load_elf(
{
EFI_STATUS status;
con_debug(L"Opening kernel file %s", (wchar_t *)kernel_name);
EFI_FILE_PROTOCOL *file = NULL;
status = root->Open(root, &file, (CHAR16 *)kernel_name, EFI_FILE_MODE_READ,
status = root->Open(root, &file, (wchar_t *)kernel_name, EFI_FILE_MODE_READ,
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
if (status == EFI_NOT_FOUND)
@@ -107,6 +158,8 @@ loader_load_elf(
status = file->Read(file, &length, &header);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF header");
con_debug(L"Read %u bytes of ELF header", length);
if (length < sizeof(struct elf_header))
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"Incomplete read of ELF header");
@@ -131,10 +184,13 @@ loader_load_elf(
header.machine != 0x3e)
CHECK_EFI_STATUS_OR_RETURN(EFI_LOAD_ERROR, L"ELF load error: wrong machine architecture");
con_debug(L"ELF is valid, entrypoint %lx", header.entrypoint);
data->kernel_entry = (void *)header.entrypoint;
struct elf_program_header prog_header;
for (int i = 0; i < header.ph_num; ++i) {
status = file->SetPosition(file, header.ph_offset + i * header.ph_entsize);
CHECK_EFI_STATUS_OR_RETURN(status, L"Setting ELF file position");
@@ -146,13 +202,14 @@ loader_load_elf(
length = prog_header.mem_size;
void *addr = (void *)(prog_header.vaddr - KERNEL_VIRT_ADDRESS);
status = loader_alloc_pages(bootsvc, KERNEL_MEMTYPE, &length, &addr);
status = loader_alloc_pages(bootsvc, memtype_kernel, &length, &addr);
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating kernel pages");
if (data->kernel == 0)
data->kernel = addr;
data->kernel_length = (uint64_t)addr + length - (uint64_t)data->kernel;
}
con_debug(L"Read %d ELF program headers", header.ph_num);
struct elf_section_header sec_header;
for (int i = 0; i < header.sh_num; ++i) {
@@ -163,7 +220,9 @@ loader_load_elf(
status = file->Read(file, &length, &sec_header);
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading ELF section header");
if ((sec_header.flags & ELF_SHF_ALLOC) == 0) continue;
if ((sec_header.flags & ELF_SHF_ALLOC) == 0) {
continue;
}
void *addr = (void *)(sec_header.addr - KERNEL_VIRT_ADDRESS);
@@ -178,6 +237,7 @@ loader_load_elf(
bootsvc->SetMem(addr, sec_header.size, 0);
}
}
con_debug(L"Read %d ELF section headers", header.ph_num);
status = file->Close(file);
CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle");
@@ -217,19 +277,19 @@ loader_load_kernel(
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_elf: %s", kernel_name);
data->font = (void *)((uint64_t)data->kernel + data->kernel_length);
status = loader_load_font(bootsvc, root, data);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name);
data->data = (void *)((uint64_t)data->font + data->font_length);
data->data = (void *)((uint64_t)data->kernel + data->kernel_length);
data->data_length += PAGE_SIZE; // extra page for map growth
status = loader_alloc_pages(
status = loader_alloc_aligned(
bootsvc,
KERNEL_DATA_MEMTYPE,
memtype_data,
&data->data_length,
&data->data);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data");
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_aligned: kernel data");
data->initrd = (void *)((uint64_t)data->data + data->data_length);
status = loader_load_initrd(bootsvc, root, data);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", initrd_name);
return EFI_SUCCESS;
}

View File

@@ -12,28 +12,12 @@
#define KERNEL_VIRT_ADDRESS 0xFFFFFF0000000000
#endif
#ifndef KERNEL_MEMTYPE
#define KERNEL_MEMTYPE 0x80000000
#endif
#ifndef KERNEL_FONT_MEMTYPE
#define KERNEL_FONT_MEMTYPE 0x80000001
#endif
#ifndef KERNEL_DATA_MEMTYPE
#define KERNEL_DATA_MEMTYPE 0x80000002
#endif
#ifndef KERNEL_PT_MEMTYPE
#define KERNEL_PT_MEMTYPE 0x80000004
#endif
#ifndef KERNEL_FILENAME
#define KERNEL_FILENAME L"kernel.elf"
#endif
#ifndef KERNEL_FONT
#define KERNEL_FONT L"screenfont.psf"
#ifndef INITRD_FILENAME
#define INITRD_FILENAME L"initrd.img"
#endif
struct loader_data {
@@ -41,8 +25,8 @@ struct loader_data {
void *kernel_entry;
size_t kernel_length;
void *font;
size_t font_length;
void *initrd;
size_t initrd_length;
void *data;
size_t data_length;

View File

@@ -5,11 +5,15 @@
#include "console.h"
#include "guids.h"
#include "kernel_data.h"
#include "kernel_args.h"
#include "loader.h"
#include "memory.h"
#include "utility.h"
#ifndef SCRATCH_PAGES
#define SCRATCH_PAGES 64
#endif
#ifndef GIT_VERSION_WIDE
#define GIT_VERSION_WIDE L"no version"
#endif
@@ -30,20 +34,68 @@ struct kernel_header {
};
#pragma pack(pop)
using kernel_entry = void (*)(kernel_args *);
static void
type_to_wchar(wchar_t *into, uint32_t type)
{
for (int j=0; j<4; ++j)
into[j] = static_cast<wchar_t>(reinterpret_cast<char *>(&type)[j]);
}
EFI_STATUS
detect_debug_mode(EFI_RUNTIME_SERVICES *run, kernel_args *header) {
wchar_t var_name[] = L"debug";
EFI_STATUS status;
uint8_t debug = 0;
UINTN var_size = sizeof(debug);
#ifdef __JSIX_SET_DEBUG_UEFI_VAR__
debug = __JSIX_SET_DEBUG_UEFI_VAR__;
uint32_t attrs =
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS;
status = run->SetVariable(
var_name,
&guid_jsix_vendor,
attrs,
var_size,
&debug);
CHECK_EFI_STATUS_OR_RETURN(status, "detect_debug_mode::SetVariable");
#endif
status = run->GetVariable(
var_name,
&guid_jsix_vendor,
nullptr,
&var_size,
&debug);
CHECK_EFI_STATUS_OR_RETURN(status, "detect_debug_mode::GetVariable");
if (debug)
header->flags |= JSIX_FLAG_DEBUG;
return EFI_SUCCESS;
}
extern "C" EFI_STATUS
efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
{
EFI_STATUS status;
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
EFI_RUNTIME_SERVICES *runsvc = system_table->RuntimeServices;
console con(system_table);
// When checking console initialization, use CHECK_EFI_STATUS_OR_RETURN
// because we can't be sure if the console was fully set up
status = con_initialize(system_table, GIT_VERSION_WIDE);
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
status = con.initialize(GIT_VERSION_WIDE);
CHECK_EFI_STATUS_OR_RETURN(status, "console::initialize");
// From here on out, we can use CHECK_EFI_STATUS_OR_FAIL instead
memory_init_pointer_fixup(bootsvc, runsvc);
memory_init_pointer_fixup(bootsvc, runsvc, SCRATCH_PAGES);
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
//
@@ -65,55 +117,56 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
status = memory_get_map_length(bootsvc, &data_length);
CHECK_EFI_STATUS_OR_FAIL(status);
size_t header_size = sizeof(struct popcorn_data);
const size_t header_align = alignof(struct popcorn_data);
size_t header_size = sizeof(kernel_args);
const size_t header_align = alignof(kernel_args);
if (header_size % header_align)
header_size += header_align - (header_size % header_align);
data_length += header_size;
// Load the kernel image from disk and check it
//
con_printf(L"Loading kernel into memory...\r\n");
console::print(L"Loading kernel into memory...\r\n");
struct loader_data load;
load.data_length = data_length;
status = loader_load_kernel(bootsvc, &load);
CHECK_EFI_STATUS_OR_FAIL(status);
con_printf(L" image bytes at 0x%x : %x\r\n", load.kernel, load.kernel_length);
con_printf(L" font bytes at 0x%x : %x\r\n", load.font, load.font_length);
con_printf(L" data bytes at 0x%x : %x\r\n", load.data, load.data_length);
console::print(L" %x image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
console::print(L" %x data bytes at 0x%x\r\n", load.data_length, load.data);
console::print(L" %x initrd bytes at 0x%x\r\n", load.initrd_length, load.initrd);
struct kernel_header *version = (struct kernel_header *)load.kernel;
if (version->magic != KERNEL_HEADER_MAGIC) {
con_printf(L" bad magic %x\r\n", version->magic);
console::print(L" bad magic %x\r\n", version->magic);
CHECK_EFI_STATUS_OR_FAIL(EFI_CRC_ERROR);
}
con_printf(L" Kernel version %d.%d.%d %x%s\r\n",
console::print(L" Kernel version %d.%d.%d %x%s\r\n",
version->major, version->minor, version->patch, version->gitsha & 0x0fffffff,
version->gitsha & 0xf0000000 ? "*" : "");
con_printf(L" Entrypoint 0x%x\r\n", load.kernel_entry);
version->gitsha & 0xf0000000 ? L"*" : L"");
console::print(L" Entrypoint 0x%x\r\n", load.kernel_entry);
void (*kernel_main)() = load.kernel_entry;
kernel_entry kernel_main =
reinterpret_cast<kernel_entry>(load.kernel_entry);
memory_mark_pointer_fixup((void **)&kernel_main);
// Set up the kernel data pages to pass to the kernel
//
struct popcorn_data *data_header = (struct popcorn_data *)load.data;
struct kernel_args *data_header = (struct kernel_args *)load.data;
memory_mark_pointer_fixup((void **)&data_header);
data_header->magic = DATA_HEADER_MAGIC;
data_header->version = DATA_HEADER_VERSION;
data_header->length = sizeof(struct popcorn_data);
data_header->length = sizeof(struct kernel_args);
data_header->scratch_pages = SCRATCH_PAGES;
data_header->flags = 0;
data_header->font = load.font;
data_header->font_length = load.font_length;
memory_mark_pointer_fixup((void **)&data_header->font);
data_header->initrd = load.initrd;
data_header->initrd_length = load.initrd_length;
memory_mark_pointer_fixup((void **)&data_header->initrd);
data_header->data = load.data;
data_header->data_length = load.data_length;
@@ -148,8 +201,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
// Save the memory map and tell the firmware we're taking control.
//
struct memory_map map;
map.entries = data_header->memory_map;
map.length = (load.data_length - header_size);
map.entries =
reinterpret_cast<EFI_MEMORY_DESCRIPTOR *>(data_header->memory_map);
status = memory_get_map(bootsvc, &map);
CHECK_EFI_STATUS_OR_FAIL(status);
@@ -157,6 +211,8 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
data_header->memory_map_length = map.length;
data_header->memory_map_desc_size = map.size;
detect_debug_mode(runsvc, data_header);
// bootsvc->Stall(5000000);
status = bootsvc->ExitBootServices(image_handle, map.key);

View File

@@ -4,13 +4,18 @@
#include "memory.h"
#include "utility.h"
const EFI_MEMORY_TYPE memtype_kernel = static_cast<EFI_MEMORY_TYPE>(0x80000000);
const EFI_MEMORY_TYPE memtype_data = static_cast<EFI_MEMORY_TYPE>(0x80000001);
const EFI_MEMORY_TYPE memtype_initrd = static_cast<EFI_MEMORY_TYPE>(0x80000002);
const EFI_MEMORY_TYPE memtype_scratch = static_cast<EFI_MEMORY_TYPE>(0x80000003);
#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b))
size_t fixup_pointer_index = 0;
void **fixup_pointers[64];
uint64_t *new_pml4 = 0;
const CHAR16 *memory_type_names[] = {
const wchar_t *memory_type_names[] = {
L"EfiReservedMemoryType",
L"EfiLoaderCode",
L"EfiLoaderData",
@@ -28,13 +33,17 @@ const CHAR16 *memory_type_names[] = {
L"EfiPersistentMemory",
};
static const CHAR16 *
static const wchar_t *
memory_type_name(UINT32 value)
{
if (value >= (sizeof(memory_type_names) / sizeof(CHAR16 *))) {
if (value == KERNEL_DATA_MEMTYPE) return L"Kernel Data";
else if (value == KERNEL_MEMTYPE) return L"Kernel Image";
else return L"Bad Type Value";
if (value >= (sizeof(memory_type_names) / sizeof(wchar_t *))) {
switch (value) {
case memtype_kernel: return L"Kernel Data";
case memtype_data: return L"Kernel Data";
case memtype_initrd: return L"Initial Ramdisk";
case memtype_scratch: return L"Kernel Scratch Space";
default: return L"Bad Type Value";
}
}
return memory_type_names[value];
}
@@ -50,7 +59,7 @@ memory_update_marked_addresses(EFI_EVENT UNUSED *event, void *context)
}
EFI_STATUS
memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc)
memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc, unsigned scratch_pages)
{
EFI_STATUS status;
EFI_EVENT event;
@@ -67,7 +76,7 @@ memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runs
// Reserve a page for our replacement PML4, plus some pages for the kernel to use
// as page tables while it gets started.
EFI_PHYSICAL_ADDRESS addr = 0;
status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 16, &addr);
status = bootsvc->AllocatePages(AllocateAnyPages, memtype_scratch, scratch_pages, &addr);
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate page table pages.");
new_pml4 = (uint64_t *)addr;
@@ -139,19 +148,19 @@ memory_dump_map(struct memory_map *map)
const size_t count = map->length / map->size;
con_printf(L"Memory map:\n");
con_printf(L"\t Descriptor Count: %d (%d bytes)\n", count, map->length);
con_printf(L"\t Descriptor Size: %d bytes\n", map->size);
con_printf(L"\t Type offset: %d\n\n", offsetof(EFI_MEMORY_DESCRIPTOR, Type));
console::print(L"Memory map:\n");
console::print(L"\t Descriptor Count: %d (%d bytes)\n", count, map->length);
console::print(L"\t Descriptor Size: %d bytes\n", map->size);
console::print(L"\t Type offset: %d\n\n", offsetof(EFI_MEMORY_DESCRIPTOR, Type));
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
EFI_MEMORY_DESCRIPTOR *d = map->entries;
while (d < end) {
int runtime = (d->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME;
con_printf(L"%s%s ", memory_type_name(d->Type), runtime ? L"*" : L" ");
con_printf(L"%lx ", d->PhysicalStart);
con_printf(L"%lx ", d->VirtualStart);
con_printf(L"[%4d]\n", d->NumberOfPages);
console::print(L"%s%s ", memory_type_name(d->Type), runtime ? L"*" : L" ");
console::print(L"%lx ", d->PhysicalStart);
console::print(L"%lx ", d->VirtualStart);
console::print(L"[%4d]\n", d->NumberOfPages);
d = INCREMENT_DESC(d, map->size);
}
@@ -187,9 +196,10 @@ memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
EFI_MEMORY_DESCRIPTOR *d = map->entries;
while (d < end) {
switch (d->Type) {
case KERNEL_MEMTYPE:
case KERNEL_FONT_MEMTYPE:
case KERNEL_DATA_MEMTYPE:
case memtype_kernel:
case memtype_data:
case memtype_initrd:
case memtype_scratch:
d->Attribute |= EFI_MEMORY_RUNTIME;
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;

View File

@@ -1,6 +1,11 @@
#pragma once
#include <efi/efi.h>
extern const EFI_MEMORY_TYPE memtype_kernel;
extern const EFI_MEMORY_TYPE memtype_data;
extern const EFI_MEMORY_TYPE memtype_initrd;
extern const EFI_MEMORY_TYPE memtype_scratch;
struct memory_map {
size_t length;
size_t size;
@@ -9,7 +14,10 @@ struct memory_map {
EFI_MEMORY_DESCRIPTOR *entries;
};
EFI_STATUS memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc);
EFI_STATUS memory_init_pointer_fixup(
EFI_BOOT_SERVICES *bootsvc,
EFI_RUNTIME_SERVICES *runsvc,
unsigned scratch_pages);
void memory_mark_pointer_fixup(void **p);
EFI_STATUS memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);

View File

@@ -38,6 +38,7 @@
#include <efi/efi.h>
#include <elf.h>
extern "C"
EFI_STATUS _relocate (long ldbase, Elf64_Dyn *dyn,
EFI_HANDLE image EFI_UNUSED,
EFI_SYSTEM_TABLE *systab EFI_UNUSED)

View File

@@ -2,7 +2,7 @@
struct error_code_desc {
EFI_STATUS code;
CHAR16 *name;
const wchar_t *name;
};
// Based off the gnu-efi table
@@ -47,7 +47,7 @@ struct error_code_desc error_table[] = {
{ 0, NULL }
};
const CHAR16 *
const wchar_t *
util_error_message(EFI_STATUS status)
{
int32_t i = -1;
@@ -62,7 +62,7 @@ util_error_message(EFI_STATUS status)
}
size_t
wstrlen(const CHAR16 *s)
wstrlen(const wchar_t *s)
{
size_t count = 0;
while (s && *s++) count++;

View File

@@ -5,18 +5,18 @@
#define UNUSED __attribute__((unused))
size_t wstrlen(const CHAR16 *s);
const CHAR16 *util_error_message(EFI_STATUS status);
size_t wstrlen(const wchar_t *s);
const wchar_t *util_error_message(EFI_STATUS status);
#define CHECK_EFI_STATUS_OR_RETURN(s, msg, ...) \
if (EFI_ERROR((s))) { \
con_printf(L"ERROR: " msg L": %s\r\n", ##__VA_ARGS__, util_error_message(s)); \
console::print(L"ERROR: " msg L": %s\r\n", ##__VA_ARGS__, util_error_message(s)); \
return (s); \
}
#define CHECK_EFI_STATUS_OR_FAIL(s) \
if (EFI_ERROR((s))) { \
con_status_fail(util_error_message(s)); \
console::get().status_fail(util_error_message(s)); \
while (1) __asm__("hlt"); \
}
@@ -32,3 +32,9 @@ const CHAR16 *util_error_message(EFI_STATUS status);
: "r"((uint64_t)s), "r"((uint64_t)d), "r"((uint64_t)__LINE__) \
: "rax", "rdx", "r8", "r9", "r10"); \
}
#ifdef BOOTLOADER_DEBUG
#define con_debug(msg, ...) console::print(L"DEBUG: " msg L"\r\n", __VA_ARGS__)
#else
#define con_debug(msg, ...)
#endif

View File

@@ -1,61 +0,0 @@
def configure(ctx):
from os.path import join
lds_path = join(ctx.env.ARCH_D, "boot.ld")
ctx.env.append_value('DEFINES_EFI', [
'KERNEL_FILENAME=L"{}"'.format(ctx.env.KERNEL_FILENAME),
'GNU_EFI_USE_MS_ABI',
'HAVE_USE_MS_ABI',
'EFI_DEBUG=0',
'EFI_DEBUG_CLEAR_MEMORY=0',
])
ctx.env.append_value('CFLAGS_EFI', ['-fPIC', '-fshort-wchar'])
ctx.env.append_value('LINKFLAGS_EFI', [
'-shared',
'-T', lds_path,
])
ctx.env.append_value('SECTIONS_EFI', [
])
def build(bld):
sources = bld.path.ant_glob("**/*.c")
sources += bld.path.ant_glob("**/*.s")
bld.program(
source = sources,
target = "boot.elf",
use = 'EFI',
)
from waflib.Task import Task
class make_efi(Task):
color = 'YELLOW'
def keyword(self):
return "Creating"
def __str__(self):
node = self.outputs[0]
return node.path_from(node.ctx.launch_node())
def run(self):
from subprocess import check_call as call
args = self.env.objcopy
sections = [".text", ".sdata", ".data", ".dynamic",
".dynsym", ".rel", ".rela", ".reloc"]
for s in sections: args.extend(['-j', s])
args.append('--target=efi-app-' + self.env.POPCORN_ARCH)
args.append(self.inputs[0].abspath())
args.append(self.outputs[0].abspath())
call(args)
src = bld.path
out = bld.path.get_bld()
efi = make_efi(env=bld.env)
efi.set_inputs([out.make_node("boot.elf")])
efi.set_outputs([out.make_node("boot.efi")])
bld.add_to_group(efi)
# vim: ft=python et sw=4

View File

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

View File

@@ -6,13 +6,15 @@
class pci_device;
namespace ahci {
/// Basic AHCI driver
class ahci_driver
class driver
{
public:
/// Constructor.
ahci_driver();
driver();
/// Register a device with the driver
/// \arg device The PCI device to handle
@@ -26,3 +28,4 @@ private:
kutil::vector<ahci::hba> m_devices;
};
} // namespace

View File

@@ -253,7 +253,7 @@ port::make_command(size_t length, fis_register_h2d **fis)
void *mem = pm->map_offset_pages(page_count(prd_len));
kutil::memset(mem, 0xaf, prd_len);
addr_t phys = pm->offset_phys(mem);
uintptr_t phys = pm->offset_phys(mem);
cmdt.entries[i].data_base_low = phys & 0xffffffff;
cmdt.entries[i].data_base_high = phys >> 32;
cmdt.entries[i].byte_count = prd_len - 1;
@@ -442,9 +442,9 @@ port::finish_read(int slot)
for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
addr_t phys =
static_cast<addr_t>(cmdt.entries[i].data_base_low) |
static_cast<addr_t>(cmdt.entries[i].data_base_high) << 32;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = kutil::offset_pointer(pm->offset_virt(phys), offset);
@@ -485,9 +485,9 @@ port::finish_identify(int slot)
size_t prd_len = (cmdt.entries[0].byte_count & 0x7fffffff) + 1;
addr_t phys =
static_cast<addr_t>(cmdt.entries[0].data_base_low) |
static_cast<addr_t>(cmdt.entries[0].data_base_high) << 32;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[0].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[0].data_base_high) << 32;
log::debug(logs::driver, "Reading ident PRD:");
@@ -535,9 +535,9 @@ port::free_command(int slot)
for (int i = 0; i < ent.prd_table_length; ++i) {
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
addr_t phys =
static_cast<addr_t>(cmdt.entries[i].data_base_low) |
static_cast<addr_t>(cmdt.entries[i].data_base_high) << 32;
uintptr_t phys =
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
void *mem = pm->offset_virt(phys);
pm->unmap_pages(mem, page_count(prd_len));
}
@@ -557,7 +557,7 @@ port::rebase()
size_t pages = 1 + page_count(prd_size * 32);
void *mem = pm->map_offset_pages(pages);
addr_t phys = pm->offset_phys(mem);
uintptr_t phys = pm->offset_phys(mem);
log::debug(logs::driver, "Rebasing address for AHCI port %d to %lx [%d]", m_index, mem, pages);

View File

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

View File

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

View File

@@ -20,9 +20,9 @@ Revision History
--*/
typedef UINT16 CHAR16;
typedef UINT8 CHAR8;
typedef UINT8 BOOLEAN;
typedef wchar_t CHAR16;
typedef char CHAR8;
typedef uint8_t BOOLEAN;
#ifndef CONST
#define CONST const
#endif

View File

@@ -4,7 +4,16 @@
#include <efi/eficompiler.h>
#include <efi/efisetjmp_arch.h>
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if ! __has_builtin(setjmp)
extern UINTN setjmp(jmp_buf *env) __attribute__((returns_twice));
#endif
#if ! __has_builtin(longjmp)
extern VOID longjmp(jmp_buf *env, UINTN value) __attribute__((noreturn));
#endif
#endif /* GNU_EFI_SETJMP_H */

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

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

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

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

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

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

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

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

View File

@@ -7,17 +7,20 @@
#define DATA_HEADER_MAGIC 0x600dda7a
#define DATA_HEADER_VERSION 1
#define JSIX_FLAG_DEBUG 0x00000001
#pragma pack(push, 1)
struct popcorn_data {
struct kernel_args {
uint32_t magic;
uint16_t version;
uint16_t length;
uint32_t _reserved0;
uint16_t _reserved0;
uint16_t scratch_pages;
uint32_t flags;
void *font;
size_t font_length;
void *initrd;
size_t initrd_length;
void *data;
size_t data_length;

View File

@@ -0,0 +1,35 @@
#pragma once
/// \file kernel_memory.h
/// Constants related to the kernel's memory layout
#include <stddef.h>
#include <stdint.h>
namespace memory {
/// Size of a single page frame.
static const size_t frame_size = 0x1000;
/// Start of kernel memory.
static const uintptr_t kernel_offset = 0xffffff0000000000;
/// Offset from physical where page tables are mapped.
static const uintptr_t page_offset = 0xffffff8000000000;
/// Initial process thread's stack address
static const uintptr_t initial_stack = 0x0000800000000000;
/// Initial process thread's stack size, in pages
static const unsigned initial_stack_pages = 1;
/// Max size of the kernel heap
static const size_t kernel_max_heap = 0x800000000; // 32GiB
/// Start of the kernel heap
static const uintptr_t heap_start = page_offset - kernel_max_heap;
/// Helper to determine if a physical address can be accessed
/// through the page_offset area.
inline bool page_mappable(uintptr_t a) { return (a & page_offset) == 0; }
} // namespace memory

11
src/include/log_areas.inc Normal file
View File

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

9
src/include/syscalls.inc Normal file
View File

@@ -0,0 +1,9 @@
SYSCALL(0x00, object_noop, void)
SYSCALL(0x01, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *)
SYSCALL(0x11, process_exit, int64_t)
//SYSCALL(0x12, process_fork, j6_koid_t*)
//SYSCALL(0x13, process_getpid, j6_koid_t*)
SYSCALL(0x14, process_log, const char *)
SYSCALL(0x15, process_pause, void)
SYSCALL(0x16, process_sleep, uint64_t)

View File

@@ -1,18 +0,0 @@
#include "kutil/memory_manager.h"
#include "log.h"
kutil::memory_manager g_kernel_memory_manager;
// kutil malloc/free implementation
namespace kutil {
void *
malloc(size_t n) {
void *p = g_kernel_memory_manager.allocate(n);
log::debug(logs::memory, "Heap allocated %ld bytes: %016lx", n, p);
return p;
}
void free(void *p) { g_kernel_memory_manager.free(p); }
}

View File

@@ -1,6 +1,7 @@
#include "kutil/assert.h"
#include "apic.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "page_manager.h"
@@ -50,7 +51,46 @@ lapic::lapic(uint32_t *base, isr spurious) :
}
void
lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat)
lapic::calibrate_timer()
{
interrupts_disable();
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);
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);
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);
interrupts_enable();
}
uint32_t
lapic::enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat)
{
uint32_t divbits = 0;
@@ -71,17 +111,34 @@ lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat)
if (repeat)
lvte |= 0x20000;
log::debug(logs::apic, "Enabling APIC timer with isr %d.", vector);
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;
}
uint32_t
lapic::enable_timer(isr vector, uint64_t interval, bool repeat)
{
uint64_t ticks = interval * m_ticks_per_us;
int divisor = 1;
while (ticks > -1u) {
ticks /= 2;
divisor *= 2;
}
return enable_timer_internal(vector, divisor, static_cast<uint32_t>(ticks), repeat);
}
uint32_t
lapic::reset_timer(uint32_t count)
{
uint32_t remaining = apic_read(m_base, 0x380);
uint32_t remaining = apic_read(m_base, 0x390);
apic_write(m_base, 0x380, count);
return remaining;
}

View File

@@ -32,10 +32,10 @@ public:
/// Enable interrupts for the LAPIC timer.
/// \arg vector Interrupt vector the timer should use
/// \arg divisor The frequency divisor of the bus Hz (power of 2, <= 128)
/// \arg count The count of ticks before an interrupt
/// \arg interval The timer interval, in microseconds
/// \arg repeat If false, this timer is one-off, otherwise repeating
void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true);
/// \returns The count of ticks the timer is set for
uint32_t enable_timer(isr vector, uint64_t interval, bool repeat = true);
/// Reset the timer countdown.
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
@@ -55,6 +55,14 @@ public:
void enable(); ///< Enable servicing of interrupts
void disable(); ///< Disable (temporarily) servicing of interrupts
/// Calibrate the timer speed against the PIT
void calibrate_timer();
private:
uint32_t enable_timer_internal(isr vector, uint8_t divisor, uint32_t count, bool repeat);
uint32_t m_ticks_per_us;
};

View File

@@ -8,13 +8,14 @@ __kernel_assert(const char *file, unsigned line, const char *message)
if (cons) {
cons->set_color(9 , 0);
cons->puts("\n\n ERROR: ");
cons->puts(message);
cons->puts("\n ");
cons->puts(file);
cons->puts(":");
cons->put_dec(line);
cons->puts(": ");
cons->puts(message);
cons->puts("\n");
}
__asm__ ( "int $0e7h" );
while (1) __asm__ ("hlt");
__asm__ ( "int $0xe4" );
}

View File

@@ -1,4 +1,4 @@
MAGIC equ 0x600db007 ; Popcorn OS header magic number
MAGIC equ 0x600db007 ; jsix OS header magic number
section .header
align 4
@@ -18,11 +18,11 @@ global _start:function (_start.end - _start)
_start:
cli
mov rsp, stack_end
push 0 ; signal end of stack with 0 return address
push 0 ; and a few extra entries in case of stack
push 0 ; problems
push 0
mov rsp, idle_stack_end
mov qword [rsp + 0x00], 0 ; signal end of stack with 0 return address
mov qword [rsp + 0x08], 0 ; and a few extra entries in case of stack
mov qword [rsp + 0x10], 0 ; problems
mov qword [rsp + 0x18], 0
mov rbp, rsp
extern kernel_main
@@ -47,6 +47,9 @@ interrupts_disable:
section .bss
align 0x100
stack_begin:
idle_stack_begin:
resb 0x4000 ; 16KiB stack space
stack_end:
global idle_stack_end
idle_stack_end:
resq 4

View File

@@ -1,9 +1,9 @@
#include "kutil/coord.h"
#include "kutil/guid.h"
#include "kutil/memory.h"
#include "kutil/printf.h"
#include "console.h"
#include "font.h"
#include "memory.h"
#include "screen.h"
#include "serial.h"
@@ -279,100 +279,9 @@ console::putc(char c)
void console::vprintf(const char *fmt, va_list args)
{
static const unsigned buf_size = 256;
char buffer[256];
const char *r = fmt;
char *w = buffer;
char *wend = buffer + buf_size;
#define flush() do { *w = 0; puts(buffer); w = buffer; } while(0)
while (r && *r) {
if (w == wend) flush();
if (*r != '%') {
*w++ = *r++;
continue;
}
r++; // chomp the %
flush();
bool done = false;
bool right = true;
int width = 0;
char pad = ' ';
while (!done) {
char c = *r++;
switch (c) {
case '%': *w = '%'; done = true; break;
case '0':
if (width == 0) pad = '0';
// else fall through
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': width = width * 10 + (c - '0'); break;
case '-': right = !right; break;
case 'x': put_hex<uint32_t>(va_arg(args, uint32_t), right ? width : -width, pad); done = true; break;
case 'd':
case 'u': put_dec<uint32_t>(va_arg(args, uint32_t), right ? width : -width, pad); done = true; break;
case 's': {
const char *s = va_arg(args, const char*);
if (s) puts(s);
}
done = true;
break;
case 'G': {
// Special: GUID type
kutil::guid g = va_arg(args, kutil::guid);
put_hex<uint32_t>(g.a, 8, '0');
putc('-');
put_hex<uint16_t>((g.a >> 32) & 0xffff, 4, '0');
putc('-');
put_hex<uint16_t>((g.a >> 48) & 0xffff, 4, '0');
putc('-');
put_hex<uint16_t>((kutil::byteswap(g.b) >> 16) & 0xffff, 4, '0');
putc('-');
put_hex<uint16_t>(kutil::byteswap(g.b) & 0xffff, 4, '0');
put_hex<uint32_t>(kutil::byteswap(g.b >> 32), 8, '0');
}
done = true;
break;
case 'l':
switch (*r++) {
case 'x': put_hex<uint64_t>(va_arg(args, uint64_t), right ? width : -width, pad); done = true; break;
case 'd':
case 'u': put_dec<uint32_t>(va_arg(args, uint64_t), right ? width : -width, pad); done = true; break;
default:
done = true;
break;
}
break;
default:
done = true;
break;
}
}
}
flush();
char buffer[buf_size];
vsnprintf_(buffer, buf_size, fmt, args);
puts(buffer);
}
void

View File

@@ -3,6 +3,11 @@
#include "cpu.h"
#include "log.h"
cpu_data bsp_cpu_data;
static constexpr uint32_t cpuid_extended = 0x80000000;
inline static void
__cpuid(
uint32_t leaf,
@@ -23,41 +28,89 @@ __cpuid(
if (edx) *edx = d;
}
cpu_id::cpu_id()
cpu_id::cpu_id() :
m_features(0)
{
__cpuid(0, 0,
&m_high_leaf,
&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]));
uint32_t eax = 0;
__cpuid(0, 1, &eax);
m_stepping = eax & 0xf;
m_model = (eax >> 4) & 0xf;
m_family = (eax >> 8) & 0xf;
m_cpu_type = (eax >> 12) & 0x3;
uint32_t ext_model = (eax >> 16) & 0xf;
uint32_t ext_family = (eax >> 20) & 0xff;
if (m_family == 0x6 || m_family == 0xf)
m_model = (ext_model << 4) + m_model;
if (m_family == 0xf)
m_family += ext_family;
__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
{
if (leaf > m_high_leaf) return {};
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;
regs ret;
__cpuid(leaf, sub, &ret.eax, &ret.ebx, &ret.ecx, &ret.edx);
return ret;
}
void
cpu_id::validate()
{
bool fail = false;
uint32_t leaf = 0;
uint32_t sub = 0;
regs r;
log::info(logs::boot, "CPU: %s", brand_name());
log::debug(logs::boot, " Vendor is %s", 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);
#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_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; \
}
#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)
{
return (m_features & (1 << static_cast<uint64_t>(feat))) != 0;
}

View File

@@ -1,48 +1,91 @@
#pragma once
#include <stdint.h>
struct process;
struct cpu_state
{
uint64_t ds;
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax;
uint64_t interrupt, errorcode;
uint64_t rip, cs, rflags, user_rsp, ss;
};
/// Per-cpu state data. If you change this, remember to update the assembly
/// version in 'tasking.inc'
struct cpu_data
{
uintptr_t rsp0;
uintptr_t rsp3;
process *tcb;
};
extern cpu_data bsp_cpu_data;
/// 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
};
class cpu_id
{
public:
/// CPUID result register values
struct regs {
union {
uint32_t reg[4];
uint32_t eax, ebx, ecx, edx;
};
regs() : eax(0), ebx(0), ecx(0), edx(0) {}
regs(uint32_t a, uint32_t b, uint32_t c, uint32_t d) : eax(a), ebx(b), ecx(c), edx(d) {}
regs(const regs &r) : eax(r.eax), ebx(r.ebx), ecx(r.ecx), edx(r.edx) {}
/// Return true if bit |bit| of EAX is set
bool eax_bit(unsigned bit) { return (eax >> bit) & 0x1; }
/// 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; }
inline uint8_t cpu_type() const { return m_cpu_type; }
inline uint8_t stepping() const { return m_stepping; }
inline uint16_t family() const { return m_family; }
inline uint16_t model() const { return m_model; }
/// 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:
void read();
uint32_t m_high_leaf;
uint32_t m_high_basic;
uint32_t m_high_ext;
char m_vendor_id[13];
uint8_t m_cpu_type;
uint8_t m_stepping;
uint16_t m_family;
uint16_t m_model;
char m_brand_name[48];
uint64_t m_features;
};

View File

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

14
src/kernel/crti.s Normal file
View File

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

10
src/kernel/crtn.s Normal file
View File

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

81
src/kernel/debug.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "gdt.h"
#include "page_manager.h"
size_t __counter_syscall_enter = 0;
size_t __counter_syscall_sysret = 0;
void
print_regs(const cpu_state &regs)
{
console *cons = console::get();
uint64_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
print_regL("rax", regs.rax);
print_regM("rbx", regs.rbx);
print_regR("rcx", regs.rcx);
print_regL("rdx", regs.rdx);
print_regM("rdi", regs.rdi);
print_regR("rsi", regs.rsi);
cons->puts("\n");
print_regL(" r8", regs.r8);
print_regM(" r9", regs.r9);
print_regR("r10", regs.r10);
print_regL("r11", regs.r11);
print_regM("r12", regs.r12);
print_regR("r13", regs.r13);
print_regL("r14", regs.r14);
print_regM("r15", regs.r15);
cons->puts("\n\n");
print_regL("rbp", regs.rbp);
print_regM("rsp", regs.user_rsp);
print_regR("sp0", bsp_cpu_data.rsp0);
print_regL("rip", regs.rip);
print_regM("cr3", page_manager::get()->get_pml4());
print_regR("cr2", cr2);
cons->puts("\n");
}
struct frame
{
frame *prev;
uintptr_t return_addr;
};
void
print_stacktrace(int skip)
{
console *cons = console::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);
fp = fp->prev;
}
}
void
print_stack(const cpu_state &regs)
{
console *cons = console::get();
cons->puts("\nStack:\n");
uint64_t sp = regs.user_rsp;
while (sp <= regs.rbp) {
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
sp += sizeof(uint64_t);
}
}

25
src/kernel/debug.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
/// \file debug.h
/// Debugging utilities
#include <stdint.h>
extern "C" {
uintptr_t get_rsp();
uintptr_t get_rip();
uintptr_t get_frame(int frame);
uintptr_t get_gsbase();
void _halt();
}
extern size_t __counter_syscall_enter;
extern size_t __counter_syscall_sysret;
void print_regs(const cpu_state &regs);
void print_stack(const cpu_state &regs);
void print_stacktrace(int skip);
#define print_regL(name, value) cons->printf(" %s: %016lx", name, (value));
#define print_regM(name, value) cons->printf(" %s: %016lx", name, (value));
#define print_regR(name, value) cons->printf(" %s: %016lx\n", name, (value));

View File

@@ -8,24 +8,13 @@ get_rip:
pop rax ; do the same thing as 'ret', except with 'jmp'
jmp rax ; with the return address still in rax
global get_gsbase
get_gsbase:
rdgsbase rax
ret
global _halt
_halt:
hlt
jmp _halt
global get_frame
get_frame:
mov rcx, rbp
.loop:
mov rax, [rcx + 8]
mov rcx, [rcx]
cmp rdi, 0
je .done
sub rdi, 1
jmp .loop
.done:
ret

View File

@@ -4,20 +4,17 @@
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "acpi_tables.h"
#include "ahci/driver.h"
#include "apic.h"
#include "console.h"
#include "device_manager.h"
#include "interrupts.h"
#include "log.h"
#include "memory.h"
#include "page_manager.h"
static const char expected_signature[] = "RSD PTR ";
device_manager device_manager::s_instance(nullptr);
ahci_driver ahcid;
device_manager device_manager::s_instance(nullptr, kutil::allocator::invalid);
struct acpi1_rsdp
{
@@ -42,29 +39,16 @@ struct acpi2_rsdp
uint8_t reserved[3];
} __attribute__ ((packed));
uint8_t
acpi_checksum(const void *p, size_t len, size_t off = 0)
{
uint8_t sum = 0;
const uint8_t *c = reinterpret_cast<const uint8_t *>(p);
for (int i = off; i < len; ++i) sum += c[i];
return sum;
}
bool
acpi_table_header::validate(uint32_t expected_type) const
{
if (acpi_checksum(this, length) != 0) return false;
if (kutil::checksum(this, length) != 0) return false;
return !expected_type || (expected_type == type);
}
void irq2_callback(void *)
{
console *cons = console::get();
cons->set_color(11);
cons->puts(".");
cons->set_color();
}
void irq4_callback(void *)
@@ -75,8 +59,13 @@ void irq4_callback(void *)
}
device_manager::device_manager(const void *root_table) :
m_lapic(nullptr)
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)
{
kassert(root_table != 0, "ACPI root table pointer is null.");
@@ -87,7 +76,7 @@ device_manager::device_manager(const void *root_table) :
kassert(acpi1->signature[i] == expected_signature[i],
"ACPI RSDP table signature mismatch");
uint8_t sum = acpi_checksum(acpi1, sizeof(acpi1_rsdp), 0);
uint8_t sum = kutil::checksum(acpi1, sizeof(acpi1_rsdp), 0);
kassert(sum == 0, "ACPI 1.0 RSDP checksum mismatch.");
kassert(acpi1->revision > 1, "ACPI 1.0 not supported.");
@@ -95,7 +84,7 @@ device_manager::device_manager(const void *root_table) :
const acpi2_rsdp *acpi2 =
reinterpret_cast<const acpi2_rsdp *>(acpi1);
sum = acpi_checksum(acpi2, sizeof(acpi2_rsdp), sizeof(acpi1_rsdp));
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));
@@ -109,7 +98,7 @@ device_manager::device_manager(const void *root_table) :
ioapic *
device_manager::get_ioapic(int i)
{
return (i < m_ioapics.count()) ? m_ioapics[i] : nullptr;
return (i < m_ioapics.count()) ? &m_ioapics[i] : nullptr;
}
static void
@@ -164,26 +153,44 @@ device_manager::load_apic(const acpi_apic *apic)
uint8_t const *p = apic->controller_data;
uint8_t const *end = p + count;
// Pass one: set up IOAPIC objcts
// Pass one: count IOAPIC objcts
int num_ioapics = 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.append(new ioapic(base, base_gsr));
m_ioapics.emplace(base, base_gsr);
}
p += length;
}
// Pass two: configure APIC objects
// Pass three: configure APIC objects
p = apic->controller_data;
while (p < end) {
const uint8_t type = p[0];
const uint8_t length = p[1];
switch (type) {
case 0: // Local 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);
}
break;
case 1: // I/O APIC
break;
@@ -196,7 +203,7 @@ device_manager::load_apic(const acpi_apic *apic)
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);
m_ioapics[0].redirect(source, static_cast<isr>(gsi), flags, true);
}
break;
@@ -222,16 +229,13 @@ device_manager::load_apic(const acpi_apic *apic)
p += length;
}
// m_lapic->enable_timer(isr::isrTimer, 128, 3000000);
for (uint8_t i = 0; i < m_ioapics[0]->get_num_gsi(); ++i) {
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);
default: m_ioapics[0].mask(i, false);
}
}
m_ioapics[0]->dump_redirs();
m_lapic->enable();
}
@@ -292,6 +296,7 @@ device_manager::init_drivers()
{
// Eventually this should be e.g. a lookup into a loadable driver list
// for now, just look for AHCI devices
/*
for (auto &device : m_devices) {
if (device.devclass() != 1 || device.subclass() != 6)
continue;
@@ -303,6 +308,7 @@ device_manager::init_drivers()
ahcid.register_device(&device);
}
*/
}
bool

View File

@@ -2,14 +2,13 @@
/// \file device_manager.h
/// The device manager definition
#include "kutil/vector.h"
#include "apic.h"
#include "pci.h"
struct acpi_xsdt;
struct acpi_apic;
struct acpi_mcfg;
class block_device;
class lapic;
class ioapic;
using irq_callback = void (*)(void *);
@@ -20,7 +19,8 @@ class device_manager
public:
/// Constructor.
/// \arg root_table Pointer to the ACPI RSDP
device_manager(const void *root_table);
/// \arg alloc Allocator for device arrays
device_manager(const void *root_table, kutil::allocator &alloc);
/// Get the system global device manager.
/// \returns A reference to the system device manager
@@ -105,7 +105,7 @@ private:
void bad_irq(uint8_t irq);
lapic *m_lapic;
kutil::vector<ioapic *> m_ioapics;
kutil::vector<ioapic> m_ioapics;
kutil::vector<pci_group> m_pci;
kutil::vector<pci_device> m_devices;

View File

@@ -0,0 +1,93 @@
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "frame_allocator.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
{
if (address < rhs->address)
return -1;
else if (address > rhs->address)
return 1;
return 0;
}
frame_allocator::raw_alloc::raw_alloc(frame_allocator &fa) : m_fa(fa) {}
void *
frame_allocator::raw_alloc::allocate(size_t size)
{
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)
{
}
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;
auto *first = m_free.front();
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;
}
}
inline uintptr_t end(frame_block *node) { return node->address + node->count * frame_size; }
void
frame_allocator::free(uintptr_t address, size_t count)
{
frame_block_node *node =
reinterpret_cast<frame_block_node*>(address + page_offset);
kutil::memset(node, 0, sizeof(frame_block_node));
node->address = address;
node->count = count;
m_free.sorted_insert(node);
frame_block_node *next = node->next();
if (next && end(node) == next->address) {
node->count += next->count;
m_free.remove(next);
}
frame_block_node *prev = node->prev();
if (prev && end(prev) == address) {
prev->count += node->count;
m_free.remove(node);
}
}

View File

@@ -0,0 +1,70 @@
#pragma once
/// \file frame_allocator.h
/// Allocator for physical memory frames
#include <stdint.h>
#include "kutil/allocator.h"
#include "kutil/linked_list.h"
struct frame_block;
using frame_block_list = kutil::linked_list<frame_block>;
/// Allocator for physical memory frames
class frame_allocator
{
public:
/// Default constructor
frame_allocator();
/// Get free frames from the free list. Only frames from the first free block
/// are returned, so the number may be less than requested, but they will
/// be contiguous.
/// \arg count The maximum number of frames to get
/// \arg address [out] The physical address of the first frame
/// \returns The number of frames retrieved
size_t allocate(size_t count, uintptr_t *address);
/// Free previously allocated frames.
/// \arg address The physical address of the first frame to free
/// \arg count The number of frames to be freed
void free(uintptr_t address, size_t count);
/// Get a memory allocator that allocates raw pages
/// \returns The allocator ojbect
kutil::allocator & raw_allocator() { return m_raw_alloc; }
private:
class raw_alloc :
public kutil::allocator
{
public:
raw_alloc(frame_allocator &fa);
virtual void * allocate(size_t size) override;
virtual void free(void *p) override;
private:
frame_allocator &m_fa;
};
raw_alloc m_raw_alloc;
frame_block_list m_free; ///< Free frames list
frame_allocator(const frame_allocator &) = delete;
};
/// A block of contiguous frames. Each `frame_block` represents contiguous
/// physical frames with the same attributes.
struct frame_block
{
uintptr_t address;
uint32_t count;
/// Compare two blocks by address.
/// \arg rhs The right-hand comparator
/// \returns <0 if this is sorts earlier, >0 if this sorts later, 0 for equal
int compare(const frame_block *rhs) const;
};
extern frame_allocator g_frame_allocator;

View File

@@ -136,13 +136,13 @@ idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags)
}
void
tss_set_stack(int ring, addr_t rsp)
tss_set_stack(int ring, uintptr_t rsp)
{
kassert(ring < 3, "Bad ring passed to tss_set_stack.");
g_tss.rsp[ring] = rsp;
}
addr_t
uintptr_t
tss_get_stack(int ring)
{
kassert(ring < 3, "Bad ring passed to tss_get_stack.");
@@ -170,7 +170,7 @@ gdt_init()
kutil::memset(&g_tss, 0, sizeof(tss_entry));
g_tss.iomap_offset = sizeof(tss_entry);
addr_t tss_base = reinterpret_cast<addr_t>(&g_tss);
uintptr_t tss_base = reinterpret_cast<uintptr_t>(&g_tss);
// Note that this takes TWO GDT entries
tss_set_entry(6, tss_base, sizeof(tss_entry));
@@ -184,18 +184,25 @@ gdt_init()
}
void
gdt_dump()
gdt_dump(int index)
{
const table_ptr &table = g_gdtr;
console *cons = console::get();
cons->printf(" GDT: loc:%lx size:%d\n", table.base, table.limit+1);
int start = 0;
int count = (table.limit + 1) / sizeof(gdt_descriptor);
if (index != -1) {
start = index;
count = 1;
} else {
cons->printf(" GDT: loc:%lx size:%d\n", table.base, table.limit+1);
}
const gdt_descriptor *gdt =
reinterpret_cast<const gdt_descriptor *>(table.base);
for (int i = 0; i < count; ++i) {
for (int i = start; i < start+count; ++i) {
uint32_t base =
(gdt[i].base_high << 24) |
(gdt[i].base_mid << 16) |
@@ -233,17 +240,25 @@ gdt_dump()
}
void
idt_dump()
idt_dump(int index)
{
const table_ptr &table = g_idtr;
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
int start = 0;
int count = (table.limit + 1) / sizeof(idt_descriptor);
if (index != -1) {
start = index;
count = 1;
log::info(logs::boot, "IDT FOR INDEX %02x", index);
} else {
log::info(logs::boot, "Loaded IDT at: %lx size: %d bytes", table.base, table.limit+1);
}
const idt_descriptor *idt =
reinterpret_cast<const idt_descriptor *>(table.base);
for (int i = 0; i < count; ++i) {
for (int i = start; i < start+count; ++i) {
uint64_t base =
(static_cast<uint64_t>(idt[i].base_high) << 32) |
(static_cast<uint64_t>(idt[i].base_mid) << 16) |

View File

@@ -2,7 +2,6 @@
/// \file gdt.h
/// Definitions relating to system descriptor tables: GDT, IDT, TSS
#include <stdint.h>
#include "kutil/memory.h"
/// Set up the GDT and TSS, and switch segment registers to point
/// to them.
@@ -18,15 +17,17 @@ void idt_set_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags);
/// Set the stack pointer for a given ring in the TSS
/// \arg ring Ring to set for (0-2)
/// \arg rsp Stack pointer to set
void tss_set_stack(int ring, addr_t rsp);
void tss_set_stack(int ring, uintptr_t rsp);
/// Get the stack pointer for a given ring in the TSS
/// \arg ring Ring to get (0-2)
/// \returns Stack pointers for that ring
addr_t tss_get_stack(int ring);
uintptr_t tss_get_stack(int ring);
/// Dump information about the current GDT to the screen
void gdt_dump();
/// \arg index Which entry to print, or -1 for all entries
void gdt_dump(int index = -1);
/// Dump information about the current IDT to the screen
void idt_dump();
/// \arg index Which entry to print, or -1 for all entries
void idt_dump(int index = -1);

35
src/kernel/gdt.s Normal file
View File

@@ -0,0 +1,35 @@
extern g_idtr
extern g_gdtr
global idt_write
idt_write:
lidt [rel g_idtr]
ret
global idt_load
idt_load:
sidt [rel g_idtr]
ret
global gdt_write
gdt_write:
lgdt [rel g_gdtr]
mov ax, si ; second arg is data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
push qword rdi ; first arg is code segment
lea rax, [rel .next]
push rax
o64 retf
.next:
ltr dx ; third arg is the TSS
ret
global gdt_load
gdt_load:
sgdt [rel g_gdtr]
ret

View File

@@ -134,11 +134,115 @@ IRQ (0x7d, 0x5d, irq5D)
IRQ (0x7e, 0x5e, irq5E)
IRQ (0x7f, 0x5f, irq5F)
ISR (0xe7, isrAssert)
IRQ (0x80, 0x60, irq60)
IRQ (0x81, 0x61, irq61)
IRQ (0x82, 0x62, irq62)
IRQ (0x83, 0x63, irq63)
IRQ (0x84, 0x64, irq64)
IRQ (0x85, 0x65, irq65)
IRQ (0x86, 0x66, irq66)
IRQ (0x87, 0x67, irq67)
IRQ (0x88, 0x68, irq68)
IRQ (0x89, 0x69, irq69)
IRQ (0x8a, 0x6a, irq6A)
IRQ (0x8b, 0x6b, irq6B)
IRQ (0x8c, 0x6c, irq6C)
IRQ (0x8d, 0x6d, irq6D)
IRQ (0x8e, 0x6e, irq6E)
IRQ (0x8f, 0x6f, irq6F)
ISR (0xec, isrTimer)
ISR (0xed, isrLINT0)
ISR (0xee, isrLINT1)
IRQ (0x90, 0x70, irq70)
IRQ (0x91, 0x71, irq71)
IRQ (0x92, 0x72, irq72)
IRQ (0x93, 0x73, irq73)
IRQ (0x94, 0x74, irq74)
IRQ (0x95, 0x75, irq75)
IRQ (0x96, 0x76, irq76)
IRQ (0x97, 0x77, irq77)
IRQ (0x98, 0x78, irq78)
IRQ (0x99, 0x79, irq79)
IRQ (0x9a, 0x7a, irq7A)
IRQ (0x9b, 0x7b, irq7B)
IRQ (0x9c, 0x7c, irq7C)
IRQ (0x9d, 0x7d, irq7D)
IRQ (0x9e, 0x7e, irq7E)
IRQ (0x9f, 0x7f, irq7F)
IRQ (0xa0, 0x80, irq80)
IRQ (0xa1, 0x81, irq81)
IRQ (0xa2, 0x82, irq82)
IRQ (0xa3, 0x83, irq83)
IRQ (0xa4, 0x84, irq84)
IRQ (0xa5, 0x85, irq85)
IRQ (0xa6, 0x86, irq86)
IRQ (0xa7, 0x87, irq87)
IRQ (0xa8, 0x88, irq88)
IRQ (0xa9, 0x89, irq89)
IRQ (0xaa, 0x8a, irq8A)
IRQ (0xab, 0x8b, irq8B)
IRQ (0xac, 0x8c, irq8C)
IRQ (0xad, 0x8d, irq8D)
IRQ (0xae, 0x8e, irq8E)
IRQ (0xaf, 0x8f, irq8F)
IRQ (0xb0, 0x90, irq90)
IRQ (0xb1, 0x91, irq91)
IRQ (0xb2, 0x92, irq92)
IRQ (0xb3, 0x93, irq93)
IRQ (0xb4, 0x94, irq94)
IRQ (0xb5, 0x95, irq95)
IRQ (0xb6, 0x96, irq96)
IRQ (0xb7, 0x97, irq97)
IRQ (0xb8, 0x98, irq98)
IRQ (0xb9, 0x99, irq99)
IRQ (0xba, 0x9a, irq9A)
IRQ (0xbb, 0x9b, irq9B)
IRQ (0xbc, 0x9c, irq9C)
IRQ (0xbd, 0x9d, irq9D)
IRQ (0xbe, 0x9e, irq9E)
IRQ (0xbf, 0x9f, irq9F)
IRQ (0xc0, 0xa0, irqA0)
IRQ (0xc1, 0xa1, irqA1)
IRQ (0xc2, 0xa2, irqA2)
IRQ (0xc3, 0xa3, irqA3)
IRQ (0xc4, 0xa4, irqA4)
IRQ (0xc5, 0xa5, irqA5)
IRQ (0xc6, 0xa6, irqA6)
IRQ (0xc7, 0xa7, irqA7)
IRQ (0xc8, 0xa8, irqA8)
IRQ (0xc9, 0xa9, irqA9)
IRQ (0xca, 0xaa, irqAA)
IRQ (0xcb, 0xab, irqAB)
IRQ (0xcc, 0xac, irqAC)
IRQ (0xcd, 0xad, irqAD)
IRQ (0xce, 0xae, irqAE)
IRQ (0xcf, 0xaf, irqAF)
IRQ (0xd0, 0xb0, irqB0)
IRQ (0xd1, 0xb1, irqB1)
IRQ (0xd2, 0xb2, irqB2)
IRQ (0xd3, 0xb3, irqB3)
IRQ (0xd4, 0xb4, irqB4)
IRQ (0xd5, 0xb5, irqB5)
IRQ (0xd6, 0xb6, irqB6)
IRQ (0xd7, 0xb7, irqB7)
IRQ (0xd8, 0xb8, irqB8)
IRQ (0xd9, 0xb9, irqB9)
IRQ (0xda, 0xba, irqBA)
IRQ (0xdb, 0xbb, irqBB)
IRQ (0xdc, 0xbc, irqBC)
IRQ (0xdd, 0xbd, irqBD)
IRQ (0xde, 0xbe, irqBE)
IRQ (0xdf, 0xbf, irqBF)
ISR (0xe0, isrTimer)
ISR (0xe1, isrLINT0)
ISR (0xe2, isrLINT1)
ISR (0xe4, isrAssert)
UISR(0xee, isrSyscall)
ISR (0xef, isrSpurious)
ISR (0xf0, isrIgnore0)

View File

@@ -1,24 +1,33 @@
#include <stdint.h>
#include "apic.h"
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "device_manager.h"
#include "gdt.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
#include "scheduler.h"
#include "syscall.h"
static const uint16_t PIC1 = 0x20;
static const uint16_t PIC2 = 0xa0;
extern "C" {
addr_t isr_handler(addr_t, cpu_state);
addr_t irq_handler(addr_t, cpu_state);
addr_t syscall_handler(addr_t, cpu_state);
void _halt();
void isr_handler(cpu_state*);
void irq_handler(cpu_state*);
#define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name ();
#define UISR(i, name) extern void name ();
#define IRQ(i, q, name) extern void name ();
#include "interrupt_isrs.inc"
#undef IRQ
#undef UISR
#undef EISR
#undef ISR
}
@@ -36,9 +45,11 @@ get_irq(unsigned vector)
switch (vector) {
#define ISR(i, name)
#define EISR(i, name)
#define UISR(i, name)
#define IRQ(i, q, name) case i : return q;
#include "interrupt_isrs.inc"
#undef IRQ
#undef UISR
#undef EISR
#undef ISR
@@ -49,13 +60,9 @@ get_irq(unsigned vector)
static void
disable_legacy_pic()
{
static const uint16_t PIC1 = 0x20;
static const uint16_t PIC2 = 0xa0;
// Mask all interrupts
outb(0xa1, 0xff);
outb(0x21, 0xff);
outb(PIC2+1, 0xfc);
outb(PIC1+1, 0xff);
// Start initialization sequence
outb(PIC1, 0x11); io_wait();
@@ -63,7 +70,7 @@ disable_legacy_pic()
// Remap into ignore ISRs
outb(PIC1+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore0)); io_wait();
outb(PIC2+1, static_cast<uint8_t>(isr::isrIgnore8)); io_wait();
// Tell PICs about each other
outb(PIC1+1, 0x04); io_wait();
@@ -82,9 +89,11 @@ interrupts_init()
{
#define ISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
#define EISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
#define UISR(i, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0xee);
#define IRQ(i, q, name) idt_set_entry(i, reinterpret_cast<uint64_t>(& name), 0x08, 0x8e);
#include "interrupt_isrs.inc"
#undef IRQ
#undef UISR
#undef EISR
#undef ISR
@@ -94,34 +103,112 @@ interrupts_init()
log::info(logs::boot, "Interrupts enabled.");
}
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
extern "C" uint64_t get_frame(int frame);
void
print_stacktrace(int skip = 0)
{
console *cons = console::get();
int frame = 0;
uint64_t bp = get_frame(skip);
while (bp) {
cons->printf(" frame %2d: %lx\n", frame, bp);
bp = get_frame(++frame + skip);
}
}
addr_t
isr_handler(addr_t return_rsp, cpu_state regs)
isr_handler(cpu_state *regs)
{
console *cons = console::get();
switch (static_cast<isr>(regs.interrupt & 0xff)) {
case isr::isrTimer: {
scheduler &s = scheduler::get();
return_rsp = s.tick(return_rsp);
switch (static_cast<isr>(regs->interrupt & 0xff)) {
case isr::isrDebug: {
cons->set_color(11);
cons->puts("\nDebug Exception:\n");
cons->set_color();
uint64_t dr = 0;
__asm__ __volatile__ ("mov %%dr0, %0" : "=r"(dr));
print_regL("dr0", dr);
__asm__ __volatile__ ("mov %%dr1, %0" : "=r"(dr));
print_regM("dr1", dr);
__asm__ __volatile__ ("mov %%dr2, %0" : "=r"(dr));
print_regM("dr2", dr);
__asm__ __volatile__ ("mov %%dr3, %0" : "=r"(dr));
print_regR("dr3", dr);
__asm__ __volatile__ ("mov %%dr6, %0" : "=r"(dr));
print_regL("dr6", dr);
__asm__ __volatile__ ("mov %%dr7, %0" : "=r"(dr));
print_regR("dr7", dr);
print_regL("rip", regs->rip);
print_regM("rsp", regs->user_rsp);
print_regM("fla", regs->rflags);
_halt();
}
break;
case isr::isrGPFault: {
cons->set_color(9);
cons->puts("\nGeneral Protection Fault:\n");
cons->set_color();
cons->printf(" errorcode: %lx", regs->errorcode);
if (regs->errorcode & 0x01) cons->puts(" external");
int index = (regs->errorcode & 0xffff) >> 4;
if (index) {
switch ((regs->errorcode & 0x07) >> 1) {
case 0:
cons->printf(" GDT[%x]\n", index);
gdt_dump(index);
break;
case 1:
case 3:
cons->printf(" IDT[%x]\n", index);
idt_dump(index);
break;
default:
cons->printf(" LDT[%x]??\n", index);
break;
}
} else {
cons->putc('\n');
}
print_regs(*regs);
/*
print_stacktrace(2);
print_stack(*regs);
*/
}
_halt();
break;
case isr::isrPageFault: {
uintptr_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
if ((regs->errorcode & 0x9) == 0 &&
page_manager::get()->fault_handler(cr2))
break;
cons->set_color(11);
cons->puts("\nPage Fault:\n");
cons->set_color();
cons->puts(" flags:");
if (regs->errorcode & 0x01) cons->puts(" present");
if (regs->errorcode & 0x02) cons->puts(" write");
if (regs->errorcode & 0x04) cons->puts(" user");
if (regs->errorcode & 0x08) cons->puts(" reserved");
if (regs->errorcode & 0x10) cons->puts(" ip");
cons->puts("\n");
print_regs(*regs);
print_stacktrace(2);
_halt();
}
break;
case isr::isrTimer:
scheduler::get().tick();
break;
case isr::isrLINT0:
cons->puts("\nLINT0\n");
break;
@@ -130,6 +217,24 @@ isr_handler(addr_t return_rsp, cpu_state regs)
cons->puts("\nLINT1\n");
break;
case isr::isrAssert: {
cons->set_color();
print_regs(*regs);
print_stacktrace(2);
}
_halt();
break;
/*
case isr::isrSyscall:
syscall_dispatch(regs);
break;
*/
case isr::isrSpurious:
// No EOI for the spurious interrupt
return;
case isr::isrIgnore0:
case isr::isrIgnore1:
case isr::isrIgnore2:
@@ -138,214 +243,52 @@ isr_handler(addr_t return_rsp, cpu_state regs)
case isr::isrIgnore5:
case isr::isrIgnore6:
case isr::isrIgnore7:
//cons->printf("\nIGNORED: %02x\n", regs->interrupt);
outb(PIC1, 0x20);
break;
case isr::isrGPFault: {
cons->set_color(9);
cons->puts("\nGeneral Protection Fault:\n");
cons->set_color();
cons->puts(" flags:");
if (regs.errorcode & 0x01) cons->puts(" external");
int index = (regs.errorcode & 0xf8) >> 3;
if (index) {
switch (regs.errorcode & 0x06) {
case 0:
cons->printf(" GDT[%d]\n", index);
gdt_dump();
break;
case 1:
case 3:
cons->printf(" IDT[%d]\n", index);
idt_dump();
break;
default:
cons->printf(" LDT[%d]??\n", index);
break;
}
} else {
cons->putc('\n');
}
cons->puts("\n");
print_reg(" ds", regs.ds);
print_reg(" cs", regs.cs);
print_reg(" ss", regs.ss);
cons->puts("\n");
print_reg("rax", regs.rax);
print_reg("rbx", regs.rbx);
print_reg("rcx", regs.rcx);
print_reg("rdx", regs.rdx);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
cons->puts("\n");
print_reg("rbp", regs.rbp);
cons->puts("\n");
print_reg("rip", regs.rip);
print_stacktrace(2);
cons->puts("\nStack:\n");
uint64_t sp = regs.user_rsp;
while (sp <= regs.rbp) {
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
sp += sizeof(uint64_t);
}
}
while(1) asm("hlt");
break;
case isr::isrPageFault: {
cons->set_color(11);
cons->puts("\nPage Fault:\n");
cons->set_color();
cons->puts(" flags:");
if (regs.errorcode & 0x01) cons->puts(" present");
if (regs.errorcode & 0x02) cons->puts(" write");
if (regs.errorcode & 0x04) cons->puts(" user");
if (regs.errorcode & 0x08) cons->puts(" reserved");
if (regs.errorcode & 0x10) cons->puts(" ip");
cons->puts("\n");
uint64_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
print_reg("cr2", cr2);
print_reg("rsp", regs.user_rsp);
print_reg("rip", regs.rip);
cons->puts("\n");
print_stacktrace(2);
}
while(1) asm("hlt");
break;
case isr::isrAssert: {
cons->set_color();
cons->puts("\n");
print_reg("rax", regs.rax);
print_reg("rbx", regs.rbx);
print_reg("rcx", regs.rcx);
print_reg("rdx", regs.rdx);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
cons->puts("\n");
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.user_rsp);
cons->puts("\n");
print_reg("rip", regs.rip);
print_stacktrace(2);
}
while(1) asm("hlt");
case isr::isrIgnore8:
case isr::isrIgnore9:
case isr::isrIgnoreA:
case isr::isrIgnoreB:
case isr::isrIgnoreC:
case isr::isrIgnoreD:
case isr::isrIgnoreE:
case isr::isrIgnoreF:
//cons->printf("\nIGNORED: %02x\n", regs->interrupt);
outb(PIC1, 0x20);
outb(PIC2, 0x20);
break;
default:
cons->set_color(9);
cons->puts("\nReceived ISR interrupt:\n");
cons->printf("\nReceived %02x interrupt:\n",
(static_cast<isr>(regs->interrupt)));
cons->set_color();
cons->printf(" ISR: %02lx ERR: %lx\n\n",
regs->interrupt, regs->errorcode);
cons->printf(" ISR: %02lx\n", regs.interrupt);
cons->printf(" ERR: %lx\n", regs.errorcode);
cons->puts("\n");
print_reg(" ds", regs.ds);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
print_reg("rbp", regs.rbp);
print_reg("rbx", regs.rbx);
print_reg("rdx", regs.rdx);
print_reg("rcx", regs.rcx);
print_reg("rax", regs.rax);
cons->puts("\n");
print_reg("rip", regs.rip);
print_reg(" cs", regs.cs);
print_reg(" ef", regs.rflags);
print_reg("rsp", regs.user_rsp);
print_reg(" ss", regs.ss);
cons->puts("\n");
print_regs(*regs);
print_stacktrace(2);
while(1) asm("hlt");
_halt();
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
}
addr_t
irq_handler(addr_t return_rsp, cpu_state regs)
void
irq_handler(cpu_state *regs)
{
console *cons = console::get();
uint8_t irq = get_irq(regs.interrupt);
uint8_t irq = get_irq(regs->interrupt);
if (! device_manager::get().dispatch_irq(irq)) {
cons->set_color(11);
cons->printf("\nReceived unknown IRQ: %d (vec %d)\n",
irq, regs.interrupt);
irq, regs->interrupt);
cons->set_color();
print_reg(" ds", regs.ds);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
print_reg("rbp", regs.rbp);
print_reg("rbx", regs.rbx);
print_reg("rdx", regs.rdx);
print_reg("rcx", regs.rcx);
print_reg("rax", regs.rax);
cons->puts("\n");
print_reg("rip", regs.rip);
print_reg(" cs", regs.cs);
print_reg(" ef", regs.rflags);
print_reg("rsp", regs.user_rsp);
print_reg(" ss", regs.ss);
while(1) asm("hlt");
print_regs(*regs);
_halt();
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
}
addr_t
syscall_handler(addr_t return_rsp, cpu_state regs)
{
console *cons = console::get();
cons->printf("SYSCALL\n");
cons->puts("\n");
print_reg("rax", regs.rax);
print_reg("rbx", regs.rbx);
print_reg("rcx", regs.rcx);
print_reg("rdx", regs.rdx);
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
cons->puts("\n");
print_reg(" r8", regs.r8);
print_reg(" r9", regs.r9);
print_reg("r10", regs.r10);
print_reg("r11", regs.r11);
print_reg("r12", regs.r12);
print_reg("r13", regs.r13);
print_reg("r14", regs.r14);
print_reg("r15", regs.r15);
cons->puts("\n");
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.user_rsp);
print_reg("sp0", tss_get_stack(0));
cons->puts("\n");
print_reg("rip", regs.rip);
return return_rsp;
}

View File

@@ -9,9 +9,11 @@ enum class isr : uint8_t
{
#define ISR(i, name) name = i,
#define EISR(i, name) name = i,
#define UISR(i, name) name = i,
#define IRQ(i, q, name) name = i,
#include "interrupt_isrs.inc"
#undef IRQ
#undef UISR
#undef EISR
#undef ISR

View File

@@ -1,119 +1,38 @@
extern g_idtr
extern g_gdtr
global idt_write
idt_write:
lidt [rel g_idtr]
ret
global idt_load
idt_load:
sidt [rel g_idtr]
ret
global gdt_write
gdt_write:
lgdt [rel g_gdtr]
mov ax, si ; second arg is data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
push qword rdi ; first arg is code segment
lea rax, [rel .next]
push rax
o64 retf
.next:
ltr dx
ret
global gdt_load
gdt_load:
sgdt [rel g_gdtr]
ret
%macro push_all_and_segments 0
push rax
push rcx
push rdx
push rbx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov ax, ds
push rax
%endmacro
%macro pop_all_and_segments 0
pop rax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
pop rcx
pop rax
%endmacro
%include "push_all.inc"
extern isr_handler
global isr_handler_prelude
isr_handler_prelude:
push_all_and_segments
push_all
check_swap_gs
mov rdi, rsp
mov rsi, rsp
call isr_handler
mov rsp, rax
pop_all_and_segments
add rsp, 16 ; because the ISRs added err/num
sti
iretq
jmp isr_handler_return
extern irq_handler
global irq_handler_prelude
irq_handler_prelude:
push_all_and_segments
push_all
check_swap_gs
mov rdi, rsp
mov rsi, rsp
call irq_handler
mov rsp, rax
; fall through to isr_handler_return
pop_all_and_segments
global isr_handler_return
isr_handler_return:
check_swap_gs
pop_all
add rsp, 16 ; because the ISRs added err/num
sti
iretq
%macro EMIT_ISR 2
global %1
%1:
cli
push 0
push %2
jmp isr_handler_prelude
@@ -122,7 +41,6 @@ irq_handler_prelude:
%macro EMIT_EISR 2
global %1
%1:
cli
push %2
jmp isr_handler_prelude
%endmacro
@@ -130,87 +48,15 @@ irq_handler_prelude:
%macro EMIT_IRQ 2
global %1
%1:
cli
push 0
push %2
jmp irq_handler_prelude
%endmacro
%define EISR(i, name) EMIT_EISR name, i
%define EISR(i, name) EMIT_EISR name, i ; ISR with error code
%define UISR(i, name) EMIT_ISR name, i ; ISR callable from user space
%define ISR(i, name) EMIT_ISR name, i
%define IRQ(i, q, name) EMIT_IRQ name, i
section .isrs
%include "interrupt_isrs.inc"
extern syscall_handler
syscall_handler_prelude:
push 0 ; ss, doesn't matter here
push rsp
pushf
push 0 ; cs, doesn't matter here
push rcx ; user rip
push 0 ; bogus interrupt
push 0 ; bogus errorcode
push_all_and_segments
mov rdi, rsp
call syscall_handler
mov rsp, rax
pop_all_and_segments
add rsp, 16 ; ignore bogus interrupt / error
pop rcx ; user rip
add rsp, 32 ; ignore cs, flags, rsp, ss
o64 sysret
global syscall_enable
syscall_enable:
; IA32_EFER - set bit 0, syscall enable
mov rcx, 0xc0000080
rdmsr
or rax, 0x1
wrmsr
; IA32_STAR - cs for syscall
mov rcx, 0xc0000081
mov rax, 0 ; not used
mov rdx, 0x00180008 ; GDT:3 (user code), GDT:1 (kernel code)
wrmsr
; IA32_LSTAR - RIP for syscall
mov rcx, 0xc0000082
lea rax, [rel syscall_handler_prelude]
mov rdx, rax
shr rdx, 32
wrmsr
; IA32_FMASK - FLAGS mask inside syscall
mov rcx, 0xc0000084
mov rax, 0x200
mov rdx, 0
wrmsr
ret
global taskA
taskA:
push rbp
mov rbp, rsp
mov rax, 0xaaaaaaaaaaaaaaaa
.loop:
syscall
jmp .loop
global taskB
taskB:
push rbp
mov rbp, rsp
mov rax, 0xbbbbbbbbbbbbbbbb
.loop:
syscall
jmp .loop

View File

@@ -14,22 +14,6 @@ outb(uint16_t port, uint8_t val)
__asm__ __volatile__ ( "outb %0, %1" :: "a"(val), "Nd"(port) );
}
uint64_t
rdmsr(uint64_t addr)
{
uint32_t low, high;
__asm__ __volatile__ ("rdmsr" : "=a"(low), "=d"(high) : "c"(addr));
return (static_cast<uint64_t>(high) << 32) | low;
}
void
wrmsr(uint64_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
io_wait(unsigned times)
{

View File

@@ -14,16 +14,6 @@ uint8_t inb(uint16_t port);
/// \arg val The byte to write
void outb(uint16_t port, uint8_t val);
/// Read the value of a MSR
/// \arg addr The MSR address
/// \returns The current value of the MSR
uint64_t rdmsr(uint64_t addr);
/// Write to a MSR
/// \arg addr The MSR address
/// \arg value The value to write
void wrmsr(uint64_t addr, uint64_t value);
/// Pause briefly by doing IO to port 0x80
/// \arg times Number of times to delay by writing
void io_wait(unsigned times = 1);

22
src/kernel/loader.s Normal file
View File

@@ -0,0 +1,22 @@
%include "push_all.inc"
extern load_process_image
global ramdisk_process_loader
ramdisk_process_loader:
; create_process already pushed a cpu_state onto the stack for us, this
; acts both as the cpu_state parameter to load_process_image, and the
; saved state for the following iretq
pop rdi ; the address of the program image
pop rsi ; the size of the program image
pop rdx ; the address of this process' process structure
call load_process_image
push rax ; load_process_image returns the process entrypoint
swapgs
iretq

View File

@@ -1,106 +1,55 @@
#include <type_traits>
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "console.h"
#include "log.h"
#include "scheduler.h"
static const uint64_t default_enabled[] = {0x00, 0xff, 0xff, 0xff};
static const uint8_t level_colors[] = {0x07, 0x0f, 0x0b, 0x09};
static uint8_t log_buffer[0x10000];
static log::logger g_logger(log_buffer, sizeof(log_buffer));
static const char *levels[] = {"debug", " info", " warn", "error", "fatal"};
static const char *areas[] = {
"boot ",
"memory",
"apic ",
"device",
"driver",
"file ",
"task ",
static const uint8_t level_colors[] = {0x07, 0x07, 0x0f, 0x0b, 0x09};
nullptr
};
log log::s_log;
log::log() : m_cons(nullptr)
static void
output_log(log::area_t area, log::level severity, const char *message)
{
kassert(0, "Invalid log constructor");
}
log::log(console *cons) :
m_cons(cons)
{
const int num_levels = static_cast<int>(level::max) - 1;
for (int i = 0; i < num_levels; ++i)
m_enabled[i] = default_enabled[i];
auto *cons = console::get();
cons->set_color(level_colors[static_cast<int>(severity)]);
cons->printf("%7s %5s: %s\n",
g_logger.area_name(area),
g_logger.level_name(severity),
message);
cons->set_color();
}
void
log::init(console *cons)
logger_task()
{
new (&s_log) log(cons);
log::info(logs::boot, "Logging system initialized.");
}
uint8_t buffer[257];
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
auto *cons = console::get();
static inline uint64_t
bit_mask(logs area) { return 1 << static_cast<uint64_t>(area); }
g_logger.set_immediate(nullptr);
log::info(logs::task, "Starting kernel logger task");
void
log::enable(logs type, level at_level)
{
using under_t = std::underlying_type<level>::type;
under_t at = static_cast<under_t>(at_level);
under_t max = sizeof(m_enabled) / sizeof(m_enabled[0]);
scheduler &s = scheduler::get();
for (under_t i = 0; i < max; ++i) {
if (i >= at)
s_log.m_enabled[i] |= bit_mask(type);
else
s_log.m_enabled[i] &= ~bit_mask(type);
while (true) {
if(g_logger.get_entry(buffer, sizeof(buffer))) {
buffer[ent->bytes] = 0;
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
cons->printf("%7s %5s: %s\n",
g_logger.area_name(ent->area),
g_logger.level_name(ent->severity),
ent->message);
cons->set_color();
} else {
s.schedule();
}
}
}
template <>
void
log::trylog<log::level::fatal>(logs area, const char *fmt, ...)
void logger_init()
{
va_list args;
va_start(args, fmt);
s_log.output(level::fatal, area, fmt, args);
va_end(args);
while(1) __asm__ ("hlt");
new (&g_logger) log::logger(log_buffer, sizeof(log_buffer));
g_logger.set_immediate(output_log);
}
template <log::level L>
void
log::trylog(logs area, const char *fmt, ...)
{
auto i = static_cast<std::underlying_type<level>::type>(L);
if ((s_log.m_enabled[i] & bit_mask(area)) == 0) return;
va_list args;
va_start(args, fmt);
s_log.output(L, area, fmt, args);
va_end(args);
}
void
log::output(level severity, logs area, const char *fmt, va_list args)
{
m_cons->set_color(level_colors[static_cast<int>(severity)]);
m_cons->printf("%s %s: ",
areas[static_cast<int>(area)],
levels[static_cast<int>(severity)]);
m_cons->vprintf(fmt, args);
m_cons->set_color();
m_cons->puts("\n");
}
const log::trylog_p log::debug = &trylog<level::debug>;
const log::trylog_p log::info = &trylog<level::info>;
const log::trylog_p log::warn = &trylog<level::warn>;
const log::trylog_p log::error = &trylog<level::error>;
const log::trylog_p log::fatal = &trylog<level::fatal>;

View File

@@ -1,51 +1,9 @@
#pragma once
#include <stdarg.h>
#include <stdint.h>
class console;
#include "kutil/logger.h"
namespace log = kutil::log;
namespace logs = kutil::logs;
enum class logs
{
boot,
memory,
apic,
device,
driver,
fs,
task,
max
};
class log
{
public:
enum class level {debug, info, warn, error, fatal, max};
static void init(console *cons);
static void enable(logs type, level at_level);
template <level L>
static void trylog(logs area, const char *fmt, ...);
using trylog_p = void (*)(logs area, const char *fmt, ...);
static const trylog_p debug;
static const trylog_p info;
static const trylog_p warn;
static const trylog_p error;
static const trylog_p fatal;
private:
void output(level severity, logs area, const char *fmt, va_list args);
/// Bitmasks for what categories are enabled. fatal is
/// always enabled, so leave it out.
uint64_t m_enabled[static_cast<uint64_t>(level::max) - 1];
console *m_cons;
log();
log(console *cons);
static log s_log;
};
void logger_init();
void logger_task();

View File

@@ -1,118 +1,134 @@
#include <stddef.h>
#include <stdint.h>
#include "j6/signals.h"
#include "initrd/initrd.h"
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "kutil/heap_allocator.h"
#include "kutil/vm_space.h"
#include "apic.h"
#include "block_device.h"
#include "console.h"
#include "cpu.h"
#include "device_manager.h"
#include "font.h"
#include "gdt.h"
#include "interrupts.h"
#include "io.h"
#include "kernel_data.h"
#include "kernel_args.h"
#include "kernel_memory.h"
#include "log.h"
#include "memory.h"
#include "objects/event.h"
#include "objects/handle.h"
#include "page_manager.h"
#include "scheduler.h"
#include "screen.h"
#include "serial.h"
#include "syscall.h"
extern "C" {
void kernel_main(popcorn_data *header);
void syscall_enable();
void kernel_main(kernel_args *header);
void *__bss_start, *__bss_end;
}
extern void __kernel_assert(const char *, unsigned, const char *);
extern kutil::heap_allocator g_kernel_heap;
class test_observer :
public kobject::observer
{
public:
test_observer(const char *name) : m_name(name) {}
virtual bool on_signals_changed(
kobject *obj,
j6_signal_t s,
j6_signal_t ds,
j6_status_t result)
{
log::info(logs::objs, " %s: Signals %016lx changed, object %p, result %016lx",
m_name, ds, obj, result);
return false;
}
const char *m_name;
};
void
init_console(const popcorn_data *header)
init_console()
{
serial_port *com1 = new (&g_com1) serial_port(COM1);
console *cons = new (&g_console) console(com1);
if (header->frame_buffer) {
screen *s = new screen(
header->frame_buffer,
header->hres,
header->vres,
header->rmask,
header->gmask,
header->bmask);
font *f = new font(header->font);
cons->init_screen(s, f);
}
cons->set_color(0x21, 0x00);
cons->puts("Popcorn OS ");
cons->puts("jsix OS ");
cons->set_color(0x08, 0x00);
cons->puts(GIT_VERSION " booting...\n");
log::init(cons);
log::enable(logs::apic, log::level::info);
log::enable(logs::device, log::level::debug);
log::enable(logs::driver, log::level::debug);
log::enable(logs::memory, log::level::info);
log::enable(logs::fs, log::level::debug);
log::enable(logs::task, log::level::debug);
logger_init();
}
void do_error_3() { volatile int x = 1; volatile int y = 0; volatile int z = x / y; }
void do_error_2() { do_error_3(); }
void do_error_1() { do_error_2(); }
void
kernel_main(popcorn_data *header)
kernel_main(kernel_args *header)
{
bool waiting = header && (header->flags && JSIX_FLAG_DEBUG);
while (waiting);
kutil::assert_set_callback(__kernel_assert);
gdt_init();
interrupts_init();
page_manager *pager = new (&g_page_manager) page_manager;
memory_initialize(
header->scratch_pages,
header->memory_map,
header->memory_map_length,
header->memory_map_desc_size);
pager->map_offset_pointer(
kutil::allocator &heap = g_kernel_heap;
if (header->frame_buffer && header->frame_buffer_length) {
page_manager::get()->map_offset_pointer(
&header->frame_buffer,
header->frame_buffer_length);
}
init_console(header);
init_console();
log::debug(logs::boot, " Popcorn header is at: %016lx", header);
log::debug(logs::boot, " jsix header is at: %016lx", header);
log::debug(logs::boot, " Framebuffer is at: %016lx", header->frame_buffer);
log::debug(logs::boot, " Font data is at: %016lx", header->font);
log::debug(logs::boot, " Kernel data is at: %016lx", header->data);
log::debug(logs::boot, " Memory map is at: %016lx", header->memory_map);
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
log::debug(logs::boot, "Runtime service is at: %016lx", header->runtime);
cpu_id cpu;
cpu.validate();
initrd::disk ird(header->initrd, heap);
log::info(logs::boot, "initrd loaded with %d files.", ird.files().count());
for (auto &f : ird.files())
log::info(logs::boot, " %s%s (%d bytes).",
f.type() == initrd::file_type::executable ? "*" :
f.type() == initrd::file_type::vdso ? "^" : "",
f.name(),
f.size());
/*
pager->dump_pml4(nullptr, 0);
pager->dump_blocks(true);
page_manager::get()->dump_pml4(nullptr, 0);
page_manager::get()->dump_blocks(true);
*/
device_manager *devices =
new (&device_manager::get()) device_manager(header->acpi_table);
new (&device_manager::get()) device_manager(header->acpi_table, heap);
interrupts_enable();
/*
cpu_id cpu;
log::info(logs::boot, "CPU Vendor: %s", cpu.vendor_id());
log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
cpu.family(), cpu.model(), cpu.stepping());
auto r = cpu.get(0x15);
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
addr_t cr4 = 0;
uintptr_t cr4 = 0;
__asm__ __volatile__ ( "mov %%cr4, %0" : "=r" (cr4) );
log::info(logs::boot, "cr4: %016x", cr4);
*/
@@ -143,14 +159,32 @@ kernel_main(popcorn_data *header)
}
*/
// do_error_1();
// __asm__ __volatile__("int $15");
// pager->dump_pml4();
devices->get_lapic()->calibrate_timer();
syscall_enable();
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic());
sched->start();
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic(), heap);
g_console.puts("boogity!\n");
sched->create_kernel_task(-1, logger_task);
for (auto &f : ird.files()) {
if (f.type() == initrd::file_type::executable)
sched->load_process(f.name(), f.data(), f.size());
}
log::info(logs::objs, "Testing object system:");
test_observer obs1("event");
test_observer obs2("no handles");
{
event e;
e.register_signal_observer(&obs1, j6_signal_user0);
e.register_signal_observer(&obs2, j6_signal_no_handles);
e.assert_signal(j6_signal_user0);
handle h(1, 0, &e);
}
sched->start();
}

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