Compare commits
317 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67b5f33d46 | ||
|
|
b675dfd014 | ||
|
|
fc2b884af9 | ||
|
|
cbd19fa070 | ||
|
|
83b330bf2b | ||
|
|
cc9cde9bfe | ||
|
|
774f6fc334 | ||
|
|
bfd13e7a9b | ||
|
|
35b1d37df0 | ||
|
|
fc3d919f25 | ||
|
|
75641a4394 | ||
|
|
ce0bcbd3b6 | ||
|
|
3194b460cc | ||
|
|
e1d148a34d | ||
|
|
b491a09686 | ||
|
|
6a538ad4f3 | ||
|
|
6ccc172f33 | ||
|
|
66ca3a3f9b | ||
|
|
4f4a35a7be | ||
|
|
c9722a07f3 | ||
|
|
42dfa6ccfe | ||
|
|
2adef874ee | ||
|
|
a6e4995963 | ||
|
|
2bd91c2d94 | ||
|
|
c713f4ff6f | ||
|
|
21b0b08908 | ||
|
|
88ace0a99f | ||
|
|
10c8f6e4b5 | ||
|
|
9aa749e877 | ||
|
|
f78a99927a | ||
|
|
ec794f4f99 | ||
|
|
884182217c | ||
|
|
d94907ae79 | ||
|
|
4accfd136e | ||
|
|
1a223d6ef5 | ||
|
|
36b3ad8154 | ||
|
|
460973954e | ||
|
|
303a78065e | ||
|
|
93f0b70eba | ||
|
|
6f5a2a3d3f | ||
|
|
ec563ea8e4 | ||
|
|
570638bba6 | ||
|
|
f627ea7de0 | ||
|
|
521c132801 | ||
|
|
bc5115b9ea | ||
|
|
6963304c01 | ||
|
|
ae651a4fcd | ||
|
|
17c2fe6e4e | ||
|
|
f066ac3ffd | ||
|
|
edcf633e84 | ||
|
|
d09a454b8b | ||
|
|
d6329ea9bf | ||
|
|
83897048ab | ||
|
|
7cc59770b8 | ||
|
|
b056d95920 | ||
|
|
19cd01ef8d | ||
|
|
b3f88bbe02 | ||
|
|
f57f38edbd | ||
|
|
12377ae730 | ||
|
|
bb93dcef44 | ||
|
|
678a12dc90 | ||
|
|
c3dacb2906 | ||
|
|
3a68ec439d | ||
|
|
7ce418aabc | ||
|
|
8ee5091f41 | ||
|
|
6285517ef7 | ||
|
|
2b0cd6f2f2 | ||
|
|
a653c55941 | ||
|
|
ce035d2a43 | ||
|
|
2d54eb5143 | ||
|
|
b9c8edb657 | ||
|
|
88315c25a5 | ||
|
|
806bfd1fbf | ||
|
|
910b5116f4 | ||
|
|
6302e8b73a | ||
|
|
fd1adc0262 | ||
|
|
070be0b005 | ||
|
|
5034ffbe59 | ||
|
|
cd13b88540 | ||
|
|
e050d6f151 | ||
|
|
863555ec6b | ||
|
|
c605793a9d | ||
|
|
8375870af6 | ||
|
|
11a53e792f | ||
|
|
ca2362f858 | ||
|
|
5cdbedd4d1 | ||
|
|
ee6d69bcd2 | ||
|
|
f9193b4602 | ||
|
|
979e5f036e | ||
|
|
39baec852b | ||
|
|
ed3f9410a6 | ||
|
|
b18243f098 | ||
|
|
9472dab522 | ||
|
|
9067f8d298 | ||
|
|
866073ae8a | ||
|
|
91cb00fde2 | ||
|
|
c435bcfb67 | ||
|
|
7fe1b7d1f5 | ||
|
|
39524b2b9f | ||
|
|
c592f5f537 | ||
|
|
94c491d286 | ||
|
|
645938a194 | ||
|
|
73756820bd | ||
|
|
33374ab257 | ||
|
|
bf8286d15f | ||
|
|
6410c898c5 | ||
|
|
be007c6278 | ||
|
|
f7558e3d18 | ||
|
|
97e8f2c69a | ||
|
|
49bdf47514 | ||
|
|
81162f30dc | ||
|
|
4379256c11 | ||
|
|
870ca1db45 | ||
|
|
74a5c301f8 | ||
|
|
2955e6c9c1 | ||
|
|
cd2ccb4e06 | ||
|
|
722ee4c52c | ||
|
|
67b9f45004 | ||
|
|
2035fffa1c | ||
|
|
97ac3c09fa | ||
|
|
241e1dacb0 | ||
|
|
ac19d3f532 | ||
|
|
194527e0fe | ||
|
|
28cf5562ac | ||
|
|
8cdc39fdee | ||
|
|
626eec4a31 | ||
|
|
5901237fee | ||
|
|
24316ca0c4 | ||
|
|
f9d964cccb | ||
|
|
a9ac30b991 | ||
|
|
61df9cf32c | ||
|
|
bbd31929ba | ||
|
|
ec20e9f3d9 | ||
|
|
3bcd83f5a3 | ||
|
|
341ba5146a | ||
|
|
83b37ef536 | ||
|
|
1965197ccd | ||
|
|
29747f4891 | ||
|
|
aca442ee87 | ||
|
|
8e85ae5318 | ||
|
|
8c32471e0d | ||
|
|
79711be46a | ||
|
|
863e5bda15 | ||
|
|
d19cedb12a | ||
|
|
f2d39f7df8 | ||
|
|
579f6f64e6 | ||
|
|
a71af1be96 | ||
|
|
237c242f96 | ||
|
|
c4dc52c06c | ||
|
|
e1d8dd3124 | ||
|
|
38a1197d9e | ||
|
|
bc01a37452 | ||
|
|
acdca19f59 | ||
|
|
a1fe745a53 | ||
|
|
73df20d195 | ||
|
|
7e1933d79b | ||
|
|
8d23fac6cc | ||
|
|
0f8efdb55e | ||
|
|
523d0b3b8c | ||
|
|
591ca7c83c | ||
|
|
dffdcc095d | ||
|
|
229c1e4965 | ||
|
|
d8399e3c07 | ||
|
|
f1bb3556eb | ||
|
|
cef0a71bce | ||
|
|
a9d72b8102 | ||
|
|
d469482a7f | ||
|
|
c67c1bd6a2 | ||
|
|
5e6769036c | ||
|
|
482b9f50fc | ||
|
|
f4e7eaeb40 | ||
|
|
8c2ff33c40 | ||
|
|
1308864061 | ||
|
|
62c559043d | ||
|
|
c2f85ce61b | ||
|
|
5808599005 | ||
|
|
fafe582802 | ||
|
|
593cda3ee8 | ||
|
|
d5c44645eb | ||
|
|
e7a509176d | ||
|
|
3a39d9440a | ||
|
|
cabfec3f1e | ||
|
|
956efabd8f | ||
|
|
f146a96298 | ||
|
|
585abe9a18 | ||
|
|
3d0b262435 | ||
|
|
3f264b4490 | ||
|
|
1758ee4215 | ||
|
|
dc40c2f6ad | ||
|
|
2fb92e8592 | ||
|
|
57829e1b79 | ||
|
|
bc26d7d01d | ||
|
|
b93519e06f | ||
|
|
5d861d243a | ||
|
|
f1b84ab370 | ||
|
|
d5b8902d8f | ||
|
|
799fbbdd10 | ||
|
|
d33f1bc6f2 | ||
|
|
28a90e550e | ||
|
|
647801f096 | ||
|
|
1664566bd2 | ||
|
|
cd09c17d71 | ||
|
|
f74f3f03d1 | ||
|
|
23006b2b43 | ||
|
|
7f69a6c9b1 | ||
|
|
1726d10554 | ||
|
|
757bc21550 | ||
|
|
e187679f93 | ||
|
|
2597e2002b | ||
|
|
e6f819ed90 | ||
|
|
0c8bcb2400 | ||
|
|
c5761cc51e | ||
|
|
24ccf65aba | ||
|
|
814d6f1de6 | ||
|
|
bfaab294e6 | ||
|
|
0ddcf668cb | ||
|
|
4005e9e791 | ||
|
|
abaa007c54 | ||
|
|
87d80f84c2 | ||
|
|
3fdf246a22 | ||
|
|
79b95d0045 | ||
|
|
1e66e5cd82 | ||
|
|
193d9939f0 | ||
|
|
81fc559802 | ||
|
|
0d75cc999c | ||
|
|
a5da56d02f | ||
|
|
a7e20fd390 | ||
|
|
9f38e7e5f5 | ||
|
|
93e60cc136 | ||
|
|
5f7ec50055 | ||
|
|
ff0019841f | ||
|
|
7eeeced2ca | ||
|
|
0fc369789e | ||
|
|
09f72f5ac6 | ||
|
|
716109bab5 | ||
|
|
0684fcf7e9 | ||
|
|
289104cde0 | ||
|
|
c9277e4b12 | ||
|
|
08125fc2a5 | ||
|
|
d06dd2ef43 | ||
|
|
8ae3eea19c | ||
|
|
a1bc76f305 | ||
|
|
045bede481 | ||
|
|
0a231f2e0e | ||
|
|
87e7c5f00a | ||
|
|
7ded9fe219 | ||
|
|
b389e75d33 | ||
|
|
9128bfc5f1 | ||
|
|
bb227d2c37 | ||
|
|
954da93301 | ||
|
|
1dce0f265d | ||
|
|
0f54630725 | ||
|
|
712cd69242 | ||
|
|
ff3bd640f0 | ||
|
|
abb347e1a8 | ||
|
|
949c9c0b8c | ||
|
|
627a9f7972 | ||
|
|
cce892e92f | ||
|
|
97fb8ef653 | ||
|
|
649d6169c9 | ||
|
|
9efb97c2a7 | ||
|
|
d876aa141c | ||
|
|
f64efad057 | ||
|
|
20edb87505 | ||
|
|
34156c55ae | ||
|
|
569bc243f1 | ||
|
|
bc6a42735c | ||
|
|
b2f2a9c721 | ||
|
|
8a00b9c77d | ||
|
|
d7506b6aaf | ||
|
|
3a86e89116 | ||
|
|
0e71bdab65 | ||
|
|
0c553b3406 | ||
|
|
33012f35ef | ||
|
|
a6b915f6b4 | ||
|
|
772c981c39 | ||
|
|
05905f8c3c | ||
|
|
9542bd8a44 | ||
|
|
d9fe457b44 | ||
|
|
59700b07db | ||
|
|
428e4563d0 | ||
|
|
6c3bbaa686 | ||
|
|
7009bb6d05 | ||
|
|
23a5692d59 | ||
|
|
2d4e7cfdee | ||
|
|
99222d8ab9 | ||
|
|
a845fee689 | ||
|
|
cfecf4f1d4 | ||
|
|
b3e49590a7 | ||
|
|
358837ed69 | ||
|
|
2a353830c2 | ||
|
|
14f51436d7 | ||
|
|
57e5465c2d | ||
|
|
d9619e65a2 | ||
|
|
9754994e0c | ||
|
|
34c894b15d | ||
|
|
a2665d9247 | ||
|
|
1e3ae67646 | ||
|
|
25b9625635 | ||
|
|
2404b22c1f | ||
|
|
bed882f41c | ||
|
|
fd9e0944cb | ||
|
|
7e462319c9 | ||
|
|
94de87ef86 | ||
|
|
eb13f1f4fb | ||
|
|
0a6c39ded4 | ||
|
|
ff1aac64c1 | ||
|
|
ef24894211 | ||
|
|
1113164505 | ||
|
|
1de73de2e3 | ||
|
|
571cc5a1da | ||
|
|
8cb0803605 | ||
|
|
95d52b87f4 | ||
|
|
07fd3abe2c | ||
|
|
5dedd2e0e0 | ||
|
|
57abb03deb | ||
|
|
4a38a74b16 |
11
.git-commit-template
Normal file
11
.git-commit-template
Normal file
@@ -0,0 +1,11 @@
|
||||
[section] Imperative-voiced title in less than 50
|
||||
|
||||
# Body describes what was done, and why. New obviously-needed features
|
||||
# don't necessarily require a why.
|
||||
|
||||
# Links to relevant bugs or web pages
|
||||
See: Github bug #242
|
||||
See: [frobozz blog post](https://jsix.dev/posts/frobozz/)
|
||||
|
||||
# Tags and keywords useful for searching
|
||||
Tags:
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,6 +1,10 @@
|
||||
.lock*
|
||||
build
|
||||
/build*
|
||||
*.bak
|
||||
tags
|
||||
.gdbinit
|
||||
popcorn.log
|
||||
jsix.log
|
||||
*.o
|
||||
*.a
|
||||
sysroot
|
||||
.gdb_history
|
||||
.peru
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "external/gnu-efi"]
|
||||
path = external/gnu-efi
|
||||
url = https://github.com/justinian/gnu-efi.git
|
||||
|
||||
354
LICENSE.md
Normal file
354
LICENSE.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# License
|
||||
|
||||
jsix is (c) 2017-2019 Justin C Miller, and distributed under the terms of the
|
||||
Mozilla Public License 2.0.
|
||||
|
||||
---
|
||||
|
||||
## Mozilla Public License Version 2.0
|
||||
|
||||
### 1. Definitions
|
||||
|
||||
#### 1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the creation
|
||||
of, or owns Covered Software.
|
||||
|
||||
#### 1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
#### 1.3. "Contribution
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
#### 1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the notice
|
||||
in Exhibit A, the Executable Form of such Source Code Form, and Modifications
|
||||
of such Source Code Form, in each case including portions thereof.
|
||||
|
||||
#### 1.5. "Incompatible With Secondary Licenses"
|
||||
|
||||
means
|
||||
|
||||
* **(a)** that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
* **(b)** that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
#### 1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
#### 1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
#### 1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
#### 1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed
|
||||
by this License.
|
||||
|
||||
#### 1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
* **(a)** any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
* **(b)** any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
#### 1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process, and
|
||||
apparatus claims, in any patent Licensable by such Contributor that would be
|
||||
infringed, but for the grant of the License, by the making, using, selling,
|
||||
offering for sale, having made, import, or transfer of either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
#### 1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public License,
|
||||
Version 3.0, or any later versions of those licenses.
|
||||
|
||||
#### 1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
#### 1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this License. For
|
||||
legal entities, "You" includes any entity that controls, is controlled by, or
|
||||
is under common control with You. For purposes of this definition, "control"
|
||||
means **(a)** the power, direct or indirect, to cause the direction or
|
||||
management of such entity, whether by contract or otherwise, or **(b)**
|
||||
ownership of more than fifty percent (50%) of the outstanding shares or
|
||||
beneficial ownership of such entity.
|
||||
|
||||
|
||||
### 2. License Grants and Conditions
|
||||
|
||||
#### 2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive
|
||||
license:
|
||||
|
||||
* **(a)** under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available, modify,
|
||||
display, perform, distribute, and otherwise exploit its Contributions, either
|
||||
on an unmodified basis, with Modifications, or as part of a Larger Work; and
|
||||
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions or
|
||||
its Contributor Version.
|
||||
|
||||
#### 2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
#### 2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
* **(a)** for any code that a Contributor has removed from Covered Software; or
|
||||
* **(b)** for infringements caused by: **(i)** Your and any other third party's
|
||||
modifications of Covered Software, or **(ii)** the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
* **(c)** under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the notice
|
||||
requirements in Section 3.4).
|
||||
|
||||
#### 2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to distribute
|
||||
the Covered Software under a subsequent version of this License (see Section
|
||||
10.2) or under the terms of a Secondary License (if permitted under the terms
|
||||
of Section 3.3).
|
||||
|
||||
#### 2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions are
|
||||
its original creation(s) or it has sufficient rights to grant the rights to its
|
||||
Contributions conveyed by this License.
|
||||
|
||||
#### 2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
#### 2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
### 3. Responsibilities
|
||||
|
||||
#### 3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form of
|
||||
the Covered Software is governed by the terms of this License, and how they can
|
||||
obtain a copy of this License. You may not attempt to alter or restrict the
|
||||
recipients' rights in the Source Code Form.
|
||||
|
||||
#### 3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
* **(a)** such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost of
|
||||
distribution to the recipient; and
|
||||
* **(b)** You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the license
|
||||
for the Executable Form does not attempt to limit or alter the recipients'
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
#### 3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software with
|
||||
a work governed by one or more Secondary Licenses, and the Covered Software is
|
||||
not Incompatible With Secondary Licenses, this License permits You to
|
||||
additionally distribute such Covered Software under the terms of such Secondary
|
||||
License(s), so that the recipient of the Larger Work may, at their option,
|
||||
further distribute the Covered Software under the terms of either this License
|
||||
or such Secondary License(s).
|
||||
|
||||
#### 3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations of
|
||||
liability) contained within the Source Code Form of the Covered Software,
|
||||
except that You may alter any license notices to the extent required to remedy
|
||||
known factual inaccuracies.
|
||||
|
||||
#### 3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support, indemnity
|
||||
or liability obligations to one or more recipients of Covered Software.
|
||||
However, You may do so only on Your own behalf, and not on behalf of any
|
||||
Contributor. You must make it absolutely clear that any such warranty, support,
|
||||
indemnity, or liability obligation is offered by You alone, and You hereby
|
||||
agree to indemnify every Contributor for any liability incurred by such
|
||||
Contributor as a result of warranty, support, indemnity or liability terms You
|
||||
offer. You may include additional disclaimers of warranty and limitations of
|
||||
liability specific to any jurisdiction.
|
||||
|
||||
|
||||
### 4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: **(a)** comply with the terms of this
|
||||
License to the maximum extent possible; and **(b)** describe the limitations
|
||||
and the code they affect. Such description must be placed in a text file
|
||||
included with all distributions of the Covered Software under this License.
|
||||
Except to the extent prohibited by statute or regulation, such description must
|
||||
be sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
|
||||
### 5. Termination
|
||||
|
||||
**5.1.** The rights granted under this License will terminate automatically if
|
||||
You fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor are
|
||||
reinstated **(a)** provisionally, unless and until such Contributor explicitly
|
||||
and finally terminates Your grants, and **(b)** on an ongoing basis, if such
|
||||
Contributor fails to notify You of the non-compliance by some reasonable means
|
||||
prior to 60 days after You have come back into compliance. Moreover, Your
|
||||
grants from a particular Contributor are reinstated on an ongoing basis if such
|
||||
Contributor notifies You of the non-compliance by some reasonable means, this
|
||||
is the first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after Your
|
||||
receipt of the notice.
|
||||
|
||||
**5.2.** If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims, and
|
||||
cross-claims) alleging that a Contributor Version directly or indirectly
|
||||
infringes any patent, then the rights granted to You by any and all
|
||||
Contributors for the Covered Software under Section 2.1 of this License shall
|
||||
terminate.
|
||||
|
||||
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all end
|
||||
user license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
|
||||
### 6. Disclaimer of Warranty
|
||||
|
||||
> Covered Software is provided under this License on an "as is" basis, without
|
||||
> warranty of any kind, either expressed, implied, or statutory, including,
|
||||
> without limitation, warranties that the Covered Software is free of defects,
|
||||
> merchantable, fit for a particular purpose or non-infringing. The entire risk
|
||||
> as to the quality and performance of the Covered Software is with You.
|
||||
> Should any Covered Software prove defective in any respect, You (not any
|
||||
> Contributor) assume the cost of any necessary servicing, repair, or
|
||||
> correction. This disclaimer of warranty constitutes an essential part of this
|
||||
> License. No use of any Covered Software is authorized under this License
|
||||
> except under this disclaimer.
|
||||
|
||||
### 7. Limitation of Liability
|
||||
|
||||
> Under no circumstances and under no legal theory, whether tort (including
|
||||
> negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
> distributes Covered Software as permitted above, be liable to You for any
|
||||
> direct, indirect, special, incidental, or consequential damages of any
|
||||
> character including, without limitation, damages for lost profits, loss of
|
||||
> goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
> other commercial damages or losses, even if such party shall have been
|
||||
> informed of the possibility of such damages. This limitation of liability
|
||||
> shall not apply to liability for death or personal injury resulting from such
|
||||
> party's negligence to the extent applicable law prohibits such limitation.
|
||||
> Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
> consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
|
||||
### 8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of a
|
||||
jurisdiction where the defendant maintains its principal place of business and
|
||||
such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party's ability to bring cross-claims or counter-claims.
|
||||
|
||||
|
||||
### 9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
### 10. Versions of the License
|
||||
|
||||
#### 10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section 10.3,
|
||||
no one other than the license steward has the right to modify or publish new
|
||||
versions of this License. Each version will be given a distinguishing version
|
||||
number.
|
||||
|
||||
#### 10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of the
|
||||
License under which You originally received the Covered Software, or under the
|
||||
terms of any subsequent version published by the license steward.
|
||||
|
||||
#### 10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to create a
|
||||
new license for such software, you may create and use a modified version of
|
||||
this License if you rename the license and remove any references to the name of
|
||||
the license steward (except to note that such modified license differs from
|
||||
this License).
|
||||
|
||||
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the notice
|
||||
described in Exhibit B of this License must be attached.
|
||||
|
||||
### Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
### Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as defined
|
||||
by the Mozilla Public License, v. 2.0.
|
||||
|
||||
241
Makefile
241
Makefile
@@ -1,241 +0,0 @@
|
||||
ARCH ?= x86_64
|
||||
|
||||
include src/arch/$(ARCH)/config.mk
|
||||
|
||||
BUILD_D := build
|
||||
KERN_D := src/kernel
|
||||
ARCH_D := src/arch/$(ARCH)
|
||||
VERSION ?= $(shell git describe --dirty --always)
|
||||
GITSHA ?= $(shell git rev-parse --short HEAD)
|
||||
|
||||
KERNEL_FILENAME:= popcorn.bin
|
||||
KERNEL_FONT := assets/fonts/tamsyn8x16r.psf
|
||||
|
||||
MODULES := kutil
|
||||
|
||||
|
||||
EFI_DIR := external/gnu-efi
|
||||
EFI_DATA := $(EFI_DIR)/gnuefi
|
||||
EFI_LDS := $(EFI_DATA)/elf_$(ARCH)_efi.lds
|
||||
EFI_ARCH_DIR := $(EFI_DIR)/$(ARCH)
|
||||
EFI_ARCH_DATA := $(EFI_ARCH_DIR)/gnuefi
|
||||
EFI_CRT_OBJ := $(EFI_ARCH_DATA)/crt0-efi-$(ARCH).o
|
||||
EFI_LIB := $(EFI_ARCH_DIR)/lib/libefi.a
|
||||
EFI_INCLUDES := $(EFI_DIR)/inc
|
||||
|
||||
DEPENDFLAGS := -MMD
|
||||
|
||||
INCLUDES := -I $(ARCH_D)
|
||||
INCLUDES += -I src/modules
|
||||
INCLUDES += -I src/include
|
||||
INCLUDES += -isystem $(EFI_INCLUDES)
|
||||
INCLUDES += -isystem $(EFI_INCLUDES)/$(ARCH)
|
||||
INCLUDES += -isystem $(EFI_INCLUDES)/protocol
|
||||
|
||||
BASEFLAGS := -ggdb -nostdlib
|
||||
BASEFLAGS += -ffreestanding -nodefaultlibs
|
||||
BASEFLAGS += -fno-builtin -fomit-frame-pointer
|
||||
BASEFLAGS += -mno-red-zone -fno-stack-protector
|
||||
|
||||
ifdef CPU
|
||||
BASEFLAGS += -mcpu=$(CPU)
|
||||
endif
|
||||
|
||||
# Removed Flags:: -Wcast-align
|
||||
WARNFLAGS += -Wformat=2 -Winit-self -Wfloat-equal -Winline
|
||||
WARNFLAGS += -Winvalid-pch -Wmissing-format-attribute
|
||||
WARNFLAGS += -Wmissing-include-dirs -Wswitch -Wundef
|
||||
WARNFLAGS += -Wdisabled-optimization -Wpointer-arith
|
||||
|
||||
WARNFLAGS += -Wno-attributes -Wno-sign-compare -Wno-multichar
|
||||
WARNFLAGS += -Wno-div-by-zero -Wno-endif-labels -Wno-pragmas
|
||||
WARNFLAGS += -Wno-format-extra-args -Wno-unused-result
|
||||
WARNFLAGS += -Wno-deprecated-declarations -Wno-unused-function
|
||||
WARNFLAGS += -Wno-unused-but-set-parameter
|
||||
|
||||
ASFLAGS ?=
|
||||
ASFLAGS += -p $(BUILD_D)/versions.s
|
||||
|
||||
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||
CFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
||||
CFLAGS += -std=c11 -mcmodel=large
|
||||
|
||||
CXXFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||
CXXFLAGS += -DGIT_VERSION="\"$(VERSION)\""
|
||||
CXXFLAGS += -std=c++14 -mcmodel=large
|
||||
|
||||
BOOT_CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
|
||||
BOOT_CFLAGS += -std=c11 -I src/boot -fPIC -fshort-wchar
|
||||
BOOT_CFLAGS += -DKERNEL_FILENAME="L\"$(KERNEL_FILENAME)\""
|
||||
BOOT_CFLAGS += -DGIT_VERSION_WIDE="L\"$(VERSION)\""
|
||||
BOOT_CFLAGS += -DGNU_EFI_USE_MS_ABI -DHAVE_USE_MS_ABI
|
||||
BOOT_CFLAGS += -DEFI_DEBUG=0 -DEFI_DEBUG_CLEAR_MEMORY=0
|
||||
#BOOT_CFLAGS += -DEFI_FUNCTION_WRAPPER
|
||||
|
||||
ifdef MAX_HRES
|
||||
BOOT_CFLAGS += -DMAX_HRES=$(MAX_HRES)
|
||||
endif
|
||||
|
||||
LDFLAGS := -L $(BUILD_D) -ggdb
|
||||
LDFLAGS += -nostdlib -znocombreloc -Bsymbolic -nostartfiles
|
||||
|
||||
BOOT_LDFLAGS := $(LDFLAGS) -shared
|
||||
BOOT_LDFLAGS += -L $(EFI_ARCH_DIR)/lib -L $(EFI_ARCH_DIR)/gnuefi
|
||||
|
||||
AS ?= $(CROSS)nasm
|
||||
AR ?= $(CROSS)ar
|
||||
CC ?= $(CROSS)gcc
|
||||
CXX ?= $(CROSS)g++
|
||||
LD ?= $(CROSS)ld
|
||||
OBJC := $(CROSS)objcopy
|
||||
OBJD := $(CROSS)objdump
|
||||
|
||||
INIT_DEP := $(BUILD_D)/.builddir
|
||||
|
||||
BOOT_SRCS := $(wildcard src/boot/*.c)
|
||||
BOBJS += $(patsubst src/boot/%,$(BUILD_D)/boot/%,$(patsubst %,%.o,$(BOOT_SRCS)))
|
||||
|
||||
KERN_SRCS := $(wildcard $(KERN_D)/*.s)
|
||||
KERN_SRCS += $(wildcard $(KERN_D)/*.c)
|
||||
KERN_SRCS += $(wildcard $(KERN_D)/*.cpp)
|
||||
KERN_SRCS += $(wildcard $(ARCH_D)/*.s)
|
||||
KERN_SRCS += $(wildcard $(ARCH_D)/*.c)
|
||||
|
||||
KERN_OBJS := $(patsubst src/%, $(BUILD_D)/%, $(patsubst %,%.o,$(KERN_SRCS)))
|
||||
|
||||
MOD_TARGETS :=
|
||||
|
||||
PARTED ?= /sbin/parted
|
||||
QEMU ?= qemu-system-x86_64
|
||||
GDBPORT ?= 27006
|
||||
CPUS ?= 1
|
||||
OVMF ?= assets/ovmf/x64/OVMF.fd
|
||||
|
||||
QEMUOPTS := -pflash $(BUILD_D)/flash.img
|
||||
QEMUOPTS += -drive file=$(BUILD_D)/fs.img,format=raw
|
||||
QEMUOPTS += -smp $(CPUS)
|
||||
QEMUOPTS += -m 512
|
||||
QEMUOPTS += -d guest_errors
|
||||
QEMUOPTS += $(QEMUEXTRA)
|
||||
|
||||
|
||||
all: $(BUILD_D)/fs.img
|
||||
init: $(INIT_DEP)
|
||||
|
||||
$(INIT_DEP):
|
||||
mkdir -p $(BUILD_D) $(patsubst %,$(BUILD_D)/d.%,$(MODULES))
|
||||
mkdir -p $(BUILD_D)/boot
|
||||
mkdir -p $(patsubst src/%,$(BUILD_D)/%,$(ARCH_D))
|
||||
mkdir -p $(patsubst src/%,$(BUILD_D)/%,$(KERN_D))
|
||||
touch $(INIT_DEP)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_D)/* $(BUILD_D)/.version $(BUILD_D)/.builddir
|
||||
|
||||
vars:
|
||||
@echo "KERN_SRCS: " $(KERN_SRCS)
|
||||
@echo "KERN_OBJS: " $(KERN_OBJS)
|
||||
|
||||
dist-clean: clean
|
||||
make -C external/gnu-efi clean
|
||||
|
||||
dump: $(BUILD_D)/kernel.dump
|
||||
vim $<
|
||||
|
||||
.PHONY: all clean dist-clean init dump vars
|
||||
|
||||
$(BUILD_D)/.version:
|
||||
echo '$(VERSION)' | cmp -s - $@ || echo '$(VERSION)' > $@
|
||||
|
||||
$(BUILD_D)/versions.s:
|
||||
./parse_version.py "$(VERSION)" "$(GITSHA)" > $@
|
||||
|
||||
-include x $(patsubst %,src/modules/%/module.mk,$(MODULES))
|
||||
-include x $(shell find $(BUILD_D) -type f -name '*.d')
|
||||
|
||||
$(EFI_LIB):
|
||||
make -C external/gnu-efi all
|
||||
|
||||
$(BUILD_D)/boot.elf: $(BOBJS) $(EFI_LIB)
|
||||
$(LD) $(BOOT_LDFLAGS) -T $(EFI_LDS) -o $@ \
|
||||
$(EFI_CRT_OBJ) $(BOBJS) -lefi -lgnuefi
|
||||
|
||||
$(BUILD_D)/boot.efi: $(BUILD_D)/boot.elf
|
||||
$(OBJC) -j .text -j .sdata -j .data -j .dynamic \
|
||||
-j .dynsym -j .rel -j .rela -j .reloc \
|
||||
--target=efi-app-$(ARCH) $^ $@
|
||||
|
||||
$(BUILD_D)/boot.debug.efi: $(BUILD_D)/boot.elf
|
||||
$(OBJC) -j .text -j .sdata -j .data -j .dynamic \
|
||||
-j .dynsym -j .rel -j .rela -j .reloc \
|
||||
-j .debug_info -j .debug_abbrev -j .debug_loc -j .debug_str \
|
||||
-j .debug_aranges -j .debug_line -j .debug_macinfo \
|
||||
--target=efi-app-$(ARCH) $^ $@
|
||||
|
||||
$(BUILD_D)/%.bin: $(BUILD_D)/%.elf
|
||||
$(OBJC) $< -O binary $@
|
||||
|
||||
$(BUILD_D)/boot.dump: $(BUILD_D)/boot.efi
|
||||
$(OBJD) -D -S $< > $@
|
||||
|
||||
$(BUILD_D)/boot/%.s.o: src/boot/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -i src/boot/ -o $@ $<
|
||||
|
||||
$(BUILD_D)/boot/%.c.o: src/boot/%.c $(INIT_DEP)
|
||||
$(CC) $(BOOT_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_D)/kernel.elf: $(KERN_OBJS) $(MOD_TARGETS) $(ARCH_D)/kernel.ld
|
||||
$(LD) $(LDFLAGS) -u _header -T $(ARCH_D)/kernel.ld -o $@ $(KERN_OBJS) $(patsubst %,-l%,$(MODULES))
|
||||
$(OBJC) --only-keep-debug $@ $@.sym
|
||||
|
||||
$(BUILD_D)/kernel.dump: $(BUILD_D)/kernel.elf
|
||||
$(OBJD) -D -S $< > $@
|
||||
|
||||
$(BUILD_D)/%.s.o: src/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -i $(ARCH_D)/ -i $(KERN_D)/ -o $@ $<
|
||||
|
||||
$(BUILD_D)/%.c.o: src/%.c $(INIT_DEP)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_D)/%.cpp.o: src/%.cpp $(INIT_DEP)
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_D)/flash.img: $(OVMF)
|
||||
cp $^ $@
|
||||
|
||||
$(BUILD_D)/screenfont.psf: $(KERNEL_FONT)
|
||||
cp $^ $@
|
||||
|
||||
$(BUILD_D)/fs.img: $(BUILD_D)/boot.efi $(BUILD_D)/kernel.bin $(BUILD_D)/screenfont.psf
|
||||
$(eval TEMPFILE := $(shell mktemp --suffix=.img))
|
||||
dd if=/dev/zero of=$@.tmp bs=512 count=93750
|
||||
$(PARTED) $@.tmp -s -a minimal mklabel gpt
|
||||
$(PARTED) $@.tmp -s -a minimal mkpart EFI FAT16 2048s 93716s
|
||||
$(PARTED) $@.tmp -s -a minimal toggle 1 boot
|
||||
dd if=/dev/zero of=$(TEMPFILE) bs=512 count=91669
|
||||
mformat -i $(TEMPFILE) -h 32 -t 32 -n 64 -c 1
|
||||
mmd -i $(TEMPFILE) ::/EFI
|
||||
mmd -i $(TEMPFILE) ::/EFI/BOOT
|
||||
mcopy -i $(TEMPFILE) $(BUILD_D)/boot.efi ::/EFI/BOOT/BOOTX64.efi
|
||||
mcopy -i $(TEMPFILE) $(BUILD_D)/kernel.bin ::$(KERNEL_FILENAME)
|
||||
mcopy -i $(TEMPFILE) $(BUILD_D)/screenfont.psf ::screenfont.psf
|
||||
mlabel -i $(TEMPFILE) ::Popcorn_OS
|
||||
dd if=$(TEMPFILE) of=$@.tmp bs=512 count=91669 seek=2048 conv=notrunc
|
||||
rm $(TEMPFILE)
|
||||
mv $@.tmp $@
|
||||
|
||||
$(BUILD_D)/fs.iso: $(BUILD_D)/fs.img
|
||||
mkdir -p $(BUILD_D)/iso
|
||||
cp $< $(BUILD_D)/iso/
|
||||
xorriso -as mkisofs -R -f -e fs.img -no-emul-boot -o $@ $(BUILD_D)/iso
|
||||
|
||||
qemu: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
|
||||
"$(QEMU)" $(QEMUOPTS) -nographic
|
||||
|
||||
qemu-window: $(BUILD_D)/fs.img $(BUILD_D)/flash.img
|
||||
"$(QEMU)" $(QEMUOPTS)
|
||||
|
||||
qemu-gdb: $(BUILD_D)/fs.img $(BUILD_D)/boot.debug.efi $(BUILD_D)/flash.img $(BUILD_D)/kernel.elf
|
||||
"$(QEMU)" $(QEMUOPTS) -d mmu,guest_errors,page -D popcorn.log -s -nographic
|
||||
|
||||
# vim: ft=make ts=4
|
||||
33
NOTES.md
33
NOTES.md
@@ -2,6 +2,37 @@
|
||||
|
||||
## TODO
|
||||
|
||||
- Better page-allocation model
|
||||
- Paging manager
|
||||
- Copy-on-write pages
|
||||
- Better page-allocation model?
|
||||
- Allow for more than one IOAPIC in ACPI module
|
||||
- The objects get created, but GSI lookup only uses the one at index 0
|
||||
- mark kernel memory pages global
|
||||
- Serial out based on circular/bip biffer and interrupts, not spinning on
|
||||
`write_ready()`
|
||||
- Split out more code into kutil for testing
|
||||
- AHCI / MSI interrupts on Vbox break?
|
||||
- FXSAVE to save XMM registers.
|
||||
- optimization using #NM (0x7) to detect SSE usage
|
||||
- Clean up of process memory maps
|
||||
- Better stack tracer
|
||||
- Bootloader rewrite
|
||||
- C++ and sharing library code for ELF, initrd, etc
|
||||
- Parse initrd and pre-load certain ELF images, eg the process loader process?
|
||||
- Do initial memory bootstrap?
|
||||
- Calling global ctors
|
||||
- Device Tree
|
||||
- Actual serial driver
|
||||
- Disk driver
|
||||
- File system
|
||||
- Multiprocessing
|
||||
- Fast syscalls using syscall/sysret
|
||||
|
||||
### Build
|
||||
|
||||
- Clean up build generator and its templates
|
||||
- More robust objects to represent modules & targets to pass to templates
|
||||
- Read project setup from a simple JSON/TOML/etc
|
||||
- Better compartmentalizing when doing template inheritance
|
||||
- Move to LLD as sysroot linker
|
||||
|
||||
|
||||
81
README.md
81
README.md
@@ -1,7 +1,78 @@
|
||||
# popcorn: A toy microkernel x64 UEFI OS
|
||||
# jsix: A toy OS kernel
|
||||
|
||||
**popcorn** is a hobby OS for x64 UEFI environments to play with building a
|
||||
microkenerl architecture. It's far from finished, or even being usable - for
|
||||
now, it's a sandbox for me to explore the UEFI architecture, microkernels, and
|
||||
OS-related concepts that I want to play with.
|
||||
**jsix** is the kernel for the hobby OS that I am currently building. It's
|
||||
far from finished, or even being usable. Instead, it's a sandbox for me to play
|
||||
with kernel-level code and explore architectures.
|
||||
|
||||
The design goals of the project are:
|
||||
|
||||
* Modernity - I'm not interested in designing for legacy systems, or running on
|
||||
all hardware out there. My target is only 64 bit architecutres, and modern
|
||||
commodity hardware. Currently that means x64 systems with Nehalem or newer
|
||||
CPUs and UEFI firmware. Eventually I'd like to work on an AArch64 port,
|
||||
partly to force myself to factor out the architecture-dependent pieces of the
|
||||
code base.
|
||||
|
||||
* Modularity - I'd like to pull as much of the system out into separate
|
||||
processes as possible, in the microkernel fashion. A sub-goal of this is to
|
||||
explore where the bottlenecks of such a microkernel are now, and whether
|
||||
eschewing legacy hardware will let me design a system that's less bogged down
|
||||
by the traditional microkernel problems. Given that there are no processes
|
||||
yet, the kernel is monolithic by default.
|
||||
|
||||
* Exploration - I'm really mostly doing this to have fun learning and exploring
|
||||
modern OS development. Modular design may be tossed out (hopefully
|
||||
temporarily) in some places to allow me to play around with the related
|
||||
hardware.
|
||||
|
||||
A note on the name: This kernel was originally named Popcorn, but I have since
|
||||
discovered that the Popcorn Linux project is also developing a kernel with that
|
||||
name, started around the same time as this project. So I've renamed this kernel
|
||||
jsix (Always styled _jsix_ or `j6`, never capitalized) as an homage to L4, xv6,
|
||||
and my wonderful wife.
|
||||
|
||||
## Building
|
||||
|
||||
jsix uses the [Ninja][] build tool, and generates the build files for it with a
|
||||
custom tool called [Bonnibel][]. Bonnibel can be installed with [Cargo][], or
|
||||
downloaded as a prebuilt binary from its Github repository.
|
||||
|
||||
[Ninja]: https://ninja-build.org
|
||||
[Bonnibel]: https://github.com/justinian/bonnibel
|
||||
[Cargo]: https://crates.io/crates/bonnibel
|
||||
|
||||
Requrirements:
|
||||
|
||||
* bonnibel
|
||||
* ninja
|
||||
* clang
|
||||
* nasm
|
||||
* mtools
|
||||
* curl for downloading the toolchain
|
||||
|
||||
### Setting up the cross toolchain
|
||||
|
||||
Running `pb sync` will download and unpack the toolchain into `sysroot`.
|
||||
|
||||
#### Compiling the toolchain yourself
|
||||
|
||||
If you have `clang` and `curl` installed, runing the `scripts/build_sysroot.sh`
|
||||
script will download and build a LLVM toolchain configured for building jsix
|
||||
host binaries.
|
||||
|
||||
### Building and running jsix
|
||||
|
||||
Once the toolchain has been set up, running Bonnibel's `pb init` command will
|
||||
set up the build configuration, and `pb build` will actually run the build. If
|
||||
you have `qemu-system-x86_64` installed, the `qemu.sh` script will to run jsix
|
||||
in QEMU `-nographic` mode.
|
||||
|
||||
I personally run this either from a real debian amd64 testing/buster machine or
|
||||
a windows WSL debian testing/buster installation. The following should be
|
||||
enough to set up such a system to build the kernel:
|
||||
|
||||
sudo apt install qemu-system-x86 nasm clang-6.0 mtools curl
|
||||
sudo update-alternatives /usr/bin/clang clang /usr/bin/clang-6.0 1000
|
||||
sudo update-alternatives /usr/bin/clang++ clang++ /usr/bin/clang++-6.0 1000
|
||||
curl -L -o pb https://github.com/justinian/bonnibel/releases/download/2.0.0/pb_linux_amd64 && chmod a+x pb
|
||||
|
||||
|
||||
28
assets/debugging/jsix.elf-gdb.py
Normal file
28
assets/debugging/jsix.elf-gdb.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import gdb
|
||||
|
||||
class PrintStackCommand(gdb.Command):
|
||||
def __init__(self):
|
||||
super().__init__("popc_stack", gdb.COMMAND_DATA)
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
args = gdb.string_to_argv(arg)
|
||||
|
||||
base = "$rsp"
|
||||
if len(args) > 0:
|
||||
base = args[0]
|
||||
|
||||
depth = 22
|
||||
if len(args) > 1:
|
||||
depth = int(args[1])
|
||||
|
||||
for i in range(depth-1, -1, -1):
|
||||
offset = i * 8
|
||||
base_addr = gdb.parse_and_eval(base)
|
||||
value = gdb.parse_and_eval(f"*(uint64_t*)({base} + {offset})")
|
||||
print("{:016x} (+{:04x}): {:016x}".format(int(base_addr) + offset, offset, int(value)))
|
||||
|
||||
|
||||
PrintStackCommand()
|
||||
|
||||
gdb.execute("target remote :1234")
|
||||
gdb.execute("display/i $rip")
|
||||
BIN
assets/diskbase.img
Normal file
BIN
assets/diskbase.img
Normal file
Binary file not shown.
BIN
assets/floppy.img
Normal file
BIN
assets/floppy.img
Normal file
Binary file not shown.
23
assets/initrd.toml
Normal file
23
assets/initrd.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
# This is the manifest for the initial ramdisk, read by the `makerd` tool.
|
||||
# The contents should be a table array of files to add to the ramdistk:
|
||||
#
|
||||
# [[files]]
|
||||
# dest = "foo.bar" # Name of the file in the ramdisk
|
||||
# source = "build/foo/foo.bar" # Location of the file from the project root
|
||||
# executable = true # Optional, default false. Whether this is an
|
||||
# # initial application for the kernel to execute
|
||||
# # on startup
|
||||
|
||||
[[files]]
|
||||
dest = "screenfont.psf"
|
||||
source = "../assets/fonts/tamsyn8x16r.psf"
|
||||
|
||||
[[files]]
|
||||
dest = "nulldrv1"
|
||||
source = "user/nulldrv"
|
||||
executable = true
|
||||
|
||||
[[files]]
|
||||
dest = "nulldrv2"
|
||||
source = "user/nulldrv"
|
||||
executable = true
|
||||
Binary file not shown.
BIN
assets/ovmf/x64/ovmf_code.fd
Normal file
BIN
assets/ovmf/x64/ovmf_code.fd
Normal file
Binary file not shown.
BIN
assets/ovmf/x64/ovmf_vars.fd
Normal file
BIN
assets/ovmf/x64/ovmf_vars.fd
Normal file
Binary file not shown.
BIN
assets/ovmf/x64/ovmf_vars_d.fd
Normal file
BIN
assets/ovmf/x64/ovmf_vars_d.fd
Normal file
Binary file not shown.
17075
external/catch/catch.hpp
vendored
Normal file
17075
external/catch/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3669
external/cpptoml/cpptoml.h
vendored
Normal file
3669
external/cpptoml/cpptoml.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
external/gnu-efi
vendored
1
external/gnu-efi
vendored
Submodule external/gnu-efi deleted from fc5af9e47f
101
external/uefi/boot_services.h
vendored
Normal file
101
external/uefi/boot_services.h
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_boot_services_h_
|
||||
#define _uefi_boot_services_h_
|
||||
|
||||
// This Source Code Form is part of the j6-uefi-headers project and is subject
|
||||
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
|
||||
// not distributed with this file, You can obtain one at
|
||||
// http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <uefi/tables.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace bs_impl {
|
||||
using allocate_pages = status (*)(allocate_type, memory_type, size_t, void**);
|
||||
using get_memory_map = status (*)(size_t*, memory_descriptor*, size_t*, size_t*, uint32_t*);
|
||||
using allocate_pool = status (*)(memory_type, uint64_t, void**);
|
||||
using handle_protocol = status (*)(handle, const guid*, void**);
|
||||
using create_event = status (*)(evt, tpl, event_notify, void*, event*);
|
||||
using exit_boot_services = status (*)(handle, size_t);
|
||||
using locate_protocol = status (*)(const guid*, void*, void**);
|
||||
using copy_mem = void (*)(void*, void*, size_t);
|
||||
using set_mem = void (*)(void*, uint64_t, uint8_t);
|
||||
}
|
||||
|
||||
struct boot_services {
|
||||
static constexpr uint64_t signature = 0x56524553544f4f42ull;
|
||||
|
||||
table_header header;
|
||||
|
||||
// Task Priority Level management
|
||||
void *raise_tpl;
|
||||
void *restore_tpl;
|
||||
|
||||
// Memory Services
|
||||
bs_impl::allocate_pages allocate_pages;
|
||||
void *free_pages;
|
||||
bs_impl::get_memory_map get_memory_map;
|
||||
bs_impl::allocate_pool allocate_pool;
|
||||
void *free_pool;
|
||||
|
||||
// Event & Timer Services
|
||||
bs_impl::create_event create_event;
|
||||
void *set_timer;
|
||||
void *wait_for_event;
|
||||
void *signal_event;
|
||||
void *close_event;
|
||||
void *check_event;
|
||||
|
||||
// Protocol Handler Services
|
||||
void *install_protocol_interface;
|
||||
void *reinstall_protocol_interface;
|
||||
void *uninstall_protocol_interface;
|
||||
bs_impl::handle_protocol handle_protocol;
|
||||
void *_reserved;
|
||||
void *register_protocol_notify;
|
||||
void *locate_handle;
|
||||
void *locate_device_path;
|
||||
void *install_configuration_table;
|
||||
|
||||
// Image Services
|
||||
void *load_image;
|
||||
void *start_image;
|
||||
void *exit;
|
||||
void *unload_image;
|
||||
bs_impl::exit_boot_services exit_boot_services;
|
||||
|
||||
// Miscellaneous Services
|
||||
void *get_next_monotonic_count;
|
||||
void *stall;
|
||||
void *set_watchdog_timer;
|
||||
|
||||
// DriverSupport Services
|
||||
void *connect_controller;
|
||||
void *disconnect_controller;
|
||||
|
||||
// Open and Close Protocol Services
|
||||
void *open_protocol;
|
||||
void *close_protocol;
|
||||
void *open_protocol_information;
|
||||
|
||||
// Library Services
|
||||
void *protocols_per_handle;
|
||||
void *locate_handle_buffer;
|
||||
bs_impl::locate_protocol locate_protocol;
|
||||
void *install_multiple_protocol_interfaces;
|
||||
void *uninstall_multiple_protocol_interfaces;
|
||||
|
||||
// 32-bit CRC Services
|
||||
void *calculate_crc32;
|
||||
|
||||
// Miscellaneous Services
|
||||
bs_impl::copy_mem copy_mem;
|
||||
bs_impl::set_mem set_mem;
|
||||
void *create_event_ex;
|
||||
};
|
||||
|
||||
} // namespace uefi
|
||||
|
||||
#endif
|
||||
39
external/uefi/errors.inc
vendored
Normal file
39
external/uefi/errors.inc
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
STATUS_WARNING( warn_unknown_glyph, 1)
|
||||
STATUS_WARNING( warn_delete_failure, 2)
|
||||
STATUS_WARNING( warn_write_failure, 3)
|
||||
STATUS_WARNING( warn_buffer_too_small,4)
|
||||
STATUS_WARNING( warn_stale_data, 5)
|
||||
STATUS_WARNING( warn_file_system, 6)
|
||||
|
||||
STATUS_ERROR( load_error, 1)
|
||||
STATUS_ERROR( invalid_parameter, 2)
|
||||
STATUS_ERROR( unsupported, 3)
|
||||
STATUS_ERROR( bad_buffer_size, 4)
|
||||
STATUS_ERROR( buffer_too_small, 5)
|
||||
STATUS_ERROR( not_ready, 6)
|
||||
STATUS_ERROR( device_error, 7)
|
||||
STATUS_ERROR( write_protected, 8)
|
||||
STATUS_ERROR( out_of_resources, 9)
|
||||
STATUS_ERROR( volume_corrupted, 10)
|
||||
STATUS_ERROR( volume_full, 11)
|
||||
STATUS_ERROR( no_media, 12)
|
||||
STATUS_ERROR( media_changed, 13)
|
||||
STATUS_ERROR( not_found, 14)
|
||||
STATUS_ERROR( access_denied, 15)
|
||||
STATUS_ERROR( no_response, 16)
|
||||
STATUS_ERROR( no_mapping, 17)
|
||||
STATUS_ERROR( timeout, 18)
|
||||
STATUS_ERROR( not_started, 19)
|
||||
STATUS_ERROR( already_started, 20)
|
||||
STATUS_ERROR( aborted, 21)
|
||||
STATUS_ERROR( icmp_error, 22)
|
||||
STATUS_ERROR( tftp_error, 23)
|
||||
STATUS_ERROR( protocol_error, 24)
|
||||
STATUS_ERROR( incompatible_version, 25)
|
||||
STATUS_ERROR( security_violation, 26)
|
||||
STATUS_ERROR( crc_error, 27)
|
||||
STATUS_ERROR( end_of_media, 28)
|
||||
STATUS_ERROR( end_of_file, 31)
|
||||
STATUS_ERROR( invalid_language, 32)
|
||||
STATUS_ERROR( compromised_data, 33)
|
||||
STATUS_ERROR( http_error, 35)
|
||||
92
external/uefi/graphics.h
vendored
Normal file
92
external/uefi/graphics.h
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_graphics_h_
|
||||
#define _uefi_graphics_h_
|
||||
|
||||
// This Source Code Form is part of the j6-uefi-headers project and is subject
|
||||
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
|
||||
// not distributed with this file, You can obtain one at
|
||||
// http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace uefi {
|
||||
|
||||
struct text_output_mode
|
||||
{
|
||||
int32_t max_mode;
|
||||
int32_t mode;
|
||||
int32_t attribute;
|
||||
int32_t cursor_column;
|
||||
int32_t cursor_row;
|
||||
bool cursor_visible;
|
||||
};
|
||||
|
||||
struct pixel_bitmask
|
||||
{
|
||||
uint32_t red_mask;
|
||||
uint32_t green_mask;
|
||||
uint32_t blue_mask;
|
||||
uint32_t reserved_mask;
|
||||
};
|
||||
|
||||
enum class pixel_format
|
||||
{
|
||||
rgb8,
|
||||
bgr8,
|
||||
bitmask,
|
||||
blt_only
|
||||
};
|
||||
|
||||
struct graphics_output_mode_info
|
||||
{
|
||||
uint32_t version;
|
||||
uint32_t horizontal_resolution;
|
||||
uint32_t vertical_resolution;
|
||||
pixel_format pixel_format;
|
||||
pixel_bitmask pixel_information;
|
||||
uint32_t pixels_per_scanline;
|
||||
};
|
||||
|
||||
struct graphics_output_mode
|
||||
{
|
||||
uint32_t max_mode;
|
||||
uint32_t mode;
|
||||
graphics_output_mode_info *info;
|
||||
uint64_t size_of_info;
|
||||
uintptr_t frame_buffer_base;
|
||||
uint64_t frame_buffer_size;
|
||||
};
|
||||
|
||||
enum class attribute : uint64_t
|
||||
{
|
||||
black,
|
||||
blue,
|
||||
green,
|
||||
cyan,
|
||||
red,
|
||||
magenta,
|
||||
brown,
|
||||
light_gray,
|
||||
dark_gray,
|
||||
light_blue,
|
||||
light_green,
|
||||
light_cyan,
|
||||
light_red,
|
||||
light_magenta,
|
||||
yellow,
|
||||
white,
|
||||
|
||||
background_black = 0x00,
|
||||
background_blue = 0x10,
|
||||
background_green = 0x20,
|
||||
background_cyan = 0x30,
|
||||
background_red = 0x40,
|
||||
background_magenta = 0x50,
|
||||
background_brown = 0x60,
|
||||
background_light_gray = 0x70,
|
||||
};
|
||||
|
||||
} // namespace uefi
|
||||
|
||||
#endif
|
||||
29
external/uefi/guid.h
vendored
Normal file
29
external/uefi/guid.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_guid_h_
|
||||
#define _uefi_guid_h_
|
||||
|
||||
// This Source Code Form is part of the j6-uefi-headers project and is subject
|
||||
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
|
||||
// not distributed with this file, You can obtain one at
|
||||
// http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace uefi {
|
||||
|
||||
struct guid
|
||||
{
|
||||
uint32_t data1;
|
||||
uint16_t data2;
|
||||
uint16_t data3;
|
||||
uint8_t data4[8];
|
||||
|
||||
inline bool operator==(const guid &other) const {
|
||||
return reinterpret_cast<const uint64_t*>(this)[0] == reinterpret_cast<const uint64_t*>(&other)[0]
|
||||
&& reinterpret_cast<const uint64_t*>(this)[1] == reinterpret_cast<const uint64_t*>(&other)[1];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace uefi
|
||||
|
||||
#endif
|
||||
30
external/uefi/protos/device_path.h
vendored
Normal file
30
external/uefi/protos/device_path.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_device_path_h_
|
||||
#define _uefi_protos_device_path_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct device_path;
|
||||
|
||||
struct device_path
|
||||
{
|
||||
static constexpr uefi::guid guid{ 0x09576e91,0x6d3f,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
|
||||
|
||||
uint8_t type;
|
||||
uint8_t sub_type;
|
||||
uint16_t length;
|
||||
|
||||
protected:
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_device_path_h_
|
||||
126
external/uefi/protos/file.h
vendored
Normal file
126
external/uefi/protos/file.h
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_file_h_
|
||||
#define _uefi_protos_file_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct file;
|
||||
|
||||
struct file
|
||||
{
|
||||
|
||||
|
||||
inline uefi::status open(file ** new_handle, const wchar_t * file_name, file_mode open_mode, file_attr attributes) {
|
||||
return _open(this, new_handle, file_name, open_mode, attributes);
|
||||
}
|
||||
|
||||
inline uefi::status close() {
|
||||
return _close(this);
|
||||
}
|
||||
|
||||
inline uefi::status delete_file() {
|
||||
return _delete_file(this);
|
||||
}
|
||||
|
||||
inline uefi::status read(uint64_t * buffer_size, void * buffer) {
|
||||
return _read(this, buffer_size, buffer);
|
||||
}
|
||||
|
||||
inline uefi::status write(uint64_t * buffer_size, void * buffer) {
|
||||
return _write(this, buffer_size, buffer);
|
||||
}
|
||||
|
||||
inline uefi::status get_position(uint64_t * position) {
|
||||
return _get_position(this, position);
|
||||
}
|
||||
|
||||
inline uefi::status set_position(uint64_t position) {
|
||||
return _set_position(this, position);
|
||||
}
|
||||
|
||||
inline uefi::status get_info(const guid * info_type, uint64_t * buffer_size, void * buffer) {
|
||||
return _get_info(this, info_type, buffer_size, buffer);
|
||||
}
|
||||
|
||||
inline uefi::status set_info(const guid * info_type, uint64_t buffer_size, void * buffer) {
|
||||
return _set_info(this, info_type, buffer_size, buffer);
|
||||
}
|
||||
|
||||
inline uefi::status flush() {
|
||||
return _flush(this);
|
||||
}
|
||||
|
||||
inline uefi::status open_ex(file ** new_handle, const wchar_t * file_name, uint64_t open_mode, uint64_t attributes, file_io_token * token) {
|
||||
return _open_ex(this, new_handle, file_name, open_mode, attributes, token);
|
||||
}
|
||||
|
||||
inline uefi::status read_ex(file_io_token * token) {
|
||||
return _read_ex(this, token);
|
||||
}
|
||||
|
||||
inline uefi::status write_ex(file_io_token * token) {
|
||||
return _write_ex(this, token);
|
||||
}
|
||||
|
||||
inline uefi::status flush_ex(file_io_token * token) {
|
||||
return _flush_ex(this, token);
|
||||
}
|
||||
|
||||
uint64_t revision;
|
||||
|
||||
protected:
|
||||
using _open_def = uefi::status (*)(uefi::protos::file *, file **, const wchar_t *, file_mode, file_attr);
|
||||
_open_def _open;
|
||||
|
||||
using _close_def = uefi::status (*)(uefi::protos::file *);
|
||||
_close_def _close;
|
||||
|
||||
using _delete_file_def = uefi::status (*)(uefi::protos::file *);
|
||||
_delete_file_def _delete_file;
|
||||
|
||||
using _read_def = uefi::status (*)(uefi::protos::file *, uint64_t *, void *);
|
||||
_read_def _read;
|
||||
|
||||
using _write_def = uefi::status (*)(uefi::protos::file *, uint64_t *, void *);
|
||||
_write_def _write;
|
||||
|
||||
using _get_position_def = uefi::status (*)(uefi::protos::file *, uint64_t *);
|
||||
_get_position_def _get_position;
|
||||
|
||||
using _set_position_def = uefi::status (*)(uefi::protos::file *, uint64_t);
|
||||
_set_position_def _set_position;
|
||||
|
||||
using _get_info_def = uefi::status (*)(uefi::protos::file *, const guid *, uint64_t *, void *);
|
||||
_get_info_def _get_info;
|
||||
|
||||
using _set_info_def = uefi::status (*)(uefi::protos::file *, const guid *, uint64_t, void *);
|
||||
_set_info_def _set_info;
|
||||
|
||||
using _flush_def = uefi::status (*)(uefi::protos::file *);
|
||||
_flush_def _flush;
|
||||
|
||||
using _open_ex_def = uefi::status (*)(uefi::protos::file *, file **, const wchar_t *, uint64_t, uint64_t, file_io_token *);
|
||||
_open_ex_def _open_ex;
|
||||
|
||||
using _read_ex_def = uefi::status (*)(uefi::protos::file *, file_io_token *);
|
||||
_read_ex_def _read_ex;
|
||||
|
||||
using _write_ex_def = uefi::status (*)(uefi::protos::file *, file_io_token *);
|
||||
_write_ex_def _write_ex;
|
||||
|
||||
using _flush_ex_def = uefi::status (*)(uefi::protos::file *, file_io_token *);
|
||||
_flush_ex_def _flush_ex;
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_file_h_
|
||||
35
external/uefi/protos/file_info.h
vendored
Normal file
35
external/uefi/protos/file_info.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_file_info_h_
|
||||
#define _uefi_protos_file_info_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct file_info;
|
||||
|
||||
struct file_info
|
||||
{
|
||||
static constexpr uefi::guid guid{ 0x09576e92,0x6d3f,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
|
||||
|
||||
uint64_t size;
|
||||
uint64_t file_size;
|
||||
uint64_t physical_size;
|
||||
time create_time;
|
||||
time last_access_time;
|
||||
time modification_time;
|
||||
uint64_t attribute;
|
||||
wchar_t file_name[];
|
||||
|
||||
protected:
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_file_info_h_
|
||||
50
external/uefi/protos/graphics_output.h
vendored
Normal file
50
external/uefi/protos/graphics_output.h
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_graphics_output_h_
|
||||
#define _uefi_protos_graphics_output_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/graphics.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct graphics_output;
|
||||
|
||||
struct graphics_output
|
||||
{
|
||||
static constexpr uefi::guid guid{ 0x9042a9de,0x23dc,0x4a38,{0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a} };
|
||||
|
||||
inline uefi::status query_mode(uint32_t mode_number, uint64_t * size_of_info, uefi::graphics_output_mode_info ** info) {
|
||||
return _query_mode(this, mode_number, size_of_info, info);
|
||||
}
|
||||
|
||||
inline uefi::status set_mode(uint32_t mode_number) {
|
||||
return _set_mode(this, mode_number);
|
||||
}
|
||||
|
||||
inline uefi::status blt() {
|
||||
return _blt(this);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
using _query_mode_def = uefi::status (*)(uefi::protos::graphics_output *, uint32_t, uint64_t *, uefi::graphics_output_mode_info **);
|
||||
_query_mode_def _query_mode;
|
||||
|
||||
using _set_mode_def = uefi::status (*)(uefi::protos::graphics_output *, uint32_t);
|
||||
_set_mode_def _set_mode;
|
||||
|
||||
using _blt_def = uefi::status (*)(uefi::protos::graphics_output *);
|
||||
_blt_def _blt;
|
||||
|
||||
public:
|
||||
uefi::graphics_output_mode * mode;
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_graphics_output_h_
|
||||
48
external/uefi/protos/loaded_image.h
vendored
Normal file
48
external/uefi/protos/loaded_image.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_loaded_image_h_
|
||||
#define _uefi_protos_loaded_image_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/tables.h>
|
||||
#include <uefi/protos/device_path.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct loaded_image;
|
||||
|
||||
struct loaded_image
|
||||
{
|
||||
static constexpr uefi::guid guid{ 0x5b1b31a1,0x9562,0x11d2,{0x8e,0x3f,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
|
||||
|
||||
inline uefi::status unload(uefi::handle image_handle) {
|
||||
return _unload(image_handle);
|
||||
}
|
||||
|
||||
uint32_t revision;
|
||||
uefi::handle parent_handle;
|
||||
uefi::system_table * system_table;
|
||||
uefi::handle device_handle;
|
||||
uefi::protos::device_path * file_path;
|
||||
void * reserved;
|
||||
uint32_t load_options_size;
|
||||
void * load_options;
|
||||
void * image_base;
|
||||
uint64_t image_size;
|
||||
uefi::memory_type image_code_type;
|
||||
uefi::memory_type image_data_type;
|
||||
|
||||
protected:
|
||||
using _unload_def = uefi::status (*)(uefi::handle);
|
||||
_unload_def _unload;
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_loaded_image_h_
|
||||
36
external/uefi/protos/simple_file_system.h
vendored
Normal file
36
external/uefi/protos/simple_file_system.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_simple_file_system_h_
|
||||
#define _uefi_protos_simple_file_system_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/protos/file.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct simple_file_system;
|
||||
|
||||
struct simple_file_system
|
||||
{
|
||||
static constexpr uefi::guid guid{ 0x0964e5b22,0x6459,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
|
||||
|
||||
inline uefi::status open_volume(uefi::protos::file ** root) {
|
||||
return _open_volume(this, root);
|
||||
}
|
||||
|
||||
uint64_t revision;
|
||||
|
||||
protected:
|
||||
using _open_volume_def = uefi::status (*)(uefi::protos::simple_file_system *, uefi::protos::file **);
|
||||
_open_volume_def _open_volume;
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_simple_file_system_h_
|
||||
92
external/uefi/protos/simple_text_output.h
vendored
Normal file
92
external/uefi/protos/simple_text_output.h
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_protos_simple_text_output_h_
|
||||
#define _uefi_protos_simple_text_output_h_
|
||||
|
||||
// This file was auto generated by the j6-uefi-headers project. Please see
|
||||
// https://github.com/justinian/j6-uefi-headers for more information.
|
||||
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/graphics.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace protos {
|
||||
struct simple_text_output;
|
||||
|
||||
struct simple_text_output
|
||||
{
|
||||
static constexpr uefi::guid guid{ 0x387477c2,0x69c7,0x11d2,{0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b} };
|
||||
|
||||
inline uefi::status reset(bool extended_verification) {
|
||||
return _reset(this, extended_verification);
|
||||
}
|
||||
|
||||
inline uefi::status output_string(const wchar_t * string) {
|
||||
return _output_string(this, string);
|
||||
}
|
||||
|
||||
inline uefi::status test_string(const wchar_t * string) {
|
||||
return _test_string(this, string);
|
||||
}
|
||||
|
||||
inline uefi::status query_mode(uint64_t mode_number, uint64_t * columns, uint64_t * rows) {
|
||||
return _query_mode(this, mode_number, columns, rows);
|
||||
}
|
||||
|
||||
inline uefi::status set_mode(uint64_t mode_number) {
|
||||
return _set_mode(this, mode_number);
|
||||
}
|
||||
|
||||
inline uefi::status set_attribute(uefi::attribute attribute) {
|
||||
return _set_attribute(this, attribute);
|
||||
}
|
||||
|
||||
inline uefi::status clear_screen() {
|
||||
return _clear_screen(this);
|
||||
}
|
||||
|
||||
inline uefi::status set_cursor_position(uint64_t column, uint64_t row) {
|
||||
return _set_cursor_position(this, column, row);
|
||||
}
|
||||
|
||||
inline uefi::status enable_cursor(bool visible) {
|
||||
return _enable_cursor(this, visible);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
using _reset_def = uefi::status (*)(uefi::protos::simple_text_output *, bool);
|
||||
_reset_def _reset;
|
||||
|
||||
using _output_string_def = uefi::status (*)(uefi::protos::simple_text_output *, const wchar_t *);
|
||||
_output_string_def _output_string;
|
||||
|
||||
using _test_string_def = uefi::status (*)(uefi::protos::simple_text_output *, const wchar_t *);
|
||||
_test_string_def _test_string;
|
||||
|
||||
using _query_mode_def = uefi::status (*)(uefi::protos::simple_text_output *, uint64_t, uint64_t *, uint64_t *);
|
||||
_query_mode_def _query_mode;
|
||||
|
||||
using _set_mode_def = uefi::status (*)(uefi::protos::simple_text_output *, uint64_t);
|
||||
_set_mode_def _set_mode;
|
||||
|
||||
using _set_attribute_def = uefi::status (*)(uefi::protos::simple_text_output *, uefi::attribute);
|
||||
_set_attribute_def _set_attribute;
|
||||
|
||||
using _clear_screen_def = uefi::status (*)(uefi::protos::simple_text_output *);
|
||||
_clear_screen_def _clear_screen;
|
||||
|
||||
using _set_cursor_position_def = uefi::status (*)(uefi::protos::simple_text_output *, uint64_t, uint64_t);
|
||||
_set_cursor_position_def _set_cursor_position;
|
||||
|
||||
using _enable_cursor_def = uefi::status (*)(uefi::protos::simple_text_output *, bool);
|
||||
_enable_cursor_def _enable_cursor;
|
||||
|
||||
public:
|
||||
uefi::text_output_mode * mode;
|
||||
};
|
||||
|
||||
} // namespace protos
|
||||
} // namespace uefi
|
||||
|
||||
#endif // _uefi_protos_simple_text_output_h_
|
||||
52
external/uefi/runtime_services.h
vendored
Normal file
52
external/uefi/runtime_services.h
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_runtime_services_h_
|
||||
#define _uefi_runtime_services_h_
|
||||
|
||||
// This Source Code Form is part of the j6-uefi-headers project and is subject
|
||||
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
|
||||
// not distributed with this file, You can obtain one at
|
||||
// http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <uefi/tables.h>
|
||||
|
||||
namespace uefi {
|
||||
namespace rs_impl {
|
||||
using convert_pointer = uefi::status (*)(uint64_t, void **);
|
||||
using set_virtual_address_map = uefi::status (*)(size_t, size_t, uint32_t, memory_descriptor *);
|
||||
}
|
||||
|
||||
struct runtime_services {
|
||||
static constexpr uint64_t signature = 0x56524553544e5552ull;
|
||||
|
||||
table_header header;
|
||||
|
||||
// Time Services
|
||||
void *get_time;
|
||||
void *set_time;
|
||||
void *get_wakeup_time;
|
||||
void *set_wakeup_time;
|
||||
|
||||
// Virtual Memory Services
|
||||
rs_impl::set_virtual_address_map set_virtual_address_map;
|
||||
rs_impl::convert_pointer convert_pointer;
|
||||
|
||||
// Variable Services
|
||||
void *get_variable;
|
||||
void *get_next_variable_name;
|
||||
void *set_variable;
|
||||
|
||||
// Miscellaneous Services
|
||||
void *get_next_high_monotonic_count;
|
||||
void *reset_system;
|
||||
|
||||
// UEFI 2.0 Capsule Services
|
||||
void *update_capsule;
|
||||
void *query_capsule_capabilities;
|
||||
|
||||
// Miscellaneous UEFI 2.0 Service
|
||||
void *query_variable_info;
|
||||
};
|
||||
|
||||
} // namespace uefi
|
||||
#endif
|
||||
73
external/uefi/tables.h
vendored
Normal file
73
external/uefi/tables.h
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_tables_h_
|
||||
#define _uefi_tables_h_
|
||||
|
||||
// This Source Code Form is part of the j6-uefi-headers project and is subject
|
||||
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
|
||||
// not distributed with this file, You can obtain one at
|
||||
// http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace uefi {
|
||||
|
||||
struct runtime_services;
|
||||
struct boot_services;
|
||||
namespace protos {
|
||||
struct simple_text_input;
|
||||
struct simple_text_output;
|
||||
}
|
||||
|
||||
struct table_header
|
||||
{
|
||||
uint64_t signature;
|
||||
uint32_t revision;
|
||||
uint32_t header_size;
|
||||
uint32_t crc32;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
struct configuration_table
|
||||
{
|
||||
guid vendor_guid;
|
||||
void *vendor_table;
|
||||
};
|
||||
|
||||
struct system_table
|
||||
{
|
||||
table_header header;
|
||||
|
||||
char16_t *firmware_vendor;
|
||||
uint32_t firmware_revision;
|
||||
|
||||
handle console_in_handle;
|
||||
protos::simple_text_input *con_in;
|
||||
handle console_out_handle;
|
||||
protos::simple_text_output *con_out;
|
||||
handle standard_error_handle;
|
||||
protos::simple_text_output *std_err;
|
||||
|
||||
runtime_services *runtime_services;
|
||||
boot_services *boot_services;
|
||||
|
||||
unsigned int number_of_table_entries;
|
||||
configuration_table *configuration_table;
|
||||
};
|
||||
|
||||
constexpr uint32_t make_system_table_revision(int major, int minor) {
|
||||
return (major << 16) | minor;
|
||||
}
|
||||
|
||||
constexpr uint64_t system_table_signature = 0x5453595320494249ull;
|
||||
constexpr uint32_t system_table_revision = make_system_table_revision(2, 70);
|
||||
constexpr uint32_t specification_revision = system_table_revision;
|
||||
|
||||
namespace vendor_guids {
|
||||
constexpr guid acpi1{ 0xeb9d2d30,0x2d88,0x11d3,{0x9a,0x16,0x00,0x90,0x27,0x3f,0xc1,0x4d} };
|
||||
constexpr guid acpi2{ 0x8868e871,0xe4f1,0x11d3,{0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81} };
|
||||
} // namespace vendor_guids
|
||||
} // namespace uefi
|
||||
|
||||
#endif
|
||||
157
external/uefi/types.h
vendored
Normal file
157
external/uefi/types.h
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
#ifndef _uefi_types_h_
|
||||
#define _uefi_types_h_
|
||||
|
||||
// This Source Code Form is part of the j6-uefi-headers project and is subject
|
||||
// to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was
|
||||
// not distributed with this file, You can obtain one at
|
||||
// http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace uefi {
|
||||
|
||||
using handle = void *;
|
||||
|
||||
|
||||
//
|
||||
// Error and status code definitions
|
||||
//
|
||||
constexpr uint64_t error_bit = 0x8000000000000000ull;
|
||||
constexpr uint64_t make_error(uint64_t e) { return e|error_bit; }
|
||||
|
||||
enum class status : uint64_t
|
||||
{
|
||||
success = 0,
|
||||
|
||||
#define STATUS_WARNING(name, num) name = num,
|
||||
#define STATUS_ERROR(name, num) name = make_error(num),
|
||||
#include "uefi/errors.inc"
|
||||
#undef STATUS_WARNING
|
||||
#undef STATUS_ERROR
|
||||
};
|
||||
|
||||
inline bool is_error(status s) { return static_cast<uint64_t>(s) & error_bit; }
|
||||
inline bool is_warning(status s) { return !is_error(s) && s != status::success; }
|
||||
|
||||
|
||||
//
|
||||
// Time defitions
|
||||
//
|
||||
struct time
|
||||
{
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t _pad0;
|
||||
uint32_t nanosecond;
|
||||
int16_t time_zone;
|
||||
uint8_t daylight;
|
||||
uint8_t _pad1;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Memory and allocation defitions
|
||||
//
|
||||
enum class memory_type : uint32_t
|
||||
{
|
||||
reserved,
|
||||
loader_code,
|
||||
loader_data,
|
||||
boot_services_code,
|
||||
boot_services_data,
|
||||
runtime_services_code,
|
||||
runtime_services_data,
|
||||
conventional_memory,
|
||||
unusable_memory,
|
||||
acpi_reclaim_memory,
|
||||
acpi_memory_nvs,
|
||||
memory_mapped_io,
|
||||
memory_mapped_io_port_space,
|
||||
pal_code,
|
||||
persistent_memory,
|
||||
max_memory_type
|
||||
};
|
||||
|
||||
enum class allocate_type : uint32_t
|
||||
{
|
||||
any_pages,
|
||||
max_address,
|
||||
address
|
||||
};
|
||||
|
||||
struct memory_descriptor
|
||||
{
|
||||
memory_type type;
|
||||
uintptr_t physical_start;
|
||||
uintptr_t virtual_start;
|
||||
uint64_t number_of_pages;
|
||||
uint64_t attribute;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Event handling defitions
|
||||
//
|
||||
using event = void *;
|
||||
|
||||
enum class evt : uint32_t
|
||||
{
|
||||
notify_wait = 0x00000100,
|
||||
notify_signal = 0x00000200,
|
||||
|
||||
signal_exit_boot_services = 0x00000201,
|
||||
signal_virtual_address_change = 0x60000202,
|
||||
|
||||
runtime = 0x40000000,
|
||||
timer = 0x80000000
|
||||
};
|
||||
|
||||
enum class tpl : uint64_t
|
||||
{
|
||||
application = 4,
|
||||
callback = 8,
|
||||
notify = 16,
|
||||
high_level = 31
|
||||
};
|
||||
|
||||
using event_notify = void (*)(event, void*);
|
||||
|
||||
|
||||
//
|
||||
// File IO defitions
|
||||
//
|
||||
struct file_io_token
|
||||
{
|
||||
event event;
|
||||
status status;
|
||||
uint64_t buffer_size;
|
||||
void *buffer;
|
||||
};
|
||||
|
||||
enum class file_mode : uint64_t
|
||||
{
|
||||
read = 0x0000000000000001ull,
|
||||
write = 0x0000000000000002ull,
|
||||
|
||||
create = 0x8000000000000000ull
|
||||
};
|
||||
|
||||
enum class file_attr : uint64_t
|
||||
{
|
||||
none = 0,
|
||||
read_only = 0x0000000000000001ull,
|
||||
hidden = 0x0000000000000002ull,
|
||||
system = 0x0000000000000004ull,
|
||||
reserved = 0x0000000000000008ull,
|
||||
directory = 0x0000000000000010ull,
|
||||
archive = 0x0000000000000020ull
|
||||
};
|
||||
|
||||
} // namespace uefi
|
||||
|
||||
#endif
|
||||
6
make.bat
6
make.bat
@@ -1,6 +0,0 @@
|
||||
@echo off
|
||||
if exist C:\Windows\Sysnative\bash.exe (
|
||||
C:\Windows\Sysnative\bash.exe -c "make %*"
|
||||
) else (
|
||||
C:\Windows\System32\bash.exe -c "make %*"
|
||||
)
|
||||
35
modules.mk
35
modules.mk
@@ -1,35 +0,0 @@
|
||||
ifeq "$(MOD_NAME)" ""
|
||||
include "you must specify a MOD_NAME"
|
||||
endif
|
||||
|
||||
ifndef SOURCES
|
||||
SOURCES := $(wildcard src/modules/$(MOD_NAME)/*.c)
|
||||
SOURCES += $(wildcard src/modules/$(MOD_NAME)/*.cpp)
|
||||
SOURCES += $(wildcard src/modules/$(MOD_NAME)/*.s)
|
||||
endif
|
||||
|
||||
ifeq "$(SOURCES)" ""
|
||||
include "you must specify a SOURCES list"
|
||||
endif
|
||||
|
||||
MOD_SRC_D := src/modules/$(MOD_NAME)
|
||||
MOD_BUILD_D := $(BUILD_D)/d.$(MOD_NAME)
|
||||
MOD_LIBNAME := $(BUILD_D)/lib$(MOD_NAME).a
|
||||
MOD_TARGETS += $(MOD_LIBNAME)
|
||||
|
||||
OBJS_$(MOD_NAME) := $(patsubst %,build/d.%.o,$(patsubst src/modules/%,%,$(SOURCES)))
|
||||
|
||||
$(MOD_LIBNAME): $(OBJS_$(MOD_NAME))
|
||||
$(AR) cr $@ $(OBJS_$(MOD_NAME))
|
||||
|
||||
$(MOD_BUILD_D)/%.c.o: $(MOD_SRC_D)/%.c $(INIT_DEP)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(MOD_BUILD_D)/%.cpp.o: $(MOD_SRC_D)/%.cpp $(INIT_DEP)
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
$(MOD_BUILD_D)/%.s.o: $(MOD_SRC_D)/%.s $(BUILD_D)/versions.s $(INIT_DEP)
|
||||
$(AS) $(ASFLAGS) -i $(MOD_SRC_D)/ -o $@ $<
|
||||
|
||||
DEPS += $(patsubst %.o,%.d,$(OBJS_$(MOD_NAME)))
|
||||
|
||||
137
modules.yaml
Normal file
137
modules.yaml
Normal file
@@ -0,0 +1,137 @@
|
||||
name: jsix
|
||||
templates: scripts/templates
|
||||
modules:
|
||||
kernel:
|
||||
kind: exe
|
||||
output: jsix.elf
|
||||
target: host
|
||||
deps:
|
||||
- elf
|
||||
- initrd
|
||||
- kutil
|
||||
includes:
|
||||
- src/kernel
|
||||
source:
|
||||
- src/kernel/crti.s
|
||||
- src/kernel/apic.cpp
|
||||
- src/kernel/assert.cpp
|
||||
- src/kernel/boot.s
|
||||
- src/kernel/console.cpp
|
||||
- src/kernel/cpprt.cpp
|
||||
- src/kernel/cpu.cpp
|
||||
- src/kernel/debug.cpp
|
||||
- src/kernel/debug.s
|
||||
- src/kernel/device_manager.cpp
|
||||
- src/kernel/font.cpp
|
||||
- src/kernel/frame_allocator.cpp
|
||||
- src/kernel/fs/gpt.cpp
|
||||
- src/kernel/gdt.cpp
|
||||
- src/kernel/gdt.s
|
||||
- src/kernel/interrupts.cpp
|
||||
- src/kernel/interrupts.s
|
||||
- src/kernel/io.cpp
|
||||
- src/kernel/loader.s
|
||||
- src/kernel/log.cpp
|
||||
- src/kernel/main.cpp
|
||||
- src/kernel/memory_bootstrap.cpp
|
||||
- src/kernel/msr.cpp
|
||||
- src/kernel/objects/handle.cpp
|
||||
- src/kernel/objects/kobject.cpp
|
||||
- src/kernel/page_manager.cpp
|
||||
- src/kernel/pci.cpp
|
||||
- src/kernel/process.cpp
|
||||
- src/kernel/scheduler.cpp
|
||||
- src/kernel/screen.cpp
|
||||
- src/kernel/serial.cpp
|
||||
- src/kernel/syscall.cpp
|
||||
- src/kernel/syscall.s
|
||||
- src/kernel/syscalls/object.cpp
|
||||
- src/kernel/syscalls/process.cpp
|
||||
- src/kernel/task.s
|
||||
- src/kernel/crtn.s
|
||||
|
||||
boot:
|
||||
kind: exe
|
||||
target: boot
|
||||
output: boot.efi
|
||||
source:
|
||||
- src/boot/main.cpp
|
||||
- src/boot/console.cpp
|
||||
- src/boot/error.cpp
|
||||
- src/boot/fs.cpp
|
||||
- src/boot/hardware.cpp
|
||||
- src/boot/loader.cpp
|
||||
- src/boot/memory.cpp
|
||||
- src/boot/paging.cpp
|
||||
- src/boot/support.cpp
|
||||
|
||||
nulldrv:
|
||||
kind: exe
|
||||
target: user
|
||||
output: nulldrv
|
||||
source:
|
||||
- src/drivers/nulldrv/main.cpp
|
||||
- src/drivers/nulldrv/main.s
|
||||
|
||||
elf:
|
||||
kind: lib
|
||||
output: libelf.a
|
||||
deps:
|
||||
- kutil
|
||||
includes:
|
||||
- src/libraries/elf/include
|
||||
source:
|
||||
- src/libraries/elf/elf.cpp
|
||||
|
||||
initrd:
|
||||
kind: lib
|
||||
output: libinitrd.a
|
||||
deps:
|
||||
- kutil
|
||||
includes:
|
||||
- src/libraries/initrd/include
|
||||
source:
|
||||
- src/libraries/initrd/initrd.cpp
|
||||
|
||||
kutil:
|
||||
kind: lib
|
||||
output: libkutil.a
|
||||
includes:
|
||||
- src/libraries/kutil/include
|
||||
source:
|
||||
- src/libraries/kutil/assert.cpp
|
||||
- src/libraries/kutil/bip_buffer.cpp
|
||||
- src/libraries/kutil/heap_allocator.cpp
|
||||
- src/libraries/kutil/logger.cpp
|
||||
- src/libraries/kutil/memory.cpp
|
||||
- src/libraries/kutil/printf.c
|
||||
- src/libraries/kutil/vm_space.cpp
|
||||
|
||||
makerd:
|
||||
kind: exe
|
||||
target: native
|
||||
output: makerd
|
||||
deps:
|
||||
- initrd
|
||||
source:
|
||||
- src/tools/makerd/entry.cpp
|
||||
- src/tools/makerd/main.cpp
|
||||
|
||||
tests:
|
||||
kind: exe
|
||||
target: native
|
||||
output: tests
|
||||
deps:
|
||||
- kutil
|
||||
source:
|
||||
- src/tests/address_manager.cpp
|
||||
- src/tests/constexpr_hash.cpp
|
||||
- src/tests/linked_list.cpp
|
||||
- src/tests/logger.cpp
|
||||
- src/tests/heap_allocator.cpp
|
||||
- src/tests/main.cpp
|
||||
overlays:
|
||||
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-llvm8-20190706.tar.bz2
|
||||
path: sysroot
|
||||
- url: https://f000.backblazeb2.com/file/jsix-os/sysroot-j6libc-8cb8ce7.tar.bz2
|
||||
path: sysroot
|
||||
89
other_software.md
Normal file
89
other_software.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Included works
|
||||
|
||||
jsix includes and/or is derived from a number of other works, listed here.
|
||||
|
||||
## Catch2
|
||||
|
||||
jsix uses [Catch2][] for testing. Catch2 is released under the terms of the
|
||||
Boost Software license:
|
||||
|
||||
[Catch2]: https://github.com/catchorg/Catch2
|
||||
|
||||
> Boost Software License - Version 1.0 - August 17th, 2003
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person or organization
|
||||
> obtaining a copy of the software and accompanying documentation covered by
|
||||
> this license (the "Software") to use, reproduce, display, distribute,
|
||||
> execute, and transmit the Software, and to prepare derivative works of the
|
||||
> Software, and to permit third-parties to whom the Software is furnished to
|
||||
> do so, all subject to the following:
|
||||
>
|
||||
> The copyright notices in the Software and this entire statement, including
|
||||
> the above license grant, this restriction and the following disclaimer,
|
||||
> must be included in all copies of the Software, in whole or in part, and
|
||||
> all derivative works of the Software, unless such copies or derivative
|
||||
> works are solely in the form of machine-executable object code generated by
|
||||
> a source language processor.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
> SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
> FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
> ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
> DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## cpptoml
|
||||
|
||||
jsix uses the [cpptoml][] library for parsing TOML configuration files. cpptoml
|
||||
is released under the terms of the MIT license:
|
||||
|
||||
[cpptoml]: https://github.com/skystrife/cpptoml
|
||||
|
||||
> Copyright (c) 2014 Chase Geigle
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
> this software and associated documentation files (the "Software"), to deal in
|
||||
> the Software without restriction, including without limitation the rights to
|
||||
> use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
> the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
> subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in all
|
||||
> copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
> FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
> COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
> IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
> CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## printf
|
||||
|
||||
jsix uses Marco Paland's tiny [printf][] library for its `*printf()` functions,
|
||||
which is also released under the terms of the MIT license:
|
||||
|
||||
[printf]: https://github.com/mpaland/printf
|
||||
|
||||
> The MIT License (MIT)
|
||||
>
|
||||
> Copyright (c) 2014 Marco Paland
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in all
|
||||
> copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
> SOFTWARE.
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# parse_version.py - Create a NASM version definition file given
|
||||
# version inputs. Usage:
|
||||
#
|
||||
# parse_version.py <git-describe version> <git short sha>
|
||||
|
||||
|
||||
def split_version(version_string):
|
||||
major, minor, patch_dirty = version_string.split(".")
|
||||
patch_dirty = patch_dirty.split("-")
|
||||
|
||||
return major, minor, patch_dirty[0], len(patch_dirty) > 1
|
||||
|
||||
|
||||
def make_nasm(major, minor, patch, dirty, sha):
|
||||
if dirty:
|
||||
dirty = "1"
|
||||
else:
|
||||
dirty = "0"
|
||||
|
||||
lines = [
|
||||
"%define VERSION_MAJOR {}".format(major),
|
||||
"%define VERSION_MINOR {}".format(minor),
|
||||
"%define VERSION_PATCH {}".format(patch),
|
||||
"%define VERSION_GITSHA 0x{}{}".format(dirty, sha),
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage: {} <desc version> <git sha>".format(sys.argv[0]), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
print(make_nasm(*split_version(sys.argv[1]), sys.argv[2]))
|
||||
3
qemu.bat
3
qemu.bat
@@ -1,3 +0,0 @@
|
||||
call make.bat
|
||||
del popcorn.log
|
||||
qemu-system-x86_64.exe -bios .\assets\ovmf\x64\OVMF.fd -hda .\build\fs.img -m 512 -vga cirrus -d guest_errors,int -D popcorn.log -no-reboot
|
||||
71
qemu.sh
Executable file
71
qemu.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
build="$(dirname $0)/build"
|
||||
assets="$(dirname $0)/assets"
|
||||
debug=""
|
||||
debugtarget="${build}/jsix.elf"
|
||||
flash_name="ovmf_vars"
|
||||
gfx="-nographic"
|
||||
kvm=""
|
||||
cpu="Broadwell,+pdpe1gb"
|
||||
|
||||
for arg in $@; do
|
||||
case "${arg}" in
|
||||
--debugboot)
|
||||
debug="-s -S"
|
||||
debugtarget="${build}/boot/boot.efi"
|
||||
flash_name="ovmf_vars_d"
|
||||
;;
|
||||
--debug)
|
||||
debug="-s -S"
|
||||
flash_name="ovmf_vars_d"
|
||||
;;
|
||||
--gfx)
|
||||
gfx=""
|
||||
;;
|
||||
--kvm)
|
||||
kvm="-enable-kvm"
|
||||
cpu="host"
|
||||
;;
|
||||
*)
|
||||
build="${arg}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -c /dev/kvm ]]; then
|
||||
kvm=""
|
||||
fi
|
||||
|
||||
if ! ninja -C "${build}"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n $TMUX ]]; then
|
||||
if [[ -n $debug ]]; then
|
||||
tmux split-window -h "gdb ${debugtarget}" &
|
||||
else
|
||||
tmux split-window -l 10 "sleep 1; telnet localhost 45454" &
|
||||
fi
|
||||
elif [[ $DESKTOP_SESSION = "i3" ]]; then
|
||||
if [[ -n $debug ]]; then
|
||||
i3-msg exec i3-sensible-terminal -- -e "gdb ${PWD}/${build}/jsix.elf" &
|
||||
else
|
||||
i3-msg exec i3-sensible-terminal -- -e 'telnet localhost 45454' &
|
||||
fi
|
||||
fi
|
||||
|
||||
exec qemu-system-x86_64 \
|
||||
-drive "if=pflash,format=raw,readonly,file=${assets}/ovmf/x64/ovmf_code.fd" \
|
||||
-drive "if=pflash,format=raw,file=${build}/${flash_name}.fd" \
|
||||
-drive "format=raw,file=${build}/jsix.img" \
|
||||
-device "isa-debug-exit,iobase=0xf4,iosize=0x04" \
|
||||
-monitor telnet:localhost:45454,server,nowait \
|
||||
-smp 4 \
|
||||
-m 512 \
|
||||
-d mmu,int,guest_errors \
|
||||
-D jsix.log \
|
||||
-cpu "${cpu}" \
|
||||
-M q35 \
|
||||
-no-reboot \
|
||||
$gfx $kvm $debug
|
||||
131
scripts/build_sysroot.sh
Executable file
131
scripts/build_sysroot.sh
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TARGET="x86_64-elf"
|
||||
LLVM_BRANCH="release_80"
|
||||
|
||||
TOOLS="clang lld" # lld libunwind libcxxabi libcxx"
|
||||
PROJECTS="compiler-rt libcxxabi libcxx libunwind"
|
||||
#RUNTIMES="compiler-rt libcxxabi libcxx libunwind"
|
||||
|
||||
set -e
|
||||
|
||||
README=$(realpath "$(dirname $0)/readme_for_prebuilt_sysroots.md")
|
||||
SYSROOT=$(realpath "$(dirname $0)/../sysroot")
|
||||
WORK=$(realpath "$(dirname $0)/sysroot")
|
||||
mkdir -p "${SYSROOT}"
|
||||
mkdir -p "${WORK}"
|
||||
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
|
||||
if [[ ! -d "${WORK}/llvm" ]]; then
|
||||
echo "Downloading LLVM..."
|
||||
git clone -q \
|
||||
--branch "${LLVM_BRANCH}" \
|
||||
--depth 1 \
|
||||
"https://git.llvm.org/git/llvm.git" "${WORK}/llvm"
|
||||
fi
|
||||
|
||||
for tool in ${TOOLS}; do
|
||||
if [[ ! -d "${WORK}/llvm/tools/${tool}" ]]; then
|
||||
echo "Downloading ${tool}..."
|
||||
git clone -q \
|
||||
--branch "${LLVM_BRANCH}" \
|
||||
--depth 1 \
|
||||
"https://git.llvm.org/git/${tool}.git" "${WORK}/llvm/tools/${tool}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -d "${WORK}/llvm/tools/clang/tools/extra" ]]; then
|
||||
echo "Downloading clang-tools-extra..."
|
||||
git clone -q \
|
||||
--branch "${LLVM_BRANCH}" \
|
||||
--depth 1 \
|
||||
"https://git.llvm.org/git/clang-tools-extra.git" "${WORK}/llvm/tools/clang/tools/extra"
|
||||
fi
|
||||
|
||||
for proj in ${PROJECTS}; do
|
||||
if [[ ! -d "${WORK}/llvm/projects/${proj}" ]]; then
|
||||
echo "Downloading ${proj}..."
|
||||
git clone -q \
|
||||
--branch "${LLVM_BRANCH}" \
|
||||
--depth 1 \
|
||||
"https://git.llvm.org/git/${proj}.git" "${WORK}/llvm/projects/${proj}"
|
||||
fi
|
||||
done
|
||||
|
||||
for proj in ${RUNTIMES}; do
|
||||
if [[ ! -d "${WORK}/llvm/runtimes/${proj}" ]]; then
|
||||
echo "Downloading ${proj}..."
|
||||
git clone -q \
|
||||
--branch "${LLVM_BRANCH}" \
|
||||
--depth 1 \
|
||||
"https://git.llvm.org/git/${proj}.git" "${WORK}/llvm/runtime/${proj}"
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "${WORK}/build/llvm"
|
||||
pushd "${WORK}/build/llvm"
|
||||
|
||||
echo "Configuring LLVM..."
|
||||
|
||||
cmake -G Ninja \
|
||||
-DCLANG_DEFAULT_RTLIB=compiler-rt \
|
||||
-DCLANG_DEFAULT_STD_C=c11 \
|
||||
-DCLANG_DEFAULT_STD_CXX=cxx14 \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_COMPILER="clang" \
|
||||
-DCMAKE_CXX_COMPILER="clang++" \
|
||||
-DCMAKE_CXX_FLAGS="-Wno-unused-parameter -D_LIBCPP_HAS_NO_ALIGNED_ALLOCATION -D_LIBUNWIND_IS_BAREMETAL=1 -U_LIBUNWIND_SUPPORT_DWARF_UNWIND" \
|
||||
-DCMAKE_INSTALL_PREFIX="${SYSROOT}" \
|
||||
-DCMAKE_MAKE_PROGRAM=`which ninja` \
|
||||
-DDEFAULT_SYSROOT="${SYSROOT}" \
|
||||
-DLIBCXX_CXX_ABI=libcxxabi \
|
||||
-DLIBCXX_CXX_ABI_INCLUDE_PATHS="${WORK}/llvm/projects/libcxxabi/include" \
|
||||
-DLIBCXX_CXX_ABI_LIBRARY_PATH=lib \
|
||||
-DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF \
|
||||
-DLIBCXX_ENABLE_NEW_DELETE_DEFINITIONS=ON \
|
||||
-DLIBCXX_ENABLE_SHARED=OFF \
|
||||
-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON \
|
||||
-DLIBCXX_ENABLE_THREADS=OFF \
|
||||
-DLIBCXX_INCLUDE_BENCHMARKS=OFF \
|
||||
-DLIBCXX_USE_COMPILER_RT=ON \
|
||||
-DLIBCXXABI_ENABLE_NEW_DELETE_DEFINITIONS=OFF \
|
||||
-DLIBCXXABI_ENABLE_SHARED=OFF \
|
||||
-DLIBCXXABI_ENABLE_STATIC_UNWINDER=ON \
|
||||
-DLIBCXXABI_ENABLE_THREADS=OFF \
|
||||
-DLIBCXXABI_LIBCXX_PATH="${WORK}/llvm/projects/libcxx" \
|
||||
-DLIBCXXABI_USE_COMPILER_RT=ON \
|
||||
-DLIBCXXABI_USE_LLVM_UNWINDER=ON \
|
||||
-DLIBUNWIND_ENABLE_SHARED=OFF \
|
||||
-DLIBUNWIND_ENABLE_THREADS=OFF \
|
||||
-DLIBUNWIND_USE_COMPILER_RT=ON \
|
||||
-DLLVM_CONFIG_PATH="${SYSROOT}/bin/llvm-config" \
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE="x86_64-unknown-elf" \
|
||||
-DLLVM_ENABLE_LIBCXX=ON \
|
||||
-DLLVM_ENABLE_LLD=ON \
|
||||
-DLLVM_ENABLE_PIC=OFF \
|
||||
-DLLVM_ENABLE_THREADS=OFF \
|
||||
-DLLVM_INSTALL_BINUTILS_SYMLINKS=ON \
|
||||
-DLLVM_TARGETS_TO_BUILD="X86" \
|
||||
${WORK}/llvm > cmake_configure.log
|
||||
|
||||
# -DCMAKE_ASM_COMPILER=nasm \
|
||||
# -DCMAKE_LINKER="${SYSROOT}/bin/ld.lld" \
|
||||
# -DCOMPILER_RT_ENABLE_LLD=ON \
|
||||
# -DLIBCXX_ENABLE_LLD=ON \
|
||||
# -DLIBCXX_ENABLE_STATIC_UNWINDER=ON \
|
||||
# -DLIBCXXABI_ENABLE_LLD=ON \
|
||||
# -DLIBUNWIND_ENABLE_LLD=ON \
|
||||
# -DLLVM_ENABLE_PROJECTS="libcxx;libcxxabi;libunwind;compiler-rt" \
|
||||
# -DCOMPILER_RT_BAREMETAL_BUILD=ON \
|
||||
# -DLIBCXXABI_BAREMETAL=ON \
|
||||
|
||||
echo "Building LLVM..."
|
||||
ninja && ninja install
|
||||
ninja cxx cxxabi compiler-rt
|
||||
ninja install-compiler-rt install-cxx install-cxxabi
|
||||
popd
|
||||
|
||||
|
||||
cp "${README}" "${SYSROOT}/README.md"
|
||||
10
scripts/parse_syms.py
Executable file
10
scripts/parse_syms.py
Executable 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)
|
||||
14
scripts/readme_for_prebuilt_sysroots.md
Normal file
14
scripts/readme_for_prebuilt_sysroots.md
Normal 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
|
||||
207
scripts/templates/build.ninja.j2
Normal file
207
scripts/templates/build.ninja.j2
Normal file
@@ -0,0 +1,207 @@
|
||||
ninja_required_version = 1.3
|
||||
builddir = {{ buildroot }}
|
||||
srcroot = {{ srcroot }}
|
||||
modulefile = {{ modulefile }}
|
||||
|
||||
{%- for var, value in vars %}
|
||||
{{ var }} = {{ value }}
|
||||
{%- endfor %}
|
||||
|
||||
warnflags = $
|
||||
-Wformat=2 $
|
||||
-Winit-self $
|
||||
-Wfloat-equal $
|
||||
-Winline $
|
||||
-Wmissing-format-attribute $
|
||||
-Wmissing-include-dirs $
|
||||
-Wswitch $
|
||||
-Wundef $
|
||||
-Wdisabled-optimization $
|
||||
-Wpointer-arith $
|
||||
-Wno-attributes $
|
||||
-Wno-sign-compare $
|
||||
-Wno-multichar $
|
||||
-Wno-div-by-zero $
|
||||
-Wno-endif-labels $
|
||||
-Wno-pragmas $
|
||||
-Wno-format-extra-args $
|
||||
-Wno-unused-result $
|
||||
-Wno-deprecated-declarations $
|
||||
-Wno-unused-function $
|
||||
-Wno-address-of-packed-member $
|
||||
-Werror
|
||||
|
||||
ccflags = $
|
||||
-I${srcroot}/src/include $
|
||||
-I${srcroot}/src/include/x86_64 $
|
||||
-fcolor-diagnostics $
|
||||
-DVERSION_MAJOR={{ version_major }} $
|
||||
-DVERSION_MINOR={{ version_minor }} $
|
||||
-DVERSION_PATCH={{ version_patch }} $
|
||||
-DVERSION_GITSHA=0x0{{ version_sha }} $
|
||||
-DGIT_VERSION=\"{{ version }}\" $
|
||||
-DGIT_VERSION_WIDE=L\"{{ version }}\" $
|
||||
$warnflags
|
||||
|
||||
asflags = $
|
||||
-DVERSION_MAJOR={{ version_major }} $
|
||||
-DVERSION_MINOR={{ version_minor }} $
|
||||
-DVERSION_PATCH={{ version_patch }} $
|
||||
-DVERSION_GITSHA=0x{{ version_sha }}
|
||||
|
||||
cflags = -std=c11
|
||||
cxxflags = -std=c++14
|
||||
libs =
|
||||
|
||||
rule c
|
||||
deps = gcc
|
||||
depfile = $out.d
|
||||
description = Compiling $name
|
||||
command = $cc -MMD -MF $out.d $ccflags $cflags -o $out -c $in
|
||||
|
||||
rule dump_c_defs
|
||||
description = Dumping C defines for $target
|
||||
command = echo "" | $cc $ccflags $cflags -dM -E - > $out
|
||||
|
||||
rule dump_c_run
|
||||
description = Dumping C arguments for $target
|
||||
command = $
|
||||
echo "#!/bin/bash" > $out; $
|
||||
echo '$cc $ccflags $cflags $$*' > $out; $
|
||||
chmod a+x $out
|
||||
|
||||
rule cpp
|
||||
deps = gcc
|
||||
depfile = $out.d
|
||||
description = Compiling $name
|
||||
command = $cxx -MMD -MF $out.d $cxxflags $ccflags -o $out -c $in
|
||||
|
||||
rule dump_cpp_defs
|
||||
description = Dumping C++ defines for $target
|
||||
command = echo "" | $cxx -x c++ $cxxflags $ccflags -dM -E - > $out
|
||||
|
||||
rule dump_cpp_run
|
||||
description = Dumping C++ arguments for $target
|
||||
command = $
|
||||
echo "#!/bin/bash" > $out; $
|
||||
echo '$cc $cxxflags $ccflags $$*' > $out; $
|
||||
chmod a+x $out
|
||||
|
||||
rule s
|
||||
deps = gcc
|
||||
depfile = $out.d
|
||||
description = Assembling $name
|
||||
command = $nasm -o $out -felf64 -MD $out.d $asflags $in
|
||||
|
||||
rule exe
|
||||
description = Linking $name
|
||||
command = $ld $ldflags -o $out $in $libs
|
||||
|
||||
rule lib
|
||||
description = Archiving $name
|
||||
command = $ar qcs $out $in
|
||||
|
||||
rule regen
|
||||
generator = true
|
||||
description = Regenrating build files
|
||||
command = $
|
||||
{{ generator }} $
|
||||
--file $modulefile $
|
||||
--dir $builddir $
|
||||
generate
|
||||
|
||||
rule cp
|
||||
description = Copying $name
|
||||
command = cp $in $out
|
||||
|
||||
rule dump
|
||||
description = Dumping decompiled $name
|
||||
command = objdump -DSC -M intel $in > $out
|
||||
|
||||
rule makerd
|
||||
description = Making init ramdisk
|
||||
command = $builddir/native/makerd $in $out
|
||||
|
||||
rule makeefi
|
||||
description = Converting $name
|
||||
command = objcopy $
|
||||
-j .text $
|
||||
-j .sdata $
|
||||
-j .data $
|
||||
-j .dynamic $
|
||||
-j .dynsym $
|
||||
-j .rel $
|
||||
-j .rela $
|
||||
-j .reloc $
|
||||
--target=efi-app-x86_64 $
|
||||
$in $out
|
||||
|
||||
rule makefat
|
||||
description = Creating $name
|
||||
command = $
|
||||
cp $srcroot/assets/diskbase.img $out; $
|
||||
mcopy -s -D o -i $out@@1M $builddir/fatroot/* ::/
|
||||
|
||||
rule strip
|
||||
description = Stripping $name
|
||||
command = $
|
||||
cp $in $out; $
|
||||
objcopy --only-keep-debug $out $out.debug; $
|
||||
strip -g $out; $
|
||||
objcopy --add-gnu-debuglink=$out.debug $out
|
||||
|
||||
{% for target in targets %}
|
||||
subninja {{ target }}/target.ninja
|
||||
{% endfor %}
|
||||
|
||||
build $
|
||||
{%- for buildfile in buildfiles %}
|
||||
{{ buildfile }} $
|
||||
{%- endfor %}
|
||||
: regen | $
|
||||
{%- for template in templates %}
|
||||
{{ template }} $
|
||||
{%- endfor %}
|
||||
$modulefile $
|
||||
{{ generator }}
|
||||
|
||||
build $builddir/ovmf_vars.fd : cp $srcroot/assets/ovmf/x64/ovmf_vars.fd
|
||||
name = ovmf_vars.fd
|
||||
|
||||
build $builddir/ovmf_vars_d.fd : cp $srcroot/assets/ovmf/x64/ovmf_vars_d.fd
|
||||
name = ovmf_vars_d.fd
|
||||
|
||||
build $builddir/jsix.elf | $builddir/jsix.elf.debug : strip $builddir/host/jsix.elf
|
||||
name = kernel
|
||||
|
||||
build $builddir/jsix.dump : dump $builddir/host/jsix.elf
|
||||
name = kernel
|
||||
|
||||
build $builddir/jsix.elf-gdb.py : cp ${srcroot}/assets/debugging/jsix.elf-gdb.py
|
||||
name = kernel debug python scripts
|
||||
|
||||
build $builddir/fatroot/jsix.elf : cp $builddir/jsix.elf
|
||||
name = kernel to FAT image
|
||||
|
||||
build $builddir/fatroot/efi/boot/bootx64.efi : cp $builddir/boot/boot.efi
|
||||
name = bootloader to FAT image
|
||||
|
||||
build $builddir/fatroot/initrd.img : makerd ${srcroot}/assets/initrd.toml | $
|
||||
${builddir}/native/makerd $
|
||||
${builddir}/user/nulldrv
|
||||
|
||||
build $builddir/jsix.img : makefat | $
|
||||
$builddir/fatroot/initrd.img $
|
||||
$builddir/fatroot/jsix.elf $
|
||||
$builddir/fatroot/efi/boot/bootx64.efi
|
||||
name = jsix.img
|
||||
|
||||
default $
|
||||
$builddir/ovmf_vars.fd $
|
||||
$builddir/ovmf_vars_d.fd $
|
||||
$builddir/jsix.dump $
|
||||
$builddir/jsix.elf-gdb.py $
|
||||
$builddir/jsix.img
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
14
scripts/templates/exe.boot.j2
Normal file
14
scripts/templates/exe.boot.j2
Normal file
@@ -0,0 +1,14 @@
|
||||
{% extends "exe.default.j2" %}
|
||||
{% block variables %}
|
||||
{{ super() }}
|
||||
|
||||
ccflags = $ccflags $
|
||||
-g3 $
|
||||
-DKERNEL_FILENAME=L\"jsix.elf\" $
|
||||
-I${srcroot}/external/include $
|
||||
-I${srcroot}/external/include/X64
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
8
scripts/templates/exe.default.j2
Normal file
8
scripts/templates/exe.default.j2
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends "module.base.j2" %}
|
||||
{% block variables %}
|
||||
{{ super() }}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
12
scripts/templates/exe.kernel.j2
Normal file
12
scripts/templates/exe.kernel.j2
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends "exe.default.j2" %}
|
||||
{% block variables %}
|
||||
{{ super() }}
|
||||
|
||||
asflags = $asflags -I${srcroot}/src/kernel/
|
||||
libs = $libs
|
||||
ldflags = $ldflags -T ${srcroot}/src/arch/x86_64/kernel.ld
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
10
scripts/templates/exe.makerd.j2
Normal file
10
scripts/templates/exe.makerd.j2
Normal 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
|
||||
|
||||
10
scripts/templates/exe.tests.j2
Normal file
10
scripts/templates/exe.tests.j2
Normal file
@@ -0,0 +1,10 @@
|
||||
{% extends "exe.default.j2" %}
|
||||
{% block variables %}
|
||||
{{ super() }}
|
||||
|
||||
ccflags = $ccflags -ggdb -I${srcroot}/external/catch
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
4
scripts/templates/lib.default.j2
Normal file
4
scripts/templates/lib.default.j2
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends "module.base.j2" %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
45
scripts/templates/module.base.j2
Normal file
45
scripts/templates/module.base.j2
Normal file
@@ -0,0 +1,45 @@
|
||||
moddir = ${builddir}/{{ name }}.dir
|
||||
|
||||
{% block variables %}
|
||||
ccflags = $ccflags $
|
||||
{%- for dep in depmods %}
|
||||
{%- for inc in dep.includes %}
|
||||
-I${srcroot}/{{ inc }} $
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
||||
{%- for inc in module.includes %}
|
||||
-I${srcroot}/{{ inc }} $
|
||||
{%- endfor %}
|
||||
{%- for define in module.defines %}
|
||||
-D{{ define }} $
|
||||
{%- endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% for source in module.source %}
|
||||
build ${moddir}/{{ source.output }} : {{ source.action }} ${srcroot}/{{ source.input }} || {{ buildfile }}
|
||||
name = {{ source.name }}
|
||||
{% endfor %}
|
||||
|
||||
build ${builddir}/{{ module.output }} : {{ module.kind }} $
|
||||
{%- for source in module.source %}
|
||||
${moddir}/{{ source.output }} $
|
||||
{%- endfor -%}
|
||||
{%- for dep in deplibs %}
|
||||
${builddir}/{{ dep.output }} $
|
||||
{%- endfor %}
|
||||
| $
|
||||
{%- for dep in depexes %}
|
||||
${builddir}/{{ dep.output }} $
|
||||
{%- endfor %}
|
||||
{{ buildfile }}
|
||||
name = {{ name }}
|
||||
|
||||
{% if module.default %}
|
||||
default ${builddir}/{{ module.output }}
|
||||
{% endif %}
|
||||
|
||||
{% block extra %}
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
38
scripts/templates/target.boot.j2
Normal file
38
scripts/templates/target.boot.j2
Normal file
@@ -0,0 +1,38 @@
|
||||
{% extends "target.default.j2" %}
|
||||
|
||||
{% block binaries %}
|
||||
cc = clang
|
||||
cxx = clang++
|
||||
ld = clang++
|
||||
ar = ar
|
||||
nasm = nasm
|
||||
objcopy = objcopy
|
||||
{% endblock %}
|
||||
|
||||
{% block variables %}
|
||||
|
||||
ccflags = $ccflags $
|
||||
-I $srcroot/external $
|
||||
--target=x86_64-unknown-windows $
|
||||
-ffreestanding $
|
||||
-mno-red-zone $
|
||||
-fshort-wchar $
|
||||
-fno-omit-frame-pointer $
|
||||
-ggdb
|
||||
|
||||
cxxflags = $cxxflags $
|
||||
-fno-rtti $
|
||||
-fno-exceptions
|
||||
|
||||
ldflags = $ldflags $
|
||||
--target=x86_64-unknown-windows $
|
||||
-nostdlib $
|
||||
-Wl,-entry:efi_main $
|
||||
-Wl,-subsystem:efi_application $
|
||||
-fuse-ld=lld-link $
|
||||
-g
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
26
scripts/templates/target.default.j2
Normal file
26
scripts/templates/target.default.j2
Normal file
@@ -0,0 +1,26 @@
|
||||
builddir = $builddir/{{ target }}
|
||||
target = {{ target }}
|
||||
|
||||
{% block variables %}
|
||||
{% endblock %}
|
||||
|
||||
{% block binaries %}
|
||||
cc = clang
|
||||
cxx = clang++
|
||||
ld = ld
|
||||
ar = ar
|
||||
nasm = nasm
|
||||
objcopy = objcopy
|
||||
{% endblock %}
|
||||
|
||||
{% for module in modules %}
|
||||
subninja {{ module }}.ninja
|
||||
{% endfor %}
|
||||
|
||||
build ${builddir}/c.defs : dump_c_defs | {{ buildfile }}
|
||||
build ${builddir}/cpp.defs : dump_cpp_defs | {{ buildfile }}
|
||||
build ${builddir}/c.run : dump_c_run | {{ buildfile }}
|
||||
build ${builddir}/cpp.run : dump_cpp_run | {{ buildfile }}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
43
scripts/templates/target.host.j2
Normal file
43
scripts/templates/target.host.j2
Normal file
@@ -0,0 +1,43 @@
|
||||
{% extends "target.default.j2" %}
|
||||
|
||||
{% block binaries %}
|
||||
cc = ${srcroot}/sysroot/bin/clang
|
||||
cxx = ${srcroot}/sysroot/bin/clang++
|
||||
ld = ${srcroot}/sysroot/bin/ld.lld
|
||||
ar = ${srcroot}/sysroot/bin/ar
|
||||
nasm = nasm
|
||||
objcopy = ${srcroot}/sysroot/bin/objcopy
|
||||
{% endblock %}
|
||||
|
||||
{% block variables %}
|
||||
|
||||
ccflags = $ccflags $
|
||||
-nostdlib $
|
||||
-ffreestanding $
|
||||
-nodefaultlibs $
|
||||
-fno-builtin $
|
||||
-mno-sse $
|
||||
-fno-omit-frame-pointer $
|
||||
-mno-red-zone $
|
||||
-g $
|
||||
-mcmodel=large $
|
||||
-D__ELF__ $
|
||||
-D__JSIX__ $
|
||||
-isystem${srcroot}/sysroot/include $
|
||||
--sysroot="${srcroot}/sysroot"
|
||||
|
||||
cxxflags = $cxxflags $
|
||||
-fno-exceptions $
|
||||
-fno-rtti $
|
||||
-isystem${srcroot}/sysroot/include/c++/v1
|
||||
|
||||
ldflags = $ldflags $
|
||||
-g $
|
||||
-nostdlib $
|
||||
-Bsymbolic $
|
||||
-Bstatic
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
15
scripts/templates/target.native.j2
Normal file
15
scripts/templates/target.native.j2
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends "target.default.j2" %}
|
||||
|
||||
{% block binaries %}
|
||||
{{ super() }}
|
||||
ld = clang++
|
||||
{% endblock %}
|
||||
|
||||
{% block variables %}
|
||||
|
||||
ccflags = $ccflags -g -ggdb
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
47
scripts/templates/target.user.j2
Normal file
47
scripts/templates/target.user.j2
Normal file
@@ -0,0 +1,47 @@
|
||||
{% extends "target.default.j2" %}
|
||||
|
||||
{% block binaries %}
|
||||
cc = ${srcroot}/sysroot/bin/clang
|
||||
cxx = ${srcroot}/sysroot/bin/clang++
|
||||
ld = ${srcroot}/sysroot/bin/ld.lld
|
||||
ar = ${srcroot}/sysroot/bin/ar
|
||||
nasm = nasm
|
||||
objcopy = ${srcroot}/sysroot/bin/objcopy
|
||||
{% endblock %}
|
||||
|
||||
{% block variables %}
|
||||
|
||||
ccflags = $ccflags $
|
||||
-nostdlib $
|
||||
-nodefaultlibs $
|
||||
-fno-builtin $
|
||||
-mno-sse $
|
||||
-fno-omit-frame-pointer $
|
||||
-mno-red-zone $
|
||||
-g $
|
||||
-mcmodel=large $
|
||||
-D__ELF__ $
|
||||
-D__JSIX__ $
|
||||
-isystem${srcroot}/sysroot/include $
|
||||
--sysroot="${srcroot}/sysroot"
|
||||
|
||||
cxxflags = $cxxflags $
|
||||
-fno-exceptions $
|
||||
-fno-rtti $
|
||||
-isystem${srcroot}/sysroot/include/c++/v1
|
||||
|
||||
ldflags = $ldflags $
|
||||
-g $
|
||||
-nostdlib $
|
||||
-Bsymbolic $
|
||||
-Bstatic $
|
||||
--sysroot="${srcroot}/sysroot" $
|
||||
-L "${srcroot}/sysroot/lib" $
|
||||
|
||||
libs = $libs $
|
||||
-lc
|
||||
|
||||
{% endblock %}
|
||||
|
||||
# vim: ft=ninja et ts=4 sts=4 sw=4
|
||||
|
||||
12
scripts/vmem_translate.py
Executable file
12
scripts/vmem_translate.py
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
def translate(i4 = 0, i3 = 0, i2 = 0, i1 = 0, offset = 0):
|
||||
addr = (i4 << 39) + (i3 << 30) + (i2 << 21) + (i1 << 12) + offset
|
||||
if addr & (1 << 47):
|
||||
addr |= 0xffff000000000000
|
||||
return addr
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
print("{:016x}".format(translate(*map(int, sys.argv[1:]))))
|
||||
|
||||
@@ -5,29 +5,43 @@ SECTIONS
|
||||
. = OFFSET + 0x100000;
|
||||
|
||||
.header : {
|
||||
header = .;
|
||||
__header_start = .;
|
||||
KEEP(*(.header))
|
||||
__header_end = .;
|
||||
}
|
||||
|
||||
.text : {
|
||||
code = .;
|
||||
.text ALIGN(4096) : {
|
||||
*(.text)
|
||||
*(.isrs)
|
||||
}
|
||||
|
||||
.data ALIGN(0x1000) : {
|
||||
data = .;
|
||||
.data ALIGN(4096) : {
|
||||
*(.data)
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
.bss ALIGN(0x1000) : {
|
||||
bss = .;
|
||||
.bss ALIGN(4096) : {
|
||||
__bss_start = .;
|
||||
*(.bss)
|
||||
__bss_end = .;
|
||||
}
|
||||
|
||||
.note ALIGN(0x1000) : {
|
||||
.note : {
|
||||
*(.note.*)
|
||||
}
|
||||
|
||||
.eh_frame : {
|
||||
__eh_frame_start = .;
|
||||
KEEP(*(.eh_frame))
|
||||
__eh_frame_end = .;
|
||||
}
|
||||
|
||||
.eh_frame_hdr : {
|
||||
KEEP(*(.eh_frame_hdr))
|
||||
}
|
||||
|
||||
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
|
||||
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
|
||||
|
||||
kernel_end = ALIGN(4096);
|
||||
}
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "console.h"
|
||||
#include "guids.h"
|
||||
#include "utility.h"
|
||||
|
||||
size_t ROWS = 0;
|
||||
size_t COLS = 0;
|
||||
|
||||
static EFI_SIMPLE_TEXT_OUT_PROTOCOL *con_out = 0;
|
||||
|
||||
const CHAR16 digits[] = {u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
|
||||
|
||||
EFI_STATUS
|
||||
con_pick_mode(EFI_BOOT_SERVICES *bootsvc)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL *gfx_out_proto;
|
||||
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gfx_out_proto);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
|
||||
|
||||
const uint32_t modes = gfx_out_proto->Mode->MaxMode;
|
||||
uint32_t best = gfx_out_proto->Mode->Mode;
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info =
|
||||
(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *)gfx_out_proto->Mode;
|
||||
|
||||
uint32_t res = info->HorizontalResolution * info->VerticalResolution;
|
||||
int is_fb = info->PixelFormat != PixelBltOnly;
|
||||
|
||||
for (uint32_t i = 0; i < modes; ++i) {
|
||||
size_t size = 0;
|
||||
status = gfx_out_proto->QueryMode(gfx_out_proto, i, &size, &info);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
|
||||
|
||||
#ifdef MAX_HRES
|
||||
if (info->HorizontalResolution > MAX_HRES) continue;
|
||||
#endif
|
||||
|
||||
const uint32_t new_res = info->HorizontalResolution * info->VerticalResolution;
|
||||
const int new_is_fb = info->PixelFormat == PixelBltOnly;
|
||||
|
||||
if (new_is_fb > is_fb && new_res >= res) {
|
||||
best = i;
|
||||
res = new_res;
|
||||
}
|
||||
}
|
||||
|
||||
status = gfx_out_proto->SetMode(gfx_out_proto, best);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "SetMode %d/%d", best, modes);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
con_initialize(EFI_SYSTEM_TABLE *system_table, const CHAR16 *version)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
|
||||
con_out = system_table->ConOut;
|
||||
|
||||
// Might not find a video device at all, so ignore not found errors
|
||||
status = con_pick_mode(bootsvc);
|
||||
if (status != EFI_NOT_FOUND)
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "con_pick_mode");
|
||||
|
||||
status = con_out->QueryMode(con_out, con_out->Mode->Mode, &COLS, &ROWS);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "QueryMode");
|
||||
|
||||
status = con_out->ClearScreen(con_out);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "ClearScreen");
|
||||
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTCYAN);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"Popcorn loader ");
|
||||
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTMAGENTA);
|
||||
con_out->OutputString(con_out, (CHAR16 *)version);
|
||||
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L" booting...\r\n\n");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t
|
||||
con_print_hex(uint32_t n)
|
||||
{
|
||||
CHAR16 buffer[9];
|
||||
CHAR16 *p = buffer;
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
uint8_t nibble = (n & (0xf << (i*4))) >> (i*4);
|
||||
*p++ = digits[nibble];
|
||||
}
|
||||
*p = 0;
|
||||
con_out->OutputString(con_out, buffer);
|
||||
return 8;
|
||||
}
|
||||
|
||||
size_t
|
||||
con_print_long_hex(uint64_t n)
|
||||
{
|
||||
CHAR16 buffer[17];
|
||||
CHAR16 *p = buffer;
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
uint8_t nibble = (n & (0xf << (i*4))) >> (i*4);
|
||||
*p++ = digits[nibble];
|
||||
}
|
||||
*p = 0;
|
||||
con_out->OutputString(con_out, buffer);
|
||||
return 16;
|
||||
}
|
||||
|
||||
size_t
|
||||
con_print_dec(uint32_t n)
|
||||
{
|
||||
CHAR16 buffer[11];
|
||||
CHAR16 *p = buffer + 10;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[n % 10];
|
||||
n /= 10;
|
||||
} while (n != 0);
|
||||
|
||||
con_out->OutputString(con_out, ++p);
|
||||
return 10 - (p - buffer);
|
||||
}
|
||||
|
||||
size_t
|
||||
con_print_long_dec(uint64_t n)
|
||||
{
|
||||
CHAR16 buffer[21];
|
||||
CHAR16 *p = buffer + 20;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[n % 10];
|
||||
n /= 10;
|
||||
} while (n != 0);
|
||||
|
||||
con_out->OutputString(con_out, ++p);
|
||||
return 20 - (p - buffer);
|
||||
}
|
||||
|
||||
size_t
|
||||
con_printf(const CHAR16 *fmt, ...)
|
||||
{
|
||||
CHAR16 buffer[256];
|
||||
const CHAR16 *r = fmt;
|
||||
CHAR16 *w = buffer;
|
||||
va_list args;
|
||||
size_t count = 0;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
while (r && *r) {
|
||||
if (*r != L'%') {
|
||||
count++;
|
||||
*w++ = *r++;
|
||||
continue;
|
||||
}
|
||||
|
||||
*w = 0;
|
||||
con_out->OutputString(con_out, buffer);
|
||||
w = buffer;
|
||||
|
||||
r++; // chomp the %
|
||||
|
||||
switch (*r++) {
|
||||
case L'%':
|
||||
con_out->OutputString(con_out, L"%");
|
||||
count++;
|
||||
break;
|
||||
|
||||
case L'x':
|
||||
count += con_print_hex(va_arg(args, uint32_t));
|
||||
break;
|
||||
|
||||
case L'd':
|
||||
case L'u':
|
||||
count += con_print_dec(va_arg(args, uint32_t));
|
||||
break;
|
||||
|
||||
case L's':
|
||||
{
|
||||
CHAR16 *s = va_arg(args, CHAR16*);
|
||||
count += wstrlen(s);
|
||||
con_out->OutputString(con_out, s);
|
||||
}
|
||||
break;
|
||||
|
||||
case L'l':
|
||||
switch (*r++) {
|
||||
case L'x':
|
||||
count += con_print_long_hex(va_arg(args, uint64_t));
|
||||
break;
|
||||
|
||||
case L'd':
|
||||
case L'u':
|
||||
count += con_print_long_dec(va_arg(args, uint64_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*w = 0;
|
||||
con_out->OutputString(con_out, buffer);
|
||||
|
||||
va_end(args);
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
con_status_begin(const CHAR16 *message)
|
||||
{
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)message);
|
||||
}
|
||||
|
||||
void
|
||||
con_status_ok()
|
||||
{
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"[");
|
||||
con_out->SetAttribute(con_out, EFI_GREEN);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L" ok ");
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"]\r\n");
|
||||
}
|
||||
|
||||
void
|
||||
con_status_fail(const CHAR16 *error)
|
||||
{
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"[");
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTRED);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"failed");
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"]\r\n");
|
||||
|
||||
con_out->SetAttribute(con_out, EFI_RED);
|
||||
con_out->OutputString(con_out, (CHAR16 *)error);
|
||||
con_out->SetAttribute(con_out, EFI_LIGHTGRAY);
|
||||
con_out->OutputString(con_out, (CHAR16 *)L"\r\n");
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
con_get_framebuffer(
|
||||
EFI_BOOT_SERVICES *bootsvc,
|
||||
void **buffer,
|
||||
size_t *buffer_size,
|
||||
uint32_t *hres,
|
||||
uint32_t *vres,
|
||||
uint32_t *rmask,
|
||||
uint32_t *gmask,
|
||||
uint32_t *bmask)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
|
||||
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gop);
|
||||
if (status != EFI_NOT_FOUND) {
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
|
||||
|
||||
*buffer = (void *)gop->Mode->FrameBufferBase;
|
||||
*buffer_size = gop->Mode->FrameBufferSize;
|
||||
*hres = gop->Mode->Info->HorizontalResolution;
|
||||
*vres = gop->Mode->Info->VerticalResolution;
|
||||
|
||||
switch (gop->Mode->Info->PixelFormat) {
|
||||
case PixelRedGreenBlueReserved8BitPerColor:
|
||||
*rmask = 0x0000ff;
|
||||
*gmask = 0x00ff00;
|
||||
*bmask = 0xff0000;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
case PixelBlueGreenRedReserved8BitPerColor:
|
||||
*bmask = 0x0000ff;
|
||||
*gmask = 0x00ff00;
|
||||
*rmask = 0xff0000;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
case PixelBitMask:
|
||||
*rmask = gop->Mode->Info->PixelInformation.RedMask;
|
||||
*gmask = gop->Mode->Info->PixelInformation.GreenMask;
|
||||
*bmask = gop->Mode->Info->PixelInformation.BlueMask;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
default:
|
||||
// Not a framebuffer, fall through to zeroing out
|
||||
// values below.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*buffer = NULL;
|
||||
*buffer_size = *hres = *vres = 0;
|
||||
*rmask = *gmask = *bmask = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
447
src/boot/console.cpp
Normal file
447
src/boot/console.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/graphics.h>
|
||||
#include <uefi/protos/graphics_output.h>
|
||||
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
|
||||
#ifndef GIT_VERSION_WIDE
|
||||
#define GIT_VERSION_WIDE L"no version"
|
||||
#endif
|
||||
|
||||
namespace boot {
|
||||
|
||||
size_t ROWS = 0;
|
||||
size_t COLS = 0;
|
||||
|
||||
static constexpr int level_ok = 0;
|
||||
static constexpr int level_warn = 1;
|
||||
static constexpr int level_fail = 2;
|
||||
|
||||
static const wchar_t *level_tags[] = {
|
||||
L" ok ",
|
||||
L" warn ",
|
||||
L"failed"
|
||||
};
|
||||
static const uefi::attribute level_colors[] = {
|
||||
uefi::attribute::green,
|
||||
uefi::attribute::brown,
|
||||
uefi::attribute::light_red
|
||||
};
|
||||
|
||||
console *console::s_console = nullptr;
|
||||
status_line *status_line::s_current = nullptr;
|
||||
|
||||
static const wchar_t digits[] = {u'0', u'1', u'2', u'3', u'4', u'5',
|
||||
u'6', u'7', u'8', u'9', u'a', u'b', u'c', u'd', u'e', u'f'};
|
||||
|
||||
|
||||
static size_t
|
||||
wstrlen(const wchar_t *s)
|
||||
{
|
||||
size_t count = 0;
|
||||
while (s && *s++) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
console::console(uefi::boot_services *bs, uefi::protos::simple_text_output *out) :
|
||||
m_rows(0),
|
||||
m_cols(0),
|
||||
m_out(out)
|
||||
{
|
||||
pick_mode(bs);
|
||||
|
||||
try_or_raise(
|
||||
m_out->query_mode(m_out->mode->mode, &m_cols, &m_rows),
|
||||
L"Failed to get text output mode.");
|
||||
|
||||
try_or_raise(
|
||||
m_out->clear_screen(),
|
||||
L"Failed to clear screen");
|
||||
|
||||
m_out->set_attribute(uefi::attribute::light_cyan);
|
||||
m_out->output_string(L"jsix loader ");
|
||||
|
||||
m_out->set_attribute(uefi::attribute::light_magenta);
|
||||
m_out->output_string(GIT_VERSION_WIDE);
|
||||
|
||||
m_out->set_attribute(uefi::attribute::light_gray);
|
||||
m_out->output_string(L" booting...\r\n\n");
|
||||
|
||||
s_console = this;
|
||||
}
|
||||
|
||||
void
|
||||
console::pick_mode(uefi::boot_services *bs)
|
||||
{
|
||||
uefi::status status;
|
||||
uefi::protos::graphics_output *gfx_out_proto;
|
||||
uefi::guid guid = uefi::protos::graphics_output::guid;
|
||||
|
||||
try_or_raise(
|
||||
bs->locate_protocol(&guid, nullptr, (void **)&gfx_out_proto),
|
||||
L"Failed to find a Graphics Output Protocol handle");
|
||||
|
||||
const uint32_t modes = gfx_out_proto->mode->max_mode;
|
||||
uint32_t best = gfx_out_proto->mode->mode;
|
||||
|
||||
uefi::graphics_output_mode_info *info =
|
||||
(uefi::graphics_output_mode_info *)gfx_out_proto->mode;
|
||||
|
||||
uint32_t res = info->horizontal_resolution * info->vertical_resolution;
|
||||
int is_fb = info->pixel_format != uefi::pixel_format::blt_only;
|
||||
|
||||
for (uint32_t i = 0; i < modes; ++i) {
|
||||
size_t size = 0;
|
||||
|
||||
try_or_raise(
|
||||
gfx_out_proto->query_mode(i, &size, &info),
|
||||
L"Failed to find a graphics mode the driver claimed to support");
|
||||
|
||||
#ifdef MAX_HRES
|
||||
if (info->horizontal_resolution > MAX_HRES) continue;
|
||||
#endif
|
||||
|
||||
const uint32_t new_res = info->horizontal_resolution * info->vertical_resolution;
|
||||
const int new_is_fb = info->pixel_format != uefi::pixel_format::blt_only;
|
||||
|
||||
if (new_is_fb > is_fb && new_res >= res) {
|
||||
best = i;
|
||||
res = new_res;
|
||||
}
|
||||
}
|
||||
|
||||
try_or_raise(
|
||||
gfx_out_proto->set_mode(best),
|
||||
L"Failed to set graphics mode");
|
||||
}
|
||||
|
||||
size_t
|
||||
console::print_hex(uint32_t n) const
|
||||
{
|
||||
wchar_t buffer[9];
|
||||
wchar_t *p = buffer;
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
uint8_t nibble = (n >> (i*4)) & 0xf;
|
||||
*p++ = digits[nibble];
|
||||
}
|
||||
*p = 0;
|
||||
m_out->output_string(buffer);
|
||||
return 8;
|
||||
}
|
||||
|
||||
size_t
|
||||
console::print_long_hex(uint64_t n) const
|
||||
{
|
||||
wchar_t buffer[17];
|
||||
wchar_t *p = buffer;
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
uint8_t nibble = (n >> (i*4)) & 0xf;
|
||||
*p++ = digits[nibble];
|
||||
}
|
||||
*p = 0;
|
||||
m_out->output_string(buffer);
|
||||
return 16;
|
||||
}
|
||||
|
||||
size_t
|
||||
console::print_dec(uint32_t n) const
|
||||
{
|
||||
wchar_t buffer[11];
|
||||
wchar_t *p = buffer + 10;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[n % 10];
|
||||
n /= 10;
|
||||
} while (n != 0);
|
||||
|
||||
m_out->output_string(++p);
|
||||
return 10 - (p - buffer);
|
||||
}
|
||||
|
||||
size_t
|
||||
console::print_long_dec(uint64_t n) const
|
||||
{
|
||||
wchar_t buffer[21];
|
||||
wchar_t *p = buffer + 20;
|
||||
*p-- = 0;
|
||||
do {
|
||||
*p-- = digits[n % 10];
|
||||
n /= 10;
|
||||
} while (n != 0);
|
||||
|
||||
m_out->output_string(++p);
|
||||
return 20 - (p - buffer);
|
||||
}
|
||||
|
||||
size_t
|
||||
console::vprintf(const wchar_t *fmt, va_list args) const
|
||||
{
|
||||
wchar_t buffer[256];
|
||||
const wchar_t *r = fmt;
|
||||
wchar_t *w = buffer;
|
||||
size_t count = 0;
|
||||
|
||||
while (r && *r) {
|
||||
if (*r != L'%') {
|
||||
count++;
|
||||
*w++ = *r++;
|
||||
continue;
|
||||
}
|
||||
|
||||
*w = 0;
|
||||
m_out->output_string(buffer);
|
||||
w = buffer;
|
||||
|
||||
r++; // chomp the %
|
||||
|
||||
switch (*r++) {
|
||||
case L'%':
|
||||
m_out->output_string(const_cast<wchar_t*>(L"%"));
|
||||
count++;
|
||||
break;
|
||||
|
||||
case L'x':
|
||||
count += print_hex(va_arg(args, uint32_t));
|
||||
break;
|
||||
|
||||
case L'd':
|
||||
case L'u':
|
||||
count += print_dec(va_arg(args, uint32_t));
|
||||
break;
|
||||
|
||||
case L's':
|
||||
{
|
||||
wchar_t *s = va_arg(args, wchar_t*);
|
||||
count += wstrlen(s);
|
||||
m_out->output_string(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case L'l':
|
||||
switch (*r++) {
|
||||
case L'x':
|
||||
count += print_long_hex(va_arg(args, uint64_t));
|
||||
break;
|
||||
|
||||
case L'd':
|
||||
case L'u':
|
||||
count += print_long_dec(va_arg(args, uint64_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*w = 0;
|
||||
m_out->output_string(buffer);
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t
|
||||
console::printf(const wchar_t *fmt, ...) const
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
size_t result = vprintf(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t
|
||||
console::print(const wchar_t *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
size_t result = get().vprintf(fmt, args);
|
||||
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
status_line::status_line(const wchar_t *message, const wchar_t *context) :
|
||||
m_level(level_ok)
|
||||
{
|
||||
auto out = console::get().m_out;
|
||||
m_line = out->mode->cursor_row;
|
||||
m_depth = (s_current ? 1 + s_current->m_depth : 0);
|
||||
|
||||
int indent = 2 * m_depth;
|
||||
out->set_cursor_position(indent, m_line);
|
||||
out->set_attribute(uefi::attribute::light_gray);
|
||||
out->output_string(message);
|
||||
|
||||
if (context) {
|
||||
out->output_string(L": ");
|
||||
out->output_string(context);
|
||||
}
|
||||
|
||||
out->output_string(L"\r\n");
|
||||
|
||||
m_next = s_current;
|
||||
s_current = this;
|
||||
}
|
||||
|
||||
status_line::~status_line()
|
||||
{
|
||||
if (s_current != this)
|
||||
error::raise(uefi::status::unsupported, L"Destroying non-current status_line");
|
||||
|
||||
finish();
|
||||
if (m_next && m_level > m_next->m_level) {
|
||||
m_next->m_level = m_level;
|
||||
m_next->print_status_tag();
|
||||
}
|
||||
s_current = m_next;
|
||||
}
|
||||
|
||||
void
|
||||
status_line::print_status_tag()
|
||||
{
|
||||
auto out = console::get().m_out;
|
||||
int row = out->mode->cursor_row;
|
||||
int col = out->mode->cursor_column;
|
||||
|
||||
uefi::attribute color = level_colors[m_level];
|
||||
const wchar_t *tag = level_tags[m_level];
|
||||
|
||||
out->set_cursor_position(50, m_line);
|
||||
|
||||
out->set_attribute(uefi::attribute::light_gray);
|
||||
out->output_string(L"[");
|
||||
out->set_attribute(color);
|
||||
out->output_string(tag);
|
||||
out->set_attribute(uefi::attribute::light_gray);
|
||||
out->output_string(L"]\r\n");
|
||||
|
||||
out->set_cursor_position(col, row);
|
||||
}
|
||||
|
||||
void
|
||||
status_line::do_warn(const wchar_t *message, const wchar_t *error)
|
||||
{
|
||||
auto out = console::get().m_out;
|
||||
int row = out->mode->cursor_row;
|
||||
|
||||
if (m_level < level_warn) {
|
||||
m_level = level_warn;
|
||||
print_status_tag();
|
||||
}
|
||||
|
||||
int indent = 2 + 2 * m_depth;
|
||||
out->set_cursor_position(indent, row);
|
||||
out->set_attribute(uefi::attribute::yellow);
|
||||
out->output_string(message);
|
||||
|
||||
if (error) {
|
||||
out->output_string(L": ");
|
||||
out->output_string(error);
|
||||
}
|
||||
|
||||
out->set_attribute(uefi::attribute::light_gray);
|
||||
out->output_string(L"\r\n");
|
||||
}
|
||||
|
||||
void
|
||||
status_line::do_fail(const wchar_t *message, const wchar_t *error)
|
||||
{
|
||||
auto out = console::get().m_out;
|
||||
int row = out->mode->cursor_row;
|
||||
|
||||
if (s_current->m_level < level_fail) {
|
||||
m_level = level_fail;
|
||||
print_status_tag();
|
||||
}
|
||||
|
||||
int indent = 2 + 2 * m_depth;
|
||||
out->set_cursor_position(indent, row);
|
||||
out->set_attribute(uefi::attribute::red);
|
||||
out->output_string(message);
|
||||
|
||||
if (error) {
|
||||
out->output_string(L": ");
|
||||
out->output_string(error);
|
||||
}
|
||||
|
||||
out->set_attribute(uefi::attribute::light_gray);
|
||||
out->output_string(L"\r\n");
|
||||
}
|
||||
|
||||
void
|
||||
status_line::finish()
|
||||
{
|
||||
if (m_level <= level_ok)
|
||||
print_status_tag();
|
||||
}
|
||||
|
||||
/*
|
||||
uefi::status
|
||||
con_get_framebuffer(
|
||||
EFI_BOOT_SERVICES *bootsvc,
|
||||
void **buffer,
|
||||
size_t *buffer_size,
|
||||
uint32_t *hres,
|
||||
uint32_t *vres,
|
||||
uint32_t *rmask,
|
||||
uint32_t *gmask,
|
||||
uint32_t *bmask)
|
||||
{
|
||||
uefi::status status;
|
||||
|
||||
uefi::protos::graphics_output *gop;
|
||||
status = bootsvc->LocateProtocol(&guid_gfx_out, NULL, (void **)&gop);
|
||||
if (status != EFI_NOT_FOUND) {
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "LocateProtocol gfx");
|
||||
|
||||
*buffer = (void *)gop->Mode->FrameBufferBase;
|
||||
*buffer_size = gop->Mode->FrameBufferSize;
|
||||
*hres = gop->Mode->Info->horizontal_resolution;
|
||||
*vres = gop->Mode->Info->vertical_resolution;
|
||||
|
||||
switch (gop->Mode->Info->PixelFormat) {
|
||||
case PixelRedGreenBlueReserved8BitPerColor:
|
||||
*rmask = 0x0000ff;
|
||||
*gmask = 0x00ff00;
|
||||
*bmask = 0xff0000;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
case PixelBlueGreenRedReserved8BitPerColor:
|
||||
*bmask = 0x0000ff;
|
||||
*gmask = 0x00ff00;
|
||||
*rmask = 0xff0000;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
case PixelBitMask:
|
||||
*rmask = gop->Mode->Info->PixelInformation.RedMask;
|
||||
*gmask = gop->Mode->Info->PixelInformation.GreenMask;
|
||||
*bmask = gop->Mode->Info->PixelInformation.BlueMask;
|
||||
return EFI_SUCCESS;
|
||||
|
||||
default:
|
||||
// Not a framebuffer, fall through to zeroing out
|
||||
// values below.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*buffer = NULL;
|
||||
*buffer_size = *hres = *vres = 0;
|
||||
*rmask = *gmask = *bmask = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace boot
|
||||
@@ -1,20 +1,80 @@
|
||||
/// \file console.h
|
||||
/// Text output and status message handling
|
||||
#pragma once
|
||||
#include <efi.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <uefi/boot_services.h>
|
||||
#include <uefi/protos/simple_text_output.h>
|
||||
|
||||
EFI_STATUS con_initialize(EFI_SYSTEM_TABLE *system_table, const CHAR16 *version);
|
||||
void con_status_begin(const CHAR16 *message);
|
||||
void con_status_ok();
|
||||
void con_status_fail(const CHAR16 *error);
|
||||
size_t con_printf(const CHAR16 *fmt, ...);
|
||||
namespace boot {
|
||||
|
||||
EFI_STATUS
|
||||
con_get_framebuffer(
|
||||
EFI_BOOT_SERVICES *bootsvc,
|
||||
void **buffer,
|
||||
size_t *buffer_size,
|
||||
uint32_t *hres,
|
||||
uint32_t *vres,
|
||||
uint32_t *rmask,
|
||||
uint32_t *gmask,
|
||||
uint32_t *bmask);
|
||||
/// Object providing basic output functionality to the UEFI console
|
||||
class console
|
||||
{
|
||||
public:
|
||||
console(uefi::boot_services *bs, uefi::protos::simple_text_output *out);
|
||||
|
||||
size_t print_hex(uint32_t n) const;
|
||||
size_t print_dec(uint32_t n) const;
|
||||
size_t print_long_hex(uint64_t n) const;
|
||||
size_t print_long_dec(uint64_t n) const;
|
||||
size_t printf(const wchar_t *fmt, ...) const;
|
||||
|
||||
static console & get() { return *s_console; }
|
||||
static size_t print(const wchar_t *fmt, ...);
|
||||
|
||||
private:
|
||||
friend class status_line;
|
||||
|
||||
void pick_mode(uefi::boot_services *bs);
|
||||
size_t vprintf(const wchar_t *fmt, va_list args) const;
|
||||
|
||||
size_t m_rows, m_cols;
|
||||
uefi::protos::simple_text_output *m_out;
|
||||
|
||||
static console *s_console;
|
||||
};
|
||||
|
||||
/// Scoped status line reporter. Prints a message and an "OK" if no errors
|
||||
/// or warnings were reported before destruction, otherwise reports the
|
||||
/// error or warning.
|
||||
class status_line
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg message Description of the operation in progress
|
||||
/// \arg context If non-null, printed after `message` and a colon
|
||||
status_line(const wchar_t *message, const wchar_t *context = nullptr);
|
||||
~status_line();
|
||||
|
||||
/// Set the state to warning, and print a message. If the state is already at
|
||||
/// warning or error, the state is unchanged but the message is still printed.
|
||||
/// \arg message The warning message to print
|
||||
/// \arg error If non-null, printed after `message`
|
||||
inline static void warn(const wchar_t *message, const wchar_t *error = nullptr) {
|
||||
if (s_current) s_current->do_warn(message, error);
|
||||
}
|
||||
|
||||
/// Set the state to error, and print a message. If the state is already at
|
||||
/// error, the state is unchanged but the message is still printed.
|
||||
/// \arg message The error message to print
|
||||
/// \arg error If non-null, printed after `message`
|
||||
inline static void fail(const wchar_t *message, const wchar_t *error = nullptr) {
|
||||
if (s_current) s_current->do_fail(message, error);
|
||||
}
|
||||
|
||||
private:
|
||||
void print_status_tag();
|
||||
void do_warn(const wchar_t *message, const wchar_t *error);
|
||||
void do_fail(const wchar_t *message, const wchar_t *error);
|
||||
void finish();
|
||||
|
||||
size_t m_line;
|
||||
int m_level;
|
||||
int m_depth;
|
||||
|
||||
status_line *m_next;
|
||||
static status_line *s_current;
|
||||
};
|
||||
|
||||
} // namespace boot
|
||||
|
||||
83
src/boot/elf.h
Normal file
83
src/boot/elf.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/// \file elf.h
|
||||
/// Definitions and related constants for ELF64 structures
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace boot {
|
||||
namespace elf {
|
||||
|
||||
constexpr uint8_t version = 1;
|
||||
constexpr uint8_t word_size = 2;
|
||||
constexpr uint8_t endianness = 1;
|
||||
constexpr uint8_t os_abi = 0;
|
||||
constexpr uint16_t machine = 0x3e;
|
||||
|
||||
const unsigned PT_LOAD = 1;
|
||||
const unsigned ST_PROGBITS = 1;
|
||||
const unsigned ST_NOBITS = 8;
|
||||
const unsigned long SHF_ALLOC = 0x2;
|
||||
|
||||
struct header
|
||||
{
|
||||
char magic[4];
|
||||
|
||||
uint8_t word_size;
|
||||
uint8_t endianness;
|
||||
uint8_t header_version;
|
||||
uint8_t os_abi;
|
||||
|
||||
uint64_t reserved;
|
||||
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
|
||||
uint32_t version;
|
||||
|
||||
uint64_t entrypoint;
|
||||
uint64_t ph_offset;
|
||||
uint64_t sh_offset;
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
uint16_t eh_size;
|
||||
|
||||
uint16_t ph_entsize;
|
||||
uint16_t ph_num;
|
||||
|
||||
uint16_t sh_entsize;
|
||||
uint16_t sh_num;
|
||||
|
||||
uint16_t sh_str_idx;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct program_header
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
uint64_t offset;
|
||||
|
||||
uint64_t vaddr;
|
||||
uint64_t paddr;
|
||||
|
||||
uint64_t file_size;
|
||||
uint64_t mem_size;
|
||||
|
||||
uint64_t align;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct section_header
|
||||
{
|
||||
uint32_t name;
|
||||
uint32_t type;
|
||||
uint64_t flags;
|
||||
uint64_t addr;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
uint32_t link;
|
||||
uint32_t info;
|
||||
uint64_t align;
|
||||
uint64_t entry_size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
} // namespace elf
|
||||
} // namespace boot
|
||||
94
src/boot/error.cpp
Normal file
94
src/boot/error.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "error.h"
|
||||
#include "console.h"
|
||||
|
||||
namespace boot {
|
||||
namespace error {
|
||||
|
||||
handler *handler::s_current = nullptr;
|
||||
|
||||
struct error_code_desc {
|
||||
uefi::status code;
|
||||
const wchar_t *name;
|
||||
};
|
||||
|
||||
struct error_code_desc error_table[] = {
|
||||
#define STATUS_ERROR(name, num) { uefi::status::name, L#name },
|
||||
#define STATUS_WARNING(name, num) { uefi::status::name, L#name },
|
||||
#include "uefi/errors.inc"
|
||||
#undef STATUS_ERROR
|
||||
#undef STATUS_WARNING
|
||||
{ uefi::status::success, nullptr }
|
||||
};
|
||||
|
||||
static const wchar_t *
|
||||
error_message(uefi::status status)
|
||||
{
|
||||
int32_t i = -1;
|
||||
while (error_table[++i].name != nullptr) {
|
||||
if (error_table[i].code == status) return error_table[i].name;
|
||||
}
|
||||
|
||||
if (uefi::is_error(status))
|
||||
return L"Unknown Error";
|
||||
else
|
||||
return L"Unknown Warning";
|
||||
}
|
||||
|
||||
[[ noreturn ]] void
|
||||
raise(uefi::status status, const wchar_t *message)
|
||||
{
|
||||
if (handler::s_current) {
|
||||
handler::s_current->handle(status, message);
|
||||
}
|
||||
while (1) asm("hlt");
|
||||
}
|
||||
|
||||
handler::handler() :
|
||||
m_next(s_current)
|
||||
{
|
||||
s_current = this;
|
||||
}
|
||||
|
||||
handler::~handler()
|
||||
{
|
||||
if (s_current != this)
|
||||
raise(uefi::status::warn_stale_data,
|
||||
L"Non-current error handler destructing");
|
||||
s_current = m_next;
|
||||
}
|
||||
|
||||
uefi_handler::uefi_handler(console &con) :
|
||||
handler(),
|
||||
m_con(con)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
uefi_handler::handle(uefi::status s, const wchar_t *message)
|
||||
{
|
||||
status_line::fail(message, error_message(s));
|
||||
}
|
||||
|
||||
cpu_assert_handler::cpu_assert_handler() : handler() {}
|
||||
|
||||
void
|
||||
cpu_assert_handler::handle(uefi::status s, const wchar_t *message)
|
||||
{
|
||||
asm volatile (
|
||||
"movq $0xeeeeeeebadbadbad, %%r8;"
|
||||
"movq %0, %%r9;"
|
||||
"movq $0, %%rdx;"
|
||||
"divq %%rdx;"
|
||||
:
|
||||
: "r"((uint64_t)s)
|
||||
: "rax", "rdx", "r8", "r9");
|
||||
}
|
||||
|
||||
} // namespace error
|
||||
} // namespace boot
|
||||
|
||||
void debug_break()
|
||||
{
|
||||
volatile int go = 0;
|
||||
while (!go);
|
||||
}
|
||||
74
src/boot/error.h
Normal file
74
src/boot/error.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/// \file error.h
|
||||
/// Error handling definitions
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
namespace boot {
|
||||
|
||||
class console;
|
||||
|
||||
namespace error {
|
||||
|
||||
/// Halt or exit the program with the given error status/message
|
||||
[[ noreturn ]] void raise(uefi::status status, const wchar_t *message);
|
||||
|
||||
/// Interface for error-handling functors
|
||||
class handler
|
||||
{
|
||||
public:
|
||||
/// Constructor must be called by implementing classes.
|
||||
handler();
|
||||
virtual ~handler();
|
||||
|
||||
/// Interface for implementations of error handling.
|
||||
virtual void handle(uefi::status, const wchar_t*) = 0;
|
||||
|
||||
private:
|
||||
friend void raise(uefi::status, const wchar_t *);
|
||||
|
||||
handler *m_next;
|
||||
static handler *s_current;
|
||||
};
|
||||
|
||||
/// Error handler using UEFI boot services. Integrates with `status_line`
|
||||
/// to print formatted error messages to the screen.
|
||||
class uefi_handler :
|
||||
public handler
|
||||
{
|
||||
public:
|
||||
uefi_handler(console &con);
|
||||
virtual ~uefi_handler() {}
|
||||
void handle(uefi::status, const wchar_t*) override;
|
||||
|
||||
private:
|
||||
console &m_con;
|
||||
};
|
||||
|
||||
/// Error handler that doesn't rely on UEFI. Sets status into CPU
|
||||
/// registers and then causes a CPU #DE exception.
|
||||
class cpu_assert_handler :
|
||||
public handler
|
||||
{
|
||||
public:
|
||||
cpu_assert_handler();
|
||||
virtual ~cpu_assert_handler() {}
|
||||
void handle(uefi::status, const wchar_t*) override;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace boot
|
||||
|
||||
/// Debugging psuedo-breakpoint.
|
||||
void debug_break();
|
||||
|
||||
/// Helper macro to raise an error if an operation fails.
|
||||
/// \arg s An expression evaluating to a UEFI status
|
||||
/// \arg m The error message to use on failure
|
||||
#define try_or_raise(s, m) \
|
||||
do { \
|
||||
uefi::status _s = (s); \
|
||||
if (uefi::is_error(_s)) ::boot::error::raise(_s, (m)); \
|
||||
} while(0)
|
||||
|
||||
114
src/boot/fs.cpp
Normal file
114
src/boot/fs.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/protos/file.h>
|
||||
#include <uefi/protos/file_info.h>
|
||||
#include <uefi/protos/loaded_image.h>
|
||||
#include <uefi/protos/simple_file_system.h>
|
||||
|
||||
#include "fs.h"
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
#include "memory.h"
|
||||
|
||||
namespace boot {
|
||||
namespace fs {
|
||||
|
||||
file::file(uefi::protos::file *f, uefi::boot_services *bs) :
|
||||
m_file(f),
|
||||
m_bs(bs)
|
||||
{
|
||||
}
|
||||
|
||||
file::file(file &o) :
|
||||
m_file(o.m_file),
|
||||
m_bs(o.m_bs)
|
||||
{
|
||||
o.m_file = nullptr;
|
||||
}
|
||||
|
||||
file::file(file &&o) :
|
||||
m_file(o.m_file),
|
||||
m_bs(o.m_bs)
|
||||
{
|
||||
o.m_file = nullptr;
|
||||
}
|
||||
|
||||
file::~file()
|
||||
{
|
||||
if (m_file)
|
||||
m_file->close();
|
||||
}
|
||||
|
||||
file
|
||||
file::open(const wchar_t *path)
|
||||
{
|
||||
uefi::protos::file *fh = nullptr;
|
||||
|
||||
try_or_raise(
|
||||
m_file->open(&fh, path, uefi::file_mode::read, uefi::file_attr::none),
|
||||
L"Could not open relative path to file");
|
||||
|
||||
return file(fh, m_bs);
|
||||
}
|
||||
|
||||
void *
|
||||
file::load(size_t *out_size, uefi::memory_type mem_type)
|
||||
{
|
||||
uint8_t buffer[sizeof(uefi::protos::file_info) + 100];
|
||||
size_t size = sizeof(buffer);
|
||||
uefi::guid info_guid = uefi::protos::file_info::guid;
|
||||
|
||||
try_or_raise(
|
||||
m_file->get_info(&info_guid, &size, &buffer),
|
||||
L"Could not get file info");
|
||||
|
||||
uefi::protos::file_info *info =
|
||||
reinterpret_cast<uefi::protos::file_info*>(&buffer);
|
||||
|
||||
size_t pages = memory::bytes_to_pages(info->file_size);
|
||||
void *data = nullptr;
|
||||
try_or_raise(
|
||||
m_bs->allocate_pages(
|
||||
uefi::allocate_type::any_pages,
|
||||
mem_type, pages, &data),
|
||||
L"Could not allocate pages to load file");
|
||||
|
||||
size = info->file_size;
|
||||
try_or_raise(
|
||||
m_file->read(&size, data),
|
||||
L"Could not read from file");
|
||||
|
||||
*out_size = size;
|
||||
return data;
|
||||
}
|
||||
|
||||
file
|
||||
get_boot_volume(uefi::handle image, uefi::boot_services *bs)
|
||||
{
|
||||
status_line status(L"Looking up boot volume");
|
||||
|
||||
const uefi::guid le_guid = uefi::protos::loaded_image::guid;
|
||||
uefi::protos::loaded_image *loaded_image = nullptr;
|
||||
|
||||
try_or_raise(
|
||||
bs->handle_protocol(image, &le_guid,
|
||||
reinterpret_cast<void**>(&loaded_image)),
|
||||
L"Could not find currently running UEFI loaded image");
|
||||
|
||||
const uefi::guid sfs_guid = uefi::protos::simple_file_system::guid;
|
||||
uefi::protos::simple_file_system *fs;
|
||||
try_or_raise(
|
||||
bs->handle_protocol(loaded_image->device_handle, &sfs_guid,
|
||||
reinterpret_cast<void**>(&fs)),
|
||||
L"Could not find filesystem protocol for boot volume");
|
||||
|
||||
uefi::protos::file *f;
|
||||
try_or_raise(
|
||||
fs->open_volume(&f),
|
||||
L"Could not open the boot volume");
|
||||
|
||||
return file(f, bs);
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
} // namespace boot
|
||||
|
||||
47
src/boot/fs.h
Normal file
47
src/boot/fs.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/// \file fs.h
|
||||
/// Definitions for dealing with UEFI's disk access functions
|
||||
#pragma once
|
||||
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/boot_services.h>
|
||||
#include <uefi/protos/file.h>
|
||||
|
||||
namespace boot {
|
||||
namespace fs {
|
||||
|
||||
/// A file or directory in a filesystem.
|
||||
class file
|
||||
{
|
||||
public:
|
||||
file(file &&o);
|
||||
file(file &o);
|
||||
~file();
|
||||
|
||||
/// Open another file or directory, relative to this one.
|
||||
/// \arg path Relative path to the target file from this one
|
||||
file open(const wchar_t *path);
|
||||
|
||||
/// Load the contents of this file into memory.
|
||||
/// \arg out_size _out:_ The number of bytes loaded
|
||||
/// \arg mem_type The UEFI memory type to use for allocation
|
||||
/// \returns A pointer to the loaded memory. Memory will be
|
||||
/// page-aligned.
|
||||
void * load(
|
||||
size_t *out_size,
|
||||
uefi::memory_type mem_type = uefi::memory_type::loader_data);
|
||||
|
||||
private:
|
||||
friend file get_boot_volume(uefi::handle, uefi::boot_services*);
|
||||
|
||||
file(uefi::protos::file *f, uefi::boot_services *bs);
|
||||
|
||||
uefi::protos::file *m_file;
|
||||
uefi::boot_services *m_bs;
|
||||
};
|
||||
|
||||
/// Get the filesystem this loader was loaded from.
|
||||
/// \returns A `file` object representing the root directory of the volume
|
||||
file get_boot_volume(uefi::handle image, uefi::boot_services *bs);
|
||||
|
||||
} // namespace fs
|
||||
} // namespace boot
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <efi.h>
|
||||
#include <efi/efi.h>
|
||||
|
||||
int is_guid(EFI_GUID *a, EFI_GUID *b);
|
||||
|
||||
|
||||
@@ -3,5 +3,9 @@ GUID(0x8868e871,0xe4f1,0x11d3,0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81, guid_acpi
|
||||
GUID(0x09576e92,0x6d3f,0x11d2,0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b, guid_file_info);
|
||||
GUID(0x9042a9de,0x23dc,0x4a38,0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a, guid_gfx_out);
|
||||
GUID(0x964e5b22,0x6459,0x11d2,0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b, guid_simple_filesystem);
|
||||
GUID(0x09576e91,0x6d3f,0x11d2,0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b, guid_device_path);
|
||||
GUID(0x8b843e20,0x8132,0x4852,0x90,0xcc,0x55,0x1a,0x4e,0x4a,0x7f,0x1c, guid_device_path_to_text);
|
||||
|
||||
GUID(0x10d0669c,0x9ec6,0x4268,0xbc,0x48,0xff,0x74,0x75,0x21,0xfe,0x07, guid_jsix_vendor);
|
||||
|
||||
// vim: ft=c
|
||||
|
||||
54
src/boot/hardware.cpp
Normal file
54
src/boot/hardware.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "hardware.h"
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
|
||||
namespace boot {
|
||||
namespace hw {
|
||||
|
||||
void *
|
||||
find_acpi_table(uefi::system_table *st)
|
||||
{
|
||||
status_line status(L"Searching for ACPI table");
|
||||
|
||||
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
|
||||
uintptr_t acpi1_table = 0;
|
||||
|
||||
for (size_t i = 0; i < st->number_of_table_entries; ++i) {
|
||||
uefi::configuration_table *table = &st->configuration_table[i];
|
||||
|
||||
// If we find an ACPI 2.0 table, return it immediately
|
||||
if (table->vendor_guid == uefi::vendor_guids::acpi2)
|
||||
return table->vendor_table;
|
||||
|
||||
if (table->vendor_guid == uefi::vendor_guids::acpi1) {
|
||||
// Mark a v1 table with the LSB high
|
||||
acpi1_table = reinterpret_cast<uintptr_t>(table->vendor_table);
|
||||
acpi1_table |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!acpi1_table) {
|
||||
error::raise(uefi::status::not_found, L"Could not find ACPI table");
|
||||
} else if (acpi1_table & 1) {
|
||||
status_line::warn(L"Only found ACPI 1.0 table");
|
||||
}
|
||||
|
||||
return reinterpret_cast<void*>(acpi1_table);
|
||||
}
|
||||
|
||||
void
|
||||
setup_cr4()
|
||||
{
|
||||
uint64_t cr4 = 0;
|
||||
asm volatile ( "mov %%cr4, %0" : "=r" (cr4) );
|
||||
cr4 |=
|
||||
0x000080 | // Enable global pages
|
||||
0x000200 | // Enable FXSAVE/FXRSTOR
|
||||
0x010000 | // Enable FSGSBASE
|
||||
0x020000 | // Enable PCIDs
|
||||
0;
|
||||
asm volatile ( "mov %0, %%cr4" :: "r" (cr4) );
|
||||
}
|
||||
|
||||
} // namespace hw
|
||||
} // namespace boot
|
||||
20
src/boot/hardware.h
Normal file
20
src/boot/hardware.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/// \file hardware.h
|
||||
/// Functions and definitions for detecting and dealing with hardware
|
||||
#pragma once
|
||||
|
||||
#include <uefi/tables.h>
|
||||
|
||||
namespace boot {
|
||||
namespace hw {
|
||||
|
||||
/// Find the ACPI table in the system configuration tables
|
||||
/// and return a pointer to it. If only an ACPI 1.0 table is
|
||||
/// available, the returned pointer will have its least
|
||||
/// significant bit set to 1.
|
||||
void * find_acpi_table(uefi::system_table *st);
|
||||
|
||||
/// Enable CPU options in CR4 for the kernel starting state.
|
||||
void setup_cr4();
|
||||
|
||||
} // namespace hw
|
||||
} // namespace boot
|
||||
@@ -1,160 +0,0 @@
|
||||
#include "guids.h"
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define PAGE_SIZE 0x1000
|
||||
|
||||
static CHAR16 kernel_name[] = KERNEL_FILENAME;
|
||||
static CHAR16 font_name[] = KERNEL_FONT;
|
||||
|
||||
EFI_STATUS
|
||||
loader_alloc_pages(
|
||||
EFI_BOOT_SERVICES *bootsvc,
|
||||
EFI_MEMORY_TYPE mem_type,
|
||||
size_t *length,
|
||||
void **pages,
|
||||
void **next)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
size_t page_count = ((*length - 1) / PAGE_SIZE) + 1;
|
||||
EFI_PHYSICAL_ADDRESS addr = (EFI_PHYSICAL_ADDRESS)*pages;
|
||||
|
||||
status = bootsvc->AllocatePages(AllocateAddress, mem_type, page_count, &addr);
|
||||
if (status == EFI_NOT_FOUND || status == EFI_OUT_OF_RESOURCES) {
|
||||
// couldn't get the address we wanted, try loading the kernel anywhere
|
||||
status =
|
||||
bootsvc->AllocatePages(AllocateAnyPages, mem_type, page_count, &addr);
|
||||
}
|
||||
CHECK_EFI_STATUS_OR_RETURN(status,
|
||||
L"Allocating %d kernel pages type %x",
|
||||
page_count, mem_type);
|
||||
|
||||
*length = page_count * PAGE_SIZE;
|
||||
*pages = (void *)addr;
|
||||
*next = (void*)(addr + *length);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
loader_load_file(
|
||||
EFI_BOOT_SERVICES *bootsvc,
|
||||
EFI_FILE_PROTOCOL *root,
|
||||
const CHAR16 *filename,
|
||||
EFI_MEMORY_TYPE mem_type,
|
||||
void **data,
|
||||
size_t *length,
|
||||
void **next)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
EFI_FILE_PROTOCOL *file = NULL;
|
||||
status = root->Open(root, &file, (CHAR16 *)filename, EFI_FILE_MODE_READ,
|
||||
EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
|
||||
|
||||
if (status == EFI_NOT_FOUND)
|
||||
return status;
|
||||
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Opening file %s", filename);
|
||||
|
||||
char info[sizeof(EFI_FILE_INFO) + 100];
|
||||
size_t info_length = sizeof(info);
|
||||
|
||||
status = file->GetInfo(file, &guid_file_info, &info_length, info);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Getting file info");
|
||||
|
||||
*length = ((EFI_FILE_INFO *)info)->FileSize;
|
||||
|
||||
status = loader_alloc_pages(bootsvc, mem_type, length, data, next);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Allocating pages");
|
||||
|
||||
status = file->Read(file, length, *data);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Reading file");
|
||||
|
||||
status = file->Close(file);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"Closing file handle");
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
EFI_STATUS
|
||||
loader_load_kernel(
|
||||
EFI_BOOT_SERVICES *bootsvc,
|
||||
struct loader_data *data)
|
||||
{
|
||||
if (data == NULL)
|
||||
CHECK_EFI_STATUS_OR_RETURN(EFI_INVALID_PARAMETER, L"NULL loader_data");
|
||||
|
||||
EFI_STATUS status;
|
||||
EFI_HANDLE *handles = NULL;
|
||||
size_t handleCount = 0;
|
||||
|
||||
status = bootsvc->LocateHandleBuffer(ByProtocol, &guid_simple_filesystem, NULL, &handleCount, &handles);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"LocateHandleBuffer");
|
||||
|
||||
for (unsigned i = 0; i < handleCount; ++i) {
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fileSystem = NULL;
|
||||
|
||||
status = bootsvc->HandleProtocol(handles[i], &guid_simple_filesystem, (void **)&fileSystem);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"HandleProtocol");
|
||||
|
||||
EFI_FILE_PROTOCOL *root = NULL;
|
||||
status = fileSystem->OpenVolume(fileSystem, &root);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"OpenVolume");
|
||||
|
||||
void *next = NULL;
|
||||
|
||||
data->kernel = (void *)KERNEL_PHYS_ADDRESS;
|
||||
status = loader_load_file(
|
||||
bootsvc,
|
||||
root,
|
||||
kernel_name,
|
||||
KERNEL_MEMTYPE,
|
||||
&data->kernel,
|
||||
&data->kernel_length,
|
||||
&next);
|
||||
if (status == EFI_NOT_FOUND)
|
||||
continue;
|
||||
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", kernel_name);
|
||||
|
||||
data->font = next;
|
||||
status = loader_load_file(
|
||||
bootsvc,
|
||||
root,
|
||||
font_name,
|
||||
KERNEL_FONT_MEMTYPE,
|
||||
&data->font,
|
||||
&data->font_length,
|
||||
&next);
|
||||
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_load_file: %s", font_name);
|
||||
|
||||
data->data = next;
|
||||
data->data_length += PAGE_SIZE; // extra page for map growth
|
||||
status = loader_alloc_pages(
|
||||
bootsvc,
|
||||
KERNEL_DATA_MEMTYPE,
|
||||
&data->data_length,
|
||||
&data->data,
|
||||
&next);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel data");
|
||||
|
||||
data->log = next;
|
||||
data->log_length = KERNEL_LOG_PAGES * PAGE_SIZE;
|
||||
status = loader_alloc_pages(
|
||||
bootsvc,
|
||||
KERNEL_LOG_MEMTYPE,
|
||||
&data->log_length,
|
||||
&data->log,
|
||||
&next);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, L"loader_alloc_pages: kernel log");
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
74
src/boot/loader.cpp
Normal file
74
src/boot/loader.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <uefi/boot_services.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "loader.h"
|
||||
#include "console.h"
|
||||
#include "elf.h"
|
||||
#include "error.h"
|
||||
#include "memory.h"
|
||||
#include "paging.h"
|
||||
|
||||
namespace boot {
|
||||
namespace loader {
|
||||
|
||||
static bool
|
||||
is_elfheader_valid(const elf::header *header)
|
||||
{
|
||||
return
|
||||
header->magic[0] == 0x7f &&
|
||||
header->magic[1] == 'E' &&
|
||||
header->magic[2] == 'L' &&
|
||||
header->magic[3] == 'F' &&
|
||||
header->word_size == elf::word_size &&
|
||||
header->endianness == elf::endianness &&
|
||||
header->os_abi == elf::os_abi &&
|
||||
header->machine == elf::machine &&
|
||||
header->header_version == elf::version;
|
||||
}
|
||||
|
||||
kernel::entrypoint
|
||||
load(
|
||||
const void *data, size_t size,
|
||||
kernel::args::header *args,
|
||||
uefi::boot_services *bs)
|
||||
{
|
||||
status_line status(L"Loading kernel ELF binary");
|
||||
const elf::header *header = reinterpret_cast<const elf::header*>(data);
|
||||
|
||||
if (size < sizeof(elf::header) || !is_elfheader_valid(header))
|
||||
error::raise(uefi::status::load_error, L"Kernel ELF not valid");
|
||||
|
||||
paging::page_table *pml4 = reinterpret_cast<paging::page_table*>(args->pml4);
|
||||
|
||||
for (int i = 0; i < header->ph_num; ++i) {
|
||||
ptrdiff_t offset = header->ph_offset + i * header->ph_entsize;
|
||||
const elf::program_header *pheader =
|
||||
offset_ptr<elf::program_header>(data, offset);
|
||||
|
||||
if (pheader->type != elf::PT_LOAD)
|
||||
continue;
|
||||
|
||||
size_t num_pages = memory::bytes_to_pages(pheader->mem_size);
|
||||
void *pages = nullptr;
|
||||
|
||||
try_or_raise(
|
||||
bs->allocate_pages(uefi::allocate_type::any_pages,
|
||||
memory::kernel_type, num_pages, &pages),
|
||||
L"Failed allocating space for kernel code");
|
||||
|
||||
void *data_start = offset_ptr<void>(data, pheader->offset);
|
||||
bs->copy_mem(pages, data_start, pheader->mem_size);
|
||||
|
||||
console::print(L" section %d phys: 0x%lx\r\n", i, pages);
|
||||
console::print(L" section %d virt: 0x%lx\r\n", i, pheader->vaddr);
|
||||
|
||||
// TODO: set appropriate RWX permissions
|
||||
paging::map_pages(pml4, args, reinterpret_cast<uintptr_t>(pages), pheader->vaddr, pheader->mem_size);
|
||||
}
|
||||
|
||||
console::print(L" entrypoint: 0x%lx\r\n", header->entrypoint);
|
||||
return reinterpret_cast<kernel::entrypoint>(header->entrypoint);
|
||||
}
|
||||
|
||||
} // namespace loader
|
||||
} // namespace boot
|
||||
@@ -1,65 +1,23 @@
|
||||
/// \file loader.h
|
||||
/// Definitions for loading the kernel into memory
|
||||
#pragma once
|
||||
#include <efi.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define PAGE_SIZE 0x1000
|
||||
#include <uefi/boot_services.h>
|
||||
|
||||
#ifndef KERNEL_PHYS_ADDRESS
|
||||
#define KERNEL_PHYS_ADDRESS 0x100000
|
||||
#endif
|
||||
#include "kernel_args.h"
|
||||
|
||||
#ifndef KERNEL_VIRT_ADDRESS
|
||||
#define KERNEL_VIRT_ADDRESS 0xFFFF800000000000
|
||||
#endif
|
||||
namespace boot {
|
||||
namespace loader {
|
||||
|
||||
#ifndef VIRTUAL_OFFSET
|
||||
#define VIRTUAL_OFFSET 0xf00000000
|
||||
#endif
|
||||
/// Parse and load an ELF file in memory into a loaded image.
|
||||
/// \arg data The start of the ELF file in memory
|
||||
/// \arg size The size of the ELF file in memory
|
||||
/// \arg args The kernel args, used for modifying page tables
|
||||
/// \returns A descriptor defining the loaded image
|
||||
kernel::entrypoint load(
|
||||
const void *data, size_t size,
|
||||
kernel::args::header *args,
|
||||
uefi::boot_services *bs);
|
||||
|
||||
#ifndef KERNEL_MEMTYPE
|
||||
#define KERNEL_MEMTYPE 0x80000000
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_FONT_MEMTYPE
|
||||
#define KERNEL_FONT_MEMTYPE 0x80000001
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_DATA_MEMTYPE
|
||||
#define KERNEL_DATA_MEMTYPE 0x80000002
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_LOG_MEMTYPE
|
||||
#define KERNEL_LOG_MEMTYPE 0x80000003
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_LOG_PAGES
|
||||
#define KERNEL_LOG_PAGES 4
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_PT_MEMTYPE
|
||||
#define KERNEL_PT_MEMTYPE 0x80000004
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_FILENAME
|
||||
#define KERNEL_FILENAME L"kernel.bin"
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_FONT
|
||||
#define KERNEL_FONT L"screenfont.psf"
|
||||
#endif
|
||||
|
||||
struct loader_data {
|
||||
void *kernel;
|
||||
size_t kernel_length;
|
||||
|
||||
void *font;
|
||||
size_t font_length;
|
||||
|
||||
void *data;
|
||||
size_t data_length;
|
||||
|
||||
void *log;
|
||||
size_t log_length;
|
||||
};
|
||||
|
||||
EFI_STATUS loader_load_kernel(EFI_BOOT_SERVICES *bootsvc, struct loader_data *data);
|
||||
} // namespace loader
|
||||
} // namespace boot
|
||||
|
||||
180
src/boot/main.c
180
src/boot/main.c
@@ -1,180 +0,0 @@
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <stdalign.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "console.h"
|
||||
#include "guids.h"
|
||||
#include "kernel_data.h"
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "utility.h"
|
||||
|
||||
#ifndef GIT_VERSION_WIDE
|
||||
#define GIT_VERSION_WIDE L"no version"
|
||||
#endif
|
||||
|
||||
#define KERNEL_HEADER_MAGIC 0x600db007
|
||||
#define KERNEL_HEADER_VERSION 1
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct kernel_header {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint16_t length;
|
||||
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint16_t patch;
|
||||
uint32_t gitsha;
|
||||
|
||||
void *entrypoint;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
EFI_STATUS
|
||||
efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_BOOT_SERVICES *bootsvc = system_table->BootServices;
|
||||
EFI_RUNTIME_SERVICES *runsvc = system_table->RuntimeServices;
|
||||
|
||||
// When checking console initialization, use CHECK_EFI_STATUS_OR_RETURN
|
||||
// because we can't be sure if the console was fully set up
|
||||
status = con_initialize(system_table, GIT_VERSION_WIDE);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "con_initialize");
|
||||
// From here on out, we can use CHECK_EFI_STATUS_OR_FAIL instead
|
||||
|
||||
memory_init_pointer_fixup(bootsvc, runsvc);
|
||||
|
||||
// Find ACPI tables. Ignore ACPI 1.0 if a 2.0 table is found.
|
||||
//
|
||||
void *acpi_table = NULL;
|
||||
for (size_t i=0; i<system_table->NumberOfTableEntries; ++i) {
|
||||
EFI_CONFIGURATION_TABLE *efi_table = &system_table->ConfigurationTable[i];
|
||||
if (is_guid(&efi_table->VendorGuid, &guid_acpi2)) {
|
||||
acpi_table = efi_table->VendorTable;
|
||||
break;
|
||||
} else if (is_guid(&efi_table->VendorGuid, &guid_acpi1)) {
|
||||
// Mark a v1 table with the LSB high
|
||||
acpi_table = (void *)((intptr_t)efi_table->VendorTable | 0x1);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute necessary number of data pages
|
||||
//
|
||||
size_t data_length = 0;
|
||||
status = memory_get_map_length(bootsvc, &data_length);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
|
||||
size_t header_size = sizeof(struct popcorn_data);
|
||||
const size_t header_align = alignof(struct popcorn_data);
|
||||
if (header_size % header_align)
|
||||
header_size += header_align - (header_size % header_align);
|
||||
|
||||
data_length += header_size;
|
||||
|
||||
|
||||
// Load the kernel image from disk and check it
|
||||
//
|
||||
void *kernel_image = NULL, *kernel_data = NULL;
|
||||
uint64_t kernel_length = 0;
|
||||
con_printf(L"Loading kernel into memory...\r\n");
|
||||
|
||||
struct loader_data load;
|
||||
load.data_length = data_length;
|
||||
status = loader_load_kernel(bootsvc, &load);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
|
||||
con_printf(L" %u image bytes at 0x%x\r\n", load.kernel_length, load.kernel);
|
||||
con_printf(L" %u font bytes at 0x%x\r\n", load.font_length, load.font);
|
||||
con_printf(L" %u data bytes at 0x%x\r\n", load.data_length, load.data);
|
||||
con_printf(L" %u log bytes at 0x%x\r\n", load.log_length, load.log);
|
||||
|
||||
struct kernel_header *version = (struct kernel_header *)load.kernel;
|
||||
if (version->magic != KERNEL_HEADER_MAGIC) {
|
||||
con_printf(L" bad magic %x\r\n", version->magic);
|
||||
CHECK_EFI_STATUS_OR_FAIL(EFI_CRC_ERROR);
|
||||
}
|
||||
|
||||
con_printf(L" Kernel version %d.%d.%d %x%s\r\n",
|
||||
version->major, version->minor, version->patch, version->gitsha & 0x0fffffff,
|
||||
version->gitsha & 0xf0000000 ? "*" : "");
|
||||
con_printf(L" Entrypoint 0x%x\r\n", version->entrypoint);
|
||||
|
||||
void (*kernel_main)() = version->entrypoint;
|
||||
memory_mark_pointer_fixup((void **)&kernel_main);
|
||||
|
||||
// Set up the kernel data pages to pass to the kernel
|
||||
//
|
||||
struct popcorn_data *data_header = (struct popcorn_data *)load.data;
|
||||
memory_mark_pointer_fixup((void **)&data_header);
|
||||
|
||||
data_header->magic = DATA_HEADER_MAGIC;
|
||||
data_header->version = DATA_HEADER_VERSION;
|
||||
data_header->length = sizeof(struct popcorn_data);
|
||||
|
||||
data_header->flags = 0;
|
||||
|
||||
data_header->font = load.font;
|
||||
data_header->font_length = load.font_length;
|
||||
memory_mark_pointer_fixup((void **)&data_header->font);
|
||||
|
||||
data_header->data = load.data;
|
||||
data_header->data_length = load.data_length;
|
||||
memory_mark_pointer_fixup((void **)&data_header->data);
|
||||
|
||||
data_header->log = load.log;
|
||||
data_header->log_length = load.log_length;
|
||||
memory_mark_pointer_fixup((void **)&data_header->log);
|
||||
|
||||
data_header->memory_map = (EFI_MEMORY_DESCRIPTOR *)(data_header + 1);
|
||||
memory_mark_pointer_fixup((void **)&data_header->memory_map);
|
||||
|
||||
data_header->runtime = runsvc;
|
||||
memory_mark_pointer_fixup((void **)&data_header->runtime);
|
||||
|
||||
data_header->acpi_table = acpi_table;
|
||||
memory_mark_pointer_fixup((void **)&data_header->acpi_table);
|
||||
|
||||
data_header->_reserved0 = 0;
|
||||
data_header->_reserved1 = 0;
|
||||
|
||||
// Figure out the framebuffer (if any) and add that to the data header
|
||||
//
|
||||
status = con_get_framebuffer(
|
||||
bootsvc,
|
||||
&data_header->frame_buffer,
|
||||
&data_header->frame_buffer_size,
|
||||
&data_header->hres,
|
||||
&data_header->vres,
|
||||
&data_header->rmask,
|
||||
&data_header->gmask,
|
||||
&data_header->bmask);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
memory_mark_pointer_fixup((void **)&data_header->frame_buffer);
|
||||
|
||||
// Save the memory map and tell the firmware we're taking control.
|
||||
//
|
||||
struct memory_map map;
|
||||
map.entries = data_header->memory_map;
|
||||
map.length = (load.data_length - header_size);
|
||||
|
||||
status = memory_get_map(bootsvc, &map);
|
||||
CHECK_EFI_STATUS_OR_FAIL(status);
|
||||
|
||||
data_header->memory_map_length = map.length;
|
||||
data_header->memory_map_desc_size = map.size;
|
||||
|
||||
// bootsvc->Stall(5000000);
|
||||
|
||||
status = bootsvc->ExitBootServices(image_handle, map.key);
|
||||
CHECK_EFI_STATUS_OR_ASSERT(status, 0);
|
||||
|
||||
memory_virtualize(runsvc, &map);
|
||||
|
||||
// Hand control to the kernel
|
||||
//
|
||||
kernel_main(data_header);
|
||||
return EFI_LOAD_ERROR;
|
||||
}
|
||||
165
src/boot/main.cpp
Normal file
165
src/boot/main.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#include <uefi/types.h>
|
||||
#include <uefi/guid.h>
|
||||
#include <uefi/tables.h>
|
||||
#include <uefi/protos/simple_text_output.h>
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
#include "fs.h"
|
||||
#include "hardware.h"
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "paging.h"
|
||||
|
||||
#include "kernel_args.h"
|
||||
|
||||
namespace kernel {
|
||||
#include "kernel_memory.h"
|
||||
}
|
||||
|
||||
namespace boot {
|
||||
|
||||
constexpr int max_modules = 10; // Max modules to allocate room for
|
||||
|
||||
/// Change a pointer to point to the higher-half linear-offset version
|
||||
/// of the address it points to.
|
||||
template <typename T>
|
||||
void change_pointer(T *&pointer)
|
||||
{
|
||||
pointer = offset_ptr<T>(pointer, kernel::memory::page_offset);
|
||||
}
|
||||
|
||||
/// Allocate space for kernel args. Allocates enough space from pool
|
||||
/// memory for the args header and `max_modules` module headers.
|
||||
kernel::args::header *
|
||||
allocate_args_structure(
|
||||
uefi::boot_services *bs,
|
||||
size_t max_modules)
|
||||
{
|
||||
status_line status(L"Setting up kernel args memory");
|
||||
|
||||
kernel::args::header *args = nullptr;
|
||||
|
||||
size_t args_size =
|
||||
sizeof(kernel::args::header) + // The header itself
|
||||
max_modules * sizeof(kernel::args::module); // The module structures
|
||||
|
||||
try_or_raise(
|
||||
bs->allocate_pool(memory::args_type, args_size,
|
||||
reinterpret_cast<void**>(&args)),
|
||||
L"Could not allocate argument memory");
|
||||
|
||||
bs->set_mem(args, args_size, 0);
|
||||
|
||||
args->modules =
|
||||
reinterpret_cast<kernel::args::module*>(args + 1);
|
||||
args->num_modules = 0;
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// Load a file from disk into memory. Also adds an entry to the kernel
|
||||
/// args module headers pointing at the loaded data.
|
||||
/// \arg disk The opened UEFI filesystem to load from
|
||||
/// \arg args The kernel args header to update with module information
|
||||
/// \arg name Name of the module (informational only)
|
||||
/// \arg path Path on `disk` of the file to load
|
||||
/// \arg type Type specifier of this module (eg, initrd or kernel)
|
||||
kernel::args::module *
|
||||
load_module(
|
||||
fs::file &disk,
|
||||
kernel::args::header *args,
|
||||
const wchar_t *name,
|
||||
const wchar_t *path,
|
||||
kernel::args::mod_type type)
|
||||
{
|
||||
status_line status(L"Loading module", name);
|
||||
|
||||
fs::file file = disk.open(path);
|
||||
kernel::args::module &module = args->modules[args->num_modules++];
|
||||
module.type = type;
|
||||
module.location = file.load(&module.size, memory::module_type);
|
||||
|
||||
console::print(L" Loaded at: 0x%lx, %d bytes\r\n", module.location, module.size);
|
||||
return &module;
|
||||
}
|
||||
|
||||
/// The main procedure for the portion of the loader that runs while
|
||||
/// UEFI is still in control of the machine. (ie, while the loader still
|
||||
/// has access to boot services.
|
||||
kernel::args::header *
|
||||
bootloader_main_uefi(
|
||||
uefi::handle image,
|
||||
uefi::system_table *st,
|
||||
console &con,
|
||||
kernel::entrypoint *kentry)
|
||||
{
|
||||
error::uefi_handler handler(con);
|
||||
status_line status(L"Performing UEFI pre-boot");
|
||||
|
||||
uefi::boot_services *bs = st->boot_services;
|
||||
uefi::runtime_services *rs = st->runtime_services;
|
||||
memory::init_pointer_fixup(bs, rs);
|
||||
|
||||
kernel::args::header *args =
|
||||
allocate_args_structure(bs, max_modules);
|
||||
|
||||
args->magic = kernel::args::magic;
|
||||
args->version = kernel::args::version;
|
||||
args->runtime_services = rs;
|
||||
args->acpi_table = hw::find_acpi_table(st);
|
||||
|
||||
memory::mark_pointer_fixup(&args->runtime_services);
|
||||
|
||||
fs::file disk = fs::get_boot_volume(image, bs);
|
||||
load_module(disk, args, L"initrd", L"initrd.img", kernel::args::mod_type::initrd);
|
||||
|
||||
kernel::args::module *kernel =
|
||||
load_module(disk, args, L"kernel", L"jsix.elf", kernel::args::mod_type::kernel);
|
||||
|
||||
paging::allocate_tables(args, bs);
|
||||
*kentry = loader::load(kernel->location, kernel->size, args, bs);
|
||||
|
||||
for (unsigned i = 0; i < args->num_modules; ++i) {
|
||||
kernel::args::module &mod = args->modules[i];
|
||||
change_pointer(mod.location);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
} // namespace boot
|
||||
|
||||
/// The UEFI entrypoint for the loader.
|
||||
extern "C" uefi::status
|
||||
efi_main(uefi::handle image_handle, uefi::system_table *st)
|
||||
{
|
||||
using namespace boot;
|
||||
|
||||
error::cpu_assert_handler handler;
|
||||
console con(st->boot_services, st->con_out);
|
||||
|
||||
kernel::entrypoint kentry = nullptr;
|
||||
kernel::args::header *args =
|
||||
bootloader_main_uefi(image_handle, st, con, &kentry);
|
||||
|
||||
memory::efi_mem_map map =
|
||||
memory::build_kernel_mem_map(args, st->boot_services);
|
||||
|
||||
try_or_raise(
|
||||
st->boot_services->exit_boot_services(image_handle, map.key),
|
||||
L"Failed to exit boot services");
|
||||
|
||||
memory::virtualize(args->pml4, map, st->runtime_services);
|
||||
change_pointer(args->pml4);
|
||||
hw::setup_cr4();
|
||||
|
||||
kentry(args);
|
||||
debug_break();
|
||||
return uefi::status::unsupported;
|
||||
}
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define INCREMENT_DESC(p, b) (EFI_MEMORY_DESCRIPTOR*)(((uint8_t*)(p))+(b))
|
||||
|
||||
size_t fixup_pointer_index = 0;
|
||||
void **fixup_pointers[64];
|
||||
uint64_t *new_pml4 = 0;
|
||||
|
||||
const CHAR16 *memory_type_names[] = {
|
||||
L"EfiReservedMemoryType",
|
||||
L"EfiLoaderCode",
|
||||
L"EfiLoaderData",
|
||||
L"EfiBootServicesCode",
|
||||
L"EfiBootServicesData",
|
||||
L"EfiRuntimeServicesCode",
|
||||
L"EfiRuntimeServicesData",
|
||||
L"EfiConventionalMemory",
|
||||
L"EfiUnusableMemory",
|
||||
L"EfiACPIReclaimMemory",
|
||||
L"EfiACPIMemoryNVS",
|
||||
L"EfiMemoryMappedIO",
|
||||
L"EfiMemoryMappedIOPortSpace",
|
||||
L"EfiPalCode",
|
||||
L"EfiPersistentMemory",
|
||||
};
|
||||
|
||||
static const CHAR16 *
|
||||
memory_type_name(UINT32 value)
|
||||
{
|
||||
if (value >= (sizeof(memory_type_names) / sizeof(CHAR16 *))) {
|
||||
if (value == KERNEL_DATA_MEMTYPE) return L"Kernel Data";
|
||||
else if (value == KERNEL_MEMTYPE) return L"Kernel Image";
|
||||
else return L"Bad Type Value";
|
||||
}
|
||||
return memory_type_names[value];
|
||||
}
|
||||
|
||||
void EFIAPI
|
||||
memory_update_marked_addresses(EFI_EVENT UNUSED *event, void *context)
|
||||
{
|
||||
EFI_RUNTIME_SERVICES *runsvc = (EFI_RUNTIME_SERVICES*)context;
|
||||
for (size_t i = 0; i < fixup_pointer_index; ++i) {
|
||||
if (fixup_pointers[i])
|
||||
runsvc->ConvertPointer(0, fixup_pointers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
EFI_EVENT event;
|
||||
|
||||
status = bootsvc->CreateEvent(
|
||||
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
|
||||
TPL_CALLBACK,
|
||||
(EFI_EVENT_NOTIFY)&memory_update_marked_addresses,
|
||||
runsvc,
|
||||
&event);
|
||||
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to initialize pointer update event.");
|
||||
|
||||
// Reserve a page for our replacement PML4
|
||||
EFI_PHYSICAL_ADDRESS addr = 0;
|
||||
status = bootsvc->AllocatePages(AllocateAnyPages, EfiLoaderData, 4, &addr);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to allocate PML4 page.");
|
||||
|
||||
new_pml4 = (uint64_t *)addr;
|
||||
}
|
||||
|
||||
void
|
||||
memory_mark_pointer_fixup(void **p)
|
||||
{
|
||||
if (fixup_pointer_index == 0) {
|
||||
const size_t count = sizeof(fixup_pointers) / sizeof(void*);
|
||||
for (size_t i = 0; i < count; ++i) fixup_pointers[i] = 0;
|
||||
}
|
||||
fixup_pointers[fixup_pointer_index++] = p;
|
||||
}
|
||||
|
||||
void
|
||||
copy_desc(EFI_MEMORY_DESCRIPTOR *src, EFI_MEMORY_DESCRIPTOR *dst, size_t len)
|
||||
{
|
||||
uint8_t *srcb = (uint8_t *)src;
|
||||
uint8_t *dstb = (uint8_t *)dst;
|
||||
uint8_t *endb = srcb + len;
|
||||
while (srcb < endb)
|
||||
*dstb++ = *srcb++;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size)
|
||||
{
|
||||
if (size == NULL)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
EFI_STATUS status;
|
||||
size_t key, desc_size;
|
||||
uint32_t desc_version;
|
||||
*size = 0;
|
||||
status = bootsvc->GetMemoryMap(size, 0, &key, &desc_size, &desc_version);
|
||||
if (status != EFI_BUFFER_TOO_SMALL) {
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to get memory map size");
|
||||
}
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map)
|
||||
{
|
||||
EFI_STATUS status;
|
||||
|
||||
if (map == NULL)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
size_t needs_size = 0;
|
||||
status = memory_get_map_length(bootsvc, &needs_size);
|
||||
if (EFI_ERROR(status)) return status;
|
||||
|
||||
if (map->length < needs_size)
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
|
||||
status = bootsvc->GetMemoryMap(&map->length, map->entries, &map->key, &map->size, &map->version);
|
||||
CHECK_EFI_STATUS_OR_RETURN(status, "Failed to load memory map");
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
memory_dump_map(struct memory_map *map)
|
||||
{
|
||||
if (map == NULL)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
const size_t count = map->length / map->size;
|
||||
|
||||
con_printf(L"Memory map:\n");
|
||||
con_printf(L"\t Descriptor Count: %d (%d bytes)\n", count, map->length);
|
||||
con_printf(L"\t Descriptor Size: %d bytes\n", map->size);
|
||||
con_printf(L"\t Type offset: %d\n\n", offsetof(EFI_MEMORY_DESCRIPTOR, Type));
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
|
||||
EFI_MEMORY_DESCRIPTOR *d = map->entries;
|
||||
while (d < end) {
|
||||
int runtime = (d->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME;
|
||||
con_printf(L"%s%s ", memory_type_name(d->Type), runtime ? L"*" : L" ");
|
||||
con_printf(L"%lx ", d->PhysicalStart);
|
||||
con_printf(L"%lx ", d->VirtualStart);
|
||||
con_printf(L"[%4d]\n", d->NumberOfPages);
|
||||
|
||||
d = INCREMENT_DESC(d, map->size);
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map)
|
||||
{
|
||||
memory_mark_pointer_fixup((void **)&runsvc);
|
||||
memory_mark_pointer_fixup((void **)&map);
|
||||
|
||||
// Get the pointer to the start of PML4
|
||||
uint64_t* cr3 = 0;
|
||||
__asm__ __volatile__ ( "mov %%cr3, %0" : "=r" (cr3) );
|
||||
|
||||
// PML4 is indexed with bits 39:47 of the virtual address
|
||||
uint64_t offset = (KERNEL_VIRT_ADDRESS >> 39) & 0x1ff;
|
||||
|
||||
// Double map the lower half pages that are present into the higher half
|
||||
for (unsigned i = 0; i < offset; ++i) {
|
||||
if (cr3[i] & 0x1)
|
||||
new_pml4[i] = new_pml4[offset+i] = cr3[i];
|
||||
else
|
||||
new_pml4[i] = new_pml4[offset+i] = 0;
|
||||
}
|
||||
|
||||
// Write our new PML4 pointer back to CR3
|
||||
__asm__ __volatile__ ( "mov %0, %%cr3" :: "r" (new_pml4) );
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR *end = INCREMENT_DESC(map->entries, map->length);
|
||||
EFI_MEMORY_DESCRIPTOR *d = map->entries;
|
||||
while (d < end) {
|
||||
switch (d->Type) {
|
||||
case KERNEL_MEMTYPE:
|
||||
case KERNEL_FONT_MEMTYPE:
|
||||
case KERNEL_DATA_MEMTYPE:
|
||||
case KERNEL_LOG_MEMTYPE:
|
||||
d->Attribute |= EFI_MEMORY_RUNTIME;
|
||||
|
||||
default:
|
||||
if (d->Attribute & EFI_MEMORY_RUNTIME) {
|
||||
d->VirtualStart = d->PhysicalStart + KERNEL_VIRT_ADDRESS;
|
||||
}
|
||||
}
|
||||
d = INCREMENT_DESC(d, map->size);
|
||||
}
|
||||
|
||||
runsvc->SetVirtualAddressMap(map->length, map->size, map->version, map->entries);
|
||||
}
|
||||
275
src/boot/memory.cpp
Normal file
275
src/boot/memory.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
#include <stddef.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "kernel_memory.h"
|
||||
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
#include "memory.h"
|
||||
#include "paging.h"
|
||||
|
||||
namespace boot {
|
||||
namespace memory {
|
||||
|
||||
using mem_entry = kernel::args::mem_entry;
|
||||
using mem_type = kernel::args::mem_type;
|
||||
|
||||
size_t fixup_pointer_index = 0;
|
||||
void **fixup_pointers[64];
|
||||
|
||||
static const wchar_t *memory_type_names[] = {
|
||||
L"reserved memory type",
|
||||
L"loader code",
|
||||
L"loader data",
|
||||
L"boot services code",
|
||||
L"boot services data",
|
||||
L"runtime services code",
|
||||
L"runtime services data",
|
||||
L"conventional memory",
|
||||
L"unusable memory",
|
||||
L"acpi reclaim memory",
|
||||
L"acpi memory nvs",
|
||||
L"memory mapped io",
|
||||
L"memory mapped io port space",
|
||||
L"pal code",
|
||||
L"persistent memory"
|
||||
};
|
||||
|
||||
static const wchar_t *
|
||||
memory_type_name(uefi::memory_type t)
|
||||
{
|
||||
if (t < uefi::memory_type::max_memory_type) {
|
||||
return memory_type_names[static_cast<uint32_t>(t)];
|
||||
}
|
||||
|
||||
switch(t) {
|
||||
case args_type: return L"jsix kernel args";
|
||||
case module_type: return L"jsix bootloader module";
|
||||
case kernel_type: return L"jsix kernel code";
|
||||
case table_type: return L"jsix page tables";
|
||||
default: return L"Bad Type Value";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
update_marked_addresses(uefi::event, void *context)
|
||||
{
|
||||
uefi::runtime_services *rs =
|
||||
reinterpret_cast<uefi::runtime_services*>(context);
|
||||
|
||||
for (size_t i = 0; i < fixup_pointer_index; ++i) {
|
||||
if (fixup_pointers[i])
|
||||
rs->convert_pointer(0, fixup_pointers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs)
|
||||
{
|
||||
status_line status(L"Initializing pointer virtualization event");
|
||||
|
||||
uefi::event event;
|
||||
bs->set_mem(&fixup_pointers, sizeof(fixup_pointers), 0);
|
||||
fixup_pointer_index = 0;
|
||||
|
||||
try_or_raise(
|
||||
bs->create_event(
|
||||
uefi::evt::signal_virtual_address_change,
|
||||
uefi::tpl::callback,
|
||||
(uefi::event_notify)&update_marked_addresses,
|
||||
rs,
|
||||
&event),
|
||||
L"Error creating memory virtualization event");
|
||||
}
|
||||
|
||||
void
|
||||
mark_pointer_fixup(void **p)
|
||||
{
|
||||
fixup_pointers[fixup_pointer_index++] = p;
|
||||
}
|
||||
|
||||
bool
|
||||
can_merge(mem_entry &prev, mem_type type, uefi::memory_descriptor *next)
|
||||
{
|
||||
return
|
||||
prev.type == type &&
|
||||
prev.start + (page_size * prev.pages) == next->physical_start &&
|
||||
prev.attr == (next->attribute & 0xffffffff);
|
||||
}
|
||||
|
||||
void
|
||||
get_uefi_mappings(efi_mem_map *map, bool allocate, uefi::boot_services *bs)
|
||||
{
|
||||
status_line(L"Getting UEFI memory map");
|
||||
|
||||
uefi::status status = bs->get_memory_map(
|
||||
&map->length, nullptr, &map->key, &map->size, &map->version);
|
||||
|
||||
if (status != uefi::status::buffer_too_small)
|
||||
error::raise(status, L"Error getting memory map size");
|
||||
|
||||
if (allocate) {
|
||||
map->length += 10*map->size;
|
||||
|
||||
try_or_raise(
|
||||
bs->allocate_pool(
|
||||
uefi::memory_type::loader_data, map->length,
|
||||
reinterpret_cast<void**>(&map->entries)),
|
||||
L"Allocating space for memory map");
|
||||
|
||||
try_or_raise(
|
||||
bs->get_memory_map(&map->length, map->entries, &map->key, &map->size, &map->version),
|
||||
L"Getting UEFI memory map");
|
||||
}
|
||||
}
|
||||
|
||||
efi_mem_map
|
||||
build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs)
|
||||
{
|
||||
status_line(L"Creating kernel memory map");
|
||||
|
||||
efi_mem_map efi_map;
|
||||
get_uefi_mappings(&efi_map, false, bs);
|
||||
|
||||
size_t map_size = efi_map.num_entries() * sizeof(mem_entry);
|
||||
|
||||
kernel::args::mem_entry *kernel_map = nullptr;
|
||||
try_or_raise(
|
||||
bs->allocate_pages(
|
||||
uefi::allocate_type::any_pages,
|
||||
module_type,
|
||||
bytes_to_pages(map_size),
|
||||
reinterpret_cast<void**>(&kernel_map)),
|
||||
L"Error allocating kernel memory map module space");
|
||||
|
||||
bs->set_mem(kernel_map, map_size, 0);
|
||||
get_uefi_mappings(&efi_map, true, bs);
|
||||
|
||||
size_t i = 0;
|
||||
bool first = true;
|
||||
for (auto desc : efi_map) {
|
||||
/*
|
||||
console::print(L" Range %lx (%lx) %x(%s) [%lu]\r\n",
|
||||
desc->physical_start, desc->attribute, desc->type, memory_type_name(desc->type), desc->number_of_pages);
|
||||
*/
|
||||
|
||||
mem_type type;
|
||||
switch (desc->type) {
|
||||
case uefi::memory_type::reserved:
|
||||
case uefi::memory_type::unusable_memory:
|
||||
case uefi::memory_type::acpi_memory_nvs:
|
||||
case uefi::memory_type::pal_code:
|
||||
continue;
|
||||
|
||||
case uefi::memory_type::loader_code:
|
||||
case uefi::memory_type::loader_data:
|
||||
case uefi::memory_type::boot_services_code:
|
||||
case uefi::memory_type::boot_services_data:
|
||||
case uefi::memory_type::conventional_memory:
|
||||
type = mem_type::free;
|
||||
break;
|
||||
|
||||
case uefi::memory_type::runtime_services_code:
|
||||
case uefi::memory_type::runtime_services_data:
|
||||
type = mem_type::uefi_runtime;
|
||||
break;
|
||||
|
||||
case uefi::memory_type::acpi_reclaim_memory:
|
||||
type = mem_type::acpi;
|
||||
break;
|
||||
|
||||
case uefi::memory_type::memory_mapped_io:
|
||||
case uefi::memory_type::memory_mapped_io_port_space:
|
||||
type = mem_type::mmio;
|
||||
break;
|
||||
|
||||
case uefi::memory_type::persistent_memory:
|
||||
type = mem_type::persistent;
|
||||
break;
|
||||
|
||||
case args_type:
|
||||
type = mem_type::args;
|
||||
break;
|
||||
|
||||
case module_type:
|
||||
type = mem_type::module;
|
||||
break;
|
||||
|
||||
case kernel_type:
|
||||
type = mem_type::kernel;
|
||||
break;
|
||||
|
||||
case table_type:
|
||||
type = mem_type::table;
|
||||
break;
|
||||
|
||||
default:
|
||||
error::raise(
|
||||
uefi::status::invalid_parameter,
|
||||
L"Got an unexpected memory type from UEFI memory map");
|
||||
}
|
||||
|
||||
// TODO: validate uefi's map is sorted
|
||||
if (first) {
|
||||
first = false;
|
||||
kernel_map[i].start = desc->physical_start;
|
||||
kernel_map[i].pages = desc->number_of_pages;
|
||||
kernel_map[i].type = type;
|
||||
kernel_map[i].attr = (desc->attribute & 0xffffffff);
|
||||
continue;
|
||||
}
|
||||
|
||||
mem_entry &prev = kernel_map[i];
|
||||
if (can_merge(prev, type, desc)) {
|
||||
prev.pages += desc->number_of_pages;
|
||||
} else {
|
||||
mem_entry &next = kernel_map[++i];
|
||||
next.start = desc->physical_start;
|
||||
next.pages = desc->number_of_pages;
|
||||
next.type = type;
|
||||
next.attr = (desc->attribute & 0xffffffff);
|
||||
}
|
||||
}
|
||||
|
||||
// Give just the actually-set entries in the header
|
||||
args->mem_map = kernel_map;
|
||||
args->num_map_entries = i;
|
||||
|
||||
// But pass the entire allocated area in a module as well
|
||||
kernel::args::module &module = args->modules[args->num_modules++];
|
||||
module.location = reinterpret_cast<void*>(kernel_map);
|
||||
module.size = map_size;
|
||||
module.type = kernel::args::mod_type::memory_map;
|
||||
|
||||
/*
|
||||
for (size_t i = 0; i<map.num_entries(); ++i) {
|
||||
mem_entry &ent = kernel_map[i];
|
||||
console::print(L" Range %lx (%x) %d [%lu]\r\n",
|
||||
ent.start, ent.attr, ent.type, ent.pages);
|
||||
}
|
||||
*/
|
||||
|
||||
return efi_map;
|
||||
}
|
||||
|
||||
void
|
||||
virtualize(void *pml4, efi_mem_map &map, uefi::runtime_services *rs)
|
||||
{
|
||||
paging::add_current_mappings(reinterpret_cast<paging::page_table*>(pml4));
|
||||
|
||||
for (auto desc : map)
|
||||
desc->virtual_start = desc->physical_start + ::memory::page_offset;
|
||||
|
||||
// Write our new PML4 pointer to CR3
|
||||
asm volatile ( "mov %0, %%cr3" :: "r" (pml4) );
|
||||
__sync_synchronize();
|
||||
|
||||
try_or_raise(
|
||||
rs->set_virtual_address_map(
|
||||
map.length, map.size, map.version, map.entries),
|
||||
L"Error setting virtual address map");
|
||||
}
|
||||
|
||||
|
||||
} // namespace boot
|
||||
} // namespace memory
|
||||
@@ -1,19 +1,97 @@
|
||||
/// \file memory.h
|
||||
/// Memory-related constants and functions.
|
||||
#pragma once
|
||||
#include <efi.h>
|
||||
#include <uefi/boot_services.h>
|
||||
#include <uefi/runtime_services.h>
|
||||
#include <stdint.h>
|
||||
#include "kernel_args.h"
|
||||
#include "pointer_manipulation.h"
|
||||
|
||||
struct memory_map {
|
||||
size_t length;
|
||||
size_t size;
|
||||
size_t key;
|
||||
uint32_t version;
|
||||
EFI_MEMORY_DESCRIPTOR *entries;
|
||||
namespace boot {
|
||||
namespace memory {
|
||||
|
||||
/// UEFI specifies that pages are always 4 KiB.
|
||||
constexpr size_t page_size = 0x1000;
|
||||
|
||||
/// Get the number of pages needed to hold `bytes` bytes
|
||||
inline constexpr size_t bytes_to_pages(size_t bytes) {
|
||||
return ((bytes - 1) / page_size) + 1;
|
||||
}
|
||||
|
||||
/// \defgroup memory_types
|
||||
/// Custom UEFI memory type values used for data being passed to the kernel
|
||||
/// @{
|
||||
|
||||
/// Memory containing the kernel args structure
|
||||
constexpr uefi::memory_type args_type =
|
||||
static_cast<uefi::memory_type>(0x80000000);
|
||||
|
||||
/// Memory containing any loaded modules to be passed to the kernel
|
||||
constexpr uefi::memory_type module_type =
|
||||
static_cast<uefi::memory_type>(0x80000001);
|
||||
|
||||
/// Memory containing loaded kernel code and data sections
|
||||
constexpr uefi::memory_type kernel_type =
|
||||
static_cast<uefi::memory_type>(0x80000002);
|
||||
|
||||
/// Memory containing page tables set up by the loader
|
||||
constexpr uefi::memory_type table_type =
|
||||
static_cast<uefi::memory_type>(0x80000003);
|
||||
|
||||
/// @}
|
||||
|
||||
/// \defgroup pointer_fixup
|
||||
/// Memory virtualization pointer fixup functions. Handles changing affected pointers
|
||||
/// when calling UEFI's `set_virtual_address_map` function to change the location of
|
||||
/// runtime services in virtual memory.
|
||||
/// @{
|
||||
|
||||
/// Set up the pointer fixup UEFI events. This registers the necessary callbacks for
|
||||
/// runtime services to call when `set_virtual_address_map` is called.
|
||||
void init_pointer_fixup(uefi::boot_services *bs, uefi::runtime_services *rs);
|
||||
|
||||
/// Mark a given pointer as needing to be updated when doing pointer fixup.
|
||||
void mark_pointer_fixup(void **p);
|
||||
|
||||
/// @}
|
||||
|
||||
/// Struct that represents UEFI's memory map. Contains a pointer to the map data
|
||||
/// as well as the data on how to read it.
|
||||
struct efi_mem_map
|
||||
{
|
||||
using desc = uefi::memory_descriptor;
|
||||
using iterator = offset_iterator<desc>;
|
||||
|
||||
size_t length; ///< Total length of the map data
|
||||
size_t size; ///< Size of an entry in the array
|
||||
size_t key; ///< Key for detecting changes
|
||||
uint32_t version; ///< Version of the `memory_descriptor` struct
|
||||
desc *entries; ///< The array of UEFI descriptors
|
||||
|
||||
efi_mem_map() : length(0), size(0), key(0), version(0), entries(nullptr) {}
|
||||
|
||||
/// Get the count of entries in the array
|
||||
inline size_t num_entries() const { return length / size; }
|
||||
|
||||
/// Return an iterator to the beginning of the array
|
||||
iterator begin() { return iterator(entries, size); }
|
||||
|
||||
/// Return an iterator to the end of the array
|
||||
iterator end() { return offset_ptr<desc>(entries, length); }
|
||||
};
|
||||
|
||||
EFI_STATUS memory_init_pointer_fixup(EFI_BOOT_SERVICES *bootsvc, EFI_RUNTIME_SERVICES *runsvc);
|
||||
void memory_mark_pointer_fixup(void **p);
|
||||
/// Add the kernel's memory map as a module to the kernel args.
|
||||
/// \returns The uefi memory map used to build the kernel map
|
||||
efi_mem_map build_kernel_mem_map(kernel::args::header *args, uefi::boot_services *bs);
|
||||
|
||||
EFI_STATUS memory_get_map_length(EFI_BOOT_SERVICES *bootsvc, size_t *size);
|
||||
EFI_STATUS memory_get_map(EFI_BOOT_SERVICES *bootsvc, struct memory_map *map);
|
||||
EFI_STATUS memory_dump_map(struct memory_map *map);
|
||||
/// Activate the given memory mappings. Sets the given page tables live as well
|
||||
/// as informs UEFI runtime services of the new mappings.
|
||||
/// \arg pml4 The root page table for the new mappings
|
||||
/// \arg map The UEFI memory map, used to update runtime services
|
||||
void virtualize(
|
||||
void *pml4,
|
||||
efi_mem_map &map,
|
||||
uefi::runtime_services *rs);
|
||||
|
||||
void memory_virtualize(EFI_RUNTIME_SERVICES *runsvc, struct memory_map *map);
|
||||
} // namespace boot
|
||||
} // namespace memory
|
||||
|
||||
234
src/boot/paging.cpp
Normal file
234
src/boot/paging.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "kernel_memory.h"
|
||||
|
||||
#include "console.h"
|
||||
#include "error.h"
|
||||
#include "loader.h"
|
||||
#include "memory.h"
|
||||
#include "paging.h"
|
||||
#include "pointer_manipulation.h"
|
||||
|
||||
namespace boot {
|
||||
namespace paging {
|
||||
|
||||
using memory::page_size;
|
||||
|
||||
// Flags: 0 0 0 1 0 0 0 0 0 0 1 1 = 0x0103
|
||||
// IGN | | | | | | | | +- Present
|
||||
// | | | | | | | +--- Writeable
|
||||
// | | | | | | +----- Usermode access (supervisor only)
|
||||
// | | | | | +------- PWT (determining memory type for page)
|
||||
// | | | | +---------- PCD (determining memory type for page)
|
||||
// | | | +------------ Accessed flag (not accessed yet)
|
||||
// | | +-------------- Dirty (not dirtied yet)
|
||||
// | +---------------- PAT (determining memory type for page)
|
||||
// +------------------- Global
|
||||
/// Page table entry flags for entries pointing at a page
|
||||
constexpr uint16_t page_flags = 0x103;
|
||||
|
||||
// Flags: 0 0 0 0 1 1 0 0 0 0 0 1 1 = 0x0183
|
||||
// | IGN | | | | | | | | +- Present
|
||||
// | | | | | | | | +--- Writeable
|
||||
// | | | | | | | +----- Supervisor only
|
||||
// | | | | | | +------- PWT (determining memory type for page)
|
||||
// | | | | | +---------- PCD (determining memory type for page)
|
||||
// | | | | +------------ Accessed flag (not accessed yet)
|
||||
// | | | +-------------- Dirty (not dirtied yet)
|
||||
// | | +---------------- Page size (1GiB page)
|
||||
// | +------------------- Global
|
||||
// +---------------------------- PAT (determining memory type for page)
|
||||
/// Page table entry flags for entries pointing at a huge page
|
||||
constexpr uint16_t huge_page_flags = 0x183;
|
||||
|
||||
// Flags: 0 0 0 0 0 0 0 0 0 0 1 1 = 0x0003
|
||||
// IGNORED | | | | | | | +- Present
|
||||
// | | | | | | +--- Writeable
|
||||
// | | | | | +----- Usermode access (Supervisor only)
|
||||
// | | | | +------- PWT (determining memory type for pdpt)
|
||||
// | | | +---------- PCD (determining memory type for pdpt)
|
||||
// | | +------------ Accessed flag (not accessed yet)
|
||||
// | +-------------- Ignored
|
||||
// +---------------- Reserved 0 (Table pointer, not page)
|
||||
/// Page table entry flags for entries pointing at another table
|
||||
constexpr uint16_t table_flags = 0x003;
|
||||
|
||||
/// Iterator over page table entries.
|
||||
template <unsigned D = 4>
|
||||
class page_entry_iterator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg virt Virtual address this iterator is starting at
|
||||
/// \arg pml4 Root of the page tables to iterate
|
||||
/// \arg page_cache Pointer to pages that can be used for page tables
|
||||
/// \arg page_count Number of pages pointed to by `page_cache`
|
||||
page_entry_iterator(
|
||||
uintptr_t virt,
|
||||
page_table *pml4,
|
||||
void *&page_cache,
|
||||
uint32_t &cache_count) :
|
||||
m_page_cache(page_cache),
|
||||
m_cache_count(cache_count)
|
||||
{
|
||||
m_table[0] = pml4;
|
||||
for (unsigned i = 0; i < D; ++i) {
|
||||
m_index[i] = static_cast<uint16_t>((virt >> (12 + 9*(3-i))) & 0x1ff);
|
||||
ensure_table(i);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t vaddress() const {
|
||||
uintptr_t address = 0;
|
||||
for (unsigned i = 0; i < D; ++i)
|
||||
address |= static_cast<uintptr_t>(m_index[i]) << (12 + 9*(3-i));
|
||||
if (address & (1ull<<47)) // canonicalize the address
|
||||
address |= (0xffffull<<48);
|
||||
return address;
|
||||
}
|
||||
|
||||
void increment()
|
||||
{
|
||||
for (unsigned i = D - 1; i >= 0; --i) {
|
||||
if (++m_index[i] <= 511) {
|
||||
for (unsigned j = i + 1; j < D; ++j)
|
||||
ensure_table(j);
|
||||
return;
|
||||
}
|
||||
|
||||
m_index[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t & operator*() { return entry(D-1); }
|
||||
|
||||
private:
|
||||
inline uint64_t & entry(unsigned level) { return m_table[level]->entries[m_index[level]]; }
|
||||
|
||||
void ensure_table(unsigned level)
|
||||
{
|
||||
// We're only dealing with D levels of paging, and
|
||||
// there must always be a PML4.
|
||||
if (level < 1 || level >= D)
|
||||
return;
|
||||
|
||||
// Entry in the parent that points to the table we want
|
||||
uint64_t & parent_ent = entry(level - 1);
|
||||
|
||||
if (!(parent_ent & 1)) {
|
||||
if (!m_cache_count--)
|
||||
error::raise(uefi::status::out_of_resources, L"Page table cache empty");
|
||||
|
||||
page_table *table = reinterpret_cast<page_table*>(m_page_cache);
|
||||
m_page_cache = offset_ptr<void>(m_page_cache, page_size);
|
||||
|
||||
parent_ent = (reinterpret_cast<uintptr_t>(table) & ~0xfffull) | table_flags;
|
||||
m_table[level] = table;
|
||||
} else {
|
||||
m_table[level] = reinterpret_cast<page_table*>(parent_ent & ~0xfffull);
|
||||
}
|
||||
}
|
||||
|
||||
void *&m_page_cache;
|
||||
uint32_t &m_cache_count;
|
||||
page_table *m_table[D];
|
||||
uint16_t m_index[D];
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
add_offset_mappings(page_table *pml4, void *&page_cache, uint32_t &num_pages)
|
||||
{
|
||||
uintptr_t phys = 0;
|
||||
uintptr_t virt = ::memory::page_offset; // Start of offset-mapped area
|
||||
size_t pages = 64 * 1024; // 64 TiB of 1 GiB pages
|
||||
constexpr size_t GiB = 0x40000000ull;
|
||||
|
||||
page_entry_iterator<2> iterator{
|
||||
virt, pml4,
|
||||
page_cache,
|
||||
num_pages};
|
||||
|
||||
while (true) {
|
||||
*iterator = phys | huge_page_flags;
|
||||
if (--pages == 0)
|
||||
break;
|
||||
|
||||
iterator.increment();
|
||||
phys += GiB;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
add_current_mappings(page_table *new_pml4)
|
||||
{
|
||||
// Get the pointer to the current PML4
|
||||
page_table *old_pml4 = 0;
|
||||
asm volatile ( "mov %%cr3, %0" : "=r" (old_pml4) );
|
||||
|
||||
// Only copy mappings in the lower half
|
||||
for (int i = 0; i < ::memory::pml4e_kernel; ++i) {
|
||||
uint64_t entry = old_pml4->entries[i];
|
||||
if (entry & 1)
|
||||
new_pml4->entries[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
allocate_tables(kernel::args::header *args, uefi::boot_services *bs)
|
||||
{
|
||||
status_line status(L"Allocating initial page tables");
|
||||
|
||||
static constexpr size_t offset_map_tables = 128 + 1;
|
||||
static constexpr size_t tables_needed = offset_map_tables + 49;
|
||||
|
||||
void *addr = nullptr;
|
||||
try_or_raise(
|
||||
bs->allocate_pages(
|
||||
uefi::allocate_type::any_pages,
|
||||
memory::table_type,
|
||||
tables_needed,
|
||||
&addr),
|
||||
L"Error allocating page table pages.");
|
||||
|
||||
bs->set_mem(addr, tables_needed*page_size, 0);
|
||||
|
||||
kernel::args::module &mod = args->modules[++args->num_modules];
|
||||
mod.type = kernel::args::mod_type::page_tables;
|
||||
mod.location = addr;
|
||||
mod.size = tables_needed*page_size;
|
||||
|
||||
args->pml4 = addr;
|
||||
args->num_free_tables = tables_needed - 1;
|
||||
args->page_table_cache = offset_ptr<void>(addr, page_size);
|
||||
|
||||
page_table *pml4 = reinterpret_cast<page_table*>(addr);
|
||||
add_offset_mappings(pml4, args->page_table_cache, args->num_free_tables);
|
||||
|
||||
console::print(L" Set up initial mappings, %d spare tables.\r\n", args->num_free_tables);
|
||||
}
|
||||
|
||||
void
|
||||
map_pages(
|
||||
page_table *pml4,
|
||||
kernel::args::header *args,
|
||||
uintptr_t phys, uintptr_t virt,
|
||||
size_t size)
|
||||
{
|
||||
size_t pages = memory::bytes_to_pages(size);
|
||||
page_entry_iterator<4> iterator{
|
||||
virt, pml4,
|
||||
args->page_table_cache,
|
||||
args->num_free_tables};
|
||||
|
||||
while (true) {
|
||||
*iterator = phys | page_flags;
|
||||
if (--pages == 0)
|
||||
break;
|
||||
|
||||
iterator.increment();
|
||||
phys += page_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace paging
|
||||
} // namespace boot
|
||||
54
src/boot/paging.h
Normal file
54
src/boot/paging.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
/// \file paging.h
|
||||
/// Page table structure and related definitions
|
||||
#include <stdint.h>
|
||||
#include <uefi/boot_services.h>
|
||||
#include "kernel_args.h"
|
||||
|
||||
namespace boot {
|
||||
namespace paging {
|
||||
|
||||
/// Struct to allow easy accessing of a memory page being used as a page table.
|
||||
struct page_table
|
||||
{
|
||||
uint64_t entries[512];
|
||||
|
||||
inline page_table * get(int i, uint16_t *flags = nullptr) const {
|
||||
uint64_t entry = entries[i];
|
||||
if ((entry & 1) == 0) return nullptr;
|
||||
if (flags) *flags = entry & 0xfff;
|
||||
return reinterpret_cast<page_table *>(entry & ~0xfffull);
|
||||
}
|
||||
|
||||
inline void set(int i, void *p, uint16_t flags) {
|
||||
entries[i] = reinterpret_cast<uint64_t>(p) | (flags & 0xfff);
|
||||
}
|
||||
};
|
||||
|
||||
/// Allocate memory to be used for initial page tables. Initial offset-mapped
|
||||
/// page tables are pre-filled. All pages are saved as a module in kernel args
|
||||
/// and kernel args' `page_table_cache` and `num_free_tables` are updated with
|
||||
/// the leftover space.
|
||||
void allocate_tables(
|
||||
kernel::args::header *args,
|
||||
uefi::boot_services *bs);
|
||||
|
||||
/// Copy existing page table entries to a new page table. Does not do a deep
|
||||
/// copy - the new PML4 is updated to point to the existing next-level page
|
||||
/// tables in the current PML4.
|
||||
void add_current_mappings(page_table *new_pml4);
|
||||
|
||||
/// Map a physical address to a virtual address in the given page tables.
|
||||
/// \arg pml4 The root of the set of page tables to be updated
|
||||
/// \arg args The kernel args header, used for the page table cache
|
||||
/// \arg phys The phyiscal address to map in
|
||||
/// \arg virt The virtual address to map in
|
||||
/// \arg size The size in bytes of the mapping
|
||||
void map_pages(
|
||||
page_table *pml4,
|
||||
kernel::args::header *args,
|
||||
uintptr_t phys, uintptr_t virt,
|
||||
size_t bytes);
|
||||
|
||||
} // namespace paging
|
||||
} // namespace boot
|
||||
41
src/boot/pointer_manipulation.h
Normal file
41
src/boot/pointer_manipulation.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/// \file pointer_manipulation.h
|
||||
/// Helper functions and types for doing type-safe byte-wise pointer math.
|
||||
#pragma once
|
||||
|
||||
namespace boot {
|
||||
|
||||
/// Return a pointer offset from `input` by `offset` bytes.
|
||||
/// \tparam T Cast the return value to a pointer to `T`
|
||||
/// \tparam S The type pointed to by the `input` pointer
|
||||
template <typename T, typename S>
|
||||
inline T* offset_ptr(S* input, ptrdiff_t offset) {
|
||||
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(input) + offset);
|
||||
}
|
||||
|
||||
/// Iterator for an array of `T` whose size is known at runtime
|
||||
/// \tparam T Type of the objects in the array, whose size might not be
|
||||
/// what is returned by sizeof(T).
|
||||
template <typename T>
|
||||
class offset_iterator
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg t Pointer to the first item in the array
|
||||
/// \arg off Offset applied to reach successive items. Default is 0,
|
||||
/// which creates an effectively constant iterator.
|
||||
offset_iterator(T* t, size_t off=0) : m_t(t), m_off(off) {}
|
||||
|
||||
T* operator++() { m_t = offset_ptr<T>(m_t, m_off); return m_t; }
|
||||
T* operator++(int) { T* tmp = m_t; operator++(); return tmp; }
|
||||
bool operator==(T* p) { return p == m_t; }
|
||||
|
||||
T* operator*() const { return m_t; }
|
||||
operator T*() const { return m_t; }
|
||||
T* operator->() const { return m_t; }
|
||||
|
||||
private:
|
||||
T* m_t;
|
||||
size_t m_off;
|
||||
};
|
||||
|
||||
} // namespace boot
|
||||
39
src/boot/support.cpp
Normal file
39
src/boot/support.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
/// Basic memcpy() implementation for clang. Clang requires freestanding code
|
||||
/// implement memcpy(), as it may emit references to it. This basic memcpy is
|
||||
/// not the most efficient, but will get linked if no other memcpy exists.
|
||||
__attribute__ ((__weak__))
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
|
||||
const uint8_t *csrc = reinterpret_cast<const uint8_t*>(src);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
cdest[i] = csrc[i];
|
||||
return dest;
|
||||
}
|
||||
|
||||
/// Basic memset() implementation for clang. Clang requires freestanding code
|
||||
/// implement memset(), as it may emit references to it. This basic memset is
|
||||
/// not the most efficient, but will get linked if no other memcpy exists.
|
||||
__attribute__ ((__weak__))
|
||||
void *memset(void *dest, int c, size_t n)
|
||||
{
|
||||
uint8_t *cdest = reinterpret_cast<uint8_t*>(dest);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
cdest[i] = static_cast<uint8_t>(c);
|
||||
return dest;
|
||||
}
|
||||
|
||||
int _purecall()
|
||||
{
|
||||
::boot::error::raise(uefi::status::unsupported, L"Pure virtual call");
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
void operator delete (void *) {}
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "utility.h"
|
||||
|
||||
struct error_code_desc {
|
||||
EFI_STATUS code;
|
||||
CHAR16 *name;
|
||||
};
|
||||
|
||||
// Based off the gnu-efi table
|
||||
struct error_code_desc error_table[] = {
|
||||
{ EFI_SUCCESS, L"Success" },
|
||||
{ EFI_LOAD_ERROR, L"Load Error" },
|
||||
{ EFI_INVALID_PARAMETER, L"Invalid Parameter" },
|
||||
{ EFI_UNSUPPORTED, L"Unsupported" },
|
||||
{ EFI_BAD_BUFFER_SIZE, L"Bad Buffer Size" },
|
||||
{ EFI_BUFFER_TOO_SMALL, L"Buffer Too Small" },
|
||||
{ EFI_NOT_READY, L"Not Ready" },
|
||||
{ EFI_DEVICE_ERROR, L"Device Error" },
|
||||
{ EFI_WRITE_PROTECTED, L"Write Protected" },
|
||||
{ EFI_OUT_OF_RESOURCES, L"Out of Resources" },
|
||||
{ EFI_VOLUME_CORRUPTED, L"Volume Corrupt" },
|
||||
{ EFI_VOLUME_FULL, L"Volume Full" },
|
||||
{ EFI_NO_MEDIA, L"No Media" },
|
||||
{ EFI_MEDIA_CHANGED, L"Media changed" },
|
||||
{ EFI_NOT_FOUND, L"Not Found" },
|
||||
{ EFI_ACCESS_DENIED, L"Access Denied" },
|
||||
{ EFI_NO_RESPONSE, L"No Response" },
|
||||
{ EFI_NO_MAPPING, L"No mapping" },
|
||||
{ EFI_TIMEOUT, L"Time out" },
|
||||
{ EFI_NOT_STARTED, L"Not started" },
|
||||
{ EFI_ALREADY_STARTED, L"Already started" },
|
||||
{ EFI_ABORTED, L"Aborted" },
|
||||
{ EFI_ICMP_ERROR, L"ICMP Error" },
|
||||
{ EFI_TFTP_ERROR, L"TFTP Error" },
|
||||
{ EFI_PROTOCOL_ERROR, L"Protocol Error" },
|
||||
{ EFI_INCOMPATIBLE_VERSION, L"Incompatible Version" },
|
||||
{ EFI_SECURITY_VIOLATION, L"Security Policy Violation" },
|
||||
{ EFI_CRC_ERROR, L"CRC Error" },
|
||||
{ EFI_END_OF_MEDIA, L"End of Media" },
|
||||
{ EFI_END_OF_FILE, L"End of File" },
|
||||
{ EFI_INVALID_LANGUAGE, L"Invalid Languages" },
|
||||
{ EFI_COMPROMISED_DATA, L"Compromised Data" },
|
||||
|
||||
{ EFI_WARN_UNKOWN_GLYPH, L"Warning Unknown Glyph" },
|
||||
{ EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure" },
|
||||
{ EFI_WARN_WRITE_FAILURE, L"Warning Write Failure" },
|
||||
{ EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const CHAR16 *
|
||||
util_error_message(EFI_STATUS status)
|
||||
{
|
||||
int32_t i = -1;
|
||||
while (error_table[++i].name != NULL) {
|
||||
if (error_table[i].code == status) return error_table[i].name;
|
||||
}
|
||||
|
||||
if (EFI_ERROR(status))
|
||||
return L"Unknown Error";
|
||||
else
|
||||
return L"Unknown Warning";
|
||||
}
|
||||
|
||||
size_t
|
||||
wstrlen(const CHAR16 *s)
|
||||
{
|
||||
size_t count = 0;
|
||||
while (s && *s++) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "console.h"
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
size_t wstrlen(const CHAR16 *s);
|
||||
const CHAR16 *util_error_message(EFI_STATUS status);
|
||||
|
||||
#define CHECK_EFI_STATUS_OR_RETURN(s, msg, ...) \
|
||||
if (EFI_ERROR((s))) { \
|
||||
con_printf(L"ERROR: " msg L": %s\r\n", ##__VA_ARGS__, util_error_message(s)); \
|
||||
return (s); \
|
||||
}
|
||||
|
||||
#define CHECK_EFI_STATUS_OR_FAIL(s) \
|
||||
if (EFI_ERROR((s))) { \
|
||||
con_status_fail(util_error_message(s)); \
|
||||
while (1) __asm__("hlt"); \
|
||||
}
|
||||
|
||||
#define CHECK_EFI_STATUS_OR_ASSERT(s, d) \
|
||||
if (EFI_ERROR((s))) { \
|
||||
__asm__ __volatile__( \
|
||||
"movq %0, %%r8;" \
|
||||
"movq %1, %%r9;" \
|
||||
"movq %2, %%r10;" \
|
||||
"movq $0, %%rdx;" \
|
||||
"divq %%rdx;" \
|
||||
: \
|
||||
: "r"((uint64_t)s), "r"((uint64_t)d), "r"((uint64_t)__LINE__) \
|
||||
: "rax", "rdx", "r8", "r9", "r10"); \
|
||||
}
|
||||
67
src/drivers/ahci/ata.h
Normal file
67
src/drivers/ahci/ata.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
/// \file ata.h
|
||||
/// Definitions for ATA codes
|
||||
#include <stdint.h>
|
||||
#include "kutil/enum_bitfields.h"
|
||||
|
||||
namespace ahci {
|
||||
|
||||
|
||||
enum class ata_status : uint8_t
|
||||
{
|
||||
error = 0x01,
|
||||
index = 0x02,
|
||||
corrected = 0x04,
|
||||
data_ready = 0x08,
|
||||
seek_done = 0x10,
|
||||
fault = 0x20,
|
||||
ready = 0x40,
|
||||
busy = 0x80
|
||||
};
|
||||
|
||||
|
||||
enum class ata_error : uint8_t
|
||||
{
|
||||
amnf = 0x01, // Address mark not found
|
||||
tkznf = 0x02, // Track 0 not found
|
||||
abort = 0x04, // Command abort
|
||||
mcr = 0x08, // No media
|
||||
idnf = 0x10, // Id not found
|
||||
mc = 0x20, // No media
|
||||
unc = 0x40, // Uncorrectable
|
||||
bbk = 0x80, // Bad sector
|
||||
};
|
||||
|
||||
|
||||
enum class ata_cmd : uint8_t
|
||||
{
|
||||
read_pio = 0x20,
|
||||
read_pio_ext = 0x24,
|
||||
read_dma = 0xC8,
|
||||
read_dma_ext = 0x25,
|
||||
write_pio = 0x30,
|
||||
write_pio_ext = 0x34,
|
||||
write_dma = 0xCA,
|
||||
write_dma_ext = 0x35,
|
||||
cache_flush = 0xE7,
|
||||
cache_flush_ext = 0xEA,
|
||||
packet = 0xA0,
|
||||
identify_packet = 0xA1,
|
||||
identify = 0xEC
|
||||
};
|
||||
|
||||
|
||||
enum class sata_signature : uint32_t
|
||||
{
|
||||
none = 0x00000000,
|
||||
|
||||
sata_drive = 0x00000101,
|
||||
satapi_drive = 0xeb140101,
|
||||
enclosure = 0xc33c0101,
|
||||
port_muxer = 0x96690101
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
|
||||
IS_BITFIELD(ahci::ata_status);
|
||||
IS_BITFIELD(ahci::ata_error);
|
||||
22
src/drivers/ahci/driver.cpp
Normal file
22
src/drivers/ahci/driver.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "ahci/driver.h"
|
||||
#include "log.h"
|
||||
#include "pci.h"
|
||||
|
||||
namespace ahci {
|
||||
|
||||
|
||||
driver::driver()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
driver::register_device(pci_device *device)
|
||||
{
|
||||
log::info(logs::driver, "AHCI registering device %d:%d:%d:",
|
||||
device->bus(), device->device(), device->function());
|
||||
|
||||
ahci::hba &hba = m_devices.emplace(device);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
31
src/drivers/ahci/driver.h
Normal file
31
src/drivers/ahci/driver.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
/// \file ahci.h
|
||||
/// AHCI driver and related definitions
|
||||
#include "kutil/vector.h"
|
||||
#include "ahci/hba.h"
|
||||
|
||||
class pci_device;
|
||||
|
||||
namespace ahci {
|
||||
|
||||
|
||||
/// Basic AHCI driver
|
||||
class driver
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
driver();
|
||||
|
||||
/// Register a device with the driver
|
||||
/// \arg device The PCI device to handle
|
||||
void register_device(pci_device *device);
|
||||
|
||||
/// Unregister a device from the driver
|
||||
/// \arg device The PCI device to remove
|
||||
void unregister_device(pci_device *device);
|
||||
|
||||
private:
|
||||
kutil::vector<ahci::hba> m_devices;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
49
src/drivers/ahci/fis.h
Normal file
49
src/drivers/ahci/fis.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
/// \file fis.h
|
||||
/// Definitions for Frame Information Structure types. (Not for pescatarians.)
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
namespace ahci {
|
||||
|
||||
enum class ata_cmd : uint8_t;
|
||||
|
||||
enum class fis_type : uint8_t
|
||||
{
|
||||
register_h2d = 0x27,
|
||||
register_d2h = 0x34,
|
||||
dma_activate = 0x39,
|
||||
dma_setup = 0x41,
|
||||
data = 0x46,
|
||||
bist = 0x58,
|
||||
pio_setup = 0x5f,
|
||||
device_bits = 0xa1
|
||||
};
|
||||
|
||||
|
||||
struct fis_register_h2d
|
||||
{
|
||||
fis_type type;
|
||||
uint8_t pm_port; // high bit (0x80) is set for the command register flag
|
||||
ata_cmd command;
|
||||
uint8_t features;
|
||||
|
||||
uint8_t lba0;
|
||||
uint8_t lba1;
|
||||
uint8_t lba2;
|
||||
uint8_t device;
|
||||
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t features2;
|
||||
|
||||
uint8_t count0;
|
||||
uint8_t count1;
|
||||
uint8_t icc;
|
||||
uint8_t control;
|
||||
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
143
src/drivers/ahci/hba.cpp
Normal file
143
src/drivers/ahci/hba.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <stdint.h>
|
||||
#include "ahci/ata.h"
|
||||
#include "ahci/hba.h"
|
||||
#include "console.h"
|
||||
#include "device_manager.h"
|
||||
#include "fs/gpt.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
#include "pci.h"
|
||||
|
||||
|
||||
IS_BITFIELD(ahci::hba_cap);
|
||||
IS_BITFIELD(ahci::hba_cap2);
|
||||
|
||||
namespace ahci {
|
||||
|
||||
|
||||
enum class hba_cap : uint32_t
|
||||
{
|
||||
ccc = 0x00000080, // Command completion coalescing
|
||||
ahci_only = 0x00040000, // ACHI-only mode
|
||||
clo = 0x01000000, // Command list override
|
||||
snotify = 0x40000000, // SNotification register
|
||||
ncq = 0x40000000, // Native command queuing
|
||||
addr64 = 0x80000000 // 64bit addressing
|
||||
};
|
||||
|
||||
|
||||
enum class hba_cap2 : uint32_t
|
||||
{
|
||||
handoff = 0x00000001 // BIOS OS hand-off
|
||||
};
|
||||
|
||||
|
||||
struct hba_data
|
||||
{
|
||||
hba_cap cap;
|
||||
uint32_t host_control;
|
||||
uint32_t int_status;
|
||||
uint32_t port_impl;
|
||||
uint32_t version;
|
||||
uint32_t ccc_control;
|
||||
uint32_t ccc_ports;
|
||||
uint32_t em_location;
|
||||
uint32_t em_control;
|
||||
hba_cap2 cap2;
|
||||
uint32_t handoff_control;
|
||||
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
void irq_cb(void *data)
|
||||
{
|
||||
hba *h = reinterpret_cast<hba *>(data);
|
||||
h->handle_interrupt();
|
||||
}
|
||||
|
||||
|
||||
hba::hba(pci_device *device)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
device_manager &dm = device_manager::get();
|
||||
|
||||
uint32_t bar5 = device->get_bar(5);
|
||||
log::debug(logs::driver, "HBA raw BAR5 is %08lx", bar5);
|
||||
|
||||
void *data = reinterpret_cast<void *>(bar5 & ~0xfffull);
|
||||
pm->map_offset_pointer(&data, 0x2000);
|
||||
m_data = reinterpret_cast<hba_data volatile *>(data);
|
||||
|
||||
if (! bitfield_has(m_data->cap, hba_cap::ahci_only))
|
||||
m_data->host_control |= 0x80000000; // Enable AHCI mode
|
||||
|
||||
uint32_t icap = static_cast<uint32_t>(m_data->cap);
|
||||
|
||||
unsigned ports = (icap & 0xf) + 1;
|
||||
unsigned slots = ((icap >> 8) & 0x1f) + 1;
|
||||
|
||||
log::debug(logs::driver, " %d ports: %08x", ports, m_data->port_impl);
|
||||
log::debug(logs::driver, " %d command slots", slots);
|
||||
|
||||
auto *pd = reinterpret_cast<port_data volatile *>(
|
||||
kutil::offset_pointer(m_data, 0x100));
|
||||
|
||||
bool needs_interrupt = false;
|
||||
m_ports.ensure_capacity(ports);
|
||||
for (unsigned i = 0; i < ports; ++i) {
|
||||
bool impl = ((m_data->port_impl & (1 << i)) != 0);
|
||||
port &p = m_ports.emplace(this, i, kutil::offset_pointer(pd, 0x80 * i), impl);
|
||||
if (p.get_state() == port::state::active)
|
||||
needs_interrupt = true;
|
||||
}
|
||||
|
||||
if (needs_interrupt) {
|
||||
device_manager::get().allocate_msi("AHCI Device", *device, irq_cb, this);
|
||||
m_data->host_control |= 0x02; // enable interrupts
|
||||
}
|
||||
|
||||
for (auto &p : m_ports) {
|
||||
if (!p.active()) continue;
|
||||
|
||||
if (p.get_type() == sata_signature::sata_drive) {
|
||||
p.sata_reconnect();
|
||||
/*
|
||||
if (fs::partition::load(&p) == 0)
|
||||
dm.register_block_device(&p);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hba::handle_interrupt()
|
||||
{
|
||||
uint32_t status = m_data->int_status;
|
||||
for (auto &port : m_ports) {
|
||||
if (status & (1 << port.index())) {
|
||||
port.handle_interrupt();
|
||||
}
|
||||
}
|
||||
// Write 1 to the handled interrupts
|
||||
m_data->int_status = status;
|
||||
}
|
||||
|
||||
void
|
||||
hba::dump()
|
||||
{
|
||||
console *cons = console::get();
|
||||
static const char *regs[] = {
|
||||
" CAP", " GHC", " IS", " PI", " VS", " C3C",
|
||||
" C3P", " EML", " EMC", "CAP2", "BOHC"
|
||||
};
|
||||
|
||||
cons->printf("HBA Registers:\n");
|
||||
auto *data = reinterpret_cast<uint32_t volatile *>(m_data);
|
||||
for (int i = 0; i < 11; ++i) {
|
||||
cons->printf(" %s: %08x\n", regs[i], data[i]);
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
|
||||
} // namespace ahci
|
||||
|
||||
37
src/drivers/ahci/hba.h
Normal file
37
src/drivers/ahci/hba.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
/// \file hba.h
|
||||
/// Definition for AHCI host bus adapters
|
||||
#include "kutil/vector.h"
|
||||
#include "ahci/port.h"
|
||||
|
||||
class pci_device;
|
||||
|
||||
|
||||
namespace ahci {
|
||||
|
||||
enum class hba_cap : uint32_t;
|
||||
enum class hba_cap2 : uint32_t;
|
||||
struct hba_data;
|
||||
|
||||
|
||||
/// An AHCI host bus adapter
|
||||
class hba
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg device The PCI device for this HBA
|
||||
hba(pci_device *device);
|
||||
|
||||
/// Interrupt handler.
|
||||
void handle_interrupt();
|
||||
|
||||
/// Dump the HBA registers to the console
|
||||
void dump();
|
||||
|
||||
private:
|
||||
pci_device *m_device;
|
||||
hba_data volatile *m_data;
|
||||
kutil::vector<port> m_ports;
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
623
src/drivers/ahci/port.cpp
Normal file
623
src/drivers/ahci/port.cpp
Normal file
@@ -0,0 +1,623 @@
|
||||
#include <algorithm>
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "ahci/ata.h"
|
||||
#include "ahci/hba.h"
|
||||
#include "ahci/fis.h"
|
||||
#include "ahci/port.h"
|
||||
#include "console.h"
|
||||
#include "io.h"
|
||||
#include "log.h"
|
||||
#include "page_manager.h"
|
||||
|
||||
namespace ahci {
|
||||
enum class cmd_list_flags : uint16_t;
|
||||
}
|
||||
|
||||
IS_BITFIELD(ahci::port_cmd);
|
||||
IS_BITFIELD(volatile ahci::port_cmd);
|
||||
IS_BITFIELD(ahci::cmd_list_flags);
|
||||
|
||||
namespace ahci {
|
||||
|
||||
const unsigned max_prd_count = 16;
|
||||
|
||||
|
||||
enum class cmd_list_flags : uint16_t
|
||||
{
|
||||
atapi = 0x0020,
|
||||
write = 0x0040,
|
||||
prefetch = 0x0080,
|
||||
reset = 0x0100,
|
||||
bist = 0x0200,
|
||||
clear_busy = 0x0400
|
||||
};
|
||||
|
||||
inline cmd_list_flags
|
||||
cmd_list_fis_size(uint8_t size)
|
||||
{
|
||||
return static_cast<cmd_list_flags>((size/4) & 0x1f);
|
||||
}
|
||||
|
||||
|
||||
struct cmd_list_entry
|
||||
{
|
||||
cmd_list_flags flags;
|
||||
uint16_t prd_table_length;
|
||||
uint32_t prd_byte_count;
|
||||
uint32_t cmd_table_base_low;
|
||||
uint32_t cmd_table_base_high;
|
||||
uint32_t reserved[4];
|
||||
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct prdt_entry
|
||||
{
|
||||
uint32_t data_base_low;
|
||||
uint32_t data_base_high;
|
||||
uint32_t reserved;
|
||||
uint32_t byte_count;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct cmd_table
|
||||
{
|
||||
uint8_t cmd_fis[64];
|
||||
uint8_t atapi_cmd[16];
|
||||
uint8_t reserved[48];
|
||||
prdt_entry entries[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
enum class port_cmd : uint32_t
|
||||
{
|
||||
start = 0x00000001,
|
||||
spinup = 0x00000002,
|
||||
poweron = 0x00000004,
|
||||
clo = 0x00000008,
|
||||
fis_recv = 0x00000010,
|
||||
fisr_running = 0x00004000,
|
||||
cmds_running = 0x00008000,
|
||||
|
||||
none = 0x00000000
|
||||
};
|
||||
|
||||
|
||||
struct port_data
|
||||
{
|
||||
uint32_t cmd_base_low;
|
||||
uint32_t cmd_base_high;
|
||||
uint32_t fis_base_low;
|
||||
uint32_t fis_base_high;
|
||||
|
||||
uint32_t interrupt_status;
|
||||
uint32_t interrupt_enable;
|
||||
|
||||
port_cmd command;
|
||||
|
||||
uint32_t reserved0;
|
||||
|
||||
uint8_t task_file_status;
|
||||
uint8_t task_file_error;
|
||||
uint16_t reserved1;
|
||||
|
||||
sata_signature signature;
|
||||
|
||||
uint32_t serial_status;
|
||||
uint32_t serial_control;
|
||||
uint32_t serial_error;
|
||||
uint32_t serial_active;
|
||||
uint32_t cmd_issue;
|
||||
uint32_t serial_notify;
|
||||
uint32_t fis_switching;
|
||||
uint32_t dev_sleep;
|
||||
|
||||
uint8_t reserved2[40];
|
||||
uint8_t vendor[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
port::port(hba *device, uint8_t index, port_data volatile *data, bool impl) :
|
||||
m_index(index),
|
||||
m_type(sata_signature::none),
|
||||
m_state(state::unimpl),
|
||||
m_hba(device),
|
||||
m_data(data),
|
||||
m_fis(nullptr),
|
||||
m_cmd_list(nullptr),
|
||||
m_cmd_table(nullptr)
|
||||
{
|
||||
if (impl) {
|
||||
m_state = state::inactive;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
port::~port()
|
||||
{
|
||||
if (m_cmd_list) {
|
||||
page_manager *pm = page_manager::get();
|
||||
pm->unmap_pages(m_cmd_list, 3);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
port::update()
|
||||
{
|
||||
if (m_state == state::unimpl) return;
|
||||
|
||||
uint32_t detected = m_data->serial_status & 0x0f;
|
||||
uint32_t power = (m_data->serial_status >> 8) & 0x0f;
|
||||
|
||||
if (detected == 0x3 && power == 0x1) {
|
||||
m_state = state::active;
|
||||
m_type = m_data->signature;
|
||||
|
||||
const char *name =
|
||||
m_type == sata_signature::sata_drive ? "SATA" :
|
||||
m_type == sata_signature::satapi_drive ? "SATAPI" :
|
||||
"Other";
|
||||
|
||||
log::info(logs::driver, "Found device type %s at port %d", name, m_index);
|
||||
|
||||
rebase();
|
||||
m_pending.set_size(32);
|
||||
for (auto &pend : m_pending) {
|
||||
pend.type = command_type::none;
|
||||
}
|
||||
|
||||
// Clear any old pending interrupts and enable interrupts
|
||||
m_data->interrupt_status = m_data->interrupt_status;
|
||||
m_data->interrupt_enable = 0xffffffff;
|
||||
} else {
|
||||
m_state = state::inactive;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
port::busy()
|
||||
{
|
||||
return (m_data->task_file_status & 0x88) != 0;
|
||||
}
|
||||
|
||||
void
|
||||
port::start_commands()
|
||||
{
|
||||
while (bitfield_has(m_data->command, port_cmd::cmds_running))
|
||||
io_wait();
|
||||
|
||||
m_data->command |= port_cmd::fis_recv;
|
||||
m_data->command |= port_cmd::start;
|
||||
}
|
||||
|
||||
void
|
||||
port::stop_commands()
|
||||
{
|
||||
m_data->command &= ~port_cmd::start;
|
||||
|
||||
while (
|
||||
bitfield_has(m_data->command, port_cmd::cmds_running) ||
|
||||
bitfield_has(m_data->command, port_cmd::fisr_running))
|
||||
io_wait();
|
||||
|
||||
m_data->command &= ~port_cmd::fis_recv;
|
||||
}
|
||||
|
||||
int
|
||||
port::make_command(size_t length, fis_register_h2d **fis)
|
||||
{
|
||||
int slot = -1;
|
||||
uint32_t used_slots =
|
||||
m_data->serial_active |
|
||||
m_data->cmd_issue |
|
||||
m_data->interrupt_status;
|
||||
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
if (used_slots & (1 << i)) continue;
|
||||
|
||||
if (m_pending[i].type == command_type::none) {
|
||||
slot = i;
|
||||
break;
|
||||
} else {
|
||||
log::debug(logs::driver, "Type is %d", m_pending[i].type);
|
||||
}
|
||||
}
|
||||
|
||||
if (slot < 0) {
|
||||
log::error(logs::driver, "AHCI could not get a free command slot.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
page_manager *pm = page_manager::get();
|
||||
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
|
||||
kutil::memset(&cmdt, 0, sizeof(cmd_table) +
|
||||
max_prd_count * sizeof(prdt_entry));
|
||||
|
||||
ent.flags = cmd_list_fis_size(sizeof(fis_register_h2d));
|
||||
|
||||
fis_register_h2d *cfis = reinterpret_cast<fis_register_h2d *>(&cmdt.cmd_fis);
|
||||
kutil::memset(cfis, 0, sizeof(fis_register_h2d));
|
||||
cfis->type = fis_type::register_h2d;
|
||||
cfis->pm_port = 0x80; // set command register flag
|
||||
*fis = cfis;
|
||||
|
||||
size_t remaining = length;
|
||||
for (int i = 0; i < max_prd_count; ++i) {
|
||||
size_t prd_len = std::min(remaining, 0x200000ul);
|
||||
remaining -= prd_len;
|
||||
|
||||
void *mem = pm->map_offset_pages(page_count(prd_len));
|
||||
kutil::memset(mem, 0xaf, prd_len);
|
||||
|
||||
uintptr_t phys = pm->offset_phys(mem);
|
||||
cmdt.entries[i].data_base_low = phys & 0xffffffff;
|
||||
cmdt.entries[i].data_base_high = phys >> 32;
|
||||
cmdt.entries[i].byte_count = prd_len - 1;
|
||||
if (remaining == 0 || i == max_prd_count - 1) {
|
||||
// If this is the last one, set the interrupt flag
|
||||
cmdt.entries[i].byte_count |= 0x80000000;
|
||||
ent.prd_table_length = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
log::debug(logs::driver, "Created command, slot %d, %d PRD entries.",
|
||||
slot, ent.prd_table_length);
|
||||
return slot;
|
||||
}
|
||||
|
||||
int
|
||||
port::read_async(uint64_t offset, size_t length, void *dest)
|
||||
{
|
||||
fis_register_h2d *fis;
|
||||
int slot = make_command(length, &fis);
|
||||
if (slot < 0)
|
||||
return 0;
|
||||
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
|
||||
fis->command = ata_cmd::read_dma_ext;
|
||||
fis->device = 0x40; // ATA8-ACS p.175
|
||||
|
||||
uint64_t sector = offset >> 9;
|
||||
fis->lba0 = (sector ) & 0xff;
|
||||
fis->lba1 = (sector >> 8) & 0xff;
|
||||
fis->lba2 = (sector >> 16) & 0xff;
|
||||
fis->lba3 = (sector >> 24) & 0xff;
|
||||
fis->lba4 = (sector >> 32) & 0xff;
|
||||
fis->lba5 = (sector >> 40) & 0xff;
|
||||
|
||||
size_t count = ((length - 1) >> 9) + 1; // count is in sectors
|
||||
fis->count0 = (count ) & 0xff;
|
||||
fis->count1 = (count >> 8) & 0xff;
|
||||
|
||||
log::debug(logs::driver, "Reading %d sectors, starting from %d (0x%lx)",
|
||||
count, sector, sector*512);
|
||||
|
||||
m_pending[slot].type = command_type::read;
|
||||
m_pending[slot].offset = offset % 512;
|
||||
m_pending[slot].count = 0;
|
||||
m_pending[slot].data = dest;
|
||||
if(issue_command(slot))
|
||||
return slot;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t
|
||||
port::read(uint64_t offset, size_t length, void *dest)
|
||||
{
|
||||
int slot = read_async(offset, length, dest);
|
||||
|
||||
int timeout = 0;
|
||||
while (m_pending[slot].type == command_type::read) {
|
||||
if (timeout++ > 5) {
|
||||
return 0;
|
||||
}
|
||||
asm("hlt");
|
||||
}
|
||||
kassert(m_pending[slot].type == command_type::finished,
|
||||
"Read got unexpected command type");
|
||||
|
||||
size_t count = m_pending[slot].count;
|
||||
m_pending[slot].type = command_type::none;
|
||||
m_pending[slot].count = 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
port::identify_async()
|
||||
{
|
||||
fis_register_h2d *fis;
|
||||
int slot = make_command(512, &fis);
|
||||
if (slot < 0)
|
||||
return 0;
|
||||
|
||||
fis->command = ata_cmd::identify;
|
||||
|
||||
m_pending[slot].type = command_type::identify;
|
||||
m_pending[slot].offset = 0;
|
||||
m_pending[slot].count = 0;
|
||||
m_pending[slot].data = 0;
|
||||
|
||||
if(issue_command(slot))
|
||||
return slot;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
port::sata_reconnect()
|
||||
{
|
||||
m_data->serial_control |= 1;
|
||||
io_wait(1000); // About 1ms
|
||||
m_data->serial_control &= ~1;
|
||||
}
|
||||
|
||||
bool
|
||||
port::issue_command(int slot)
|
||||
{
|
||||
const int max_tries = 10;
|
||||
int tries = 0;
|
||||
while (busy()) {
|
||||
if (++tries == max_tries) {
|
||||
log::warn(logs::driver, "AHCI port was busy too long");
|
||||
free_command(slot);
|
||||
return false;
|
||||
}
|
||||
io_wait();
|
||||
}
|
||||
|
||||
// Set bit in CI. Note that only new bits should be written, not
|
||||
// previous state.
|
||||
m_data->cmd_issue = (1 << slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
port::handle_interrupt()
|
||||
{
|
||||
log::debug(logs::driver, "AHCI port %d got an interrupt", m_index);
|
||||
|
||||
// TODO: handle other states in interrupt_status
|
||||
|
||||
uint32_t is = m_data->interrupt_status;
|
||||
|
||||
if (is & 0x00000040) {
|
||||
// Port connect status change: For now clear the "exchange"
|
||||
// bit in SERR, this should probably kick off diagnostics.
|
||||
m_data->serial_error = 0x04000000;
|
||||
identify_async();
|
||||
}
|
||||
|
||||
if (is & 0x40000000) {
|
||||
log::error(logs::driver, "AHCI task file error");
|
||||
dump();
|
||||
kassert(0, "Task file error");
|
||||
}
|
||||
|
||||
log::debug(logs::driver, "AHCI interrupt status: %08lx %08lx",
|
||||
m_data->interrupt_status, m_data->serial_error);
|
||||
|
||||
uint32_t ci = m_data->cmd_issue;
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
// Skip commands still listed as "issued"
|
||||
if (ci & (1 << i)) continue;
|
||||
|
||||
// Any commands not still listed as "issued" that are still pending for
|
||||
// the driver are now finished, so handle them.
|
||||
pending &p = m_pending[i];
|
||||
switch (p.type) {
|
||||
case command_type::read:
|
||||
finish_read(i);
|
||||
break;
|
||||
case command_type::identify:
|
||||
finish_identify(i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the whole status register to mark it as handled
|
||||
m_data->interrupt_status = m_data->interrupt_status;
|
||||
}
|
||||
|
||||
void
|
||||
port::finish_read(int slot)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
|
||||
size_t count = 0;
|
||||
void *p = m_pending[slot].data;
|
||||
uint8_t offset = m_pending[slot].offset;
|
||||
for (int i = 0; i < ent.prd_table_length; ++i) {
|
||||
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
|
||||
|
||||
uintptr_t phys =
|
||||
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
|
||||
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
|
||||
|
||||
void *mem = kutil::offset_pointer(pm->offset_virt(phys), offset);
|
||||
|
||||
log::debug(logs::driver, "Reading PRD %2d: %016lx->%016lx [%lxb]", i, mem, p, prd_len);
|
||||
|
||||
kutil::memcpy(p, mem, prd_len);
|
||||
p = kutil::offset_pointer(p, prd_len - offset);
|
||||
count += (prd_len - offset);
|
||||
offset = 0;
|
||||
|
||||
pm->unmap_pages(mem, page_count(prd_len));
|
||||
}
|
||||
|
||||
m_pending[slot].count = count;
|
||||
m_pending[slot].type = command_type::finished;
|
||||
m_pending[slot].data = nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
ident_strcpy(uint16_t *from, int words, char *dest)
|
||||
{
|
||||
for (int i = 0; i < words; ++i) {
|
||||
*dest++ = *from >> 8;
|
||||
*dest++ = *from & 0xff;
|
||||
from++;
|
||||
}
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
void
|
||||
port::finish_identify(int slot)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
|
||||
kassert(ent.prd_table_length == 1, "AHCI identify used multiple PRDs");
|
||||
|
||||
size_t prd_len = (cmdt.entries[0].byte_count & 0x7fffffff) + 1;
|
||||
|
||||
uintptr_t phys =
|
||||
static_cast<uintptr_t>(cmdt.entries[0].data_base_low) |
|
||||
static_cast<uintptr_t>(cmdt.entries[0].data_base_high) << 32;
|
||||
|
||||
log::debug(logs::driver, "Reading ident PRD:");
|
||||
|
||||
uint16_t *mem = reinterpret_cast<uint16_t *>(pm->offset_virt(phys));
|
||||
char string[41];
|
||||
|
||||
ident_strcpy(&mem[10], 10, &string[0]);
|
||||
log::debug(logs::driver, " Device serial: %s", string);
|
||||
|
||||
ident_strcpy(&mem[23], 4, &string[0]);
|
||||
log::debug(logs::driver, " Device version: %s", string);
|
||||
|
||||
ident_strcpy(&mem[27], 20, &string[0]);
|
||||
log::debug(logs::driver, " Device model: %s", string);
|
||||
|
||||
uint32_t sectors = mem[60] | (mem[61] << 16);
|
||||
log::debug(logs::driver, " Max sectors: %xh", sectors);
|
||||
|
||||
uint16_t lb_size = mem[106];
|
||||
log::debug(logs::driver, " lsects per psect: %d %s %s", 1 << (lb_size & 0xf),
|
||||
lb_size & 0x20 ? "multiple logical per physical" : "",
|
||||
lb_size & 0x10 ? "physical > 512b" : "");
|
||||
|
||||
uint32_t b_per_ls = 2 * (mem[117] | (mem[118] << 16));
|
||||
log::debug(logs::driver, " b per lsect: %d", b_per_ls);
|
||||
|
||||
/*
|
||||
for (int i=0; i<256; i += 4)
|
||||
log::debug(logs::driver, " %3d: %04x %3d: %04x %3d: %04x %3d: %04x",
|
||||
i, mem[i], i+1, mem[i+1], i+2, mem[i+2], i+3, mem[i+3]);
|
||||
*/
|
||||
|
||||
pm->unmap_pages(mem, page_count(prd_len));
|
||||
m_pending[slot].type = command_type::none;
|
||||
}
|
||||
|
||||
void
|
||||
port::free_command(int slot)
|
||||
{
|
||||
page_manager *pm = page_manager::get();
|
||||
|
||||
cmd_table &cmdt = m_cmd_table[slot];
|
||||
cmd_list_entry &ent = m_cmd_list[slot];
|
||||
|
||||
for (int i = 0; i < ent.prd_table_length; ++i) {
|
||||
size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1;
|
||||
|
||||
uintptr_t phys =
|
||||
static_cast<uintptr_t>(cmdt.entries[i].data_base_low) |
|
||||
static_cast<uintptr_t>(cmdt.entries[i].data_base_high) << 32;
|
||||
void *mem = pm->offset_virt(phys);
|
||||
pm->unmap_pages(mem, page_count(prd_len));
|
||||
}
|
||||
|
||||
ent.prd_table_length = max_prd_count;
|
||||
}
|
||||
|
||||
void
|
||||
port::rebase()
|
||||
{
|
||||
kassert(!m_cmd_list, "AHCI port called rebase() twice");
|
||||
|
||||
page_manager *pm = page_manager::get();
|
||||
size_t prd_size = sizeof(cmd_table) + (max_prd_count * sizeof(prdt_entry));
|
||||
|
||||
// 1 for FIS + command list, N for PRD
|
||||
size_t pages = 1 + page_count(prd_size * 32);
|
||||
|
||||
void *mem = pm->map_offset_pages(pages);
|
||||
uintptr_t phys = pm->offset_phys(mem);
|
||||
|
||||
log::debug(logs::driver, "Rebasing address for AHCI port %d to %lx [%d]", m_index, mem, pages);
|
||||
|
||||
stop_commands();
|
||||
|
||||
// Command list
|
||||
m_cmd_list = reinterpret_cast<cmd_list_entry *>(mem);
|
||||
m_data->cmd_base_low = phys & 0xffffffff;
|
||||
m_data->cmd_base_high = phys >> 32;
|
||||
kutil::memset(mem, 0, 1024);
|
||||
|
||||
mem = kutil::offset_pointer(mem, 32 * sizeof(cmd_list_entry));
|
||||
phys = pm->offset_phys(mem);
|
||||
|
||||
// FIS
|
||||
m_fis = mem;
|
||||
m_data->fis_base_low = phys & 0xffffffff;
|
||||
m_data->fis_base_high = phys >> 32;
|
||||
kutil::memset(mem, 0, 256);
|
||||
|
||||
mem = page_align(kutil::offset_pointer(mem, 256));
|
||||
phys = pm->offset_phys(mem);
|
||||
|
||||
// Command table
|
||||
m_cmd_table = reinterpret_cast<cmd_table *>(mem);
|
||||
size_t cmdt_len = sizeof(cmd_table) +
|
||||
max_prd_count * sizeof(prdt_entry);
|
||||
|
||||
kutil::memset(m_cmd_table, 0, cmdt_len * 32);
|
||||
|
||||
// set up each entry in the command list to point to the
|
||||
// corresponding command table
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
m_cmd_list[i].prd_table_length = max_prd_count;
|
||||
m_cmd_list[i].cmd_table_base_low = phys & 0xffffffff;
|
||||
m_cmd_list[i].cmd_table_base_high = phys >> 32;
|
||||
|
||||
mem = kutil::offset_pointer(mem, cmdt_len);
|
||||
phys = pm->offset_phys(mem);
|
||||
}
|
||||
|
||||
start_commands();
|
||||
}
|
||||
|
||||
void
|
||||
port::dump()
|
||||
{
|
||||
console *cons = console::get();
|
||||
static const char *regs[] = {
|
||||
" CLB", "+CLB", " FB", " +FB", " IS", " IE",
|
||||
" CMD", nullptr, " TFD", " SIG", "SSTS", "SCTL", "SERR",
|
||||
"SACT", " CI", "SNTF", " FBS", "DEVS"
|
||||
};
|
||||
|
||||
cons->printf("Port Registers:\n");
|
||||
auto *data = reinterpret_cast<volatile uint32_t *>(m_data);
|
||||
for (int i = 0; i < 18; ++i) {
|
||||
if (regs[i]) cons->printf(" %s: %08x\n", regs[i], data[i]);
|
||||
}
|
||||
cons->putc('\n');
|
||||
}
|
||||
|
||||
} // namespace ahci
|
||||
148
src/drivers/ahci/port.h
Normal file
148
src/drivers/ahci/port.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
/// \file port.h
|
||||
/// Definition for AHCI ports
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "kutil/vector.h"
|
||||
#include "block_device.h"
|
||||
|
||||
namespace ahci {
|
||||
|
||||
struct cmd_list_entry;
|
||||
struct cmd_table;
|
||||
struct fis_register_h2d;
|
||||
class hba;
|
||||
enum class sata_signature : uint32_t;
|
||||
enum class port_cmd : uint32_t;
|
||||
struct port_data;
|
||||
|
||||
|
||||
/// A port on an AHCI HBA
|
||||
class port :
|
||||
public block_device
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg device The HBA device this port belongs to
|
||||
/// \arg index Index of the port on its HBA
|
||||
/// \arg data Pointer to the device's registers for this port
|
||||
/// \arg impl Whether this port is marked as implemented in the HBA
|
||||
port(hba *device, uint8_t index, port_data volatile *data, bool impl);
|
||||
|
||||
/// Destructor
|
||||
~port();
|
||||
|
||||
/// Get the index of this port on the HBA
|
||||
/// \returns The port index
|
||||
inline uint8_t index() const { return m_index; }
|
||||
|
||||
enum class state : uint8_t { unimpl, inactive, active };
|
||||
|
||||
/// Get the current state of this device
|
||||
/// \returns An enum representing the state
|
||||
inline state get_state() const { return m_state; }
|
||||
|
||||
/// Check if this device is active
|
||||
/// \returns True if the device state is active
|
||||
inline bool active() const { return m_state == state::active; }
|
||||
|
||||
/// Get the type signature of this device
|
||||
/// \returns An enum representing the type of device
|
||||
inline sata_signature get_type() const { return m_type; }
|
||||
|
||||
/// Update the state of this object from the register data
|
||||
void update();
|
||||
|
||||
/// Return whether the port is currently busy
|
||||
bool busy();
|
||||
|
||||
/// Start command processing from this port
|
||||
void start_commands();
|
||||
|
||||
/// Stop command processing from this port
|
||||
void stop_commands();
|
||||
|
||||
/// Start a read operation from the drive.
|
||||
/// \arg offset Offset to start from
|
||||
/// \arg length Number of bytes to read
|
||||
/// \arg dest A buffer where the data will be placed
|
||||
/// \returns A handle to the read operation, or -1 on error
|
||||
int read_async(uint64_t offset, size_t length, void *dest);
|
||||
|
||||
/// Read from the drive, blocking until finished.
|
||||
/// \arg offset Offset to start from
|
||||
/// \arg length Number of bytes to read
|
||||
/// \arg dest A buffer where the data will be placed
|
||||
/// \returns The number of bytes read
|
||||
virtual size_t read(uint64_t offset, size_t length, void *dest);
|
||||
|
||||
/// Start an identify operation for the drive.
|
||||
/// \returns A handle to the read operation, or -1 on error
|
||||
int identify_async();
|
||||
|
||||
/// Tell the HBA to reconnect to the SATA device. A successful
|
||||
/// reconnect will kick off an identify command.
|
||||
void sata_reconnect();
|
||||
|
||||
/// Handle an incoming interrupt
|
||||
void handle_interrupt();
|
||||
|
||||
/// Dump the port registers to the console
|
||||
void dump();
|
||||
|
||||
private:
|
||||
/// Rebase the port command structures to a new location in system
|
||||
/// memory, to be allocated from the page manager.
|
||||
void rebase();
|
||||
|
||||
/// Initialize a command structure
|
||||
/// \arg length The number of bytes of data needed in the PRDs
|
||||
/// \arg fis [out] The FIS for this command
|
||||
/// \returns The index of the command slot, or -1 if none available
|
||||
int make_command(size_t length, fis_register_h2d **fis);
|
||||
|
||||
/// Send a constructed command to the hardware
|
||||
/// \arg slot The index of the command slot used
|
||||
/// \returns True if the command was successfully sent
|
||||
bool issue_command(int slot);
|
||||
|
||||
/// Free the data structures allocated by command creation
|
||||
/// \arg slot The index of the command slot used
|
||||
void free_command(int slot);
|
||||
|
||||
/// Finish a read command started by `read()`. This will
|
||||
/// free the structures allocated, so `free_command()` is
|
||||
/// not necessary.
|
||||
/// \arg slot The command slot that the read command used
|
||||
void finish_read(int slot);
|
||||
|
||||
/// Finish an identify command started by `identify_async()`.
|
||||
/// This will free the structures allocated, so `free_command()` is
|
||||
/// not necessary.
|
||||
/// \arg slot The command slot that the read command used
|
||||
void finish_identify(int slot);
|
||||
|
||||
sata_signature m_type;
|
||||
uint8_t m_index;
|
||||
state m_state;
|
||||
|
||||
hba *m_hba;
|
||||
port_data volatile *m_data;
|
||||
void *m_fis;
|
||||
cmd_list_entry *m_cmd_list;
|
||||
cmd_table *m_cmd_table;
|
||||
|
||||
enum class command_type : uint8_t { none, read, write, identify, finished };
|
||||
|
||||
struct pending
|
||||
{
|
||||
command_type type;
|
||||
uint8_t offset;
|
||||
size_t count;
|
||||
void *data;
|
||||
};
|
||||
|
||||
kutil::vector<pending> m_pending;
|
||||
};
|
||||
|
||||
} // namespace ahci
|
||||
38
src/drivers/nulldrv/main.cpp
Normal file
38
src/drivers/nulldrv/main.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "j6/types.h"
|
||||
#include "j6/errors.h"
|
||||
|
||||
extern "C" {
|
||||
j6_status_t getpid(uint64_t *);
|
||||
j6_status_t fork(uint64_t *);
|
||||
j6_status_t sleep(uint64_t til);
|
||||
j6_status_t debug();
|
||||
j6_status_t message(const char *msg);
|
||||
|
||||
int main(int, const char **);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
uint64_t pid = 0;
|
||||
uint64_t child = 0;
|
||||
|
||||
j6_status_t result = fork(&child);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
message("hello from nulldrv!");
|
||||
|
||||
result = getpid(&pid);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
for (int i = 1; i < 5; ++i)
|
||||
sleep(i*10);
|
||||
|
||||
return pid;
|
||||
}
|
||||
81
src/drivers/nulldrv/main.s
Normal file
81
src/drivers/nulldrv/main.s
Normal file
@@ -0,0 +1,81 @@
|
||||
section .bss
|
||||
mymessage:
|
||||
resq 1024
|
||||
|
||||
extern main
|
||||
extern exit
|
||||
|
||||
section .text
|
||||
global getpid
|
||||
getpid:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; address of out var should already be in rdi
|
||||
mov rax, 0x13 ; getpid syscall
|
||||
syscall ; result is now already in rax, so just return
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
global debug
|
||||
debug:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
mov rax, 0x00 ; debug syscall
|
||||
syscall
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
global sleep
|
||||
sleep:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
mov rax, 0x16 ; sleep syscall
|
||||
syscall
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
global fork
|
||||
fork:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; address of out var should already be in rdi
|
||||
mov rax, 0x12
|
||||
syscall ; result left in rax
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
|
||||
global message
|
||||
message:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; message should already be in rdi
|
||||
mov rax, 0x14
|
||||
syscall
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
|
||||
global _start
|
||||
_start:
|
||||
xor rbp, rbp ; Sentinel rbp
|
||||
push rbp
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
mov rdi, 0
|
||||
mov rsi, 0
|
||||
call main
|
||||
|
||||
mov rdi, rax
|
||||
call exit
|
||||
48
src/include/elf.h
Normal file
48
src/include/elf.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
/* elf.h - basic defines for external code written assuming <elf.h> works. Only
|
||||
* Elf64 values are included.
|
||||
*/
|
||||
|
||||
typedef uint16_t Elf64_Half;
|
||||
typedef uint32_t Elf64_Word;
|
||||
typedef int32_t Elf64_Sword;
|
||||
typedef uint64_t Elf64_Xword;
|
||||
typedef int64_t Elf64_Sxword;
|
||||
typedef uint64_t Elf64_Addr;
|
||||
typedef uint64_t Elf64_Off;
|
||||
typedef uint16_t Elf64_Section;
|
||||
typedef Elf64_Half Elf64_Versym;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Addr r_offset;
|
||||
Elf64_Xword r_info;
|
||||
} Elf64_Rel;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Addr r_offset;
|
||||
Elf64_Word r_info;
|
||||
Elf64_Sword r_addend;
|
||||
} Elf64_Rela;
|
||||
|
||||
typedef struct {
|
||||
Elf64_Sxword d_tag;
|
||||
union {
|
||||
Elf64_Xword d_val;
|
||||
Elf64_Addr d_ptr;
|
||||
} d_un;
|
||||
} Elf64_Dyn;
|
||||
|
||||
#define ELF64_R_TYPE(x) ((x) & 0xffffffff)
|
||||
|
||||
typedef enum {
|
||||
DT_NULL = 0,
|
||||
DT_RELA = 7,
|
||||
DT_RELASZ = 8,
|
||||
DT_RELAENT = 9
|
||||
} ElfDynTag;
|
||||
|
||||
typedef enum {
|
||||
R_X86_64_NONE = 0,
|
||||
R_X86_64_RELATIVE = 8
|
||||
} Elf_x86_64_RelType;
|
||||
16
src/include/j6/errors.h
Normal file
16
src/include/j6/errors.h
Normal 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
18
src/include/j6/signals.h
Normal 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
20
src/include/j6/types.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
/// \file types.h
|
||||
/// Basic kernel types exposed to userspace
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/// All interactable kernel objects have a uniqe kernel object id
|
||||
typedef uint64_t j6_koid_t;
|
||||
|
||||
/// Syscalls return status as this type
|
||||
typedef uint64_t j6_status_t;
|
||||
|
||||
/// Handles are references and capabilities to other objects
|
||||
typedef uint32_t j6_handle_t;
|
||||
|
||||
/// Some objects have signals, which are a bitmap of 64 possible signals
|
||||
typedef uint64_t j6_signal_t;
|
||||
|
||||
/// The rights of a handle/capability are a bitmap of 64 possible rights
|
||||
typedef uint64_t j6_rights_t;
|
||||
96
src/include/kernel_args.h
Normal file
96
src/include/kernel_args.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace kernel {
|
||||
namespace args {
|
||||
|
||||
constexpr uint32_t magic = 0x600dda7a;
|
||||
constexpr uint16_t version = 1;
|
||||
|
||||
enum class mod_flags : uint32_t
|
||||
{
|
||||
debug = 0x00000001
|
||||
};
|
||||
|
||||
enum class mod_type : uint32_t {
|
||||
unknown,
|
||||
|
||||
kernel,
|
||||
initrd,
|
||||
|
||||
memory_map,
|
||||
page_tables,
|
||||
framebuffer,
|
||||
|
||||
max
|
||||
};
|
||||
|
||||
enum class mode : uint8_t {
|
||||
normal,
|
||||
debug
|
||||
};
|
||||
|
||||
struct module {
|
||||
void *location;
|
||||
size_t size;
|
||||
mod_type type;
|
||||
mod_flags flags;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
|
||||
enum class mem_type : uint32_t {
|
||||
free,
|
||||
args,
|
||||
kernel,
|
||||
module,
|
||||
table,
|
||||
acpi,
|
||||
uefi_runtime,
|
||||
mmio,
|
||||
persistent
|
||||
};
|
||||
|
||||
/// Structure to hold an entry in the memory map.
|
||||
struct mem_entry
|
||||
{
|
||||
uintptr_t start;
|
||||
size_t pages;
|
||||
mem_type type;
|
||||
uint32_t attr;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
|
||||
struct header {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
|
||||
mode mode;
|
||||
|
||||
uint8_t _reserved0;
|
||||
|
||||
void *pml4;
|
||||
void *page_table_cache;
|
||||
uint32_t num_free_tables;
|
||||
|
||||
uint32_t num_modules;
|
||||
module *modules;
|
||||
|
||||
mem_entry *mem_map;
|
||||
size_t num_map_entries;
|
||||
|
||||
void *runtime_services;
|
||||
void *acpi_table;
|
||||
}
|
||||
__attribute__((aligned(alignof(max_align_t))));
|
||||
#pragma pack(pop)
|
||||
|
||||
} // namespace args
|
||||
|
||||
using entrypoint = __attribute__((sysv_abi)) void (*)(args::header *);
|
||||
|
||||
} // namespace kernel
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DATA_HEADER_MAGIC 0x600dda7a
|
||||
#define DATA_HEADER_VERSION 1
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct popcorn_data {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint16_t length;
|
||||
|
||||
uint32_t _reserved0;
|
||||
uint32_t flags;
|
||||
|
||||
void *font;
|
||||
size_t font_length;
|
||||
|
||||
void *data;
|
||||
size_t data_length;
|
||||
|
||||
void *log;
|
||||
size_t log_length;
|
||||
|
||||
void *memory_map;
|
||||
size_t memory_map_length;
|
||||
size_t memory_map_desc_size;
|
||||
|
||||
void *runtime;
|
||||
|
||||
void *acpi_table;
|
||||
|
||||
void *frame_buffer;
|
||||
size_t frame_buffer_size;
|
||||
uint32_t hres;
|
||||
uint32_t vres;
|
||||
uint32_t rmask;
|
||||
uint32_t gmask;
|
||||
uint32_t bmask;
|
||||
uint32_t _reserved1;
|
||||
}
|
||||
__attribute__((aligned(alignof(max_align_t))));
|
||||
#pragma pack(pop)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user