74 Commits
v0.2.2 ... 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
130 changed files with 11258 additions and 4236 deletions

5
.gitignore vendored
View File

@@ -2,8 +2,9 @@
/build*
*.bak
tags
.gdbinit
popcorn.log
jsix.log
*.o
*.a
sysroot
.gdb_history
.peru

View File

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

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,36 +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 `ninja` build tool, and generates the build files for it with
the `generate_build.py` script. 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 3 for generating the build config
* The Jinja2 package is also required
* clang
* mtools
[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
If you have `clang` and `curl` installed, runing the `scripts/build_sysroot_clang.sh`
script will download and build a nasm/binutils/LLVM toolchain configured for building
Popcorn host binaries.
Running `pb sync` will download and unpack the toolchain into `sysroot`.
### Building and running Popcorn
#### Compiling the toolchain yourself
Once the toolchain has been set up, running `generate_build.py` will set up the
build configuration, and `ninja -C build` will actually run the build. If you
have `qemu-system-x86_64` installed, the `qemu.sh` script will to run Popcorn
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
sudo apt install qemu-system-x86 nasm clang-6.0 mtools curl
sudo update-alternatives /usr/bin/clang clang /usr/bin/clang-6.0 1000
sudo update-alternatives /usr/bin/clang++ clang++ /usr/bin/clang++-6.0 1000
curl -L -o pb https://github.com/justinian/bonnibel/releases/download/2.0.0/pb_linux_amd64 && chmod a+x pb

View File

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

View File

@@ -8,16 +8,21 @@
# # 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 = "host/nulldrv"
executable = true
source = "user/nulldrv"
type = "executable"
[[files]]
dest = "nulldrv2"
source = "host/nulldrv"
executable = true
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

View File

@@ -1,199 +0,0 @@
#!/usr/bin/env python3
from collections import namedtuple
library = namedtuple('library', ['path', 'deps'])
program = namedtuple('program', ['path', 'deps', 'output', 'targets'])
source = namedtuple('source', ['name', 'input', 'output', 'action'])
version = namedtuple('version', ['major', 'minor', 'patch', 'sha', 'dirty'])
MODULES = {}
class Source:
Actions = {'.c': 'cc', '.cpp': 'cxx', '.s': 'nasm'}
def __init__(self, path, root, modroot):
from os.path import relpath, splitext
self.input = path
self.name = relpath(path, root)
self.output = relpath(path, modroot) + ".o"
self.action = self.Actions.get(splitext(path)[1], None)
def __str__(self):
return "{} {}:{}:{}".format(self.action, self.output, self.name, self.input)
class Module:
def __init__(self, name, output, root, **kwargs):
from os.path import commonpath, dirname, isdir, join
self.name = name
self.output = output
self.kind = kwargs.get("kind", "exe")
self.target = kwargs.get("target", None)
self.deps = kwargs.get("deps", tuple())
self.includes = kwargs.get("includes", tuple())
self.defines = kwargs.get("defines", tuple())
self.depmods = []
sources = [join(root, f) for f in kwargs.get("source", tuple())]
modroot = commonpath(sources)
while not isdir(modroot):
modroot = dirname(modroot)
self.sources = [Source(f, root, modroot) for f in sources]
def __str__(self):
return "Module {} {}\n\t".format(self.kind, self.name)
def __find_depmods(self, modules):
self.depmods = set()
open_list = set(self.deps)
closed_list = set()
while open_list:
dep = modules[open_list.pop()]
open_list |= (set(dep.deps) - closed_list)
self.depmods.add(dep)
self.libdeps = [d for d in self.depmods if d.kind == "lib"]
self.exedeps = [d for d in self.depmods if d.kind != "lib"]
@classmethod
def load(cls, filename):
from os.path import abspath, dirname
from yaml import load
root = dirname(filename)
modules = {}
moddata = load(open(filename, "r"))
for name, data in moddata.items():
modules[name] = cls(name, root=root, **data)
for mod in modules.values():
mod.__find_depmods(modules)
targets = {}
for mod in modules.values():
if mod.target is None: continue
if mod.target not in targets:
targets[mod.target] = set()
targets[mod.target].add(mod)
targets[mod.target] |= mod.depmods
return modules.values(), targets
def get_template(env, typename, name):
from jinja2.exceptions import TemplateNotFound
try:
return env.get_template("{}.{}.j2".format(typename, name))
except TemplateNotFound:
return env.get_template("{}.default.j2".format(typename))
def get_git_version():
from subprocess import run
cp = run(['git', 'describe', '--dirty'],
check=True, capture_output=True)
full_version = cp.stdout.decode('utf-8').strip()
cp = run(['git', 'rev-parse', 'HEAD'],
check=True, capture_output=True)
full_sha = cp.stdout.decode('utf-8').strip()
dirty = False
parts1 = full_version.split('-')
if parts1[-1] == "dirty":
dirty = True
parts1 = parts1[:-1]
parts2 = parts1[0].split('.')
return version(
parts2[0],
parts2[1],
parts2[2],
full_sha[:7],
dirty)
def main(buildroot="build", modulefile="modules.yaml"):
import os
from os.path import abspath, dirname, isabs, isdir, join
generator = abspath(__file__)
srcroot = dirname(generator)
if not isabs(modulefile):
modulefile = join(srcroot, modulefile)
if not isabs(buildroot):
buildroot = join(srcroot, buildroot)
if not isdir(buildroot):
os.mkdir(buildroot)
git_version = get_git_version()
print("Generating build files for Popcorn {}.{}.{}-{}...".format(
git_version.major, git_version.minor, git_version.patch, git_version.sha))
from jinja2 import Environment, FileSystemLoader
template_dir = join(srcroot, "scripts", "templates")
env = Environment(loader=FileSystemLoader(template_dir))
buildfiles = []
templates = set()
modules, targets = Module.load(modulefile)
for mod in modules:
buildfile = join(buildroot, mod.name + ".ninja")
buildfiles.append(buildfile)
with open(buildfile, 'w') as out:
template = get_template(env, mod.kind, mod.name)
templates.add(template.filename)
out.write(template.render(
module=mod,
buildfile=buildfile,
version=git_version))
for target, mods in targets.items():
root = join(buildroot, target)
if not isdir(root):
os.mkdir(root)
buildfile = join(root, "target.ninja")
buildfiles.append(buildfile)
with open(buildfile, 'w') as out:
template = get_template(env, "target", target)
templates.add(template.filename)
out.write(template.render(
target=target,
modules=mods,
buildfile=buildfile,
version=git_version))
# Top level buildfile cannot use an absolute path or ninja won't
# reload itself properly on changes.
# See: https://github.com/ninja-build/ninja/issues/1240
buildfile = "build.ninja"
buildfiles.append(buildfile)
with open(join(buildroot, buildfile), 'w') as out:
template = env.get_template("build.ninja.j2")
templates.add(template.filename)
out.write(template.render(
targets=targets,
buildroot=buildroot,
srcroot=srcroot,
buildfile=buildfile,
buildfiles=buildfiles,
templates=[abspath(f) for f in templates],
generator=generator,
modulefile=modulefile,
version=git_version))
if __name__ == "__main__":
import sys
main(*sys.argv[1:])

View File

@@ -1,10 +1,15 @@
kernel:
output: popcorn.elf
name: jsix
templates: scripts/templates
modules:
kernel:
kind: exe
output: jsix.elf
target: host
deps:
- elf
- initrd
- kutil
- vdso
includes:
- src/kernel
source:
@@ -19,6 +24,7 @@ kernel:
- 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
@@ -30,6 +36,8 @@ kernel:
- 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
@@ -38,9 +46,12 @@ kernel:
- 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:
boot:
kind: exe
target: boot
output: boot.elf
@@ -54,14 +65,25 @@ boot:
- src/boot/reloc.cpp
- src/boot/utility.cpp
nulldrv:
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:
elf:
kind: lib
output: libelf.a
deps:
@@ -71,7 +93,7 @@ elf:
source:
- src/libraries/elf/elf.cpp
initrd:
initrd:
kind: lib
output: libinitrd.a
deps:
@@ -81,18 +103,21 @@ initrd:
source:
- src/libraries/initrd/initrd.cpp
kutil:
kutil:
kind: lib
output: libkutil.a
includes:
- src/libraries/kutil/include
source:
- src/libraries/kutil/assert.cpp
- src/libraries/kutil/frame_allocator.cpp
- src/libraries/kutil/heap_manager.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:
makerd:
kind: exe
target: native
output: makerd
@@ -102,7 +127,7 @@ makerd:
- src/tools/makerd/entry.cpp
- src/tools/makerd/main.cpp
tests:
tests:
kind: exe
target: native
output: tests
@@ -110,7 +135,13 @@ tests:
- kutil
source:
- src/tests/address_manager.cpp
- src/tests/frame_allocator.cpp
- src/tests/constexpr_hash.cpp
- src/tests/linked_list.cpp
- src/tests/heap_manager.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.

46
qemu.sh
View File

@@ -1,37 +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
kvm=""
if [[ -c /dev/kvm ]]; then
kvm="-enable-kvm"
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
ninja -C "${build}" && \
exec qemu-system-x86_64 \
-drive "if=pflash,format=raw,file=${build}/flash.img" \
-drive "format=raw,file=${build}/popcorn.img" \
-smp 1 \
-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 popcorn.log \
-cpu Broadwell \
-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"

View File

@@ -1,220 +0,0 @@
#!/usr/bin/env bash
TARGET="x86_64-elf"
NASM_VERSION="2.13.03"
BINUTILS_VERSION="2.31.1"
TOOLS="clang" # lld libunwind libcxxabi libcxx"
PROJECTS="compiler-rt libcxxabi libcxx libunwind"
#RUNTIMES="compiler-rt libcxxabi libcxx libunwind"
set -e
SYSROOT=$(realpath "$(dirname $0)/../sysroot")
WORK=$(realpath "$(dirname $0)/sysroot")
mkdir -p "${SYSROOT}"
mkdir -p "${WORK}"
export CC=clang
export CXX=clang++
function build_nasm() {
if [[ ! -d "${WORK}/nasm-${NASM_VERSION}" ]]; then
echo "Downloading NASM..."
tarball="nasm-${NASM_VERSION}.tar.gz"
curl -sSOL "https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/${tarball}"
tar xzf "${tarball}" -C "${WORK}" && rm "${tarball}"
fi
mkdir -p "${WORK}/build/nasm"
pushd "${WORK}/build/nasm"
if [[ ! -f "${WORK}/build/nasm/config.cache" ]]; then
echo "Configuring NASM..."
"${WORK}/nasm-${NASM_VERSION}/configure" \
--quiet \
--config-cache \
--disable-werror \
--prefix="${SYSROOT}" \
--srcdir="${WORK}/nasm-${NASM_VERSION}"
fi
echo "Building NASM..."
(make -j && make install) > "${WORK}/build/nasm_build.log"
popd
}
function build_binutils() {
if [[ ! -d "${WORK}/binutils-${BINUTILS_VERSION}" ]]; then
echo "Downloading binutils..."
tarball="binutils-${BINUTILS_VERSION}.tar.gz"
curl -sSOL "https://ftp.gnu.org/gnu/binutils/${tarball}"
tar xzf "${tarball}" -C "${WORK}" && rm "${tarball}"
fi
mkdir -p "${WORK}/build/binutils"
pushd "${WORK}/build/binutils"
if [[ ! -f "${WORK}/build/binutils/config.cache" ]]; then
echo "Configuring binutils..."
"${WORK}/binutils-${BINUTILS_VERSION}/configure" \
--quiet \
--config-cache \
--target="${TARGET}" \
--prefix="${SYSROOT}" \
--with-sysroot="${SYSROOT}" \
--with-lib-path="${SYSROOT}/lib" \
--disable-nls \
--disable-werror
fi
echo "Building binutils..."
(make -j && make install) > "${WORK}/build/binutils_build.log"
popd
}
function build_llvm() {
if [[ ! -d "${WORK}/llvm" ]]; then
echo "Downloading LLVM..."
git clone -q \
--branch release_70 \
--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 release_70 \
--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 release_70 \
--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 release_70 \
--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 release_70 \
--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_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_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
}
function build_libc() {
if [[ ! -d "${WORK}/poplibc" ]]; then
echo "Downloading poplibc..."
git clone \
"https://github.com/justinian/poplibc.git" \
"${WORK}/poplibc"
else
echo "Updating poplibc..."
git -C "${WORK}/poplibc" pull
fi
pushd "${WORK}/poplibc"
echo "Building poplibc..."
make install PREFIX="${SYSROOT}"
popd
}
function update_links() {
for exe in `ls "${SYSROOT}/bin/${TARGET}-"*`; do
base=$(echo "$exe" | sed -e "s/${TARGET}-//")
ln -fs "${exe}" "${base}"
done
}
build_nasm
build_binutils
build_libc
build_llvm
update_links
export CC="${SYSROOT}/bin/clang"
export CXX="${SYSROOT}/bin/clang++"
export LD="${SYSROOT}/bin/ld"
build_libc

View File

@@ -1,186 +0,0 @@
#!/usr/bin/env bash
TARGET="x86_64-elf"
NASM_VERSION="2.14.02"
GCC_VERSION="7.4.0"
BINUTILS_VERSION="2.31.1"
SYSROOT=$(realpath "$(dirname $0)/../sysroot")
WORK=$(realpath "$(dirname $0)/sysroot")
echo "Not currently supported"
exit 1
set -e
mkdir -p "${SYSROOT}"
mkdir -p "${WORK}"
function build_nasm() {
if [[ ! -d "${WORK}/nasm-${NASM_VERSION}" ]]; then
echo "Downloading NASM..."
tarball="nasm-${NASM_VERSION}.tar.gz"
curl -sSOL "https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/${tarball}"
tar xzf "${tarball}" -C "${WORK}" && rm "${tarball}"
fi
mkdir -p "${WORK}/build/nasm"
pushd "${WORK}/build/nasm"
if [[ ! -f "${WORK}/build/nasm/config.cache" ]]; then
echo "Configuring NASM..."
"${WORK}/nasm-${NASM_VERSION}/configure" \
--quiet \
--config-cache \
--disable-werror \
--prefix="${SYSROOT}" \
--srcdir="${WORK}/nasm-${NASM_VERSION}"
fi
echo "Building NASM..."
(make -j && make install) > "${WORK}/build/nasm_build.log"
popd
}
function build_binutils() {
if [[ ! -d "${WORK}/binutils-${BINUTILS_VERSION}" ]]; then
echo "Downloading binutils..."
tarball="binutils-${BINUTILS_VERSION}.tar.gz"
curl -sSOL "https://ftp.gnu.org/gnu/binutils/${tarball}"
tar xzf "${tarball}" -C "${WORK}" && rm "${tarball}"
fi
mkdir -p "${WORK}/build/binutils"
pushd "${WORK}/build/binutils"
if [[ ! -f "${WORK}/build/binutils/config.cache" ]]; then
echo "Configuring binutils..."
"${WORK}/binutils-${BINUTILS_VERSION}/configure" \
--quiet \
--config-cache \
--target="${TARGET}" \
--prefix="${SYSROOT}" \
--with-sysroot="${SYSROOT}" \
--with-lib-path="${SYSROOT}/lib" \
--disable-nls \
--disable-werror
fi
echo "Building binutils..."
(make -j && make install) > "${WORK}/build/binutils_build.log"
popd
}
function build_gcc() {
if [[ ! -d "${WORK}/gcc-${GCC_VERSION}" ]]; then
echo "Downloading GCC..."
tarball="gcc-${GCC_VERSION}.tar.gz"
curl -sSOL "https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/${tarball}"
tar xzf "${tarball}" -C "${WORK}" && rm "${tarball}"
# no-red-zone support version of libgcc
echo "MULTILIB_OPTIONS += mno-red-zone" > "${WORK}/gcc-${GCC_VERSION}/gcc/config/i386/t-${TARGET}"
echo "MULTILIB_DIRNAMES += no-red-zone" >> "${WORK}/gcc-${GCC_VERSION}/gcc/config/i386/t-${TARGET}"
cat <<EOF >> "${WORK}/gcc-${GCC_VERSION}/gcc/config.gcc"
case \${target} in
${TARGET})
tmake_file="\${tmake_file} i386/t-${TARGET}"
;;
esac
EOF
fi
mkdir -p "${WORK}/build/gcc"
pushd "${WORK}/build/gcc"
if [[ ! -f "${WORK}/build/gcc/config.cache" ]]; then
echo "Configuring GCC..."
"${WORK}/gcc-${GCC_VERSION}/configure" \
--quiet \
--config-cache \
--target="${TARGET}" \
--prefix="${SYSROOT}" \
--with-sysroot="${SYSROOT}" \
--with-native-system-header-dir="${SYSROOT}/include" \
--with-newlib \
--without-headers \
--disable-nls \
--enable-languages=c,c++ \
--disable-shared \
--disable-multilib \
--disable-decimal-float \
--disable-threads \
--disable-libatomic \
--disable-libgomp \
--disable-libmpx \
--disable-libquadmath \
--disable-libssp \
--disable-libvtv \
--disable-libstdcxx
fi
echo "Building GCC..."
(make -j all-gcc && make -j all-target-libgcc && \
make install-gcc && make install-target-libgcc) > "${WORK}/build/gcc_build.log"
popd
}
function build_libstdcxx() {
mkdir -p "${WORK}/build/libstdcxx"
pushd "${WORK}/build/libstdcxx"
if [[ ! -f "${WORK}/build/libstdcxx/config.cache" ]]; then
echo "Configuring libstdc++..."
CFLAGS="-I${SYSROOT}/include" \
CXXFLAGS="-I${SYSROOT}/include" \
"${WORK}/gcc-${GCC_VERSION}/libstdc++-v3/configure" \
--config-cache \
--host="${TARGET}" \
--target="${TARGET}" \
--prefix="${SYSROOT}" \
--disable-nls \
--disable-multilib \
--with-newlib \
--disable-libstdcxx-threads \
--disable-libstdcxx-pch \
--with-gxx-include-dir="${SYSROOT}/include/c++"
fi
echo "Building libstdc++..."
(make -j && make install) > "${WORK}/build/libstdcxx_build.log"
popd
}
function build_libc() {
if [[ ! -d "${WORK}/poplibc" ]]; then
echo "Downloading poplibc..."
git clone \
"https://github.com/justinian/poplibc.git" \
"${WORK}/poplibc"
else
echo "Updating poplibc..."
git -C "${WORK}/poplibc" pull
fi
pushd "${WORK}/poplibc"
echo "Building poplibc..."
make install PREFIX="${SYSROOT}"
popd
}
function update_links() {
for exe in `ls "${SYSROOT}/bin/${TARGET}-"*`; do
base=$(echo "$exe" | sed -e "s/${TARGET}-//")
ln -fs "${exe}" "${base}"
done
}
build_nasm
build_binutils
build_gcc
update_links
export PATH="${SYSROOT}/bin:${PATH}"
build_libc
build_libstdcxx

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

@@ -3,6 +3,10 @@ builddir = {{ buildroot }}
srcroot = {{ srcroot }}
modulefile = {{ modulefile }}
{%- for var, value in vars %}
{{ var }} = {{ value }}
{%- endfor %}
warnflags = $
-Wformat=2 $
-Winit-self $
@@ -29,59 +33,61 @@ warnflags = $
ccflags = $
-I${srcroot}/src/include $
-I${srcroot}/src/include/x86_64 $
-DVERSION_MAJOR={{ version.major }} $
-DVERSION_MINOR={{ version.minor }} $
-DVERSION_PATCH={{ version.patch }} $
-DVERSION_GITSHA=0x{% if version.dirty %}1{% else %}0{% endif %}{{ version.sha }} $
-DGIT_VERSION=\"{{ version.major }}.{{ version.minor }}.{{ version.patch }}-{{ version.sha }}\" $
-DGIT_VERSION_WIDE=L\"{{ version.major }}.{{ version.minor }}.{{ version.patch }}-{{ version.sha }}\" $
-fcolor-diagnostics $
-DVERSION_MAJOR={{ version_major }} $
-DVERSION_MINOR={{ version_minor }} $
-DVERSION_PATCH={{ version_patch }} $
-DVERSION_GITSHA=0x0{{ version_sha }} $
-DGIT_VERSION=\"{{ version }}\" $
-DGIT_VERSION_WIDE=L\"{{ version }}\" $
$warnflags
asflags = $
-DVERSION_MAJOR={{ version.major }} $
-DVERSION_MINOR={{ version.minor }} $
-DVERSION_PATCH={{ version.patch }} $
-DVERSION_GITSHA=0x{% if version.dirty %}1{% else %}0{% endif %}{{ version.sha }}
-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 cc
rule c
deps = gcc
depfile = $out.d
description = Compiling $name
command = $cc -MMD -MF $out.d $ccflags $cflags -o $out -c $in
rule dump_cc_defs
description = Dumping CC defines for $target
rule dump_c_defs
description = Dumping C defines for $target
command = echo "" | $cc $ccflags $cflags -dM -E - > $out
rule dump_cc_run
description = Dumping CC arguments for $target
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 cxx
rule cpp
deps = gcc
depfile = $out.d
description = Compiling $name
command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in
rule dump_cxx_defs
rule dump_cpp_defs
description = Dumping C++ defines for $target
command = echo "" | $cxx -x c++ $cxxflags $ccflags -dM -E - > $out
rule dump_cxx_run
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 nasm
rule s
deps = gcc
depfile = $out.d
description = Assembling $name
@@ -98,12 +104,20 @@ rule lib
rule regen
generator = true
description = Regenrating build files
command = {{ generator }} $builddir $modulefile
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
@@ -136,7 +150,6 @@ rule strip
strip -g $out; $
objcopy --add-gnu-debuglink=$out.debug $out
{% for target in targets %}
subninja {{ target }}/target.ninja
{% endfor %}
@@ -152,27 +165,44 @@ build $
$modulefile $
{{ generator }}
build $builddir/flash.img : cp $srcroot/assets/ovmf/x64/OVMF.fd
name = flash.img
build $builddir/ovmf_vars.fd : cp $srcroot/assets/ovmf/x64/ovmf_vars.fd
name = ovmf_vars.fd
build $builddir/popcorn.elf | $builddir/popcorn.elf.debug : strip $builddir/host/popcorn.elf
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/fatroot/popcorn.elf : cp $builddir/popcorn.elf
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}/host/nulldrv
${builddir}/user/nulldrv
build $builddir/popcorn.img : makefat | $
build $builddir/jsix.img : makefat | $
$builddir/fatroot/initrd.img $
$builddir/fatroot/popcorn.elf $
$builddir/fatroot/jsix.elf $
$builddir/fatroot/efi/boot/bootx64.efi
name = popcorn.img
name = jsix.img
# vim: et ts=4 sts=4 sw=4
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

@@ -2,18 +2,13 @@
{% block variables %}
{{ super() }}
ld = ld
cc = clang
cxx = clang++
ccflags = $ccflags $
-DKERNEL_FILENAME=L\"popcorn.elf\" $
-DKERNEL_FILENAME=L\"jsix.elf\" $
-DGNU_EFI_USE_MS_ABI $
-DHAVE_USE_MS_ABI $
-DEFI_DEBUG=0 $
-DEFI_DEBUG_CLEAR_MEMORY=0 $
-DBOOTLOADER_DEBUG $
-fPIC
-DBOOTLOADER_DEBUG
ldflags = $ldflags $
-T ${srcroot}/src/arch/x86_64/boot.ld $
@@ -27,3 +22,6 @@ build $builddir/boot.efi : makeefi ${builddir}/{{ module.output }}
name = boot.efi
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -2,11 +2,7 @@
{% block variables %}
{{ super() }}
libs = $
-L${builddir} $
{%- for dep in module.libdeps %}
-l{{ dep.name }} $
{%- endfor %}
$libs
{% endblock %}
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -7,3 +7,6 @@ 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

@@ -2,6 +2,9 @@
{% block variables %}
{{ super() }}
ccflags = $ccflags -ggdb
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

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

View File

@@ -1,8 +1,8 @@
moddir = ${builddir}/{{ module.name }}.dir
moddir = ${builddir}/{{ name }}.dir
{% block variables %}
ccflags = $ccflags $
{%- for dep in module.depmods %}
{%- for dep in depmods %}
{%- for inc in dep.includes %}
-I${srcroot}/{{ inc }} $
{%- endfor %}
@@ -15,24 +15,28 @@ ccflags = $ccflags $
{%- endfor %}
{% endblock %}
{% for source in module.sources %}
build ${moddir}/{{ source.output }} : {{ source.action }} {{ source.input }} || {{ buildfile }}
{% 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.sources %}
{%- for source in module.source %}
${moddir}/{{ source.output }} $
{%- endfor -%}
{%- for dep in module.libdeps %}
{%- for dep in deplibs %}
${builddir}/{{ dep.output }} $
{%- endfor %}
| $
{% for dep in module.exedeps %}
{%- for dep in depexes %}
${builddir}/{{ dep.output }} $
{%- endfor -%}
{%- endfor %}
{{ buildfile }}
name = {{ module.name }}
name = {{ name }}
{% if module.default %}
default ${builddir}/{{ module.output }}
{% endif %}
{% block extra %}
{% endblock %}

View File

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

View File

@@ -7,18 +7,20 @@ target = {{ target }}
{% block binaries %}
cc = clang
cxx = clang++
ld = clang++
ld = ld
ar = ar
nasm = nasm
objcopy = objcopy
{% endblock %}
{% for module in modules %}
subninja {{ module.name }}.ninja
subninja {{ module }}.ninja
{% endfor %}
build ${builddir}/cc.defs : dump_cc_defs | {{ buildfile }}
build ${builddir}/cxx.defs : dump_cxx_defs | {{ buildfile }}
build ${builddir}/cc.run : dump_cc_run | {{ buildfile }}
build ${builddir}/cxx.run : dump_cxx_run | {{ buildfile }}
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

@@ -3,10 +3,10 @@
{% block binaries %}
cc = ${srcroot}/sysroot/bin/clang
cxx = ${srcroot}/sysroot/bin/clang++
ld = ${srcroot}/sysroot/bin/x86_64-elf-ld
ar = ${srcroot}/sysroot/bin/x86_64-elf-ar
nasm = ${srcroot}/sysroot/bin/nasm
objcopy = ${srcroot}/sysroot/bin/x86_64-elf-objcopy
ld = ${srcroot}/sysroot/bin/ld.lld
ar = ${srcroot}/sysroot/bin/ar
nasm = nasm
objcopy = ${srcroot}/sysroot/bin/objcopy
{% endblock %}
{% block variables %}
@@ -22,7 +22,7 @@ ccflags = $ccflags $
-g $
-mcmodel=large $
-D__ELF__ $
-D__POPCORN__ $
-D__JSIX__ $
-isystem${srcroot}/sysroot/include $
--sysroot="${srcroot}/sysroot"
@@ -34,10 +34,10 @@ cxxflags = $cxxflags $
ldflags = $ldflags $
-g $
-nostdlib $
-znocombreloc $
-Bsymbolic $
-nostartfiles $
-Bstatic
{% endblock %}
# vim: et ts=4 sts=4 sw=4
# vim: ft=ninja et ts=4 sts=4 sw=4

View File

@@ -1,8 +1,15 @@
{% extends "target.default.j2" %}
{% block binaries %}
{{ super() }}
ld = clang++
{% endblock %}
{% block variables %}
ccflags = $ccflags -g -ggdb
{% endblock %}
# vim: et ts=4 sts=4 sw=4
# 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,29 +10,26 @@ 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(16) : {
__bss_start = .;
*(.bss)
__bss_end = .;
}
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))

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

@@ -40,7 +40,7 @@ console::initialize(const wchar_t *version)
CHECK_EFI_STATUS_OR_RETURN(status, "ClearScreen");
m_out->SetAttribute(m_out, EFI_LIGHTCYAN);
m_out->OutputString(m_out, (wchar_t *)L"Popcorn loader ");
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);

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

@@ -9,31 +9,73 @@
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(
EFI_BOOT_SERVICES *bootsvc,
EFI_MEMORY_TYPE mem_type,
size_t *length,
void **pages,
bool align)
void **pages)
{
EFI_STATUS status;
size_t page_count = ((*length - 1) / PAGE_SIZE) + 1;
EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)*pages;
if (align) {
// Align addr to the next multiple of N pages
size_t align_size = page_count * PAGE_SIZE;
addr = ((addr - 1) & ~(align_size - 1)) + align_size;
}
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);
@@ -69,12 +111,11 @@ loader_load_initrd(
data->initrd_length = ((EFI_FILE_INFO *)info)->FileSize;
status = loader_alloc_pages(
status = loader_alloc_aligned(
bootsvc,
memtype_initrd,
&data->initrd_length,
&data->initrd,
true);
&data->initrd);
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
status = file->Read(file, &data->initrd_length, data->initrd);
@@ -95,7 +136,7 @@ loader_load_elf(
{
EFI_STATUS status;
con_debug(L"Opening kernel file %s\r\n", (wchar_t *)kernel_name);
con_debug(L"Opening kernel file %s", (wchar_t *)kernel_name);
EFI_FILE_PROTOCOL *file = NULL;
status = root->Open(root, &file, (wchar_t *)kernel_name, EFI_FILE_MODE_READ,
@@ -117,7 +158,7 @@ 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\r\n", length);
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");
@@ -143,7 +184,7 @@ 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 %lu\r\n", header.entrypoint);
con_debug(L"ELF is valid, entrypoint %lx", header.entrypoint);
data->kernel_entry = (void *)header.entrypoint;
@@ -161,14 +202,14 @@ loader_load_elf(
length = prog_header.mem_size;
void *addr = (void *)(prog_header.vaddr - KERNEL_VIRT_ADDRESS);
status = loader_alloc_pages(bootsvc, memtype_kernel, &length, &addr, false);
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\r\n", header.ph_num);
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) {
@@ -196,7 +237,7 @@ loader_load_elf(
bootsvc->SetMem(addr, sec_header.size, 0);
}
}
con_debug(L"Read %d ELF section headers\r\n", header.ph_num);
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");
@@ -236,20 +277,19 @@ loader_load_kernel(
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_elf: %s", kernel_name);
data->initrd = (void *)((uint64_t)data->kernel + data->kernel_length);
status = loader_load_initrd(bootsvc, root, data);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", initrd_name);
data->data = (void *)((uint64_t)data->initrd + data->initrd_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,
memtype_data,
&data->data_length,
&data->data,
true);
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data");
&data->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

@@ -5,7 +5,7 @@
#include "console.h"
#include "guids.h"
#include "kernel_data.h"
#include "kernel_args.h"
#include "loader.h"
#include "memory.h"
#include "utility.h"
@@ -34,7 +34,51 @@ struct kernel_header {
};
#pragma pack(pop)
using kernel_entry = void (*)(popcorn_data *);
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)
@@ -73,14 +117,13 @@ 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(popcorn_data);
const size_t header_align = alignof(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
//
console::print(L"Loading kernel into memory...\r\n");
@@ -90,9 +133,9 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
status = loader_load_kernel(bootsvc, &load);
CHECK_EFI_STATUS_OR_FAIL(status);
console::print(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
console::print(L" %u initrd bytes at 0x%x\r\n", load.initrd_length, load.initrd);
console::print(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
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) {
@@ -102,7 +145,7 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
console::print(L" Kernel version %d.%d.%d %x%s\r\n",
version->major, version->minor, version->patch, version->gitsha & 0x0fffffff,
version->gitsha & 0xf0000000 ? "*" : "");
version->gitsha & 0xf0000000 ? L"*" : L"");
console::print(L" Entrypoint 0x%x\r\n", load.kernel_entry);
kernel_entry kernel_main =
@@ -111,12 +154,12 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
// 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;
@@ -168,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

@@ -34,7 +34,7 @@ const wchar_t *util_error_message(EFI_STATUS status);
}
#ifdef BOOTLOADER_DEBUG
#define con_debug(...) console::print(L"DEBUG: " __VA_ARGS__)
#define con_debug(msg, ...) console::print(L"DEBUG: " msg L"\r\n", __VA_ARGS__)
#else
#define con_debug(...)
#define con_debug(msg, ...)
#endif

View File

@@ -1 +1,38 @@
int main(int argc, const char **argv) { return 0; }
#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

@@ -1,61 +1,81 @@
section .bss
mypid: resq 1
mychild: resq 1
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 rax, 5 ; GETPID syscall
int 0xee
mov [mypid], rax
mov rdi, 0
mov rsi, 0
call main
mov rax, 8 ; FORK syscall
int 0xee
mov [mychild], rax
mov r12, [mypid]
mov r13, [mychild]
mov rax, 1 ; DEBUG syscall
int 0xee
cmp r12, 1
je .dosend
jne .doreceive
.preloop:
mov r11, 0 ; counter
mov rbx, 20 ; sleep timeout
.loop:
mov rax, 1 ; MESSAGE syscall
;mov rax, 0 ; NOOP syscall
;syscall
int 0xee
inc r11
cmp r11, 2
jle .loop
mov rax, 4 ; SLEEP syscall
; syscall
int 0xee
add rbx, 20
mov r11, 0
jmp .loop
.dosend:
mov rax, 6 ; SEND syscall
mov rdi, 2 ; target is pid 2
int 0xee
jmp .preloop
.doreceive:
mov rax, 7 ; RECEIVE syscall
mov rdi, 1 ; source is pid 2
int 0xee
jmp .preloop
mov rdi, rax
call exit

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,8 +7,10 @@
#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;

View File

@@ -2,6 +2,9 @@
/// \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.
@@ -19,4 +22,14 @@ namespace memory {
/// 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

@@ -8,19 +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");
}
extern "C" [[noreturn]] void
__assert_fail(const char *message, const char *file, unsigned int line, const char *function)
{
__kernel_assert(file, line, message);
__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,6 +1,7 @@
#include "kutil/coord.h"
#include "kutil/guid.h"
#include "kutil/memory.h"
#include "kutil/printf.h"
#include "console.h"
#include "font.h"
#include "screen.h"
@@ -278,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,42 +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

@@ -2,49 +2,90 @@
#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)

View File

@@ -4,56 +4,65 @@
#include "gdt.h"
#include "page_manager.h"
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
size_t __counter_syscall_enter = 0;
size_t __counter_syscall_sysret = 0;
void
print_regs(const cpu_state &regs)
{
console *cons = console::get();
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);
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_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);
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");
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.user_rsp);
print_reg("sp0", tss_get_stack(0));
cons->puts("\n");
print_reg(" ds", regs.ds);
print_reg(" cs", regs.cs);
print_reg(" ss", regs.ss);
cons->puts("\n");
print_reg("rip", regs.rip);
cons->puts("\n");
print_reg("cr3", page_manager::get()->get_pml4());
}
struct frame
{
frame *prev;
uintptr_t return_addr;
};
void
print_stacktrace(int skip)
{
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);
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;
}
}

View File

@@ -8,12 +8,18 @@ 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 = 0);
void print_stacktrace(int skip);
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
#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

@@ -14,7 +14,7 @@
static const char expected_signature[] = "RSD PTR ";
device_manager device_manager::s_instance(nullptr);
device_manager device_manager::s_instance(nullptr, kutil::allocator::invalid);
struct acpi1_rsdp
{
@@ -59,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.");
@@ -93,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
@@ -148,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;
@@ -180,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;
@@ -206,10 +229,10 @@ device_manager::load_apic(const acpi_apic *apic)
p += length;
}
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);
}
}

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

@@ -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

@@ -25,7 +25,9 @@ void tss_set_stack(int ring, uintptr_t rsp);
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);

View File

@@ -18,9 +18,8 @@ static const uint16_t PIC2 = 0xa0;
extern "C" {
void _halt();
uintptr_t isr_handler(uintptr_t, cpu_state*);
uintptr_t irq_handler(uintptr_t, cpu_state*);
uintptr_t syscall_handler(uintptr_t, cpu_state);
void isr_handler(cpu_state*);
void irq_handler(cpu_state*);
#define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name ();
@@ -104,13 +103,45 @@ interrupts_init()
log::info(logs::boot, "Interrupts enabled.");
}
uintptr_t
isr_handler(uintptr_t return_rsp, cpu_state *regs)
void
isr_handler(cpu_state *regs)
{
console *cons = console::get();
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");
@@ -124,13 +155,13 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
switch ((regs->errorcode & 0x07) >> 1) {
case 0:
cons->printf(" GDT[%x]\n", index);
gdt_dump();
gdt_dump(index);
break;
case 1:
case 3:
cons->printf(" IDT[%x]\n", index);
idt_dump();
idt_dump(index);
break;
default:
@@ -145,12 +176,18 @@ isr_handler(uintptr_t return_rsp, cpu_state *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();
@@ -162,24 +199,14 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
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);
}
print_regs(*regs);
print_stacktrace(2);
_halt();
}
break;
case isr::isrTimer: {
scheduler &s = scheduler::get();
return_rsp = s.tick(return_rsp);
}
case isr::isrTimer:
scheduler::get().tick();
break;
case isr::isrLINT0:
@@ -198,14 +225,15 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
_halt();
break;
case isr::isrSyscall: {
return_rsp = syscall_dispatch(return_rsp, *regs);
}
/*
case isr::isrSyscall:
syscall_dispatch(regs);
break;
*/
case isr::isrSpurious:
// No EOI for the spurious interrupt
return return_rsp;
return;
case isr::isrIgnore0:
case isr::isrIgnore1:
@@ -242,16 +270,14 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
regs->interrupt, regs->errorcode);
print_regs(*regs);
//print_stacktrace(2);
print_stacktrace(2);
_halt();
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
}
uintptr_t
irq_handler(uintptr_t return_rsp, cpu_state *regs)
void
irq_handler(cpu_state *regs)
{
console *cons = console::get();
uint8_t irq = get_irq(regs->interrupt);
@@ -265,11 +291,4 @@ irq_handler(uintptr_t return_rsp, cpu_state *regs)
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
}
uintptr_t
syscall_handler(uintptr_t return_rsp, cpu_state regs)
{
return syscall_dispatch(return_rsp, regs);
}

View File

@@ -3,39 +3,36 @@
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
@@ -44,7 +41,6 @@ irq_handler_prelude:
%macro EMIT_EISR 2
global %1
%1:
cli
push %2
jmp isr_handler_prelude
%endmacro
@@ -52,7 +48,6 @@ irq_handler_prelude:
%macro EMIT_IRQ 2
global %1
%1:
cli
push 0
push %2
jmp irq_handler_prelude

View File

@@ -1,25 +1,22 @@
%include "push_all.inc"
extern load_process
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, and the saved
; state for the following iretq
;
; Additional parameters:
; rax - the address of the program image
; rbx - the size of the program image
; rcx - the address of this process' process structure
mov rdi, rax
mov rsi, rbx
mov rdx, rcx
call load_process
; acts both as the cpu_state parameter to load_process_image, and the
; saved state for the following iretq
pop_all_and_segments
add rsp, 16 ; because the ISRs add err/num
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,107 +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 ",
"paging",
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,52 +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,
paging,
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,8 +1,12 @@
#include <stddef.h>
#include <stdint.h>
#include "j6/signals.h"
#include "initrd/initrd.h"
#include "kutil/assert.h"
#include "kutil/heap_allocator.h"
#include "kutil/vm_space.h"
#include "apic.h"
#include "block_device.h"
#include "console.h"
@@ -11,21 +15,45 @@
#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 "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 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()
{
@@ -33,34 +61,18 @@ init_console()
console *cons = new (&g_console) console(com1);
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::info);
log::enable(logs::driver, log::level::debug);
log::enable(logs::memory, log::level::debug);
log::enable(logs::fs, log::level::debug);
log::enable(logs::task, log::level::debug);
log::enable(logs::boot, log::level::debug);
log::enable(logs::paging, log::level::debug);
logger_init();
}
void
kernel_main(popcorn_data *header)
kernel_main(kernel_args *header)
{
#ifdef DEBUG
// Run `waf configure --debug` to enable compiling with DEBUG turned on.
// Then attach to QEMU's gdb server and `set waiting = false` to start
// the kernel. This compensates for GDB's poor handling of QEMU going
// through the x86 PC startup and switching to 64 bit mode when you
// attach to qemu with the -S option.
bool waiting = true;
bool waiting = header && (header->flags && JSIX_FLAG_DEBUG);
while (waiting);
#endif
kutil::assert_set_callback(__kernel_assert);
@@ -73,6 +85,8 @@ kernel_main(popcorn_data *header)
header->memory_map_length,
header->memory_map_desc_size);
kutil::allocator &heap = g_kernel_heap;
if (header->frame_buffer && header->frame_buffer_length) {
page_manager::get()->map_offset_pointer(
&header->frame_buffer,
@@ -81,17 +95,24 @@ kernel_main(popcorn_data *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, " 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);
initrd::disk ird(header->initrd);
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.executable() ? "*" : "", f.name(), f.size());
log::info(logs::boot, " %s%s (%d bytes).",
f.type() == initrd::file_type::executable ? "*" :
f.type() == initrd::file_type::vdso ? "^" : "",
f.name(),
f.size());
/*
page_manager::get()->dump_pml4(nullptr, 0);
@@ -99,16 +120,11 @@ kernel_main(popcorn_data *header)
*/
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);
@@ -146,12 +162,29 @@ kernel_main(popcorn_data *header)
devices->get_lapic()->calibrate_timer();
syscall_enable();
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic());
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic(), heap);
sched->create_kernel_task(-1, logger_task);
for (auto &f : ird.files()) {
if (f.executable())
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();
}

View File

@@ -1,73 +1,29 @@
#include <algorithm>
#include <utility>
#include "kutil/address_manager.h"
#include "kutil/assert.h"
#include "kutil/frame_allocator.h"
#include "kutil/heap_manager.h"
#include "kutil/heap_allocator.h"
#include "kutil/vm_space.h"
#include "frame_allocator.h"
#include "io.h"
#include "log.h"
#include "page_manager.h"
using kutil::frame_block;
using kutil::frame_block_flags;
using kutil::frame_block_list;
using memory::frame_size;
using memory::heap_start;
using memory::kernel_max_heap;
using memory::kernel_offset;
using memory::page_offset;
static const unsigned ident_page_flags = 0xb;
kutil::vm_space g_kspace;
kutil::heap_allocator g_kernel_heap;
bool g_memory_initialized = false;
kutil::frame_allocator g_frame_allocator;
kutil::address_manager g_kernel_address_manager;
kutil::heap_manager g_kernel_heap_manager;
void * mm_grow_callback(size_t length)
{
kassert(length % frame_size == 0,
"Heap manager requested a fractional page.");
size_t pages = length / frame_size;
log::info(logs::memory, "Heap manager growing heap by %d pages.", pages);
uintptr_t addr = g_kernel_address_manager.allocate(length);
g_page_manager.map_pages(addr, pages);
return reinterpret_cast<void *>(addr);
}
namespace {
// Page-by-page initial allocator for the initial frame_block allocator
struct page_consumer
{
page_consumer(uintptr_t start, unsigned count, unsigned used = 0) :
current(start + used * frame_size),
used(used),
max(count) {}
void * get_page() {
kassert(used++ < max, "page_consumer ran out of pages");
void *retval = reinterpret_cast<void *>(current);
current += frame_size;
return retval;
}
void * operator()(size_t size) {
kassert(size == frame_size, "page_consumer used with non-page size!");
return get_page();
}
unsigned left() const { return max - used; }
uintptr_t current;
unsigned used, max;
};
using block_allocator =
kutil::slab_allocator<kutil::frame_block, page_consumer &>;
using region_allocator =
kutil::slab_allocator<kutil::buddy_region, page_consumer &>;
}
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
void operator delete (void *p) noexcept { return g_kernel_heap.free(p); }
void operator delete [] (void *p) noexcept { return g_kernel_heap.free(p); }
enum class efi_memory_type : uint32_t
{
@@ -89,12 +45,12 @@ enum class efi_memory_type : uint32_t
efi_max,
popcorn_kernel = 0x80000000,
popcorn_data,
popcorn_initrd,
popcorn_scratch,
jsix_kernel = 0x80000000,
jsix_data,
jsix_initrd,
jsix_scratch,
popcorn_max
jsix_max
};
struct efi_memory_descriptor
@@ -107,100 +63,112 @@ struct efi_memory_descriptor
uint64_t flags;
};
static const efi_memory_descriptor *
desc_incr(const efi_memory_descriptor *d, size_t desc_length)
struct memory_map
{
return reinterpret_cast<const efi_memory_descriptor *>(
reinterpret_cast<const uint8_t *>(d) + desc_length);
}
memory_map(const void *efi_map, size_t map_length, size_t desc_length) :
efi_map(efi_map), map_length(map_length), desc_length(desc_length) {}
void
gather_block_lists(
block_allocator &allocator,
frame_block_list &used,
frame_block_list &free,
const void *memory_map,
size_t map_length,
size_t desc_length)
{
efi_memory_descriptor const *desc = reinterpret_cast<efi_memory_descriptor const *>(memory_map);
efi_memory_descriptor const *end = desc_incr(desc, map_length);
class iterator
{
public:
iterator(const memory_map &map, efi_memory_descriptor const *item) :
map(map), item(item) {}
while (desc < end) {
auto *block = allocator.pop();
block->address = desc->physical_start;
block->count = desc->pages;
bool block_used;
switch (desc->type) {
case efi_memory_type::loader_code:
case efi_memory_type::loader_data:
block_used = true;
block->flags = frame_block_flags::pending_free;
break;
case efi_memory_type::boot_services_code:
case efi_memory_type::boot_services_data:
case efi_memory_type::available:
block_used = false;
break;
case efi_memory_type::acpi_reclaim:
block_used = true;
block->flags =
frame_block_flags::acpi_wait |
frame_block_flags::map_ident;
break;
case efi_memory_type::persistent:
block_used = false;
block->flags = frame_block_flags::nonvolatile;
break;
case efi_memory_type::popcorn_kernel:
block_used = true;
block->flags =
frame_block_flags::permanent |
frame_block_flags::map_kernel;
break;
case efi_memory_type::popcorn_data:
case efi_memory_type::popcorn_initrd:
block_used = true;
block->flags =
frame_block_flags::pending_free |
frame_block_flags::map_kernel;
break;
case efi_memory_type::popcorn_scratch:
block_used = true;
block->flags = frame_block_flags::map_offset;
break;
default:
block_used = true;
block->flags = frame_block_flags::permanent;
break;
inline efi_memory_descriptor const * operator*() const { return item; }
inline bool operator!=(const iterator &other) { return item != other.item; }
inline iterator & operator++() {
item = kutil::offset_pointer(item, map.desc_length);
return *this;
}
if (block_used)
used.push_back(block);
else
free.push_back(block);
private:
const memory_map &map;
efi_memory_descriptor const *item;
};
desc = desc_incr(desc, desc_length);
iterator begin() const {
return iterator(*this, reinterpret_cast<efi_memory_descriptor const *>(efi_map));
}
}
iterator end() const {
const void *end = kutil::offset_pointer(efi_map, map_length);
return iterator(*this, reinterpret_cast<efi_memory_descriptor const *>(end));
}
const void *efi_map;
size_t map_length;
size_t desc_length;
};
class memory_bootstrap
{
public:
memory_bootstrap(const void *memory_map, size_t map_length, size_t desc_length) :
map(memory_map, map_length, desc_length) {}
void add_free_frames(frame_allocator &fa) {
for (auto *desc : map) {
if (desc->type == efi_memory_type::loader_code ||
desc->type == efi_memory_type::loader_data ||
desc->type == efi_memory_type::boot_services_code ||
desc->type == efi_memory_type::boot_services_data ||
desc->type == efi_memory_type::available)
{
fa.free(desc->physical_start, desc->pages);
}
}
}
void add_used_frames(kutil::vm_space &vm) {
for (auto *desc : map) {
if (desc->type == efi_memory_type::jsix_data ||
desc->type == efi_memory_type::jsix_initrd ||
desc->type == efi_memory_type::jsix_kernel)
{
uintptr_t virt_addr = desc->physical_start + kernel_offset;
vm.commit(virt_addr, desc->pages * frame_size);
}
}
}
void page_in_kernel(page_manager &pm, page_table *pml4) {
for (auto *desc : map) {
if (desc->type == efi_memory_type::jsix_kernel ||
desc->type == efi_memory_type::jsix_data ||
desc->type == efi_memory_type::jsix_initrd)
{
uintptr_t virt_addr = desc->physical_start + kernel_offset;
pm.page_in(pml4, desc->physical_start, virt_addr, desc->pages);
}
if (desc->type == efi_memory_type::acpi_reclaim) {
pm.page_in(pml4, desc->physical_start, desc->physical_start, desc->pages);
}
}
// Put our new PML4 into CR3 to start using it
page_manager::set_pml4(pml4);
pm.m_kernel_pml4 = pml4;
}
private:
const memory_map map;
};
void
memory_initialize(uint16_t scratch_pages, const void *memory_map, size_t map_length, size_t desc_length)
{
g_memory_initialized = false;
// make sure the options we want in CR4 are set
uint64_t cr4;
__asm__ __volatile__ ( "mov %%cr4, %0" : "=r" (cr4) );
cr4 |= 0x00080; // Enable global pages
cr4 |= 0x00200; // Enable FXSAVE/FXRSTOR
cr4 |= 0x20000; // Enable PCIDs
cr4 |=
0x000080 | // Enable global pages
0x000200 | // Enable FXSAVE/FXRSTOR
0x010000 | // Enable FSGSBASE
0x020000 | // Enable PCIDs
0;
__asm__ __volatile__ ( "mov %0, %%cr4" :: "r" (cr4) );
// The bootloader reserved "scratch_pages" pages for page tables and
@@ -216,89 +184,64 @@ memory_initialize(uint16_t scratch_pages, const void *memory_map, size_t map_len
page_table *id_pml4 = &tables[0];
page_table *id_pdp = &tables[1];
// Flags: 0 0 0 0 1 1 0 0 0 0 0 1 1 = 0x0183
// | IGN | | | | | | | | +- Present
// | | | | | | | | +--- Writeable
// | | | | | | | +----- Supervisor only
// | | | | | | +------- PWT (determining memory type for page)
// | | | | | +---------- PCD (determining memory type for page)
// | | | | +------------ Accessed flag (not accessed yet)
// | | | +-------------- Dirty (not dirtied yet)
// | | +---------------- Page size (1GiB page)
// | +------------------- Global
// +---------------------------- PAT (determining memory type for page)
for (int i=0; i<512; ++i)
id_pdp->entries[i] = (static_cast<uintptr_t>(i) << 30) | 0x18b;
id_pml4->entries[511] = reinterpret_cast<uintptr_t>(id_pdp) | 0x10b;
id_pdp->entries[i] = (static_cast<uintptr_t>(i) << 30) | 0x0183;
// Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003
// IGNORED | | | | | | | +- Present
// | | | | | | +--- Writeable
// | | | | | +----- Supervisor only
// | | | | +------- PWT (determining memory type for pdpt)
// | | | +---------- PCD (determining memory type for pdpt)
// | | +------------ Accessed flag (not accessed yet)
// | +-------------- Ignored
// +---------------- Reserved 0
id_pml4->entries[511] = reinterpret_cast<uintptr_t>(id_pdp) | 0x003;
// Make sure the page table is finished updating before we write to memory
__sync_synchronize();
io_wait();
// We now have pages starting at "scratch_virt" to bootstrap ourselves. Start by
// taking inventory of free pages.
uintptr_t scratch_virt = scratch_phys + page_offset;
uint64_t used_pages = 2; // starts with PML4 + offset PDP
page_consumer allocator(scratch_virt, scratch_pages, used_pages);
memory_bootstrap bootstrap {memory_map, map_length, desc_length};
block_allocator block_slab(frame_size, allocator);
frame_block_list used;
frame_block_list free;
// Now tell the frame allocator what's free
frame_allocator *fa = new (&g_frame_allocator) frame_allocator;
bootstrap.add_free_frames(*fa);
gather_block_lists(block_slab, used, free, memory_map, map_length, desc_length);
block_slab.allocate(); // Make sure we have extra
new (&g_kernel_heap) kutil::heap_allocator(heap_start, kernel_max_heap);
new (&g_kspace) kutil::vm_space(kernel_offset, (page_offset-kernel_offset), g_kernel_heap);
bootstrap.add_used_frames(g_kspace);
// Now go back through these lists and consolidate
block_slab.append(frame_block::consolidate(free));
// Create the page manager
page_manager *pm = new (&g_page_manager) page_manager(*fa);
region_allocator region_slab(frame_size, allocator);
region_slab.allocate(); // Allocate some buddy regions for the address_manager
// Give the frame_allocator back the rest of the scratch pages
fa->free(scratch_phys + (3 * frame_size), scratch_pages - 3);
kutil::address_manager *am =
new (&g_kernel_address_manager) kutil::address_manager(std::move(region_slab));
am->add_regions(kernel_offset, page_offset - kernel_offset);
// Finally, build an acutal set of kernel page tables that just contains
// Finally, build an acutal set of kernel page tables where we'll only add
// what the kernel actually has mapped, but making everything writable
// (especially the page tables themselves)
page_table *pml4 = reinterpret_cast<page_table *>(allocator.get_page());
page_table *pml4 = &tables[2];
pml4 = kutil::offset_pointer(pml4, page_offset);
kutil::memset(pml4, 0, sizeof(page_table));
pml4->entries[511] = reinterpret_cast<uintptr_t>(id_pdp) | 0x10b;
pml4->entries[511] = reinterpret_cast<uintptr_t>(id_pdp) | 0x003;
kutil::frame_allocator *fa =
new (&g_frame_allocator) kutil::frame_allocator(std::move(block_slab));
page_manager *pm = new (&g_page_manager) page_manager(*fa, *am);
bootstrap.page_in_kernel(*pm, pml4);
// Give the rest to the page_manager's cache for use in page_in
pm->free_table_pages(
reinterpret_cast<void *>(allocator.current),
allocator.left());
for (auto *block : used) {
uintptr_t virt_addr = 0;
switch (block->flags & frame_block_flags::map_mask) {
case frame_block_flags::map_ident:
virt_addr = block->address;
break;
case frame_block_flags::map_kernel:
virt_addr = block->address + kernel_offset;
if (block->flags && frame_block_flags::permanent)
am->mark_permanent(virt_addr, block->count * frame_size);
else
am->mark(virt_addr, block->count * frame_size);
break;
default:
break;
}
block->flags -= frame_block_flags::map_mask;
if (virt_addr)
pm->page_in(pml4, block->address, virt_addr, block->count);
}
fa->init(std::move(free), std::move(used));
// Put our new PML4 into CR3 to start using it
page_manager::set_pml4(pml4);
pm->m_kernel_pml4 = pml4;
// Give the old pml4 back to the page_manager to recycle
pm->free_table_pages(reinterpret_cast<void *>(scratch_virt), 1);
// Set the heap manager
new (&g_kernel_heap_manager) kutil::heap_manager(mm_grow_callback);
kutil::setup::set_heap(&g_kernel_heap_manager);
// Reclaim the old PML4
fa->free(scratch_phys, 1);
}

View File

@@ -0,0 +1,13 @@
#pragma once
/// \file event.h
/// Definition of event kobject types
#include "objects/kobject.h"
class event :
public kobject
{
public:
event() :
kobject(type::event) {}
};

View File

@@ -0,0 +1,26 @@
#include "objects/handle.h"
handle::handle(handle&& other) :
m_owner(other.m_owner),
m_object(other.m_object),
m_rights(other.m_rights)
{
other.m_owner = 0;
other.m_object = nullptr;
other.m_rights = 0;
}
handle::handle(j6_koid_t owner, j6_rights_t rights, kobject *obj) :
m_owner(owner),
m_object(obj),
m_rights(rights)
{
if (m_object)
m_object->handle_retain();
}
handle::~handle()
{
if (m_object)
m_object->handle_release();
}

View File

@@ -0,0 +1,30 @@
#pragma once
/// \file handle.h
/// Defines types for user handles to kernel objects
#include "j6/types.h"
#include "objects/kobject.h"
class handle
{
public:
/// Move constructor. Takes ownership of the object from other.
handle(handle&& other);
/// Constructor.
/// \arg owner koid of the process that has this handle
/// \arg rights access rights this handle has over the object
/// \arg obj the object held
handle(j6_koid_t owner, j6_rights_t rights, kobject *obj);
~handle();
handle() = delete;
handle(const handle &other) = delete;
handle & operator=(const handle& other) = delete;
private:
j6_koid_t m_owner;
kobject *m_object;
j6_rights_t m_rights;
};

View File

@@ -0,0 +1,98 @@
#include "j6/errors.h"
#include "j6/signals.h"
#include "j6/types.h"
#include "kutil/heap_allocator.h"
#include "objects/kobject.h"
extern kutil::heap_allocator g_kernel_heap;
// TODO: per-cpu this?
static j6_koid_t next_koid;
kobject::kobject(type t, j6_signal_t signals) :
m_koid(koid_generate(t)),
m_signals(signals),
m_observers(g_kernel_heap),
m_handle_count(0)
{}
kobject::~kobject()
{
notify_signal_observers(0, j6_status_destroyed);
}
j6_koid_t
kobject::koid_generate(type t)
{
return (static_cast<uint64_t>(t) << 48) | next_koid++;
}
kobject::type
kobject::koid_type(j6_koid_t koid)
{
return static_cast<type>((koid >> 48) & 0xffffull);
}
void
kobject::assert_signal(j6_signal_t s)
{
m_signals |= s;
notify_signal_observers(s);
}
void
kobject::deassert_signal(j6_signal_t s)
{
m_signals &= ~s;
notify_signal_observers(s);
}
void
kobject::register_signal_observer(observer *object, j6_signal_t s)
{
m_observers.emplace(object, s);
}
void
kobject::deregister_signal_observer(observer *object)
{
for (size_t i = 0; i < m_observers.count(); ++i) {
auto &reg = m_observers[i];
if (reg.object != object) continue;
reg = m_observers[m_observers.count() - 1];
m_observers.remove();
break;
}
}
void
kobject::notify_signal_observers(j6_signal_t mask, j6_status_t result)
{
for (auto &reg : m_observers) {
if (mask == 0 || (reg.signals & mask) != 0) {
if (!reg.object->on_signals_changed(this, m_signals, mask, result))
reg.object = nullptr;
}
}
// Compact the observer list
long last = m_observers.count() - 1;
while (m_observers[last].object == nullptr && last >= 0) last--;
for (long i = 0; i < long(m_observers.count()) && i < last; ++i) {
auto &reg = m_observers[i];
if (reg.object != nullptr) continue;
reg = m_observers[last--];
while (m_observers[last].object == nullptr && last >= i) last--;
}
m_observers.set_size(last + 1);
}
void
kobject::on_no_handles()
{
assert_signal(j6_signal_no_handles);
}

View File

@@ -0,0 +1,110 @@
#pragma once
/// \file kobject.h
/// Definition of base type for user-interactable kernel objects
#include "j6/errors.h"
#include "j6/types.h"
#include "kutil/vector.h"
/// Base type for all user-interactable kernel objects
class kobject
{
public:
/// Types of kernel objects.
enum class type : uint16_t
{
none,
event,
eventpair,
vms,
vmo,
job,
process,
thread,
};
kobject(type t, j6_signal_t signals = 0ull);
virtual ~kobject();
/// Generate a new koid for a given type
/// \arg t The object type
/// \returns A new unique koid
static j6_koid_t koid_generate(type t);
/// Get the kobject type from a given koid
/// \arg koid An existing koid
/// \returns The object type for the koid
static type koid_type(j6_koid_t koid);
/// Set the given signals active on this object
/// \arg s The set of signals to assert
void assert_signal(j6_signal_t s);
/// Clear the given signals on this object
/// \arg s The set of signals to deassert
void deassert_signal(j6_signal_t s);
class observer
{
public:
/// Callback for when signals change.
/// \arg obj The object triggering the callback
/// \arg s The current state of obj's signals
/// \arg ds Which signals caused the callback
/// \arg result Status code for this notification
/// \returns True if this object wants to keep watching signals
virtual bool on_signals_changed(
kobject *obj,
j6_signal_t s,
j6_signal_t ds,
j6_status_t result) = 0;
};
/// Register a signal observer
/// \arg object The observer
/// \arg s The signals the object wants notifiations for
void register_signal_observer(observer *object, j6_signal_t s);
/// Deegister a signal observer
/// \arg object The observer
void deregister_signal_observer(observer *object);
/// Increment the handle refcount
inline void handle_retain() { ++m_handle_count; }
/// Decrement the handle refcount
inline void handle_release() {
if (--m_handle_count == 0) on_no_handles();
}
protected:
kobject() = delete;
kobject(const kobject &other) = delete;
kobject(const kobject &&other) = delete;
/// Notifiy observers of this object
/// \arg mask The signals that triggered this call. If 0, notify all observers.
/// \arg result The result to pass to the observers
void notify_signal_observers(j6_signal_t mask, j6_status_t result = j6_status_ok);
/// Interface for subclasses to handle when all handles are closed. Subclasses
/// should either call the base version, or assert j6_signal_no_handles.
virtual void on_no_handles();
j6_koid_t m_koid;
j6_signal_t m_signals;
uint16_t m_handle_count;
struct observer_registration
{
observer_registration(observer* o = nullptr, j6_signal_t s = 0) :
object(o), signals(s) {}
observer *object;
j6_signal_t signals;
};
kutil::vector<observer_registration> m_observers;
};

View File

@@ -1,20 +1,28 @@
#include <algorithm>
#include "kutil/assert.h"
#include "kutil/vm_space.h"
#include "console.h"
#include "io.h"
#include "log.h"
#include "page_manager.h"
using memory::frame_size;
using memory::heap_start;
using memory::kernel_max_heap;
using memory::kernel_offset;
using memory::page_offset;
using memory::page_mappable;
extern kutil::frame_allocator g_frame_allocator;
extern kutil::address_manager g_kernel_address_manager;
page_manager g_page_manager(
g_frame_allocator,
g_kernel_address_manager);
page_manager g_page_manager(g_frame_allocator);
extern kutil::vm_space g_kspace;
// NB: in 4KiB page table entries, bit 7 isn't pagesize but PAT. Currently this
// doesn't matter, becasue in the default PAT table, both 000 and 100 are WB.
constexpr uint64_t sys_page_flags = 0x183; // global, pagesize, write, present
constexpr uint64_t sys_table_flags = 0x003; // write, present
constexpr uint64_t user_page_flags = 0x087; // pagesize, user, write, present
constexpr uint64_t user_table_flags = 0x007; // user, write, present
static uintptr_t
pt_to_phys(page_table *pt)
@@ -37,12 +45,10 @@ struct free_page_header
};
page_manager::page_manager(
kutil::frame_allocator &frames,
kutil::address_manager &addrs) :
page_manager::page_manager(frame_allocator &frames) :
m_page_cache(nullptr),
m_frames(frames),
m_addrs(addrs)
m_memory_initialized(false)
{
}
@@ -68,28 +74,26 @@ page_manager::create_process_map()
uintptr_t
page_manager::copy_page(uintptr_t orig)
{
uintptr_t virt = m_addrs.allocate(2 * frame_size);
uintptr_t copy = 0;
bool paged_orig = false;
bool paged_copy = false;
uintptr_t copy = 0;
size_t n = m_frames.allocate(1, &copy);
kassert(n, "copy_page could not allocate page");
page_in(get_pml4(), orig, virt, 1);
page_in(get_pml4(), copy, virt + frame_size, 1);
uintptr_t orig_virt = orig + page_offset;
uintptr_t copy_virt = copy + page_offset;
kutil::memcpy(
reinterpret_cast<void *>(virt + frame_size),
reinterpret_cast<void *>(virt),
reinterpret_cast<void *>(copy_virt),
reinterpret_cast<void *>(orig_virt),
frame_size);
page_out(get_pml4(), virt, 2);
m_addrs.free(virt);
return copy;
}
page_table *
page_manager::copy_table(page_table *from, page_table::level lvl)
page_manager::copy_table(page_table *from, page_table::level lvl, page_table_indices index)
{
page_table *to = get_table_page();
log::debug(logs::paging, "Page manager copying level %d table at %016lx to %016lx.", lvl, from, to);
@@ -105,12 +109,17 @@ page_manager::copy_table(page_table *from, page_table::level lvl)
512;
unsigned pages_copied = 0;
uintptr_t from_addr = 0;
uintptr_t to_addr = 0;
for (int i = 0; i < max; ++i) {
if (!from->is_present(i)) {
to->entries[i] = 0;
continue;
}
index[lvl] = i;
bool is_page =
lvl == page_table::level::pt ||
from->is_large_page(lvl, i);
@@ -119,17 +128,20 @@ page_manager::copy_table(page_table *from, page_table::level lvl)
uint16_t flags = from->entries[i] & 0xfffull;
uintptr_t orig = from->entries[i] & ~0xfffull;
to->entries[i] = copy_page(orig) | flags;
pages_copied++;
if (!pages_copied++)
from_addr = index.addr();
to_addr = index.addr();
} else {
uint16_t flags = 0;
page_table *next_from = from->get(i, &flags);
page_table *next_to = copy_table(next_from, page_table::deeper(lvl));
page_table *next_to = copy_table(next_from, page_table::deeper(lvl), index);
to->set(i, next_to, flags);
}
}
if (pages_copied)
log::debug(logs::paging, " copied %3u pages", pages_copied);
log::debug(logs::paging, " copied %3u pages %016lx - %016lx",
pages_copied, from_addr, to_addr + frame_size);
return to;
}
@@ -137,13 +149,20 @@ page_manager::copy_table(page_table *from, page_table::level lvl)
void
page_manager::delete_process_map(page_table *pml4)
{
bool was_pml4 = (pml4 == get_pml4());
if (was_pml4)
set_pml4(m_kernel_pml4);
log::info(logs::paging, "Deleting process pml4 at %016lx%s",
pml4, was_pml4 ? " (was current)" : "");
unmap_table(pml4, page_table::level::pml4, true);
}
void
page_manager::map_offset_pointer(void **pointer, size_t length)
{
log::info(logs::paging, "Mapping offset pointer region at %016lx size 0x%lx", *pointer, length);
log::debug(logs::paging, "Mapping offset pointer region at %016lx size 0x%lx", *pointer, length);
*pointer = kutil::offset_pointer(*pointer, page_offset);
}
@@ -178,6 +197,7 @@ page_manager::get_table_page()
free_page_header *page = m_page_cache;
m_page_cache = page->next;
return reinterpret_cast<page_table *>(page);
}
@@ -197,6 +217,8 @@ page_manager::free_table_pages(void *pages, size_t count)
void *
page_manager::map_pages(uintptr_t address, size_t count, bool user, page_table *pml4)
{
kassert(address, "Cannot call map_pages with 0 address");
void *ret = reinterpret_cast<void *>(address);
if (!pml4) pml4 = get_pml4();
@@ -217,7 +239,7 @@ page_manager::map_pages(uintptr_t address, size_t count, bool user, page_table *
}
void
page_manager::unmap_table(page_table *table, page_table::level lvl, bool free)
page_manager::unmap_table(page_table *table, page_table::level lvl, bool free, page_table_indices index)
{
const int max =
lvl == page_table::level::pml4 ?
@@ -225,6 +247,7 @@ page_manager::unmap_table(page_table *table, page_table::level lvl, bool free)
512;
uintptr_t free_start = 0;
uintptr_t free_start_virt = 0;
uintptr_t free_count = 0;
size_t size =
@@ -236,34 +259,80 @@ page_manager::unmap_table(page_table *table, page_table::level lvl, bool free)
for (int i = 0; i < max; ++i) {
if (!table->is_present(i)) continue;
index[lvl] = i;
bool is_page =
lvl == page_table::level::pt ||
table->is_large_page(lvl, i);
if (is_page) {
uintptr_t frame = table->entries[i] & ~0xfffull;
if (!free_count || free_start != frame + free_count * size) {
if (free_count && free)
m_frames.free(free_start, free_count * size / frame_size);
free_start = frame;
free_count = 1;
if (!free_count || frame != free_start + free_count * size) {
if (free_count && free) {
log::debug(logs::paging,
" freeing v:%016lx-%016lx p:%016lx-%016lx",
free_start_virt, free_start_virt + free_count * frame_size,
free_start, free_start + free_count * frame_size);
m_frames.free(free_start, (free_count * size) / frame_size);
free_count = 0;
}
} else {
page_table *next = table->get(i);
unmap_table(next, page_table::deeper(lvl), free);
if (!free_count) {
free_start = frame;
free_start_virt = index.addr();
}
}
if (free_count && free)
m_frames.free(free_start, free_count * size / frame_size);
free_count += 1;
} else {
page_table *next = table->get(i);
unmap_table(next, page_table::deeper(lvl), free, index);
}
}
if (free_count && free) {
log::debug(logs::paging,
" freeing v:%016lx-%016lx p:%016lx-%016lx",
free_start_virt, free_start_virt + free_count * frame_size,
free_start, free_start + free_count * frame_size);
m_frames.free(free_start, (free_count * size) / frame_size);
}
free_table_pages(table, 1);
log::debug(logs::paging, "Unmapped%s lv %d table at %016lx",
free ? " (and freed)" : "", lvl, table);
}
void
page_manager::unmap_pages(void* address, size_t count, page_table *pml4)
{
if (!pml4) pml4 = get_pml4();
page_out(pml4, reinterpret_cast<uintptr_t>(address), count, true);
if (!pml4)
pml4 = get_pml4();
uintptr_t iaddr = reinterpret_cast<uintptr_t>(address);
page_out(pml4, iaddr, count, true);
}
bool
page_manager::fault_handler(uintptr_t addr)
{
if (!addr)
return false;
if (m_memory_initialized &&
g_kspace.get(addr) != kutil::vm_state::committed)
return false;
uintptr_t page = addr & ~0xfffull;
log::debug(logs::memory, "PF: attempting to page in %016lx for %016lx", page, addr);
bool user = addr < kernel_offset;
map_pages(page, 1, user);
return true;
}
void
@@ -273,21 +342,21 @@ page_manager::check_needs_page(page_table *table, unsigned index, bool user)
page_table *new_table = get_table_page();
for (int i=0; i<512; ++i) new_table->entries[i] = 0;
table->entries[index] = pt_to_phys(new_table) | (user ? 0xf : 0xb);
table->entries[index] = pt_to_phys(new_table) | (user ? user_table_flags : sys_table_flags);
}
void
page_manager::page_in(page_table *pml4, uintptr_t phys_addr, uintptr_t virt_addr, size_t count, bool user, bool large)
{
/*
log::debug(logs::paging, "page_in for table %016lx p:%016lx v:%016lx c:%4d u:%d l:%d",
pml4, phys_addr, virt_addr, count, user, large);
*/
page_table_indices idx{virt_addr};
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
uint64_t flags = user ?
0x00f: // writethru, user, write, present
0x10b; // global, writethru, write, present
uint64_t flags = user ? user_table_flags : sys_table_flags;
for (; idx[0] < 512; idx[0] += 1) {
check_needs_page(tables[0], idx[0], user);

View File

@@ -5,11 +5,10 @@
#include <stddef.h>
#include <stdint.h>
#include "kutil/address_manager.h"
#include "kutil/enum_bitfields.h"
#include "kutil/frame_allocator.h"
#include "kutil/linked_list.h"
#include "kutil/slab_allocator.h"
#include "kutil/memory.h"
#include "frame_allocator.h"
#include "kernel_memory.h"
#include "page_table.h"
@@ -19,9 +18,9 @@ struct free_page_header;
class page_manager
{
public:
page_manager(
kutil::frame_allocator &frames,
kutil::address_manager &addrs);
/// Constructor.
/// \arg frames The frame allocator to get physical frames from
page_manager(frame_allocator &frames);
/// Helper to get the number of pages needed for a given number of bytes.
/// \arg bytes The number of bytes desired
@@ -44,8 +43,9 @@ public:
/// \arg pml4 A pointer to the PML4 table to install.
static inline void set_pml4(page_table *pml4)
{
uintptr_t p = reinterpret_cast<uintptr_t>(pml4) - memory::page_offset;
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p & ~0xfffull) );
constexpr uint64_t phys_mask = ~memory::page_offset & ~0xfffull;
uintptr_t p = reinterpret_cast<uintptr_t>(pml4) & phys_mask;
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (p) );
}
/// Allocate but don't switch to a new PML4 table. This table
@@ -62,7 +62,8 @@ public:
/// \arg lvl Level of the given tables (default is PML4)
/// \returns The new page table
page_table * copy_table(page_table *from,
page_table::level lvl = page_table::level::pml4);
page_table::level lvl = page_table::level::pml4,
page_table_indices index = {});
/// Allocate and map pages into virtual memory.
/// \arg address The virtual address at which to map the pages
@@ -108,6 +109,14 @@ public:
/// \returns A pointer to the system page manager
static page_manager * get();
/// Get a pointer to the kernel's PML4
inline page_table * get_kernel_pml4() { return m_kernel_pml4; }
/// Attempt to handle a page fault.
/// \arg addr Address that triggered the fault
/// \returns True if the fault was handled
bool fault_handler(uintptr_t addr);
private:
/// Copy a physical page
/// \arg orig Physical address of the page to copy
@@ -158,15 +167,17 @@ private:
bool free = false);
/// Low-level routine for unmapping an entire table of memory at once
void unmap_table(page_table *table, page_table::level lvl, bool free);
void unmap_table(page_table *table, page_table::level lvl, bool free,
page_table_indices index = {});
page_table *m_kernel_pml4; ///< The PML4 of just kernel pages
free_page_header *m_page_cache; ///< Cache of free pages to use for tables
kutil::frame_allocator &m_frames;
kutil::address_manager &m_addrs;
frame_allocator &m_frames;
friend void memory_initialize(uint16_t, const void *, size_t, size_t);
bool m_memory_initialized;
friend class memory_bootstrap;
page_manager(const page_manager &) = delete;
};
@@ -199,4 +210,8 @@ page_table_align(T p)
/// Bootstrap the memory managers.
void memory_initialize(uint16_t scratch_pages, const void *memory_map, size_t map_length, size_t desc_length);
void memory_initialize(
uint16_t scratch_pages,
const void *memory_map,
size_t map_length,
size_t desc_length);

View File

@@ -1,11 +1,23 @@
#include "kutil/heap_allocator.h"
#include "cpu.h"
#include "debug.h"
#include "log.h"
#include "process.h"
#include "scheduler.h"
extern "C" void task_fork_return_thunk();
extern kutil::heap_allocator g_kernel_heap; // TODO: this is a bad hack to get access to the heap
void
process::exit(uint32_t code)
{
return_code = code;
flags -= process_flags::running;
page_manager::get()->delete_process_map(pml4);
}
pid_t
process::fork(uintptr_t in_rsp)
process::fork()
{
auto &sched = scheduler::get();
auto *child = sched.create_process();
@@ -18,40 +30,79 @@ process::fork(uintptr_t in_rsp)
sched.m_runlists[child->priority].push_back(child);
child->rsp = in_rsp;
child->pml4 = page_manager::get()->copy_table(pml4);
kassert(child->pml4, "process::fork() got null pml4");
child->setup_kernel_stack(kernel_stack_size, kernel_stack);
child->rsp = child->kernel_stack + (in_rsp - kernel_stack);
child->rsp3 = bsp_cpu_data.rsp3;
child->setup_kernel_stack();
log::debug(logs::task, "Copied process %d to %d, new PML4 %016lx.",
pid, child->pid, child->pml4);
log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx to %016lx.",
kernel_stack, child->kernel_stack, in_rsp, child->rsp);
log::debug(logs::task, "Copied process %d to %d",
pid, child->pid);
// Add in the faked fork return value
cpu_state *regs = reinterpret_cast<cpu_state *>(child->rsp);
regs->rax = 0;
log::debug(logs::task, " PML4 %016lx", child->pml4);
log::debug(logs::task, " RSP3 %016lx", child->rsp3);
log::debug(logs::task, " RSP0 %016lx", child->rsp0);
// Initialize a new empty stack with a fake saved state
// for returning out of syscall_handler_prelude
size_t ret_seg_size = sizeof(uintptr_t) * 8;
child->rsp -= ret_seg_size;
void *this_ret_seg =
reinterpret_cast<void*>(rsp0 - ret_seg_size);
void *child_ret_seg =
reinterpret_cast<void*>(child->rsp);
kutil::memcpy(child_ret_seg, this_ret_seg, ret_seg_size);
child->add_fake_task_return(
reinterpret_cast<uintptr_t>(task_fork_return_thunk));
log::debug(logs::task, " RSP %016lx", child->rsp);
return child->pid;
}
void *
process::setup_kernel_stack(size_t size, uintptr_t orig)
process::setup_kernel_stack()
{
void *stack0 = kutil::malloc(size);
constexpr unsigned null_frame_entries = 2;
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
if (orig)
kutil::memcpy(stack0, reinterpret_cast<void*>(orig), size);
else
kutil::memset(stack0, 0, size);
void *stack_bottom = g_kernel_heap.allocate(initial_stack_size);
kutil::memset(stack_bottom, 0, initial_stack_size);
kernel_stack_size = size;
kernel_stack = reinterpret_cast<uintptr_t>(stack0);
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
stack_bottom, initial_stack_size);
return stack0;
void *stack_top =
kutil::offset_pointer(stack_bottom,
initial_stack_size - null_frame_size);
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_top);
for (unsigned i = 0; i < null_frame_entries; ++i)
null_frame[i] = 0;
kernel_stack_size = initial_stack_size;
kernel_stack = reinterpret_cast<uintptr_t>(stack_bottom);
rsp0 = reinterpret_cast<uintptr_t>(stack_top);
rsp = rsp0;
return stack_top;
}
void
process::add_fake_task_return(uintptr_t rip)
{
rsp -= sizeof(uintptr_t) * 7;
uintptr_t *stack = reinterpret_cast<uintptr_t*>(rsp);
stack[6] = rip; // return rip
stack[5] = rsp0; // rbp
stack[4] = 0xbbbbbbbb; // rbx
stack[3] = 0x12121212; // r12
stack[2] = 0x13131313; // r13
stack[1] = 0x14141414; // r14
stack[0] = 0x15151515; // r15
}
bool

View File

@@ -7,7 +7,8 @@
#include "kutil/linked_list.h"
#include "page_manager.h"
typedef uint32_t pid_t;
typedef int32_t pid_t;
struct cpu_state;
enum class process_flags : uint32_t
@@ -32,9 +33,20 @@ enum class process_wait : uint8_t
receive
};
/// A process
/// A process.
///
struct process
{
static const size_t initial_stack_size = 0x1000;
// Fields used by assembly routines go first. If you change any of these,
// be sure to change the assembly definitions in 'tasking.inc'
uintptr_t rsp;
uintptr_t rsp0;
uintptr_t rsp3;
page_table *pml4;
// End of assembly fields
pid_t pid;
pid_t ppid;
@@ -51,17 +63,17 @@ struct process
uint32_t reserved1;
uintptr_t rsp;
page_table *pml4;
uintptr_t kernel_stack;
size_t kernel_stack_size;
/// Terminate this process.
/// \arg code The return code to exit with.
void exit(unsigned code);
/// Copy this process.
/// \arg in_rsp The RSP of the calling process
/// \returns Returns the child's pid to the parent, and
/// 0 to the child.
pid_t fork(uint64_t in_rsp);
pid_t fork();
/// Unready this process until it gets a signal
/// \arg sigmask A bitfield of signals to wake on
@@ -118,13 +130,15 @@ struct process
private:
friend class scheduler;
/// Set up a new kernel stack for this process, optionally copying the
/// given stack. Sets the kernel stack on the process object, but also
/// returns it.
/// \arg size Size of the stack to allocate
/// \arg orig Address of a stack to copy, or 0 for no copying.
/// \returns The address of the new stack as a pointer
void * setup_kernel_stack(size_t size, uintptr_t orig);
/// Set up a new empty kernel stack for this process. Sets rsp0 on this
/// process object, but also returns it.
/// \returns The new rsp0 as a pointer
void * setup_kernel_stack();
/// Initialize this process' kenrel stack with a fake return segment for
/// returning out of task_switch.
/// \arg rip The rip to return to
void add_fake_task_return(uintptr_t rip);
};
using process_list = kutil::linked_list<process>;

View File

@@ -1,48 +1,82 @@
%macro push_all_and_segments 0
push rax
push rcx
push rdx
push rbx
push rbp
push rsi
push rdi
struc REGS
.r15 resq 1 ; 0x00
.r14 resq 1 ; 0x08
.r13 resq 1 ; 0x10
.r12 resq 1 ; 0x18
.r11 resq 1 ; 0x20
.r10 resq 1 ; 0x28
.r9 resq 1 ; 0x30
.r8 resq 1 ; 0x38
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
.rdi resq 1 ; 0x40
.rsi resq 1 ; 0x48
.rbp resq 1 ; 0x50
.rbx resq 1 ; 0x58
.rdx resq 1 ; 0x60
.rcx resq 1 ; 0x68
.rax resq 1 ; 0x70
mov ax, ds
push rax
.int resq 1 ; 0x78
.err resq 1 ; 0x80
.rip resq 1 ; 0x88
.cs3 resq 1 ; 0x90
.rflags resq 1 ; 0x98
.rsp3 resq 1 ; 0xa0
.ss3 resq 1 ; 0xa8
endstruc
regs_total_size equ 0xb0
regs_extra_size equ 0x78
%macro push_all 0
sub rsp, regs_extra_size
mov [rsp + REGS.rax], rax
mov [rsp + REGS.rcx], rcx
mov [rsp + REGS.rdx], rdx
mov [rsp + REGS.rbx], rbx
mov [rsp + REGS.rbp], rbp
mov [rsp + REGS.rsi], rsi
mov [rsp + REGS.rdi], rdi
mov [rsp + REGS.r8 ], r8
mov [rsp + REGS.r9 ], r9
mov [rsp + REGS.r10], r10
mov [rsp + REGS.r11], r11
mov [rsp + REGS.r12], r12
mov [rsp + REGS.r13], r13
mov [rsp + REGS.r14], r14
mov [rsp + REGS.r15], r15
%endmacro
%macro pop_all_and_segments 0
pop rax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
%macro pop_all 0
mov rax, [rsp + REGS.rax]
mov rcx, [rsp + REGS.rcx]
mov rdx, [rsp + REGS.rdx]
mov rbx, [rsp + REGS.rbx]
mov rbp, [rsp + REGS.rbp]
mov rsi, [rsp + REGS.rsi]
mov rdi, [rsp + REGS.rdi]
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
mov r8, [rsp + REGS.r8 ]
mov r9, [rsp + REGS.r9 ]
mov r10, [rsp + REGS.r10]
mov r11, [rsp + REGS.r11]
mov r12, [rsp + REGS.r12]
mov r13, [rsp + REGS.r13]
mov r14, [rsp + REGS.r14]
mov r15, [rsp + REGS.r15]
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
pop rcx
pop rax
add rsp, regs_extra_size
%endmacro
%macro check_swap_gs 0
mov rax, [rsp+0x90]
and rax, 0x03 ; mask out the RPL
cmp rax, 0x03
jne %%noswapgs
swapgs
%%noswapgs:
%endmacro
; vim: ft=asm

View File

@@ -1,6 +1,7 @@
#include "apic.h"
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "gdt.h"
#include "interrupts.h"
#include "io.h"
@@ -15,23 +16,24 @@
using memory::initial_stack;
scheduler scheduler::s_instance(nullptr);
scheduler scheduler::s_instance(nullptr, kutil::allocator::invalid);
const int stack_size = 0x1000;
const uint64_t rflags_noint = 0x002;
const uint64_t rflags_int = 0x202;
extern "C" {
void ramdisk_process_loader();
void load_process(const void *image_start, size_t bytes, process *proc, cpu_state state);
uintptr_t load_process_image(const void *image_start, size_t bytes, process *proc);
};
scheduler::scheduler(lapic *apic) :
extern uint64_t idle_stack_end;
scheduler::scheduler(lapic *apic, kutil::allocator &alloc) :
m_apic(apic),
m_next_pid(1)
m_next_pid(1),
m_process_allocator(alloc)
{
auto *idle = m_process_allocator.pop();
uint8_t last_pri = num_priorities - 1;
// The kernel idle task, also the thread we're in now
@@ -39,6 +41,8 @@ scheduler::scheduler(lapic *apic) :
idle->ppid = 0;
idle->priority = last_pri;
idle->rsp = 0; // This will get set when we switch away
idle->rsp3 = 0; // Never used for the idle task
idle->rsp0 = reinterpret_cast<uintptr_t>(&idle_stack_end);
idle->pml4 = page_manager::get_pml4();
idle->quanta = process_quanta;
idle->flags =
@@ -48,10 +52,13 @@ scheduler::scheduler(lapic *apic) :
m_runlists[last_pri].push_back(idle);
m_current = idle;
bsp_cpu_data.rsp0 = idle->rsp0;
bsp_cpu_data.tcb = idle;
}
void
load_process(const void *image_start, size_t bytes, process *proc, cpu_state state)
uintptr_t
load_process_image(const void *image_start, size_t bytes, process *proc)
{
// We're now in the process space for this process, allocate memory for the
// process code and load it
@@ -61,7 +68,7 @@ load_process(const void *image_start, size_t bytes, process *proc, cpu_state sta
// TODO: Handle bad images gracefully
elf::elf image(image_start, bytes);
kassert(image.valid(), "Invalid ELF passed to load_process");
kassert(image.valid(), "Invalid ELF passed to load_process_image");
const unsigned program_count = image.program_count();
for (unsigned i = 0; i < program_count; ++i) {
@@ -102,17 +109,20 @@ load_process(const void *image_start, size_t bytes, process *proc, cpu_state sta
kutil::memcpy(dest, src, header->size);
}
state.rip = image.entrypoint();
proc->flags &= ~process_flags::loading;
log::debug(logs::task, " Loaded! New process rip: %016lx", state.rip);
uintptr_t entrypoint = image.entrypoint();
log::debug(logs::task, " Loaded! New process rip: %016lx", entrypoint);
return entrypoint;
}
process_node *
scheduler::create_process()
scheduler::create_process(pid_t pid)
{
kassert(pid <= 0, "Cannot specify a positive pid in create_process");
auto *proc = m_process_allocator.pop();
proc->pid = m_next_pid++;
proc->pid = pid ? pid : m_next_pid++;
proc->priority = default_priority;
return proc;
}
@@ -129,39 +139,28 @@ scheduler::load_process(const char *name, const void *data, size_t size)
uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3
// Set up the page tables - this also allocates an initial user stack
page_table *pml4 = page_manager::get()->create_process_map();
proc->pml4 = page_manager::get()->create_process_map();
// Create a one-page kernel stack space
void *stack0 = proc->setup_kernel_stack(stack_size, 0);
// Create an initial kernel stack space
void *sp0 = proc->setup_kernel_stack();
uintptr_t *stack = reinterpret_cast<uintptr_t *>(sp0) - 7;
// Stack grows down, point to the end
void *sp0 = kutil::offset_pointer(stack0, stack_size);
// Pass args to ramdisk_process_loader on the stack
stack[0] = reinterpret_cast<uintptr_t>(data);
stack[1] = reinterpret_cast<uintptr_t>(size);
stack[2] = reinterpret_cast<uintptr_t>(proc);
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
proc->rsp = reinterpret_cast<uintptr_t>(stack);
proc->add_fake_task_return(
reinterpret_cast<uintptr_t>(ramdisk_process_loader));
// Highest state in the stack is the process' kernel stack for the loader
// to iret to:
state->ds = state->ss = ss;
state->cs = cs;
state->rflags = rflags_int;
state->rip = 0; // to be filled by the loader
state->user_rsp = initial_stack;
// Arguments for iret - rip will be pushed on before these
stack[3] = cs;
stack[4] = rflags_int;
stack[5] = initial_stack;
stack[6] = ss;
// Next state in the stack is the loader's kernel stack. The scheduler will
// iret to this which will kick off the loading:
cpu_state *loader_state = reinterpret_cast<cpu_state *>(sp0) - 2;
loader_state->ds = loader_state->ss = kss;
loader_state->cs = kcs;
loader_state->rflags = rflags_noint;
loader_state->rip = reinterpret_cast<uint64_t>(ramdisk_process_loader);
loader_state->user_rsp = reinterpret_cast<uint64_t>(state);
loader_state->rax = reinterpret_cast<uint64_t>(data);
loader_state->rbx = size;
proc->rsp = reinterpret_cast<uintptr_t>(loader_state);
proc->pml4 = pml4;
proc->rsp3 = initial_stack;
proc->quanta = process_quanta;
proc->flags =
process_flags::running |
@@ -170,17 +169,44 @@ scheduler::load_process(const char *name, const void *data, size_t size)
m_runlists[default_priority].push_back(proc);
loader_state->rcx = reinterpret_cast<uint64_t>(proc);
log::debug(logs::task, "Creating process %s: pid %d pri %d", name, proc->pid, proc->priority);
log::debug(logs::task, " RSP0 %016lx", state);
log::debug(logs::task, " PML4 %016lx", pml4);
log::debug(logs::task, " RSP %016lx", proc->rsp);
log::debug(logs::task, " RSP0 %016lx", proc->rsp0);
log::debug(logs::task, " PML4 %016lx", proc->pml4);
}
void
scheduler::create_kernel_task(pid_t pid, void (*task)())
{
auto *proc = create_process(pid);
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
// Create an initial kernel stack space
proc->setup_kernel_stack();
proc->add_fake_task_return(
reinterpret_cast<uintptr_t>(task));
proc->pml4 = page_manager::get()->get_kernel_pml4();
proc->quanta = process_quanta;
proc->flags =
process_flags::running |
process_flags::ready;
m_runlists[default_priority].push_back(proc);
log::debug(logs::task, "Creating kernel task: pid %d pri %d", proc->pid, proc->priority);
log::debug(logs::task, " RSP0 %016lx", proc->rsp0);
log::debug(logs::task, " RSP %016lx", proc->rsp);
log::debug(logs::task, " PML4 %016lx", proc->pml4);
}
void
scheduler::start()
{
log::info(logs::task, "Starting scheduler.");
wrmsr(msr::ia32_gs_base, reinterpret_cast<uintptr_t>(&bsp_cpu_data));
m_tick_count = m_apic->enable_timer(isr::isrTimer, quantum_micros, false);
}
@@ -233,16 +259,15 @@ void scheduler::prune(uint64_t now)
}
}
uintptr_t
scheduler::schedule(uintptr_t rsp0)
void
scheduler::schedule()
{
// TODO: lol a real clock
static uint64_t now = 0;
m_current->rsp = rsp0;
m_runlists[m_current->priority].remove(m_current);
pid_t lastpid = m_current->pid;
m_runlists[m_current->priority].remove(m_current);
if (m_current->flags && process_flags::ready) {
m_runlists[m_current->priority].push_back(m_current);
} else {
@@ -258,32 +283,24 @@ scheduler::schedule(uintptr_t rsp0)
}
m_current = m_runlists[pri].pop_front();
rsp0 = m_current->rsp;
// Set rsp0 to after the end of the about-to-be-popped cpu state
tss_set_stack(0, rsp0 + sizeof(cpu_state));
wrmsr(msr::ia32_kernel_gs_base, rsp0);
// Swap page tables
page_table *pml4 = m_current->pml4;
page_manager::set_pml4(pml4);
if (lastpid != m_current->pid) {
task_switch(m_current);
bool loading = m_current->flags && process_flags::loading;
log::debug(logs::task, "Scheduler switched to process %d, priority %d%s.",
m_current->pid, m_current->priority, loading ? " (loading)" : "");
return rsp0;
}
}
uintptr_t
scheduler::tick(uintptr_t rsp0)
void
scheduler::tick()
{
if (--m_current->quanta == 0) {
m_current->quanta = process_quanta;
rsp0 = schedule(rsp0);
schedule();
}
m_apic->reset_timer(m_tick_count);
return rsp0;
}
process_node *

View File

@@ -3,6 +3,7 @@
/// The task scheduler and related definitions
#include <stdint.h>
#include "kutil/allocator.h"
#include "kutil/slab_allocator.h"
#include "process.h"
@@ -10,7 +11,9 @@ class lapic;
struct page_table;
struct cpu_state;
extern "C" uintptr_t isr_handler(uintptr_t, cpu_state*);
extern "C" void isr_handler(cpu_state*);
extern "C" void task_switch(process *next);
extern "C" void task_fork(process *child);
/// The task scheduler
@@ -21,14 +24,15 @@ public:
static const uint8_t default_priority = num_priorities / 2;
/// How long the timer quantum is
static const uint64_t quantum_micros = 100000;
static const uint64_t quantum_micros = 1000;
/// How many quantums a process gets before being rescheduled
static const uint16_t process_quanta = 10;
static const uint16_t process_quanta = 100;
/// Constructor.
/// \arg apic Pointer to the local APIC object
scheduler(lapic *apic);
/// \arg alloc Allocator to use for TCBs
scheduler(lapic *apic, kutil::allocator &alloc);
/// Create a new process from a program image in memory.
/// \arg name Name of the program image
@@ -36,14 +40,17 @@ public:
/// \arg size Size of the program image, in bytes
void load_process(const char *name, const void *data, size_t size);
/// Create a new kernel task
/// \arg pid Pid to use for this task, must be negative
/// \arg proc Function to run as a kernel task
void create_kernel_task(pid_t pid, void (*task)());
/// Start the scheduler working. This may involve starting
/// timer interrupts or other preemption methods.
void start();
/// Run the scheduler, possibly switching to a new task
/// \arg rsp0 The stack pointer of the current interrupt handler
/// \returns The stack pointer to switch to
uintptr_t schedule(uintptr_t rsp0);
void schedule();
/// Get the current process.
/// \returns A pointer to the current process' process struct
@@ -60,18 +67,17 @@ public:
private:
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
friend uintptr_t isr_handler(uintptr_t, cpu_state*);
friend void isr_handler(cpu_state*);
friend class process;
/// Create a new process object. This process will have its pid
/// set but nothing else.
/// \arg pid The pid to give the process (0 for automatic)
/// \returns The new process object
process_node * create_process();
process_node * create_process(pid_t pid = 0);
/// Handle a timer tick
/// \arg rsp0 The stack pointer of the current interrupt handler
/// \returns The stack pointer to switch to
uintptr_t tick(uintptr_t rsp0);
void tick();
void prune(uint64_t now);

View File

@@ -1,16 +1,121 @@
#include "console.h"
#include "cpu.h"
#include "debug.h"
#include "log.h"
#include "msr.h"
#include "process.h"
#include "scheduler.h"
#include "syscall.h"
extern "C" {
void _halt();
void syscall_invalid(uint64_t call);
void syscall_handler_prelude();
}
namespace syscalls {
} // namespace syscalls
uintptr_t syscall_registry[static_cast<unsigned>(syscall::MAX)];
const char * syscall_names[static_cast<unsigned>(syscall::MAX)];
void
syscall_invalid(uint64_t call)
{
console *cons = console::get();
cons->set_color(9);
cons->printf("\nReceived unknown syscall: %02x\n", call);
const unsigned num_calls =
static_cast<unsigned>(syscall::MAX);
cons->printf(" Known syscalls:\n");
cons->printf(" invalid %016lx\n", syscall_invalid);
for (unsigned i = 0; i < num_calls; ++i) {
const char *name = syscall_names[i];
uintptr_t handler = syscall_registry[i];
if (name)
cons->printf(" %02x %10s %016lx\n", i, name, handler);
}
cons->set_color();
_halt();
}
/*
void
syscall_dispatch(cpu_state *regs)
{
console *cons = console::get();
syscall call = static_cast<syscall>(regs->rax);
auto &s = scheduler::get();
auto *p = s.current();
switch (call) {
case syscall::noop:
break;
case syscall::debug:
cons->set_color(11);
cons->printf("\nProcess %d: Received DEBUG syscall\n", p->pid);
cons->set_color();
print_regs(*regs);
cons->printf("\n Syscall enters: %8d\n", __counter_syscall_enter);
cons->printf(" Syscall sysret: %8d\n", __counter_syscall_sysret);
break;
case syscall::send:
{
pid_t target = regs->rdi;
uintptr_t data = regs->rsi;
cons->set_color(11);
cons->printf("\nProcess %d: Received SEND syscall, target %d, data %016lx\n", p->pid, target, data);
cons->set_color();
if (p->wait_on_send(target))
s.schedule();
}
break;
case syscall::receive:
{
pid_t source = regs->rdi;
uintptr_t data = regs->rsi;
cons->set_color(11);
cons->printf("\nProcess %d: Received RECEIVE syscall, source %d, dat %016lx\n", p->pid, source, data);
cons->set_color();
if (p->wait_on_receive(source))
s.schedule();
}
break;
case syscall::fork:
{
cons->set_color(11);
cons->printf("\nProcess %d: Received FORK syscall\n", p->pid);
cons->set_color();
pid_t pid = p->fork(regs);
cons->printf("\n fork returning %d\n", pid);
regs->rax = pid;
}
break;
default:
cons->set_color(9);
cons->printf("\nReceived unknown syscall: %02x\n", call);
cons->set_color();
_halt();
break;
}
}
*/
void
syscall_enable()
{
@@ -32,113 +137,19 @@ syscall_enable()
// IA32_FMASK - FLAGS mask inside syscall
wrmsr(msr::ia32_fmask, 0x200);
}
uintptr_t
syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
{
console *cons = console::get();
syscall call = static_cast<syscall>(regs.rax);
auto &s = scheduler::get();
auto *p = s.current();
switch (call) {
case syscall::noop:
break;
case syscall::debug:
cons->set_color(11);
cons->printf("\nProcess %u: Received DEBUG syscall\n", p->pid);
cons->set_color();
print_regs(regs);
break;
case syscall::message:
cons->set_color(11);
cons->printf("\nProcess %u: Received MESSAGE syscall\n", p->pid);
cons->set_color();
break;
case syscall::pause:
{
cons->set_color(11);
auto &s = scheduler::get();
auto *p = s.current();
p->wait_on_signal(-1ull);
cons->printf("\nProcess %u: Received PAUSE syscall\n", p->pid);
cons->set_color();
return_rsp = s.schedule(return_rsp);
}
break;
case syscall::sleep:
{
cons->set_color(11);
cons->printf("\nProcess %u: Received SLEEP syscall\n", p->pid);
cons->printf("Sleeping until %lu\n", regs.rbx);
cons->set_color();
p->wait_on_time(regs.rbx);
return_rsp = s.schedule(return_rsp);
}
break;
case syscall::getpid:
cons->set_color(11);
cons->printf("\nProcess %u: Received GETPID syscall\n", p->pid);
cons->set_color();
regs.rax = p->pid;
break;
case syscall::send:
{
uint32_t target = regs.rdi;
cons->set_color(11);
cons->printf("\nProcess %u: Received SEND syscall, target %u\n", p->pid, target);
cons->set_color();
if (p->wait_on_send(target))
return_rsp = s.schedule(return_rsp);
}
break;
case syscall::receive:
{
uint32_t source = regs.rdi;
cons->set_color(11);
cons->printf("\nProcess %u: Received RECEIVE syscall, source %u\n", p->pid, source);
cons->set_color();
if (p->wait_on_receive(source))
return_rsp = s.schedule(return_rsp);
}
break;
case syscall::fork:
{
cons->set_color(11);
cons->printf("\nProcess %u: Received FORK syscall\n", p->pid);
cons->set_color();
pid_t pid = p->fork(return_rsp);
if (pid == scheduler::get().current()->pid)
pid = 0;
regs.rax = pid;
}
break;
default:
cons->set_color(9);
cons->printf("\nReceived unknown syscall: %02x\n", call);
cons->set_color();
_halt();
break;
}
return return_rsp;
static constexpr unsigned num_calls =
static_cast<unsigned>(syscall::MAX);
kutil::memset(&syscall_registry, 0, sizeof(syscall_registry));
kutil::memset(&syscall_names, 0, sizeof(syscall_names));
#define SYSCALL(id, name, result, ...) \
syscall_registry[id] = reinterpret_cast<uintptr_t>(syscalls::name); \
syscall_names[id] = #name; \
static_assert( id <= num_calls, "Syscall " #name " has id > syscall::MAX" ); \
log::debug(logs::syscall, "Enabling syscall 0x%02x as " #name , id);
#include "syscalls.inc"
#undef SYSCALL
}

View File

@@ -1,24 +1,26 @@
#pragma once
#include <stdint.h>
#include "j6/types.h"
struct cpu_state;
enum class syscall : uint64_t
{
noop = 0x0000,
debug = 0x0001,
message = 0x0002,
pause = 0x0003,
sleep = 0x0004,
getpid = 0x0005,
send = 0x0006,
receive = 0x0007,
fork = 0x0008,
#define SYSCALL(id, name, result, ...) name = id,
#include "syscalls.inc"
#undef SYSCALL
last_syscall
// Maximum syscall id. If you change this, also change
// MAX_SYSCALLS in syscall.s
MAX = 0x40
};
void syscall_enable();
uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
namespace syscalls
{
#define SYSCALL(id, name, ...) j6_status_t name (__VA_ARGS__);
#include "syscalls.inc"
#undef SYSCALL
}

View File

@@ -1,24 +1,66 @@
%include "push_all.inc"
%include "tasking.inc"
; Make sure to keep MAX_SYSCALLS in sync with
; syscall::MAX in syscall.h
MAX_SYSCALLS equ 0x40
extern __counter_syscall_enter
extern __counter_syscall_sysret
extern syscall_registry
extern syscall_invalid
extern syscall_handler
global syscall_handler_prelude
global syscall_handler_prelude.return
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
swapgs
mov [gs:CPU_DATA.rsp3], rsp
mov rsp, [gs:CPU_DATA.rsp0]
mov rdi, rsp
call syscall_handler
mov rsp, rax
push rcx
push rbp
mov rbp, rsp
pop_all_and_segments
add rsp, 16 ; ignore bogus interrupt / error
pop rcx ; user rip
add rsp, 32 ; ignore cs, flags, rsp, ss
push rbx
push r11
push r12
push r13
push r14
push r15
inc qword [rel __counter_syscall_enter]
cmp rax, MAX_SYSCALLS
jle .ok_syscall
.bad_syscall:
mov rdi, rax
call syscall_invalid
.ok_syscall:
lea r11, [rel syscall_registry]
mov r11, [r11 + rax * 8]
cmp r11, 0
je .bad_syscall
call r11
inc qword [rel __counter_syscall_sysret]
.return:
pop r15
pop r14
pop r13
pop r12
pop r11
pop rbx
pop rbp
pop rcx
mov [gs:CPU_DATA.rsp0], rsp
mov rsp, [gs:CPU_DATA.rsp3]
swapgs
o64 sysret

View File

@@ -0,0 +1,24 @@
#include "j6/errors.h"
#include "j6/types.h"
#include "log.h"
#include "scheduler.h"
namespace syscalls {
j6_status_t
object_noop()
{
auto &s = scheduler::get();
auto *p = s.current();
log::debug(logs::syscall, "Process %d called noop syscall.", p->pid);
return j6_status_ok;
}
j6_status_t
object_wait(j6_handle_t, j6_signal_t, j6_signal_t*)
{
return j6_err_nyi;
}
} // namespace syscalls

View File

@@ -0,0 +1,93 @@
#include "j6/errors.h"
#include "j6/types.h"
#include "log.h"
#include "scheduler.h"
namespace syscalls {
j6_status_t
process_exit(int64_t status)
{
auto &s = scheduler::get();
auto *p = s.current();
log::debug(logs::syscall, "Process %d exiting with code %d", p->pid, status);
p->exit(status);
s.schedule();
log::error(logs::syscall, "returned to exit syscall");
return j6_err_unexpected;
}
j6_status_t
process_fork(pid_t *pid)
{
if (pid == nullptr) {
return j6_err_invalid_arg;
}
auto &s = scheduler::get();
auto *p = s.current();
pid_t ppid = p->pid;
log::debug(logs::syscall, "Process %d calling fork(%016llx)", ppid, pid);
*pid = p->fork();
p = s.current();
log::debug(logs::syscall, "Process %d's fork: returning %d from process %d", ppid, *pid, p->pid);
return j6_status_ok;
}
j6_status_t
process_getpid(pid_t *pid)
{
if (pid == nullptr) {
return j6_err_invalid_arg;
}
auto &s = scheduler::get();
auto *p = s.current();
*pid = p->pid;
return j6_status_ok;
}
j6_status_t
process_log(const char *message)
{
if (message == nullptr) {
return j6_err_invalid_arg;
}
auto &s = scheduler::get();
auto *p = s.current();
log::info(logs::syscall, "Message[%d]: %s", p->pid, message);
return j6_status_ok;
}
j6_status_t
process_pause()
{
auto &s = scheduler::get();
auto *p = s.current();
p->wait_on_signal(-1ull);
s.schedule();
return j6_status_ok;
}
j6_status_t
process_sleep(uint64_t til)
{
auto &s = scheduler::get();
auto *p = s.current();
log::debug(logs::syscall, "Process %d sleeping until %d", p->pid, til);
p->wait_on_time(til);
s.schedule();
return j6_status_ok;
}
} // namespace syscalls

63
src/kernel/task.s Normal file
View File

@@ -0,0 +1,63 @@
%include "tasking.inc"
extern g_tss
global task_switch
task_switch:
push rbp
mov rbp, rsp
; Save the rest of the callee-saved regs
push rbx
push r12
push r13
push r14
push r15
; Update previous task's TCB
mov rax, [gs:CPU_DATA.tcb] ; rax: current task TCB
mov [rax + TCB.rsp], rsp
; Copy off saved user rsp
mov rcx, [gs:CPU_DATA.rsp3] ; rcx: curretn task's saved user rsp
mov [rax + TCB.rsp3], rcx
; Install next task's TCB
mov [gs:CPU_DATA.tcb], rdi ; rdi: next TCB (function param)
mov rsp, [rdi + TCB.rsp] ; next task's stack pointer
mov rax, 0x0000007fffffffff
and rax, [rdi + TCB.pml4] ; rax: next task's pml4 (phys portion of address)
; Update syscall/interrupt rsp
mov rcx, [rdi + TCB.rsp0] ; rcx: top of next task's kernel stack
mov [gs:CPU_DATA.rsp0], rcx
; Update saved user rsp
mov rcx, [rdi + TCB.rsp3] ; rcx: new task's saved user rsp
mov [gs:CPU_DATA.rsp3], rcx
lea rdx, [rel g_tss] ; rdx: address of TSS
mov [rdx + TSS.rsp0], rcx
; check if we need to update CR3
mov rdx, cr3 ; rdx: old CR3
cmp rax, rdx
je .no_cr3
mov cr3, rax
.no_cr3:
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
ret
extern syscall_handler_prelude.return
global task_fork_return_thunk
task_fork_return_thunk:
mov rax, 0
jmp syscall_handler_prelude.return

33
src/kernel/tasking.inc Normal file
View File

@@ -0,0 +1,33 @@
struc TCB
.rsp: resq 1
.rsp0: resq 1
.rsp3: resq 1
.pml4: resq 1
endstruc
struc CPU_DATA
.rsp0: resq 1
.rsp3: resq 1
.tcb: resq 1
endstruc
struc TSS
.res0: resd 1
.rsp0: resq 1
.rsp1: resq 1
.rsp2: resq 1
.ist0: resq 1
.ist1: resq 1
.ist2: resq 1
.ist3: resq 1
.ist4: resq 1
.ist5: resq 1
.ist6: resq 1
.ist7: resq 1
.res1: resq 1
.res2: resw 1
.iomap: resw 1
endstruc
; vim: ft=asm

View File

@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
namespace initrd {
enum class file_type : uint8_t
{
unknown,
executable,
vdso
};
} // namespace initrd

View File

@@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include "kutil/enum_bitfields.h"
#include "initrd/file_type.h"
namespace initrd {
@@ -18,10 +19,8 @@ struct disk_header
uint8_t reserved[3];
} __attribute__ ((packed));
enum class file_flags : uint16_t
enum class file_flags : uint8_t
{
executable = 0x01
};
struct file_header
@@ -29,10 +28,10 @@ struct file_header
uint32_t offset;
uint32_t length;
uint16_t name_offset;
file_type type;
file_flags flags;
} __attribute__ ((packed));
} // namepsace initrd
IS_BITFIELD(initrd::disk_flags);
IS_BITFIELD(initrd::file_flags);

View File

@@ -1,10 +1,11 @@
#pragma once
/// \file initrd.h
/// Definitions defining the simple inital ramdisk file format used by the
/// popcorn kernel.
/// jsix kernel.
#include <stdint.h>
#include "kutil/vector.h"
#include "initrd/file_type.h"
// File format:
// 1x disk_header
@@ -33,8 +34,8 @@ public:
/// Get a pointer to the file data
const void * data() const;
/// Whether this file is an executable
bool executable() const;
/// Type of file
file_type type() const;
private:
const file_header *m_header;
@@ -49,7 +50,7 @@ class disk
public:
/// Constructor.
/// \arg start The start of the initrd in memory
disk(const void *start);
disk(const void *start, kutil::allocator &alloc);
/// Get the vector of files on the disk
const kutil::vector<file> & files() const { return m_files; }

View File

@@ -16,14 +16,11 @@ file::file(const file_header *header, const void *start) :
const char * file::name() const { return m_name; }
const size_t file::size() const { return m_header->length; }
const void * file::data() const { return m_data; }
bool
file::executable() const {
return bitfield_has(m_header->flags, file_flags::executable);
}
file_type file::type() const { return m_header->type; }
disk::disk(const void *start)
disk::disk(const void *start, kutil::allocator &alloc) :
m_files(alloc)
{
auto *header = reinterpret_cast<const disk_header *>(start);
size_t length = header->length;

View File

@@ -0,0 +1,95 @@
#include "kutil/assert.h"
#include "kutil/bip_buffer.h"
namespace kutil {
bip_buffer::bip_buffer() :
m_start_a(0),
m_start_b(0),
m_size_a(0),
m_size_b(0),
m_buffer_size(0),
m_buffer(nullptr)
{}
bip_buffer::bip_buffer(uint8_t *buffer, size_t size) :
m_start_a(0),
m_start_b(0),
m_size_a(0),
m_size_b(0),
m_buffer_size(size),
m_buffer(buffer)
{}
size_t bip_buffer::reserve(size_t size, void **area)
{
if (m_size_r) {
*area = nullptr;
return 0;
}
size_t remaining = 0;
if (m_size_b) {
// If B exists, we're appending there. Get space between
// the end of B and start of A.
remaining = m_start_a - m_start_b - m_size_b;
m_start_r = m_start_b + m_size_b;
} else {
// B doesn't exist, check the space both before and after A.
// If the end of A has enough room for this write, put it there.
remaining = m_buffer_size - m_start_a - m_size_a;
m_start_r = m_start_a + m_size_a;
// Otherwise use the bigger of the areas in front of and after A
if (remaining < size && m_start_a > remaining) {
remaining = m_start_a;
m_start_r = 0;
}
}
if (!remaining) {
*area = nullptr;
return 0;
}
m_size_r = (remaining < size) ? remaining : size;
*area = &m_buffer[m_start_r];
return m_size_r;
}
void bip_buffer::commit(size_t size)
{
kassert(size <= m_size_r, "Tried to commit more than reserved");
if (m_start_r == m_start_a + m_size_a) {
// We were adding to A
m_size_a += size;
} else {
// We were adding to B
kassert(m_start_r == m_start_b + m_size_b, "Bad m_start_r!");
m_size_b += size;
}
m_start_r = m_size_r = 0;
}
size_t bip_buffer::get_block(void **area) const
{
*area = m_size_a ? &m_buffer[m_start_a] : nullptr;
return m_size_a;
}
void bip_buffer::consume(size_t size)
{
kassert(size <= m_size_a, "Consumed more bytes than exist in A");
if (size >= m_size_a) {
m_size_a = m_size_b;
m_start_a = m_start_b;
m_size_b = m_start_b = 0;
} else {
m_size_a -= size;
m_start_a += size;
}
}
} // namespace kutil

View File

@@ -1,159 +0,0 @@
#include <algorithm>
#include "kutil/frame_allocator.h"
namespace kutil {
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_block_list
frame_block::consolidate(frame_block_list &list)
{
frame_block_list freed;
for (auto *cur : list) {
auto *next = cur->next();
while ( next &&
cur->flags == next->flags &&
cur->end() == next->address) {
cur->count += next->count;
list.remove(next);
freed.push_back(next);
}
}
return freed;
}
void
frame_block::zero()
{
address = 0;
count = 0;
flags = frame_block_flags::none;
}
void
frame_block::copy(frame_block *other)
{
address = other->address;
count = other->count;
flags = other->flags;
}
frame_allocator::frame_allocator(
frame_block_list cache)
{
m_block_slab.append(cache);
}
void
frame_allocator::init(
frame_block_list free,
frame_block_list used)
{
m_free.append(free);
m_used.append(used);
}
void
frame_allocator::consolidate_blocks()
{
m_block_slab.append(frame_block::consolidate(m_free));
m_block_slab.append(frame_block::consolidate(m_used));
}
size_t
frame_allocator::allocate(size_t count, uintptr_t *address)
{
kassert(!m_free.empty(), "frame_allocator::pop_frames ran out of free frames!");
auto *first = m_free.front();
unsigned n = std::min(count, static_cast<size_t>(first->count));
*address = first->address;
if (count >= first->count) {
m_free.remove(first);
m_used.sorted_insert(first);
} else {
auto *used = m_block_slab.pop();
used->copy(first);
used->count = n;
m_used.sorted_insert(used);
first->address += n * frame_size;
first->count -= n;
}
m_block_slab.append(frame_block::consolidate(m_used));
return n;
}
void
frame_allocator::free(uintptr_t address, size_t count)
{
size_t block_count = 0;
for (auto *block : m_used) {
if (!block->contains(address)) continue;
size_t size = frame_size * count;
uintptr_t end = address + size;
size_t leading = address - block->address;
size_t trailing =
end > block->end() ?
0 : (block->end() - end);
if (leading) {
size_t frames = leading / frame_size;
auto *lead_block = m_block_slab.pop();
lead_block->copy(block);
lead_block->count = frames;
block->count -= frames;
block->address += leading;
m_used.insert_before(block, lead_block);
}
if (trailing) {
size_t frames = trailing / frame_size;
auto *trail_block = m_block_slab.pop();
trail_block->copy(block);
trail_block->count = frames;
trail_block->address += size;
trail_block->address += size;
block->count -= frames;
m_used.insert_after(block, trail_block);
}
address += block->count * frame_size;
m_used.remove(block);
m_free.sorted_insert(block);
++block_count;
}
kassert(block_count, "Couldn't find existing allocated frames to free");
}
} // namespace kutil

View File

@@ -1,12 +1,11 @@
#include <stdint.h>
#include "kutil/assert.h"
#include "kutil/memory.h"
#include "kutil/heap_manager.h"
#include "kutil/heap_allocator.h"
namespace kutil {
struct heap_manager::mem_header
struct heap_allocator::mem_header
{
mem_header(mem_header *prev, mem_header *next, uint8_t size) :
m_prev(prev), m_next(next)
@@ -14,34 +13,29 @@ struct heap_manager::mem_header
set_size(size);
}
inline void set_size(uint8_t size)
{
inline void set_size(uint8_t size) {
m_prev = reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(prev()) | (size & 0x3f));
}
inline void set_used(bool used)
{
inline void set_used(bool used) {
m_next = reinterpret_cast<mem_header *>(
reinterpret_cast<uintptr_t>(next()) | (used ? 1 : 0));
}
inline void set_next(mem_header *next)
{
inline void set_next(mem_header *next) {
bool u = used();
m_next = next;
set_used(u);
}
inline void set_prev(mem_header *prev)
{
inline void set_prev(mem_header *prev) {
uint8_t s = size();
m_prev = prev;
set_size(s);
}
void remove()
{
void remove() {
if (next()) next()->set_prev(prev());
if (prev()) prev()->set_next(next());
set_prev(nullptr);
@@ -67,25 +61,24 @@ private:
};
heap_manager::heap_manager() :
m_grow(nullptr)
{
}
heap_allocator::heap_allocator() : m_next(0), m_size(0) {}
heap_manager::heap_manager(grow_callback grow_cb) :
m_grow(grow_cb)
heap_allocator::heap_allocator(uintptr_t start, size_t size) :
m_next(start), m_size(size)
{
kutil::memset(m_free, 0, sizeof(m_free));
grow_memory();
}
void *
heap_manager::allocate(size_t length)
heap_allocator::allocate(size_t length)
{
size_t total = length + sizeof(mem_header);
unsigned size = min_size;
while (total > (1 << size)) size++;
kassert(size <= max_size, "Tried to allocate a block bigger than max_size");
if (size > max_size)
return nullptr;
mem_header *header = pop_free(size);
header->set_used(true);
@@ -93,18 +86,28 @@ heap_manager::allocate(size_t length)
}
void
heap_manager::free(void *p)
heap_allocator::free(void *p)
{
if (!p) return;
mem_header *header = reinterpret_cast<mem_header *>(p);
header -= 1; // p points after the header
header->set_used(false);
while (header->size() != max_size) {
auto size = header->size();
mem_header *buddy = header->buddy();
if (buddy->used() || buddy->size() != header->size()) break;
if (buddy->used() || buddy->size() != size)
break;
if (get_free(size) == buddy)
get_free(size) = buddy->next();
buddy->remove();
header = header->eldest() ? header : buddy;
header->set_size(header->size() + 1);
header->set_size(size + 1);
}
uint8_t size = header->size();
@@ -115,47 +118,60 @@ heap_manager::free(void *p)
}
void
heap_manager::grow_memory()
heap_allocator::ensure_block(unsigned size)
{
size_t length = (1 << max_size);
kassert(m_grow, "Tried to grow heap without a growth callback");
void *next = m_grow(length);
mem_header *block = new (next) mem_header(nullptr, get_free(max_size), max_size);
get_free(max_size) = block;
if (block->next())
block->next()->set_prev(block);
}
void
heap_manager::ensure_block(unsigned size)
{
if (get_free(size) != nullptr) return;
else if (size == max_size) {
grow_memory();
if (get_free(size) != nullptr)
return;
if (size == max_size) {
size_t bytes = (1 << max_size);
if (bytes <= m_size) {
mem_header *next = reinterpret_cast<mem_header *>(m_next);
new (next) mem_header(nullptr, nullptr, size);
get_free(size) = next;
m_next += bytes;
m_size -= bytes;
}
} else {
mem_header *orig = pop_free(size + 1);
if (orig) {
mem_header *next = kutil::offset_pointer(orig, 1 << size);
new (next) mem_header(orig, nullptr, size);
orig->set_next(next);
orig->set_size(size);
get_free(size) = orig;
}
}
}
heap_manager::mem_header *
heap_manager::pop_free(unsigned size)
heap_allocator::mem_header *
heap_allocator::pop_free(unsigned size)
{
ensure_block(size);
mem_header *block = get_free(size);
if (block) {
get_free(size) = block->next();
block->remove();
}
return block;
}
class invalid_allocator :
public allocator
{
public:
virtual void * allocate(size_t) override {
kassert(false, "Attempting to allocate from allocator::invalid");
return nullptr;
}
virtual void free(void *) override {
kassert(false, "Attempting to free from allocator::invalid");
}
} _invalid_allocator;
allocator &allocator::invalid = _invalid_allocator;
} // namespace kutil

View File

@@ -0,0 +1,31 @@
#pragma once
/// \file allocator.h
/// Allocator interface
#include <stdint.h>
#include "kernel_memory.h"
namespace kutil {
class allocator
{
public:
/// Allocate memory.
/// \arg length The amount of memory to allocate, in bytes
/// \returns A pointer to the allocated memory, or nullptr if
/// allocation failed.
virtual void * allocate(size_t size) = 0;
/// Free a previous allocation.
/// \arg p A pointer previously retuned by allocate()
virtual void free(void *p) = 0;
template <typename T>
inline T * allocate(unsigned count) {
return reinterpret_cast<T*>(allocate(count * sizeof(T)));
}
static allocator &invalid;
};
} // namespace kutil

View File

@@ -0,0 +1,250 @@
#pragma once
/// \file avl_tree.h
/// Templated container class for an AVL tree
#include <algorithm>
#include <stdint.h>
#include "kutil/assert.h"
namespace kutil {
template <typename T> class avl_tree;
/// A node in a `avl_tree<T>`
template <typename T>
class avl_node :
public T
{
public:
using item_type = T;
using node_type = avl_node<T>;
/// Dereference operator. Helper to cast this node to the contained type.
/// \returns A pointer to the node, cast to T*.
inline item_type & operator*() { return *this; }
/// Dereference operator. Helper to cast this node to the contained type.
/// \returns A pointer to the node, cast to T*.
inline const item_type & operator*() const { return *this; }
/// Cast operator. Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to T&.
inline operator item_type& () { return *this; }
/// Cast operator. Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to const T&.
inline operator const item_type& () { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to const T&.
inline const item_type& item() const { return *this; }
/// Helper to cast this node to the contained type.
/// \returns A reference to the node, cast to T&.
inline item_type& item() { return *this; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline node_type * left() { return m_left; }
/// Accessor for the left child.
/// \returns A pointer to the left child, or nullptr.
inline const node_type * left() const { return m_left; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline node_type * right() { return m_right; }
/// Accessor for the right child.
/// \returns A pointer to the right child, or nullptr.
inline const node_type * right() const { return m_right; }
private:
friend class avl_tree<T>;
inline static int height(node_type *l) { return (l ? l->m_height : 0); }
// Update this node's height and return its new balance factor
inline int update_height()
{
int left = height(m_left);
int right = height(m_right);
m_height = std::max(left, right) + 1;
return left - right;
}
int bias(node_type *addend)
{
const item_type &this_item = *this;
const item_type &that_item = *addend;
if (that_item < this_item)
return -1;
else if (that_item > this_item)
return 1;
kassert(false, "Equal items not allowed in AVL tree");
return 0;
}
static node_type * rotate_right(node_type *existing)
{
node_type *root = existing->m_left;
node_type *left = root->m_right;
root->m_right = existing;
existing->m_left = left;
existing->update_height();
root->update_height();
return root;
}
static node_type * rotate_left(node_type *existing)
{
node_type *root = existing->m_right;
node_type *right = root->m_left;
root->m_left = existing;
existing->m_right = right;
existing->update_height();
root->update_height();
return root;
}
static node_type * insert(node_type *existing, node_type *addend)
{
if (existing == nullptr)
return addend;
if (existing->compare(addend) < 0)
existing->m_left = insert(existing->m_left, addend);
else
existing->m_right = insert(existing->m_right, addend);
int balance = existing->update_height();
if (balance > 1) {
// Left-heavy
if (existing->m_left->compare(addend) < 0) {
// Left Left
return rotate_right(existing);
} else {
// Left Right
existing->m_left = rotate_left(existing->m_left);
return rotate_right(existing);
}
} else if (balance < -1) {
// Right-heavy
if (existing->m_right->compare(addend) > 0) {
// Right Right
return rotate_left(existing);
} else {
// Right Left
existing->m_right = rotate_right(existing->m_right);
return rotate_left(existing);
}
}
return existing;
}
static node_type * remove(node_type *existing, node_type *subtrahend, allocator &alloc)
{
if (existing == nullptr)
return existing;
if (existing == subtrahend) {
if (!existing->m_left || !existing->m_right) {
// At least one child is null
node_type *temp = existing->m_left ?
existing->m_left : existing->m_right;
if (temp == nullptr) {
// Both were null
temp = existing;
existing = nullptr;
} else {
*existing = *temp;
}
alloc.free(temp);
} else {
// Both children exist, find next node
node_type *temp = existing->m_right;
while (temp->m_left)
temp = temp->m_left;
*existing = *temp;
existing->m_right = remove(existing->m_right, temp, alloc);
}
} else if (existing->compare(subtrahend) < 0) {
existing->m_left = remove(existing->m_left, subtrahend, alloc);
} else {
existing->m_right = remove(existing->m_right, subtrahend, alloc);
}
if (!existing)
return nullptr;
int balance = existing->update_height();
if (balance > 1) {
int left_balance = existing->m_left->update_height();
if (left_balance < 0)
existing->m_left = rotate_left(existing->m_left);
return rotate_right(existing);
} else if (balance < -1) {
int right_balance = existing->m_right->update_height();
if (right_balance > 0)
existing->m_right = rotate_right(existing->m_right);
return rotate_left(existing);
}
return existing;
}
int m_height;
node_type *m_left;
node_type *m_right;
};
template <typename T>
class avl_tree
{
public:
using item_type = T;
using node_type = avl_node<T>;
avl_tree() = default;
avl_tree(avl_tree &&other) :
m_count(other.m_count), m_root(other.m_root)
{
other.m_root = nullptr;
other.m_count = 0;
}
inline node_type * root() { return m_root; }
inline unsigned count() const { return m_count; }
inline void remove(node_type *subtrahend, allocator &alloc) {
m_root = node_type::remove(m_root, subtrahend, alloc);
m_count--;
}
inline void insert(node_type *addend) {
m_root = node_type::insert(m_root, addend);
m_count++;
}
private:
unsigned m_count {0};
node_type *m_root {nullptr};
};
} // namespace kutil

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