Compare commits
857 Commits
main
...
a9412aa741
| Author | SHA1 | Date | |
|---|---|---|---|
| a9412aa741 | |||
| 8aab3a62cc | |||
| b0b39c56ba | |||
| 055b1a2a1a | |||
| d99ef11e48 | |||
| 732eb9da41 | |||
| 8faad47843 | |||
| aa4f3046ff | |||
| b4775fbe75 | |||
| 8a5753b0fe | |||
| 1a75262b04 | |||
| 39801e51da | |||
| 6e3f176457 | |||
| 447da99f0b | |||
| a3a287f5ca | |||
| c47f6a78bc | |||
| 430a006acf | |||
| 845ed66e5e | |||
| 2191ca46bb | |||
| cec04a2858 | |||
| b87351f6d5 | |||
| 464737fbe9 | |||
| 8b4f661acb | |||
| 27963febc0 | |||
| 6d4b684219 | |||
| 670c787af3 | |||
| a0fbf18d3b | |||
| 1acc0abf2e | |||
| c20f773c5d | |||
| a46b2f43d9 | |||
| a20f8607de | |||
| af330f7b8e | |||
| e33b3bcdff | |||
| 181d139c7d | |||
| 639fd8804c | |||
| cbb2c37e00 | |||
| ab4f033385 | |||
| 1ed08f62d3 | |||
| 8164c15b6c | |||
| f9bf47ab30 | |||
| e5ffadb109 | |||
| 061d10e635 | |||
| 6d899aa6ce | |||
| 120f7329b1 | |||
| 4f25c20c97 | |||
| 5e396851f4 | |||
| a44482639d | |||
| 3bac19e518 | |||
| 4dbe15aa0e | |||
| 1c5985148c | |||
| ce2461d0e8 | |||
| 4e785a133c | |||
| 26c7aee327 | |||
| 0405461742 | |||
| 8a10853ba7 | |||
| 5d34cebeca | |||
| 3d899d2e44 | |||
| f72fdeeb59 | |||
| 382f9d9bb3 | |||
| bc1d1bf919 | |||
| f05b9a6877 | |||
| ea5ed3001e | |||
| f312c3a4d7 | |||
| 1af3ca19ab | |||
| 09fcc613c7 | |||
| 3940f53231 | |||
| 0757834176 | |||
| 3bffbe330d | |||
| 8d583c8b67 | |||
| 99bde9aa49 | |||
| 98d702ac60 | |||
| 1ec341e2dd | |||
| d09310f388 | |||
| 126edea119 | |||
| 74bfb930f2 | |||
| 091c5b6a66 | |||
| fda4a4ad24 | |||
| 8bb47aee02 | |||
| 1f794e4ac0 | |||
| e85f9ac6a1 | |||
| 6ee5576dcc | |||
| b890e2fc14 | |||
| 4f4b8ada8c | |||
| 9e4adc1264 | |||
| 7a54a088b4 | |||
| 15bb1804ef | |||
| e8890062d6 | |||
| 1e2c2fb973 | |||
| 988a4e1cd8 | |||
| adbbdf73c4 | |||
| e8d20bc653 | |||
| 00ee86920a | |||
| 51ad27ea3c | |||
| df69612bb1 | |||
| 5bfeb9f3ca | |||
| db5c24b2a5 | |||
| 781c950af6 | |||
| e2e5c31d54 | |||
| be3efb0b92 | |||
| 792bb2df1c | |||
| e01928d186 | |||
| 48980b56ab | |||
| b767317a7a | |||
| 6f8fce94a0 | |||
| 31aa157201 | |||
| 5977341610 | |||
| 76f17bd569 | |||
| 6b1b3d333c | |||
| cb65be3e33 | |||
| dafc016293 | |||
| c7b6fc950a | |||
| 45a6783c3d | |||
| 6b180da4e8 | |||
| cf4f5f64a5 | |||
| 5630f64175 | |||
| 1d61bccfc3 | |||
| f842a9255f | |||
| 72f3c378dd | |||
| 39be6ab099 | |||
| 773dcdd3a2 | |||
| f0820e6f24 | |||
| a2b5e71654 | |||
| d3e5c8e0aa | |||
| f4b901a646 | |||
| 790064d248 | |||
| ab8b77406d | |||
| 1b9e14a53b | |||
| d2cfc843e4 | |||
| 521513bed2 | |||
| 400db176d1 | |||
| 211cad03ff | |||
| 8a9816d6e0 | |||
| 03d2bf4002 | |||
| f071240b33 | |||
| 27364f64a6 | |||
| bcf62c5f2e | |||
| 4d6322ff9c | |||
| 2eef581737 | |||
|
|
7ce8e2d57b | ||
| e780eaa45f | |||
| 44cb0af64f | |||
| bb0989fdef | |||
|
|
f0b6844feb | ||
|
|
b712c70c75 | ||
|
|
797ca65c66 | ||
|
|
762b7a4276 | ||
|
|
a511441f7e | ||
|
|
cd61d710df | ||
|
|
f88ad7efcd | ||
|
|
38320018dc | ||
|
|
d883d212b1 | ||
|
|
dedb2a2399 | ||
|
|
8604c55de8 | ||
|
|
e949e8550c | ||
|
|
eb5c6cf736 | ||
|
|
94ce2c97be | ||
|
|
3f164c6b82 | ||
|
|
f953f3d3ff | ||
|
|
9fc75fe445 | ||
|
|
7a5bb6a56b | ||
|
|
d54c6b7f6b | ||
|
|
db5d6a7f80 | ||
|
|
4a92f44cf6 | ||
|
|
376b9f7272 | ||
|
|
7e9e4c47ae | ||
|
|
603fc200e6 | ||
|
|
c11e84b248 | ||
|
|
be13120554 | ||
|
|
9943edad5a | ||
|
|
f4049be975 | ||
|
|
6cf7e01fe9 | ||
|
|
b51d2f5295 | ||
|
|
49d941ad65 | ||
|
|
a66c3bdae5 | ||
|
|
547eabb403 | ||
|
|
79851394b3 | ||
|
|
fcdc922343 | ||
|
|
0b11d76576 | ||
|
|
f7097398ca | ||
|
|
85b1252b9e | ||
|
|
1cd12b5f16 | ||
|
|
c84b66d078 | ||
|
|
27adb9486b | ||
|
|
8d5369fafe | ||
|
|
feafc57b63 | ||
|
|
f9b347f9d9 | ||
|
|
6e1825d6b4 | ||
|
|
ec2baeb276 | ||
|
|
6cb8bda6e1 | ||
|
|
05e57801e7 | ||
|
|
b924c85669 | ||
|
|
09c1aa44d8 | ||
|
|
1d470fb5ba | ||
|
|
b4e4f7a6cc | ||
|
|
55d30a7cc3 | ||
|
|
b62186441b | ||
|
|
8caba1e774 | ||
|
|
7bdb428938 | ||
|
|
3ea707c0e7 | ||
|
|
18d582c6ce | ||
|
|
8b2bb95b81 | ||
|
|
2ef496a24a | ||
|
|
c0a89e8951 | ||
|
|
fc953df281 | ||
|
|
fe2dca16f0 | ||
|
|
f662aa6da2 | ||
|
|
fee3677fb9 | ||
|
|
4818c6e3dd | ||
|
|
971eb737c1 | ||
|
|
9a3286ad57 | ||
|
|
c9e09b840e | ||
|
|
8136248a67 | ||
|
|
0d67e46041 | ||
|
|
bc1087f5a7 | ||
|
|
3a9c6fc51a | ||
|
|
7774f56ab6 | ||
|
|
14a608effd | ||
|
|
5fae3cec2a | ||
|
|
b0c22b61ec | ||
|
|
82b049204d | ||
|
|
aa59142bfa | ||
|
|
c55bb77ff5 | ||
|
|
9b4e2e1e21 | ||
|
|
202c38a65a | ||
|
|
720bc418a6 | ||
|
|
d77f455065 | ||
|
|
7e08f0fb66 | ||
|
|
9e4a87021c | ||
|
|
5887f6bcaa | ||
|
|
5d67559e33 | ||
|
|
e631eb7a7a | ||
|
|
64ff4c232a | ||
|
|
0ec4f970f7 | ||
|
|
afe95be42f | ||
|
|
14ac1c9904 | ||
|
|
7c11ea3694 | ||
|
|
c1fd341698 | ||
|
|
0deab1be51 | ||
|
|
5a623adaa6 | ||
|
|
4363118d9d | ||
|
|
d613da4b6c | ||
|
|
f46f5b2050 | ||
|
|
d9c4114b5f | ||
|
|
ddd36af0f1 | ||
|
|
35fd30ee29 | ||
|
|
4a0652684c | ||
|
|
33c81f00b7 | ||
|
|
55714b90cd | ||
|
|
9b47603a1d | ||
|
|
2e2a913412 | ||
|
|
42b85dc33b | ||
|
|
a15ffcb071 | ||
|
|
692b77fb8e | ||
|
|
044378cfa3 | ||
|
|
f1d4d5f995 | ||
|
|
19d0fb6fcd | ||
|
|
7933265095 | ||
|
|
d810644018 | ||
|
|
a7bfc1c2ec | ||
|
|
104b2740bc | ||
|
|
65501837b7 | ||
|
|
3ed0a54847 | ||
|
|
cbd2519b9a | ||
|
|
e8a73f9696 | ||
|
|
1a0d114861 | ||
|
|
5c3497681e | ||
|
|
b05cf9ef09 | ||
|
|
a74eb357a1 | ||
|
|
8eb71084f0 | ||
|
|
ef1077fd7b | ||
|
|
f1ba5c7e0f | ||
|
|
97ea4986af | ||
|
|
25c021c15b | ||
|
|
2bf12a52d1 | ||
|
|
6ada36d3cb | ||
|
|
42b90ae76c | ||
|
|
ccc61cb10c | ||
|
|
be5b81740e | ||
|
|
e7458ca10a | ||
|
|
b10168eb1c | ||
|
|
30463c9082 | ||
|
|
dc7391dc89 | ||
|
|
eb98d70a0b | ||
|
|
dcd8374b89 | ||
|
|
8e4216215e | ||
|
|
5bd7099b96 | ||
|
|
9a63d3b2da | ||
|
|
bf02330db9 | ||
|
|
5806a6484f | ||
|
|
0fa5401800 | ||
|
|
b30f4cbfb5 | ||
|
|
ba37183c9c | ||
|
|
2f298a1979 | ||
|
|
8c282a5d83 | ||
|
|
d34c0a5abe | ||
|
|
8f3348cf2b | ||
|
|
38c0bc7bae | ||
|
|
313b00b11f | ||
|
|
165a379c73 | ||
|
|
a7f37236bf | ||
|
|
51532336b0 | ||
|
|
03d4b47f63 | ||
|
|
8b57edde6b | ||
|
|
778778fede | ||
|
|
f7449c4ab9 | ||
|
|
fd2bcc9156 | ||
|
|
a5b1555725 | ||
|
|
e74fdbc55b | ||
|
|
008c777a9f | ||
|
|
d8a9d4a24e | ||
|
|
bc0e1fa898 | ||
|
|
17f1737c9a | ||
|
|
868444f043 | ||
|
|
fdae253695 | ||
|
|
d4adcff958 | ||
|
|
2c59c9a3cc | ||
|
|
3a59a64355 | ||
|
|
9363c1cdaf | ||
|
|
198e6d7cf6 | ||
|
|
07ee898f4f | ||
|
|
6feb8a99d2 | ||
|
|
e57c1fc9fc | ||
|
|
a11b5ae41f | ||
|
|
c67a7cec5b | ||
|
|
91f04ce250 | ||
|
|
926df2b276 | ||
|
|
9fe878bbec | ||
|
|
217dbca7b7 | ||
|
|
13852e865c | ||
|
|
679d47131d | ||
|
|
8b1bccb79b | ||
|
|
e86e755c51 | ||
|
|
8ec6d4c9fc | ||
|
|
08cdf88586 | ||
|
|
6f7d97cf94 | ||
|
|
5e434f5131 | ||
|
|
a152d0aac5 | ||
|
|
879706e6e9 | ||
|
|
00f1f30a08 | ||
|
|
a5813f9ba5 | ||
|
|
5652af3384 | ||
|
|
22cd9af8cc | ||
|
|
a9cf9bceef | ||
|
|
6c0f864a6e | ||
|
|
e4509d9482 | ||
|
|
0f23e1f0f4 | ||
|
|
642a6aa4ad | ||
|
|
432c296b7b | ||
|
|
b576d373c4 | ||
|
|
522aa8e762 | ||
|
|
146802fa4c | ||
|
|
cc8af25d73 | ||
|
|
f5f4bf58ad | ||
|
|
3784da0d18 | ||
|
|
9eb72f4392 | ||
|
|
f7bf6d5e62 | ||
|
|
adb14ba373 | ||
|
|
7391d91317 | ||
|
|
2149cec29f | ||
|
|
ad756c36fc | ||
|
|
b56316e9da | ||
|
|
a989c44211 | ||
|
|
217e5f81cc | ||
|
|
5f2549b198 | ||
|
|
dcd4d0daeb | ||
|
|
faf4220b38 | ||
|
|
193ddaa2f6 | ||
|
|
46eb27883a | ||
|
|
2db7cdb71e | ||
|
|
5411c5aa4a | ||
|
|
f8a1a10897 | ||
|
|
adbe13938e | ||
|
|
4d5b14753d | ||
|
|
ba9fa00947 | ||
|
|
98cedf155c | ||
|
|
88e3998664 | ||
|
|
c0c0bbc1bf | ||
|
|
650e1b4fc5 | ||
|
|
6c1ada8d0a | ||
|
|
7d00c2670f | ||
|
|
bca7e9a1e8 | ||
|
|
3748f0304f | ||
|
|
2576bdbd14 | ||
|
|
e341a36287 | ||
|
|
bba09a3cd0 | ||
|
|
985df3532b | ||
|
|
72041a52e8 | ||
|
|
891144dac1 | ||
|
|
41e7b53903 | ||
|
|
6b0920e8c0 | ||
|
|
4285729d5c | ||
|
|
a9c10d0751 | ||
|
|
74c79c7eff | ||
|
|
9174a89971 | ||
|
|
5c94a583bc | ||
|
|
6e1fc2766f | ||
|
|
d3bb00cb55 | ||
|
|
5a5656b2d3 | ||
|
|
1a1e584cba | ||
|
|
65fa05f998 | ||
|
|
2276fc95b8 | ||
|
|
1e173c178d | ||
|
|
773747cf9c | ||
|
|
4972284dde | ||
|
|
45789fda08 | ||
|
|
3b5bc63d1b | ||
|
|
f1089e2b8a | ||
|
|
6d93c1eb92 | ||
|
|
363c325c79 | ||
|
|
583504ebe0 | ||
|
|
b354b77f8b | ||
|
|
74af46cb4a | ||
|
|
19dab08275 | ||
|
|
3840fbf957 | ||
|
|
78c091f7f8 | ||
|
|
274ecbba78 | ||
|
|
683c2a68cd | ||
|
|
ad98181069 | ||
|
|
a549336530 | ||
|
|
4eb95c963d | ||
|
|
22caacd2a9 | ||
|
|
af30d537da | ||
|
|
f1bd26fb92 | ||
|
|
5c6bbcb62f | ||
|
|
21bd87bb07 | ||
|
|
79450df04c | ||
|
|
7f8b7b811e | ||
|
|
3c068aa0ae | ||
|
|
86df258365 | ||
|
|
d99e704728 | ||
|
|
0d620f3e0f | ||
|
|
4dce0f9074 | ||
|
|
54f89cba33 | ||
|
|
de88f60d1a | ||
|
|
f7060970e6 | ||
|
|
e7a98ac6cc | ||
|
|
10544db52e | ||
|
|
5e123031aa | ||
|
|
388f530edd | ||
|
|
d354cccd37 | ||
|
|
714305ef56 | ||
|
|
f83ae1e9c6 | ||
|
|
c38e8de6b5 | ||
|
|
97638f7ade | ||
|
|
326a30d1af | ||
|
|
32e1473c94 | ||
|
|
bf617036c7 | ||
|
|
ce55422a24 | ||
|
|
388cc7c3bb | ||
|
|
37f9404d93 | ||
|
|
38dff41e25 | ||
|
|
d360340b9e | ||
|
|
0f63cfa43f | ||
|
|
537780ee1e | ||
|
|
4ca99fcb4e | ||
|
|
eb7ee13f43 | ||
|
|
b2de706693 | ||
|
|
6a8180470d | ||
|
|
12d56be5cc | ||
|
|
bb4d81a4fa | ||
|
|
b254ade69b | ||
|
|
ef4ebaa969 | ||
|
|
99f8133b91 | ||
|
|
51eb44bf40 | ||
|
|
a0be415e09 | ||
|
|
071da18fa3 | ||
|
|
c62e820bcf | ||
|
|
46c34db6cb | ||
|
|
25a2a4879c | ||
|
|
8be28012ee | ||
|
|
5aed186827 | ||
|
|
91f812e17f | ||
|
|
f0b22c48b2 | ||
|
|
52c4eebd77 | ||
|
|
24f0d26fce | ||
|
|
825ec221b7 | ||
|
|
e31080bce3 | ||
|
|
7a5d5cabad | ||
|
|
f7de310889 | ||
|
|
e209ca7c82 | ||
|
|
ee8de77a90 | ||
|
|
db49cbd6e2 | ||
|
|
e001eecb7b | ||
|
|
7f34d00c95 | ||
|
|
2c18adbddd | ||
|
|
97c7fc42d1 | ||
|
|
7da0627f8e | ||
|
|
27cef23823 | ||
|
|
b7fc2dc3d0 | ||
|
|
8af390e0f6 | ||
|
|
96d6453ea8 | ||
|
|
2b9900e56e | ||
|
|
86f58f60cb | ||
|
|
064ce568c2 | ||
|
|
6aff459e1c | ||
|
|
0b1b4d8f7e | ||
|
|
3fc2c3529a | ||
|
|
b0e9ab0519 | ||
|
|
668517a723 | ||
|
|
649f08ec78 | ||
|
|
2f2c298c68 | ||
|
|
90e48970e6 | ||
|
|
480842a203 | ||
|
|
5425394880 | ||
|
|
a365813fa9 | ||
|
|
9d64dbd5c2 | ||
|
|
30bb61a775 | ||
|
|
1f36ed0cf9 | ||
|
|
d54c76f88a | ||
|
|
cbb9f47ee5 | ||
|
|
b68d5a5833 | ||
|
|
94d2090777 | ||
|
|
e97585daf9 | ||
|
|
924fc2118c | ||
|
|
51f4c0c750 | ||
|
|
37b93da650 | ||
|
|
35e739dcdd | ||
|
|
8352392b38 | ||
|
|
413f05bfca | ||
|
|
dc1aff58ed | ||
|
|
9f75d9cfe5 | ||
|
|
a42af7e973 | ||
|
|
2ce244d303 | ||
|
|
a775a920d0 | ||
|
|
4f84faf392 | ||
|
|
a4cb5d8360 | ||
|
|
da7f09cf82 | ||
|
|
0166af472b | ||
|
|
884d986bd6 | ||
|
|
59b807189f | ||
|
|
fb1c7015b1 | ||
|
|
d4123f62b2 | ||
|
|
a3f410d1a1 | ||
|
|
1d19a4bffe | ||
|
|
271dd91292 | ||
|
|
9bd4d68f9c | ||
|
|
3c3c7826ef | ||
|
|
2207357b93 | ||
|
|
3a69768eb0 | ||
|
|
afb29ff3ec | ||
|
|
e6f0f891a6 | ||
|
|
36e5aa4683 | ||
|
|
7738050105 | ||
|
|
4bf11ec349 | ||
|
|
d821012eed | ||
|
|
35c6edd989 | ||
|
|
633cb4f282 | ||
|
|
4d4d0e26a9 | ||
|
|
feea2d4024 | ||
|
|
0ffd2a5c1d | ||
|
|
232fdcb82c | ||
|
|
0ccc23d544 | ||
|
|
789ca3db1a | ||
|
|
cb359a05dc | ||
|
|
14982c137a | ||
|
|
0acab11620 | ||
|
|
02f0239016 | ||
|
|
ab61b49aca | ||
|
|
4da1d6fd27 | ||
|
|
909e847369 | ||
|
|
eafa09fecf | ||
|
|
8175348284 | ||
|
|
b2832cb47a | ||
|
|
9f499991c8 | ||
|
|
9a416e8ae8 | ||
|
|
911922c6a3 | ||
|
|
1f2fd59ad5 | ||
|
|
708d401d2c | ||
|
|
ed0dcacab3 | ||
|
|
e86050f343 | ||
|
|
57f7da6ce1 | ||
|
|
93e6455171 | ||
|
|
8f38780197 | ||
|
|
341f7e41e5 | ||
|
|
265fe9c62e | ||
|
|
3b9d60d7cb | ||
|
|
278b873e89 | ||
|
|
e640344d7a | ||
|
|
7151bb86a8 | ||
|
|
2a34391b71 | ||
|
|
3d95cf02f3 | ||
|
|
dd3f34cb2c | ||
|
|
0c316ebfb2 | ||
|
|
282bf24f65 | ||
|
|
f964f6be8d | ||
|
|
0202ccec5f | ||
|
|
636c308993 | ||
|
|
6fdbe6f9c2 | ||
|
|
c19f4c019a | ||
|
|
83eb3dc0cb | ||
|
|
481c8406f3 | ||
|
|
0129619d9a | ||
|
|
e0479b291d | ||
|
|
b847d7dfd5 | ||
|
|
245dff8027 | ||
|
|
fed690a7f2 | ||
|
|
54d981120d | ||
|
|
f79250c4d4 | ||
|
|
78b62776d2 | ||
|
|
bda4614783 | ||
|
|
0ab3332ad3 | ||
|
|
9e0abbc2f0 | ||
|
|
496adb61a4 | ||
|
|
4a4a3bf184 | ||
|
|
f33e78882e | ||
|
|
0ff067bdb7 | ||
|
|
31ac3260ed | ||
|
|
d82c6c2337 | ||
|
|
632b699475 | ||
|
|
85039020d3 | ||
|
|
1a0fdc5a44 | ||
|
|
fb1bab7c30 | ||
|
|
7eb43990ad | ||
|
|
53f4b5a9da | ||
|
|
1d4a6c3a42 | ||
|
|
40083e4aa1 | ||
|
|
bd929bff07 | ||
|
|
cd4a0530fa | ||
|
|
273fdd2235 | ||
|
|
b20f2e8d31 | ||
|
|
e756cde2b1 | ||
|
|
de18d3e64d | ||
|
|
441999ba9f | ||
|
|
dd046b1ace | ||
|
|
926aa238ab | ||
|
|
01fa521a03 | ||
|
|
f31da19266 | ||
|
|
48edc38817 | ||
|
|
ac12132ac0 | ||
|
|
13fabcc1f1 | ||
|
|
67005a80be | ||
|
|
f43bfcb398 | ||
|
|
d5ce4c9d2c | ||
|
|
1cf7ef3de6 | ||
|
|
5248a3fe48 | ||
|
|
812e61ca70 | ||
|
|
2d0a5a9e15 | ||
|
|
f32d594879 | ||
|
|
c2ad76fe4f | ||
|
|
10d9b72da1 | ||
|
|
2fe9af7165 | ||
|
|
0deda83d05 | ||
|
|
ff5bcd4416 | ||
|
|
b65cd1d09b | ||
|
|
bc35a561d3 | ||
|
|
06bc807e34 | ||
|
|
6262e41de1 | ||
|
|
0cb53efa01 | ||
|
|
4e859bedbc | ||
|
|
f139fc2229 | ||
|
|
e48acbb03b | ||
|
|
d1155c968e | ||
|
|
88a2c60065 | ||
|
|
5bfcf6783e | ||
|
|
94f8a657f1 | ||
|
|
7fac2a7526 | ||
|
|
46dcf98fc1 | ||
|
|
58ce907327 | ||
|
|
6ecc8cac0e | ||
|
|
bd95f17426 | ||
|
|
0718bea5a1 | ||
|
|
175f07cd2f | ||
|
|
7b19d6e479 | ||
|
|
77c83e5552 | ||
|
|
b15deb420f | ||
|
|
b38989d594 | ||
|
|
79e6de325f | ||
|
|
163d2e4ba8 | ||
|
|
4f8f3ddc29 | ||
|
|
82a1a29260 | ||
|
|
8a5608df91 | ||
|
|
3f1c0ec91b | ||
|
|
1406a75a92 | ||
|
|
8001493df3 | ||
|
|
8c1f5bfe1e | ||
|
|
ec8b9640e2 | ||
|
|
4ae1332a43 | ||
|
|
10c884bba4 | ||
|
|
c15f031c3f | ||
|
|
1b4c744974 | ||
|
|
d9068eebb5 | ||
|
|
3ad0d2328d | ||
|
|
3f2beb4547 | ||
|
|
be14a6c239 | ||
|
|
3aa0eeb4a3 | ||
|
|
b3eeb6412f | ||
|
|
d38470c8e2 | ||
|
|
a159c980ee | ||
|
|
a993d997ad | ||
|
|
4475e3e184 | ||
|
|
cf0320e47d | ||
|
|
cd03a95128 | ||
|
|
51e299c7e3 | ||
|
|
6f65453fd4 | ||
|
|
67e0c21e0f | ||
|
|
702016a6e3 | ||
|
|
d74ce4950c | ||
|
|
59a682c720 | ||
|
|
7bd4593748 | ||
|
|
c5b006bf19 | ||
|
|
115c44630d | ||
|
|
1dc81abca4 | ||
|
|
5aaf2128a8 | ||
|
|
6aeac17072 | ||
|
|
6d425182a2 | ||
|
|
04ac23b67c | ||
|
|
5494e2c125 | ||
|
|
aba82564f5 | ||
|
|
93abee9c7c | ||
|
|
4034bef42e | ||
|
|
821d065eba | ||
|
|
2614437ba0 | ||
|
|
1aac3a0425 | ||
|
|
a4568f9263 | ||
|
|
a180e72b6f | ||
|
|
2de64b592d | ||
|
|
9c0f3dd996 | ||
|
|
079df39ca8 | ||
|
|
60a99d1d23 | ||
|
|
fe87c08a02 | ||
|
|
8637959289 | ||
|
|
6be53668b9 | ||
|
|
d1b7249803 | ||
|
|
ff7c50c627 | ||
|
|
12779cdef8 | ||
|
|
f5e676b2b7 | ||
|
|
8e5e5f819f | ||
|
|
370a958379 | ||
|
|
0ee7da92a3 | ||
|
|
a0bd3dc54f | ||
|
|
809eb2fe3e | ||
|
|
7010d8614f | ||
|
|
69f13f1896 | ||
|
|
bdaf7cddcb | ||
|
|
8d6db168d6 | ||
|
|
2fabe1949c | ||
|
|
c660df14ec | ||
|
|
e704968f96 | ||
|
|
32359df939 | ||
|
|
641ed23380 | ||
|
|
9f977488fa | ||
|
|
ac0b22f9b9 | ||
|
|
7752b02fb7 | ||
|
|
7610670287 | ||
|
|
31a1b23fb7 | ||
|
|
91c8f9a596 | ||
|
|
f70cd3ea77 | ||
|
|
5db5ff069a | ||
|
|
b8d852ddb7 | ||
|
|
46eedbd1a4 | ||
|
|
e760bafeeb | ||
|
|
12351d5cb6 | ||
|
|
e84f613c4d | ||
|
|
5db4e5b4d5 | ||
|
|
b00dd8d68d | ||
|
|
abbbf7ec15 | ||
|
|
22c72d8c70 | ||
|
|
d0b1457f30 | ||
|
|
a423cd8bb3 | ||
|
|
db076058b9 | ||
|
|
fe10ea85db | ||
|
|
a1100624bf | ||
|
|
28e1497f88 | ||
|
|
8d6111641e | ||
|
|
3ee20d1a84 | ||
|
|
002c2d0aca | ||
|
|
de9f109f2a | ||
|
|
461a5774f8 | ||
|
|
914f718767 | ||
|
|
ebfd092075 | ||
|
|
e322826347 | ||
|
|
3998c5f955 | ||
|
|
762d22ed28 | ||
|
|
f2362b2b78 | ||
|
|
471ac80420 | ||
|
|
4a887fc706 | ||
|
|
e49d3c7bfe | ||
|
|
c5b83074ac | ||
|
|
79090c2648 | ||
|
|
7a6b1c8e47 | ||
|
|
8988ce2766 | ||
|
|
dcde2ae6b4 | ||
|
|
c62849a783 | ||
|
|
f453e8e170 | ||
|
|
990887891e | ||
|
|
5da801d12b | ||
|
|
3a4557d417 | ||
|
|
26d9a3e253 | ||
|
|
eef3631a5a | ||
|
|
88ee35165f | ||
|
|
c8f05b4a7a | ||
|
|
c32584cca0 | ||
|
|
2995a36942 | ||
|
|
c1dbafc101 | ||
|
|
3e8ab8271d | ||
|
|
5b3a00c64f | ||
|
|
0ce9fd8597 | ||
|
|
c9badb5a1c | ||
|
|
a513bc5749 | ||
|
|
5d5487315f | ||
|
|
3508df67b1 | ||
|
|
06ce1f0667 | ||
|
|
f9c3ae7090 | ||
|
|
1fb8c211f0 | ||
|
|
9c7670847e | ||
|
|
a24c2d9be2 | ||
|
|
f4db246658 | ||
|
|
7f90079ea7 | ||
|
|
f4b4987d43 | ||
|
|
7f88ba70d4 | ||
|
|
ac094a48d6 | ||
|
|
779912d8af | ||
|
|
f205b8e883 | ||
|
|
f9a0412e78 | ||
|
|
0ef318633c | ||
|
|
2f8c9746e3 | ||
|
|
6d6bef1b04 | ||
|
|
3dab392296 | ||
|
|
f8a2c60c8d | ||
|
|
770f7716a0 | ||
|
|
a011c0384f | ||
|
|
0d356c5bbc | ||
|
|
d67de70126 | ||
|
|
6f334756c5 | ||
|
|
310713d203 | ||
|
|
7d2ab53baa | ||
|
|
2152b8b95f | ||
|
|
8ac1ae1574 | ||
|
|
4fd21bc303 | ||
|
|
15037bfc7a | ||
|
|
5831c72aad | ||
|
|
a063d041c9 | ||
|
|
3572e9794a | ||
|
|
cef6999dc7 | ||
|
|
6ed9651176 | ||
|
|
3efbe22a1b | ||
|
|
96579b88cf | ||
|
|
2ec18855f2 | ||
|
|
b222581d18 | ||
|
|
a8e3ee6f19 | ||
|
|
a083e588ba | ||
|
|
9b500842a0 | ||
|
|
b21348379f | ||
|
|
633055293e | ||
|
|
ae9d618803 | ||
|
|
9c744dfc44 | ||
|
|
faf1b661bb | ||
|
|
22e45278a2 | ||
|
|
43f4657566 | ||
|
|
70f2908056 | ||
|
|
ef381d0600 | ||
|
|
cfa87526a7 | ||
|
|
39b560fde3 | ||
|
|
0c582b4490 | ||
|
|
61caf566fc | ||
|
|
76d5364a55 | ||
|
|
5224df321e |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,7 +1,5 @@
|
||||
*.img
|
||||
isodir
|
||||
sysroot
|
||||
.vscode/
|
||||
.idea/
|
||||
bochsrc
|
||||
bx_enh_dbg.ini
|
||||
build/
|
||||
base/
|
||||
script/fakeroot-context
|
||||
|
||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[submodule "kernel/lai"]
|
||||
path = kernel/lai
|
||||
url = https://github.com/managarm/lai.git
|
||||
ignore = untracked
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <BAN/Memory.h>
|
||||
|
||||
void* operator new(size_t size) { return BAN::allocator(size); }
|
||||
void* operator new[](size_t size) { return BAN::allocator(size); }
|
||||
|
||||
void operator delete(void* addr) { BAN::deallocator(addr); }
|
||||
void operator delete[](void* addr) { BAN::deallocator(addr); }
|
||||
void operator delete(void* addr, size_t) { BAN::deallocator(addr); }
|
||||
void operator delete[](void* addr, size_t) { BAN::deallocator(addr); }
|
||||
9
BAN/BAN/New.cpp
Normal file
9
BAN/BAN/New.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <BAN/New.h>
|
||||
|
||||
void* operator new(size_t size) { return BAN::allocator(size); }
|
||||
void* operator new[](size_t size) { return BAN::allocator(size); }
|
||||
|
||||
void operator delete(void* addr) { BAN::deallocator(addr); }
|
||||
void operator delete[](void* addr) { BAN::deallocator(addr); }
|
||||
void operator delete(void* addr, size_t) { BAN::deallocator(addr); }
|
||||
void operator delete[](void* addr, size_t) { BAN::deallocator(addr); }
|
||||
@@ -1,102 +1,109 @@
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Memory.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
String::String()
|
||||
{
|
||||
MUST(copy_impl(""sv));
|
||||
}
|
||||
|
||||
String::String(const String& other)
|
||||
{
|
||||
MUST(copy_impl(other.sv()));
|
||||
*this = other;
|
||||
}
|
||||
|
||||
String::String(String&& other)
|
||||
{
|
||||
move_impl(move(other));
|
||||
*this = move(other);
|
||||
}
|
||||
|
||||
String::String(StringView other)
|
||||
{
|
||||
MUST(copy_impl(other));
|
||||
*this = other;
|
||||
}
|
||||
|
||||
String::~String()
|
||||
{
|
||||
BAN::deallocator(m_data);
|
||||
clear();
|
||||
}
|
||||
|
||||
String& String::operator=(const String& other)
|
||||
{
|
||||
MUST(copy_impl(other.sv()));
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(String&& other)
|
||||
{
|
||||
BAN::deallocator(m_data);
|
||||
move_impl(move(other));
|
||||
clear();
|
||||
|
||||
if (other.has_sso())
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
else
|
||||
{
|
||||
m_storage.general_storage = other.m_storage.general_storage;
|
||||
m_has_sso = false;
|
||||
}
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_size = 0;
|
||||
other.m_storage.sso_storage = SSOStorage();
|
||||
other.m_has_sso = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(StringView other)
|
||||
{
|
||||
MUST(copy_impl(other));
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size());
|
||||
m_size = other.size();
|
||||
data()[m_size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::push_back(char ch)
|
||||
ErrorOr<void> String::push_back(char c)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 2));
|
||||
m_data[m_size] = ch;
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
data()[m_size] = c;
|
||||
m_size++;
|
||||
m_data[m_size] = '\0';
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::insert(char ch, size_type index)
|
||||
ErrorOr<void> String::insert(char c, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1 + 1));
|
||||
memmove(m_data + index + 1, m_data + index, m_size - index);
|
||||
m_data[index] = ch;
|
||||
m_size += 1;
|
||||
m_data[m_size] = '\0';
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
memmove(data() + index + 1, data() + index, m_size - index);
|
||||
data()[index] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::insert(StringView other, size_type index)
|
||||
ErrorOr<void> String::insert(StringView str, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
||||
memmove(m_data + index + other.size(), m_data + index, m_size - index);
|
||||
memcpy(m_data + index, other.data(), other.size());
|
||||
m_size += other.size();
|
||||
m_data[m_size] = '\0';
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||
memcpy(data() + index, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::append(StringView other)
|
||||
ErrorOr<void> String::append(StringView str)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
||||
memcpy(m_data + m_size, other.data(), other.size());
|
||||
m_size += other.size();
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::append(const String& string)
|
||||
{
|
||||
TRY(append(string.sv()));
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memcpy(data() + m_size, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -104,159 +111,159 @@ namespace BAN
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
m_size--;
|
||||
m_data[m_size] = '\0';
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void String::remove(size_type index)
|
||||
{
|
||||
erase(index, 1);
|
||||
}
|
||||
|
||||
void String::erase(size_type index, size_type count)
|
||||
{
|
||||
ASSERT(index + count <= m_size);
|
||||
memmove(m_data + index, m_data + index + count, m_size - index - count);
|
||||
m_size -= count;
|
||||
m_data[m_size] = '\0';
|
||||
ASSERT(index < m_size);
|
||||
memcpy(data() + index, data() + index + 1, m_size - index);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void String::clear()
|
||||
{
|
||||
if (!has_sso())
|
||||
{
|
||||
deallocator(m_storage.general_storage.data);
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
}
|
||||
m_size = 0;
|
||||
m_data[0] = '\0';
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
char String::operator[](size_type index) const
|
||||
bool String::operator==(StringView str) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
char& String::operator[](size_type index)
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
bool String::operator==(const String& other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
||||
}
|
||||
|
||||
bool String::operator==(StringView other) const
|
||||
{
|
||||
if (m_size != other.size())
|
||||
return false;
|
||||
return memcmp(m_data, other.data(), m_size) == 0;
|
||||
}
|
||||
|
||||
bool String::operator==(const char* other) const
|
||||
{
|
||||
for (size_type i = 0; i <= m_size; i++)
|
||||
if (m_data[i] != other[i])
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::resize(size_type size, char ch)
|
||||
bool String::operator==(const char* cstr) const
|
||||
{
|
||||
if (size < m_size)
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != cstr[i])
|
||||
return false;
|
||||
if (cstr[size()] != '\0')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::resize(size_type new_size, char init_c)
|
||||
{
|
||||
if (m_size == new_size)
|
||||
return {};
|
||||
|
||||
// expanding
|
||||
if (m_size < new_size)
|
||||
{
|
||||
m_data[size] = '\0';
|
||||
m_size = size;
|
||||
TRY(ensure_capacity(new_size));
|
||||
memset(data() + m_size, init_c, new_size - m_size);
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
else if (size > m_size)
|
||||
{
|
||||
TRY(ensure_capacity(size + 1));
|
||||
for (size_type i = m_size; i < size; i++)
|
||||
m_data[i] = ch;
|
||||
m_data[size] = '\0';
|
||||
m_size = size;
|
||||
}
|
||||
m_size = size;
|
||||
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::reserve(size_type size)
|
||||
ErrorOr<void> String::reserve(size_type new_size)
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
TRY(ensure_capacity(new_size));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::shrink_to_fit()
|
||||
{
|
||||
size_type temp = m_capacity;
|
||||
m_capacity = 0;
|
||||
auto error_or = ensure_capacity(m_size);
|
||||
if (error_or.is_error())
|
||||
if (has_sso())
|
||||
return {};
|
||||
|
||||
if (fits_in_sso())
|
||||
{
|
||||
m_capacity = temp;
|
||||
return error_or;
|
||||
char* data = m_storage.general_storage.data;
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
memcpy(this->data(), data, m_size + 1);
|
||||
deallocator(data);
|
||||
return {};
|
||||
}
|
||||
|
||||
GeneralStorage& storage = m_storage.general_storage;
|
||||
if (storage.capacity == m_size)
|
||||
return {};
|
||||
|
||||
char* new_data = (char*)allocator(m_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
memcpy(new_data, storage.data, m_size);
|
||||
deallocator(storage.data);
|
||||
|
||||
storage.capacity = m_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringView String::sv() const
|
||||
{
|
||||
return StringView(*this);
|
||||
}
|
||||
|
||||
bool String::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
String::size_type String::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
String::size_type String::capacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
if (has_sso())
|
||||
return sso_capacity;
|
||||
return m_storage.general_storage.capacity;
|
||||
}
|
||||
|
||||
char* String::data()
|
||||
{
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
|
||||
const char* String::data() const
|
||||
{
|
||||
return m_data;
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::ensure_capacity(size_type size)
|
||||
ErrorOr<void> String::ensure_capacity(size_type new_size)
|
||||
{
|
||||
if (m_capacity >= size)
|
||||
if (m_size >= new_size)
|
||||
return {};
|
||||
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
||||
void* new_data = BAN::allocator(new_cap);
|
||||
if (has_sso() && fits_in_sso(new_size))
|
||||
return {};
|
||||
|
||||
char* new_data = (char*)allocator(new_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
if (m_data)
|
||||
memcpy(new_data, m_data, m_size + 1);
|
||||
BAN::deallocator(m_data);
|
||||
m_data = (char*)new_data;
|
||||
m_capacity = new_cap;
|
||||
|
||||
memcpy(new_data, data(), m_size + 1);
|
||||
|
||||
if (has_sso())
|
||||
{
|
||||
m_storage.general_storage = GeneralStorage();
|
||||
m_has_sso = false;
|
||||
}
|
||||
else
|
||||
deallocator(m_storage.general_storage.data);
|
||||
|
||||
auto& storage = m_storage.general_storage;
|
||||
storage.capacity = new_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::copy_impl(StringView other)
|
||||
bool String::has_sso() const
|
||||
{
|
||||
TRY(ensure_capacity(other.size() + 1));
|
||||
memcpy(m_data, other.data(), other.size());
|
||||
m_size = other.size();
|
||||
m_data[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
void String::move_impl(String&& other)
|
||||
{
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
m_capacity = other.m_capacity;
|
||||
|
||||
other.m_data = nullptr;
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
return m_has_sso;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,9 +63,38 @@ namespace BAN
|
||||
|
||||
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties)
|
||||
{
|
||||
// FIXME: Won't work while multithreading
|
||||
static char s_delim = delim;
|
||||
return split([](char c){ return c == s_delim; }, allow_empties);
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<StringView>> StringView::split(bool(*comp)(char), bool allow_empties)
|
||||
@@ -116,6 +145,14 @@ namespace BAN
|
||||
return m_data[0];
|
||||
}
|
||||
|
||||
bool StringView::contains(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
StringView::size_type StringView::count(char ch) const
|
||||
{
|
||||
size_type result = 0;
|
||||
|
||||
71
BAN/BAN/Time.cpp
Normal file
71
BAN/BAN/Time.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <BAN/Time.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
static constexpr bool is_leap_year(uint64_t year)
|
||||
{
|
||||
if (year % 400 == 0)
|
||||
return true;
|
||||
if (year % 100 == 0)
|
||||
return false;
|
||||
if (year % 4 == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr uint64_t leap_days_since_epoch(const BAN::Time& time)
|
||||
{
|
||||
uint64_t leap_years = 0;
|
||||
for (uint32_t year = 1970; year < time.year; year++)
|
||||
if (is_leap_year(year))
|
||||
leap_years++;
|
||||
if (is_leap_year(time.year) && time.month >= 3)
|
||||
leap_years++;
|
||||
return leap_years;
|
||||
}
|
||||
|
||||
static constexpr uint64_t month_days[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
|
||||
|
||||
uint64_t to_unix_time(const BAN::Time& time)
|
||||
{
|
||||
uint64_t years = time.year - 1970;
|
||||
uint64_t days = years * 365 + month_days[time.month - 1] + leap_days_since_epoch(time) + (time.day - 1);
|
||||
uint64_t hours = days * 24 + time.hour;
|
||||
uint64_t minutes = hours * 60 + time.minute;
|
||||
uint64_t seconds = minutes * 60 + time.second;
|
||||
return seconds;
|
||||
}
|
||||
|
||||
BAN::Time from_unix_time(uint64_t unix_time)
|
||||
{
|
||||
BAN::Time time {};
|
||||
|
||||
time.second = unix_time % 60; unix_time /= 60;
|
||||
time.minute = unix_time % 60; unix_time /= 60;
|
||||
time.hour = unix_time % 24; unix_time /= 24;
|
||||
|
||||
uint64_t total_days = unix_time;
|
||||
|
||||
time.week_day = (total_days + 4) % 7 + 1;
|
||||
|
||||
time.year = 1970;
|
||||
while (total_days >= 365U + is_leap_year(time.year))
|
||||
{
|
||||
total_days -= 365U + is_leap_year(time.year);
|
||||
time.year++;
|
||||
}
|
||||
|
||||
bool is_leap_day = is_leap_year(time.year) && total_days == month_days[2];
|
||||
bool had_leap_day = is_leap_year(time.year) && total_days > month_days[2];
|
||||
|
||||
for (time.month = 1; time.month < 12; time.month++)
|
||||
if (total_days < month_days[time.month] + (is_leap_day || had_leap_day))
|
||||
break;
|
||||
|
||||
time.day = total_days - month_days[time.month - 1] + !had_leap_day;
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
}
|
||||
24
BAN/CMakeLists.txt
Normal file
24
BAN/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(BAN CXX)
|
||||
|
||||
set(BAN_SOURCES
|
||||
BAN/New.cpp
|
||||
BAN/String.cpp
|
||||
BAN/StringView.cpp
|
||||
BAN/Time.cpp
|
||||
)
|
||||
|
||||
add_custom_target(ban-headers
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
|
||||
DEPENDS sysroot
|
||||
)
|
||||
|
||||
add_library(ban ${BAN_SOURCES})
|
||||
add_dependencies(ban headers libc-install)
|
||||
|
||||
add_custom_target(ban-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/libban.a ${BANAN_LIB}/
|
||||
DEPENDS ban
|
||||
BYPRODUCTS ${BANAN_LIB}/libban.a
|
||||
)
|
||||
89
BAN/Makefile
89
BAN/Makefile
@@ -1,89 +0,0 @@
|
||||
DEFAULT_HOST!=../default-host.sh
|
||||
HOST?=DEFAULT_HOST
|
||||
HOSTARCH!=../target-triplet-to-arch.sh $(HOST)
|
||||
|
||||
CFLAGS?=-O2 -g
|
||||
CPPFLAGS?=
|
||||
LDFLAGS?=
|
||||
LIBS?=
|
||||
|
||||
DESTDIR?=
|
||||
PREFIX?=/usr/local
|
||||
EXEC_PREFIX?=$(PREFIX)
|
||||
INCLUDEDIR?=$(PREFIX)/include
|
||||
LIBDIR?=$(EXEC_PREFIX)/lib
|
||||
|
||||
CFLAGS:=$(CFLAGS) -Iinclude -ffreestanding -Wall -Wextra -Werror=return-type
|
||||
CPPFLAGS:=$(CPPFLAGS)
|
||||
LIBBANK_CFLAGS:=$(CFLAGS) -D__is_kernel -Iinclude -ffreestanding -Wall -Wextra
|
||||
LIBBANK_CPPFLAGS:=$(CPPFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ARCHDIR=arch/$(HOSTARCH)
|
||||
|
||||
include $(ARCHDIR)/make.config
|
||||
|
||||
CFLAGS:=$(CFLAGS) $(ARCH_CFLAGS)
|
||||
CPPFLAGS:=$(CPPFLAGS) $(ARCH_CPPFLAGS)
|
||||
LIBBANK_CFLAGS:=$(LIBBANK_CFLAGS) $(KERNEL_ARCH_CFLAGS)
|
||||
LIBBANK_CPPFLAGS:=$(LIBBANK_CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS)
|
||||
|
||||
BUILDDIR=$(abspath build)
|
||||
|
||||
FREEOBJS= \
|
||||
$(ARCH_FREEOBJS) \
|
||||
BAN/Memory.o \
|
||||
BAN/String.o \
|
||||
BAN/StringView.o \
|
||||
|
||||
HOSTEDOBJS=\
|
||||
$(ARCH_HOSTEDOBJS) \
|
||||
|
||||
OBJS=\
|
||||
$(FREEOBJS) \
|
||||
$(HOSTEDOBJS) \
|
||||
|
||||
LIBBANK_OBJS=$(FREEOBJS:.o=.bank.o)
|
||||
|
||||
BINARIES=libbank.a
|
||||
|
||||
.PHONY: all always clean install install-headers install-libs
|
||||
.SUFFIXES: .o .bank.o .cpp .S
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
libban.a: always $(OBJS)
|
||||
cd $(BUILDDIR) && $(AR) rcs $@ $(OBJS)
|
||||
|
||||
libbank.a: always $(LIBBANK_OBJS)
|
||||
cd $(BUILDDIR) && $(AR) rcs $@ $(LIBBANK_OBJS)
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS) $(CPPFLAGS)
|
||||
|
||||
.S.o:
|
||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS) $(CPPFLAGS)
|
||||
|
||||
.cpp.bank.o:
|
||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(LIBBANK_CFLAGS) $(LIBBANK_CPPFLAGS)
|
||||
|
||||
.S.bank.o:
|
||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(LIBBANK_CFLAGS) $(LIBBANK_CPPFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
always:
|
||||
mkdir -p $(BUILDDIR)/BAN
|
||||
|
||||
install: install-headers install-libs
|
||||
|
||||
install-headers:
|
||||
mkdir -p $(DESTDIR)$(INCLUDEDIR)
|
||||
cp -R --preserve=timestamps include/. $(DESTDIR)$(INCLUDEDIR)/.
|
||||
|
||||
install-libs: $(BINARIES)
|
||||
mkdir -p $(DESTDIR)$(LIBDIR)
|
||||
cp $(BUILDDIR)/$(BINARIES) $(DESTDIR)$(LIBDIR)
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
-include $(LIBBANK_OBJS:.o=.d)
|
||||
@@ -1,8 +0,0 @@
|
||||
ARCH_CFLAGS=
|
||||
ARCH_CPPFLAGS=
|
||||
KERNEL_ARCH_CFLAGS=
|
||||
KERNEL_ARCH_CPPFLAGS=
|
||||
|
||||
ARCH_FREEOBJS=\
|
||||
|
||||
ARCH_HOSTEDOBJS=\
|
||||
@@ -1,8 +0,0 @@
|
||||
ARCH_CFLAGS=
|
||||
ARCH_CPPFLAGS=
|
||||
KERNEL_ARCH_CFLAGS=
|
||||
KERNEL_ARCH_CPPFLAGS=
|
||||
|
||||
ARCH_FREEOBJS=\
|
||||
|
||||
ARCH_HOSTEDOBJS=\
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Span.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -13,11 +15,18 @@ namespace BAN
|
||||
public:
|
||||
using size_type = decltype(S);
|
||||
using value_type = T;
|
||||
using iterator = IteratorSimple<T, Array>;
|
||||
using const_iterator = ConstIteratorSimple<T, Array>;
|
||||
|
||||
public:
|
||||
Array();
|
||||
Array(const T&);
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + size()); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + size()); }
|
||||
|
||||
const T& operator[](size_type) const;
|
||||
T& operator[](size_type);
|
||||
|
||||
@@ -26,8 +35,14 @@ namespace BAN
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
Span<T> span() { return Span(m_data, size()); }
|
||||
const Span<T> span() const { return Span(m_data, size()); }
|
||||
|
||||
constexpr size_type size() const;
|
||||
|
||||
const T* data() const { return m_data; }
|
||||
T* data() { return m_data; }
|
||||
|
||||
private:
|
||||
T m_data[S];
|
||||
};
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#if defined(__is_kernel)
|
||||
#include <kernel/Panic.h>
|
||||
#define ASSERT(cond) do { if (!(cond)) Kernel::panic("ASSERT("#cond") failed"); } while(false)
|
||||
|
||||
#define ASSERT(cond) \
|
||||
do { \
|
||||
if (!(cond)) \
|
||||
Kernel::panic("ASSERT(" #cond ") failed"); \
|
||||
} while (false)
|
||||
|
||||
#define __ASSERT_BIN_OP(lhs, rhs, name, op) \
|
||||
do { \
|
||||
auto&& _lhs = lhs; \
|
||||
auto&& _rhs = rhs; \
|
||||
if (!(_lhs op _rhs)) \
|
||||
Kernel::panic(name "(" #lhs ", " #rhs ") ({} " #op " {}) failed", _lhs, _rhs); \
|
||||
} while (false)
|
||||
|
||||
#define ASSERT_LT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LT", <)
|
||||
#define ASSERT_LTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LTE", <=)
|
||||
#define ASSERT_GT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GT", >)
|
||||
#define ASSERT_GTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GTE", >=)
|
||||
#define ASSERT_EQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_EQ", ==)
|
||||
#define ASSERT_NEQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_NEQ", !=)
|
||||
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
|
||||
#else
|
||||
#include <assert.h>
|
||||
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
|
||||
#define ASSERT_NOT_REACHED() assert(false && "ASSERT_NOT_REACHED() failed")
|
||||
#define ASSERT_NOT_REACHED() do { assert(false && "ASSERT_NOT_REACHED() failed"); __builtin_unreachable(); } while (false)
|
||||
#endif
|
||||
140
BAN/include/BAN/ByteSpan.h
Normal file
140
BAN/include/BAN/ByteSpan.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Span.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<bool CONST>
|
||||
class ByteSpanGeneral
|
||||
{
|
||||
public:
|
||||
using value_type = maybe_const_t<CONST, uint8_t>;
|
||||
using size_type = size_t;
|
||||
|
||||
public:
|
||||
ByteSpanGeneral() = default;
|
||||
ByteSpanGeneral(value_type* data, size_type size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{ }
|
||||
|
||||
ByteSpanGeneral(ByteSpanGeneral& other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
template<bool C2>
|
||||
ByteSpanGeneral(const ByteSpanGeneral<C2>& other) requires(CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
ByteSpanGeneral(Span<uint8_t> other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
ByteSpanGeneral(const Span<const uint8_t>& other) requires(CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
|
||||
ByteSpanGeneral& operator=(ByteSpanGeneral other)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
template<bool C2>
|
||||
ByteSpanGeneral& operator=(const ByteSpanGeneral<C2>& other) requires(CONST)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
ByteSpanGeneral& operator=(Span<uint8_t> other)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
ByteSpanGeneral& operator=(const Span<const uint8_t>& other) requires(CONST)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
requires(CONST || !is_const_v<S>)
|
||||
static ByteSpanGeneral from(S& value)
|
||||
{
|
||||
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
requires(!CONST && !is_const_v<S>)
|
||||
S& as()
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(m_size >= sizeof(S));
|
||||
return *reinterpret_cast<S*>(m_data);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
requires(is_const_v<S>)
|
||||
S& as() const
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(m_size >= sizeof(S));
|
||||
return *reinterpret_cast<S*>(m_data);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
requires(!CONST && !is_const_v<S>)
|
||||
Span<S> as_span()
|
||||
{
|
||||
ASSERT(m_data);
|
||||
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
const Span<S> as_span() const
|
||||
{
|
||||
ASSERT(m_data);
|
||||
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
|
||||
}
|
||||
|
||||
ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1))
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(m_size >= offset);
|
||||
if (length == size_type(-1))
|
||||
length = m_size - offset;
|
||||
ASSERT(m_size >= offset + length);
|
||||
return ByteSpanGeneral(m_data + offset, length);
|
||||
}
|
||||
|
||||
value_type& operator[](size_type offset)
|
||||
{
|
||||
ASSERT(offset < m_size);
|
||||
return m_data[offset];
|
||||
}
|
||||
const value_type& operator[](size_type offset) const
|
||||
{
|
||||
ASSERT(offset < m_size);
|
||||
return m_data[offset];
|
||||
}
|
||||
|
||||
value_type* data() { return m_data; }
|
||||
const value_type* data() const { return m_data; }
|
||||
|
||||
size_type size() const { return m_size; }
|
||||
|
||||
private:
|
||||
value_type* m_data { nullptr };
|
||||
size_type m_size { 0 };
|
||||
};
|
||||
|
||||
using ByteSpan = ByteSpanGeneral<false>;
|
||||
using ConstByteSpan = ByteSpanGeneral<true>;
|
||||
|
||||
}
|
||||
113
BAN/include/BAN/CircularQueue.h
Normal file
113
BAN/include/BAN/CircularQueue.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, size_t S>
|
||||
class CircularQueue
|
||||
{
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using value_type = T;
|
||||
|
||||
public:
|
||||
CircularQueue() = default;
|
||||
~CircularQueue();
|
||||
|
||||
void push(const T&);
|
||||
void push(T&&);
|
||||
template<typename... Args>
|
||||
void emplace(Args&&... args);
|
||||
|
||||
void pop();
|
||||
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
size_type size() const { return m_size; }
|
||||
bool empty() const { return size() == 0; }
|
||||
bool full() const { return size() == capacity(); }
|
||||
|
||||
static constexpr size_type capacity() { return S; }
|
||||
|
||||
private:
|
||||
T* element_at(size_type);
|
||||
const T* element_at(size_type) const;
|
||||
|
||||
private:
|
||||
alignas(T) uint8_t m_storage[sizeof(T) * capacity()];
|
||||
size_type m_first { 0 };
|
||||
size_type m_size { 0 };
|
||||
};
|
||||
|
||||
template<typename T, size_t S>
|
||||
CircularQueue<T, S>::~CircularQueue()
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
element_at((m_first + i) % capacity())->~T();
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
void CircularQueue<T, S>::push(const T& value)
|
||||
{
|
||||
emplace(BAN::move(T(value)));
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
void CircularQueue<T, S>::push(T&& value)
|
||||
{
|
||||
emplace(BAN::move(value));
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
template<typename... Args>
|
||||
void CircularQueue<T, S>::emplace(Args&&... args)
|
||||
{
|
||||
ASSERT(!full());
|
||||
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
|
||||
m_size++;
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
void CircularQueue<T, S>::pop()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
element_at(m_first)->~T();
|
||||
m_first = (m_first + 1) % capacity();
|
||||
m_size--;
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T& CircularQueue<T, S>::front() const
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return *element_at(m_first);
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
T& CircularQueue<T, S>::front()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return *element_at(m_first);
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T* CircularQueue<T, S>::element_at(size_type index) const
|
||||
{
|
||||
ASSERT(index < capacity());
|
||||
return (const T*)(m_storage + index * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
T* CircularQueue<T, S>::element_at(size_type index)
|
||||
{
|
||||
ASSERT(index < capacity());
|
||||
return (T*)(m_storage + index * sizeof(T));
|
||||
}
|
||||
|
||||
}
|
||||
80
BAN/include/BAN/Endianness.h
Normal file
80
BAN/include/BAN/Endianness.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<integral T>
|
||||
constexpr T swap_endianness(T value)
|
||||
{
|
||||
if constexpr(sizeof(T) == 1)
|
||||
return value;
|
||||
if constexpr(sizeof(T) == 2)
|
||||
return (((value >> 8) & 0xFF) << 0)
|
||||
| (((value >> 0) & 0xFF) << 8);
|
||||
if constexpr(sizeof(T) == 4)
|
||||
return (((value >> 24) & 0xFF) << 0)
|
||||
| (((value >> 16) & 0xFF) << 8)
|
||||
| (((value >> 8) & 0xFF) << 16)
|
||||
| (((value >> 0) & 0xFF) << 24);
|
||||
if constexpr(sizeof(T) == 8)
|
||||
return (((value >> 56) & 0xFF) << 0)
|
||||
| (((value >> 48) & 0xFF) << 8)
|
||||
| (((value >> 40) & 0xFF) << 16)
|
||||
| (((value >> 32) & 0xFF) << 24)
|
||||
| (((value >> 24) & 0xFF) << 32)
|
||||
| (((value >> 16) & 0xFF) << 40)
|
||||
| (((value >> 8) & 0xFF) << 48)
|
||||
| (((value >> 0) & 0xFF) << 56);
|
||||
T result { 0 };
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
result |= ((value >> (i * 8)) & 0xFF) << ((sizeof(T) - i - 1) * 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_little_endian(T value)
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return swap_endianness(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_big_endian(T value)
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return swap_endianness(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
struct LittleEndian
|
||||
{
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_little_endian(raw);
|
||||
}
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
||||
template<integral T>
|
||||
struct BigEndian
|
||||
{
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_big_endian(raw);
|
||||
}
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,62 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Variant.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__is_kernel)
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/Panic.h>
|
||||
#define MUST(expr) ({ auto e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
|
||||
#include <kernel/Errors.h>
|
||||
#define MUST(expr) ({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
|
||||
#define MUST_REF(expr) *({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
|
||||
#else
|
||||
#define MUST(expr) ({ auto e = expr; assert(!e.is_error()); e.release_value(); })
|
||||
#include <assert.h>
|
||||
#define MUST(expr) ({ auto&& e = expr; assert(!e.is_error()); e.release_value(); })
|
||||
#define MUST_REF(expr) *({ auto&& e = expr; assert(!e.is_error()); &e.release_value(); })
|
||||
#endif
|
||||
|
||||
#define TRY(expr) ({ auto e = expr; if (e.is_error()) return e.release_error(); e.release_value(); })
|
||||
#define TRY(expr) ({ auto&& e = expr; if (e.is_error()) return e.release_error(); e.release_value(); })
|
||||
#define TRY_REF(expr) *({ auto&& e = expr; if (e.is_error()) return e.release_error(); &e.release_value(); })
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
class Error
|
||||
{
|
||||
#ifdef __is_kernel
|
||||
private:
|
||||
static constexpr uint64_t kernel_error_mask = uint64_t(1) << 63;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static Error from_c_string(const char* message)
|
||||
#ifdef __is_kernel
|
||||
static Error from_error_code(Kernel::ErrorCode error)
|
||||
{
|
||||
Error result;
|
||||
strncpy(result.m_message, message, sizeof(Error::m_message));
|
||||
result.m_message[sizeof(Error::m_message) - 1] = '\0';
|
||||
result.m_error_code = 0xFF;
|
||||
return result;
|
||||
}
|
||||
template<typename... Args>
|
||||
static Error from_format(const char* format, Args&&... args)
|
||||
{
|
||||
char buffer[sizeof(Error::m_message)] {};
|
||||
size_t index = 0;
|
||||
auto putc = [&](char ch)
|
||||
{
|
||||
if (index < sizeof(buffer) - 1)
|
||||
buffer[index++] = ch;
|
||||
};
|
||||
Formatter::print(putc, format, forward<Args>(args)...);
|
||||
return from_c_string(buffer);
|
||||
return Error((uint64_t)error | kernel_error_mask);
|
||||
}
|
||||
#endif
|
||||
static Error from_errno(int error)
|
||||
{
|
||||
Error result;
|
||||
strncpy(result.m_message, strerror(error), sizeof(Error::m_message));
|
||||
result.m_message[sizeof(Error::m_message) - 1] = '\0';
|
||||
result.m_error_code = error;
|
||||
return result;
|
||||
return Error(error);
|
||||
}
|
||||
|
||||
uint8_t get_error_code() const { return m_error_code; }
|
||||
const char* get_message() const { return m_message; }
|
||||
#ifdef __is_kernel
|
||||
Kernel::ErrorCode kernel_error() const
|
||||
{
|
||||
return (Kernel::ErrorCode)(m_error_code & ~kernel_error_mask);
|
||||
}
|
||||
|
||||
bool is_kernel_error() const
|
||||
{
|
||||
return m_error_code & kernel_error_mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t get_error_code() const { return m_error_code; }
|
||||
BAN::StringView get_message() const
|
||||
{
|
||||
#ifdef __is_kernel
|
||||
if (m_error_code & kernel_error_mask)
|
||||
return Kernel::error_string(kernel_error());
|
||||
#endif
|
||||
return strerror(m_error_code);
|
||||
}
|
||||
|
||||
private:
|
||||
char m_message[128];
|
||||
uint8_t m_error_code;
|
||||
Error(uint64_t error)
|
||||
: m_error_code(error)
|
||||
{}
|
||||
|
||||
uint64_t m_error_code;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -76,19 +90,46 @@ namespace BAN
|
||||
: m_data(move(error))
|
||||
{}
|
||||
|
||||
bool is_error() const { return m_data.template is<Error>(); }
|
||||
bool is_error() const { return m_data.template has<Error>(); }
|
||||
const Error& error() const { return m_data.template get<Error>(); }
|
||||
Error& error() { return m_data.template get<Error>(); }
|
||||
const T& value() const { return m_data.template get<T>(); }
|
||||
T& value() { return m_data.template get<T>(); }
|
||||
|
||||
Error release_error() { return move(error()); m_data.clear(); }
|
||||
Error release_error() { return move(error()); m_data.clear(); }
|
||||
T release_value() { return move(value()); m_data.clear(); }
|
||||
|
||||
private:
|
||||
Variant<Error, T> m_data;
|
||||
};
|
||||
|
||||
template<lvalue_reference T>
|
||||
class [[nodiscard]] ErrorOr<T>
|
||||
{
|
||||
public:
|
||||
ErrorOr(T value)
|
||||
{
|
||||
m_data.template set<T>(value);
|
||||
}
|
||||
ErrorOr(Error&& error)
|
||||
: m_data(move(error))
|
||||
{ }
|
||||
ErrorOr(const Error& error)
|
||||
: m_data(error)
|
||||
{ }
|
||||
|
||||
bool is_error() const { return m_data.template has<Error>(); }
|
||||
Error& error() { return m_data.template get<Error>(); }
|
||||
const Error& error() const { return m_data.template get<Error>(); }
|
||||
T value() { return m_data.template get<T>(); }
|
||||
|
||||
Error release_error() { return move(error()); m_data.clear(); }
|
||||
T release_value() { return value(); m_data.clear(); }
|
||||
|
||||
private:
|
||||
Variant<Error, T> m_data;
|
||||
};
|
||||
|
||||
template<>
|
||||
class [[nodiscard]] ErrorOr<void>
|
||||
{
|
||||
@@ -102,12 +143,12 @@ namespace BAN
|
||||
const Error& error() const { return m_data; }
|
||||
void value() { }
|
||||
|
||||
Error release_error() { return move(m_data); m_data = Error(); }
|
||||
void release_value() { m_data = Error(); }
|
||||
Error release_error() { return move(m_data); }
|
||||
void release_value() { }
|
||||
|
||||
private:
|
||||
Error m_data;
|
||||
bool m_has_error = false;
|
||||
Error m_data { Error::from_errno(0) };
|
||||
bool m_has_error { false };
|
||||
};
|
||||
|
||||
}
|
||||
@@ -115,11 +156,8 @@ namespace BAN
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
template<typename F>
|
||||
void print_argument(F putc, const Error& error, const ValueFormat&)
|
||||
void print_argument(F putc, const Error& error, const ValueFormat& format)
|
||||
{
|
||||
if (error.get_error_code() == 0xFF)
|
||||
print(putc, error.get_message());
|
||||
else
|
||||
print(putc, "{} ({})", error.get_message(), error.get_error_code());
|
||||
print_argument(putc, error.get_message(), format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace BAN::Formatter
|
||||
static size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
IMPLEMENTATION
|
||||
@@ -243,10 +242,7 @@ namespace BAN::Formatter
|
||||
|
||||
template<typename F> void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
|
||||
template<typename F> void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
|
||||
template<typename F> void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value);}
|
||||
|
||||
//template<typename F> void print_argument(F putc, signed char value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||
//template<typename F> void print_argument(F putc, unsigned char value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||
//template<typename F, typename T> void print_argument(F putc, T* value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
||||
template<typename F> void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
|
||||
template<typename F> void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
@@ -14,5 +15,6 @@ namespace BAN
|
||||
class StringView;
|
||||
template<typename> class Vector;
|
||||
template<typename> class LinkedList;
|
||||
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/Memory.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -43,10 +43,10 @@ namespace BAN
|
||||
clear();
|
||||
}
|
||||
|
||||
Ret operator()(Args... args)
|
||||
Ret operator()(Args... args) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return reinterpret_cast<CallableBase*>(m_storage)->call(forward<Args>(args)...);
|
||||
return reinterpret_cast<const CallableBase*>(m_storage)->call(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
@@ -70,7 +70,7 @@ namespace BAN
|
||||
struct CallableBase
|
||||
{
|
||||
virtual ~CallableBase() {}
|
||||
virtual Ret call(Args...) = 0;
|
||||
virtual Ret call(Args...) const = 0;
|
||||
};
|
||||
|
||||
struct CallablePointer : public CallableBase
|
||||
@@ -79,7 +79,7 @@ namespace BAN
|
||||
: m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) override
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return m_function(forward<Args>(args)...);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace BAN
|
||||
, m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) override
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||
}
|
||||
@@ -114,7 +114,7 @@ namespace BAN
|
||||
, m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) override
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ namespace BAN
|
||||
: m_lambda(lambda)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) override
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return m_lambda(forward<Args>(args)...);
|
||||
}
|
||||
@@ -141,7 +141,7 @@ namespace BAN
|
||||
};
|
||||
|
||||
private:
|
||||
static constexpr size_t m_size = sizeof(void*) * 4;
|
||||
static constexpr size_t m_size = sizeof(void*) * 5;
|
||||
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
||||
};
|
||||
|
||||
|
||||
@@ -7,13 +7,31 @@
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename Container>
|
||||
class HashMapIterator;
|
||||
|
||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||
class HashMap
|
||||
{
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
template<typename... Args>
|
||||
Entry(const Key& key, Args&&... args)
|
||||
: key(key)
|
||||
, value(forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
Key key;
|
||||
T value;
|
||||
};
|
||||
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using key_type = Key;
|
||||
using value_type = T;
|
||||
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||
|
||||
public:
|
||||
HashMap() = default;
|
||||
@@ -29,6 +47,11 @@ namespace BAN
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(const Key&, Args&&...);
|
||||
|
||||
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
||||
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
|
||||
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
|
||||
|
||||
ErrorOr<void> reserve(size_type);
|
||||
|
||||
void remove(const Key&);
|
||||
@@ -42,19 +65,6 @@ namespace BAN
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
template<typename... Args>
|
||||
Entry(const Key& key, Args&&... args)
|
||||
: key(key)
|
||||
, value(forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
Key key;
|
||||
T value;
|
||||
};
|
||||
|
||||
private:
|
||||
ErrorOr<void> rebucket(size_type);
|
||||
LinkedList<Entry>& get_bucket(const Key&);
|
||||
@@ -63,6 +73,8 @@ namespace BAN
|
||||
private:
|
||||
Vector<LinkedList<Entry>> m_buckets;
|
||||
size_type m_size = 0;
|
||||
|
||||
friend iterator;
|
||||
};
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
|
||||
@@ -85,8 +85,6 @@ namespace BAN
|
||||
friend class HashSet<T, HASH>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
|
||||
: m_buckets(other.m_buckets)
|
||||
@@ -231,8 +229,6 @@ namespace BAN
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
||||
{
|
||||
|
||||
12
BAN/include/BAN/Iteration.h
Normal file
12
BAN/include/BAN/Iteration.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
enum class Iteration
|
||||
{
|
||||
Continue,
|
||||
Break
|
||||
};
|
||||
|
||||
}
|
||||
229
BAN/include/BAN/Iterators.h
Normal file
229
BAN/include/BAN/Iterators.h
Normal file
@@ -0,0 +1,229 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, typename Container, bool CONST>
|
||||
class IteratorSimpleGeneral
|
||||
{
|
||||
public:
|
||||
IteratorSimpleGeneral() = default;
|
||||
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||
IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||
: m_pointer(other.m_pointer)
|
||||
{
|
||||
}
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
enable_if_t<!CONST2, T&> operator*()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
enable_if_t<!CONST2, T*> operator->()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
IteratorSimpleGeneral& operator++()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
IteratorSimpleGeneral operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
IteratorSimpleGeneral& operator--()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return --m_pointer;
|
||||
}
|
||||
IteratorSimpleGeneral operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool operator==(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
return m_pointer == other.m_pointer;
|
||||
}
|
||||
bool operator!=(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
private:
|
||||
IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
|
||||
: m_pointer(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
maybe_const_t<CONST, T>* m_pointer = nullptr;
|
||||
|
||||
friend IteratorSimpleGeneral<T, Container, !CONST>;
|
||||
friend Container;
|
||||
};
|
||||
|
||||
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container, bool CONST>
|
||||
class IteratorDoubleGeneral
|
||||
{
|
||||
public:
|
||||
using Inner = InnerContainer<T>;
|
||||
using Outer = OuterContainer<Inner>;
|
||||
|
||||
using InnerIterator = either_or_t<CONST, typename Inner::const_iterator, typename Inner::iterator>;
|
||||
using OuterIterator = either_or_t<CONST, typename Outer::const_iterator, typename Outer::iterator>;
|
||||
|
||||
public:
|
||||
IteratorDoubleGeneral() = default;
|
||||
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||
IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
|
||||
: m_outer_end(other.m_outer_end)
|
||||
, m_outer_current(other.m_outer_current)
|
||||
, m_inner_current(other.m_inner_current)
|
||||
{
|
||||
}
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
ASSERT(m_inner_current);
|
||||
return m_inner_current.operator*();
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
enable_if_t<!CONST2, T&> operator*()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
ASSERT(m_inner_current);
|
||||
return m_inner_current.operator*();
|
||||
}
|
||||
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
ASSERT(m_inner_current);
|
||||
return m_inner_current.operator->();
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
enable_if_t<!CONST2, T*> operator->()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
ASSERT(m_inner_current);
|
||||
return m_inner_current.operator->();
|
||||
}
|
||||
|
||||
IteratorDoubleGeneral& operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
ASSERT(m_inner_current);
|
||||
m_inner_current++;
|
||||
find_valid_or_end();
|
||||
return *this;
|
||||
}
|
||||
IteratorDoubleGeneral operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool operator==(const IteratorDoubleGeneral& other) const
|
||||
{
|
||||
if (!*this || !other)
|
||||
return false;
|
||||
if (m_outer_end != other.m_outer_end)
|
||||
return false;
|
||||
if (m_outer_current != other.m_outer_current)
|
||||
return false;
|
||||
if (m_outer_current == m_outer_end)
|
||||
return true;
|
||||
return m_inner_current == other.m_inner_current;
|
||||
}
|
||||
bool operator!=(const IteratorDoubleGeneral& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_outer_end && m_outer_current;
|
||||
}
|
||||
|
||||
private:
|
||||
IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||
: m_outer_end(outer_end)
|
||||
, m_outer_current(outer_current)
|
||||
{
|
||||
if (outer_current != outer_end)
|
||||
{
|
||||
m_inner_current = m_outer_current->begin();
|
||||
find_valid_or_end();
|
||||
}
|
||||
}
|
||||
|
||||
void find_valid_or_end()
|
||||
{
|
||||
while (m_inner_current == m_outer_current->end())
|
||||
{
|
||||
m_outer_current++;
|
||||
if (m_outer_current == m_outer_end)
|
||||
break;
|
||||
m_inner_current = m_outer_current->begin();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
OuterIterator m_outer_end;
|
||||
OuterIterator m_outer_current;
|
||||
InnerIterator m_inner_current;
|
||||
|
||||
friend class IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, !CONST>;
|
||||
friend Container;
|
||||
};
|
||||
|
||||
template<typename T, typename Container>
|
||||
using IteratorSimple = IteratorSimpleGeneral<T, Container, false>;
|
||||
|
||||
template<typename T, typename Container>
|
||||
using ConstIteratorSimple = IteratorSimpleGeneral<T, Container, true>;
|
||||
|
||||
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
|
||||
using IteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, false>;
|
||||
|
||||
template<typename T, template<typename> typename OuterContainer, template<typename> typename InnerContainer, typename Container>
|
||||
using ConstIteratorDouble = IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, true>;
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Memory.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -38,7 +38,7 @@ namespace BAN
|
||||
ErrorOr<void> emplace(iterator, Args&&...);
|
||||
|
||||
void pop_back();
|
||||
void remove(iterator);
|
||||
iterator remove(iterator);
|
||||
void clear();
|
||||
|
||||
iterator begin() { return iterator(m_data, empty()); }
|
||||
@@ -114,8 +114,6 @@ namespace BAN
|
||||
friend class LinkedListIterator<T, !CONST>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
||||
{
|
||||
@@ -197,11 +195,11 @@ namespace BAN
|
||||
template<typename T>
|
||||
void LinkedList<T>::pop_back()
|
||||
{
|
||||
return remove(m_last);
|
||||
remove(iterator(m_last, false));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LinkedList<T>::remove(iterator iter)
|
||||
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
|
||||
{
|
||||
ASSERT(!empty() && iter);
|
||||
Node* node = iter.m_current;
|
||||
@@ -212,6 +210,7 @@ namespace BAN
|
||||
(prev ? prev->next : m_data) = next;
|
||||
(next ? next->prev : m_last) = prev;
|
||||
m_size--;
|
||||
return next ? iterator(next, false) : iterator(m_last, true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -292,8 +291,6 @@ namespace BAN
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T, bool CONST>
|
||||
template<bool C>
|
||||
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
|
||||
|
||||
@@ -52,20 +52,53 @@ namespace BAN::Math
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
inline constexpr T little_endian_to_host(const uint8_t* bytes)
|
||||
inline constexpr bool is_power_of_two(T value)
|
||||
{
|
||||
T result = 0;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
result |= (T)bytes[i] << (i * 8);
|
||||
if (value == 0)
|
||||
return false;
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log2(T value)
|
||||
{
|
||||
T result;
|
||||
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"((T)1.0) : "st(1)");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
inline constexpr T big_endian_to_host(const uint8_t* bytes)
|
||||
template<floating_point T>
|
||||
inline constexpr T log10(T value)
|
||||
{
|
||||
T result = 0;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
result |= (T)bytes[i] << (8 * (sizeof(T) - i - 1));
|
||||
constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
|
||||
T result;
|
||||
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log(T value, T base)
|
||||
{
|
||||
return log2(value) / log2(base);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T pow(T base, T exp)
|
||||
{
|
||||
T result;
|
||||
asm volatile(
|
||||
"fyl2x;"
|
||||
"fld1;"
|
||||
"fld %%st(1);"
|
||||
"fprem;"
|
||||
"f2xm1;"
|
||||
"faddp;"
|
||||
"fscale;"
|
||||
"fxch %%st(1);"
|
||||
"fstp %%st;"
|
||||
: "=t"(result)
|
||||
: "0"(base), "u"(exp)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
@@ -25,3 +27,6 @@ namespace BAN
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||
18
BAN/include/BAN/New.h
Normal file
18
BAN/include/BAN/New.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__is_kernel)
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
#if defined(__is_kernel)
|
||||
static constexpr void*(&allocator)(size_t) = kmalloc;
|
||||
static constexpr void(&deallocator)(void*) = kfree;
|
||||
#else
|
||||
static constexpr void*(&allocator)(size_t) = malloc;
|
||||
static constexpr void(&deallocator)(void*) = free;
|
||||
#endif
|
||||
}
|
||||
195
BAN/include/BAN/Optional.h
Normal file
195
BAN/include/BAN/Optional.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Move.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class Optional
|
||||
{
|
||||
public:
|
||||
Optional();
|
||||
Optional(Optional&&);
|
||||
Optional(const Optional&);
|
||||
Optional(const T&);
|
||||
Optional(T&&);
|
||||
template<typename... Args>
|
||||
Optional(Args&&...);
|
||||
|
||||
~Optional();
|
||||
|
||||
Optional& operator=(Optional&&);
|
||||
Optional& operator=(const Optional&);
|
||||
|
||||
template<typename... Args>
|
||||
Optional& emplace(Args&&...);
|
||||
|
||||
T* operator->();
|
||||
const T* operator->() const;
|
||||
|
||||
T& operator*();
|
||||
const T& operator*() const;
|
||||
|
||||
bool has_value() const;
|
||||
|
||||
T release_value();
|
||||
T& value();
|
||||
const T& value() const;
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
alignas(T) uint8_t m_storage[sizeof(T)];
|
||||
bool m_has_value { false };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::Optional()
|
||||
: m_has_value(false)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::Optional(Optional<T>&& other)
|
||||
: m_has_value(other.has_value())
|
||||
{
|
||||
if (other.has_value())
|
||||
new (m_storage) T(move(other.release_value()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::Optional(const Optional<T>& other)
|
||||
: m_has_value(other.has_value())
|
||||
{
|
||||
if (other.has_value())
|
||||
new (m_storage) T(other.value());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::Optional(const T& value)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::Optional(T&& value)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(move(value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
Optional<T>::Optional(Args&&... args)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>::~Optional()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
if (other.has_value())
|
||||
new (m_storage) T(move(other.release_value()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
if (other.has_value)
|
||||
new (m_storage) T(other.value());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
Optional<T>& Optional<T>::emplace(Args&&... args)
|
||||
{
|
||||
clear();
|
||||
m_has_value = true;
|
||||
new (m_storage) T(forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Optional<T>::operator->()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return &value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Optional<T>::operator->() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return &value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& Optional<T>::operator*()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& Optional<T>::operator*() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Optional<T>::has_value() const
|
||||
{
|
||||
return m_has_value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Optional<T>::release_value()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
T released_value = move(value());
|
||||
value().~T();
|
||||
m_has_value = false;
|
||||
return move(released_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& Optional<T>::value()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return (T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& Optional<T>::value() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return (const T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Optional<T>::clear()
|
||||
{
|
||||
if (m_has_value)
|
||||
value().~T();
|
||||
m_has_value = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Memory.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -14,6 +15,8 @@ namespace BAN
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using value_type = T;
|
||||
using iterator = IteratorSimple<T, Queue>;
|
||||
using const_iterator = ConstIteratorSimple<T, Queue>;
|
||||
|
||||
public:
|
||||
Queue() = default;
|
||||
@@ -32,6 +35,11 @@ namespace BAN
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + m_size); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
|
||||
void pop();
|
||||
void clear();
|
||||
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
|
||||
#if defined(__is_kernel)
|
||||
#include <kernel/kmalloc.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
#if defined(__is_kernel)
|
||||
static constexpr void*(&allocator)(size_t) = kmalloc;
|
||||
static constexpr void(&deallocator)(void*) = kfree;
|
||||
#else
|
||||
static constexpr void*(&allocator)(size_t) = malloc;
|
||||
static constexpr void(&deallocator)(void*) = free;
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
class RefCounted
|
||||
@@ -68,7 +53,6 @@ namespace BAN
|
||||
if (m_pointer)
|
||||
m_pointer->ref();
|
||||
}
|
||||
|
||||
~RefPtr() { clear(); }
|
||||
|
||||
template<typename U>
|
||||
@@ -90,6 +74,10 @@ namespace BAN
|
||||
|
||||
RefPtr(const RefPtr& other) { *this = other; }
|
||||
RefPtr(RefPtr&& other) { *this = move(other); }
|
||||
template<typename U>
|
||||
RefPtr(const RefPtr<U>& other) { *this = other; }
|
||||
template<typename U>
|
||||
RefPtr(RefPtr<U>&& other) { *this = move(other); }
|
||||
|
||||
RefPtr& operator=(const RefPtr& other)
|
||||
{
|
||||
@@ -108,6 +96,25 @@ namespace BAN
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
RefPtr& operator=(const RefPtr<U>& other)
|
||||
{
|
||||
clear();
|
||||
m_pointer = other.m_pointer;
|
||||
if (m_pointer)
|
||||
m_pointer->ref();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
RefPtr& operator=(RefPtr<U>&& other)
|
||||
{
|
||||
clear();
|
||||
m_pointer = other.m_pointer;
|
||||
other.m_pointer = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* ptr() { ASSERT(!empty()); return m_pointer; }
|
||||
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
||||
|
||||
@@ -129,9 +136,9 @@ namespace BAN
|
||||
|
||||
private:
|
||||
T* m_pointer = nullptr;
|
||||
|
||||
template<typename U>
|
||||
friend class RefPtr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||
@@ -13,10 +13,16 @@ namespace BAN
|
||||
{ }
|
||||
~ScopeGuard()
|
||||
{
|
||||
m_func();
|
||||
if (m_enabled)
|
||||
m_func();
|
||||
}
|
||||
void disable()
|
||||
{
|
||||
m_enabled = false;
|
||||
}
|
||||
private:
|
||||
BAN::Function<void()> m_func;
|
||||
bool m_enabled { true };
|
||||
};
|
||||
|
||||
}
|
||||
134
BAN/include/BAN/Span.h
Normal file
134
BAN/include/BAN/Span.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Iterators.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class Span
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorSimple<T, Span>;
|
||||
using const_iterator = ConstIteratorSimple<T, Span>;
|
||||
|
||||
public:
|
||||
Span() = default;
|
||||
Span(T*, size_type);
|
||||
Span(Span<T>&);
|
||||
template<typename S>
|
||||
requires(is_same_v<T, const S>)
|
||||
Span(const Span<S>&);
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + m_size); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
|
||||
T& operator[](size_type);
|
||||
const T& operator[](size_type) const;
|
||||
|
||||
T* data();
|
||||
const T* data() const;
|
||||
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
|
||||
void clear();
|
||||
|
||||
Span slice(size_type, size_type = ~size_type(0));
|
||||
|
||||
Span<const T> as_const() const { return Span<const T>(m_data, m_size); }
|
||||
|
||||
private:
|
||||
T* m_data = nullptr;
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Span<T>::Span(T* data, size_type size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T>::Span(Span& other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename S>
|
||||
requires(is_same_v<T, const S>)
|
||||
Span<T>::Span(const Span<S>& other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& Span<T>::operator[](size_type index)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& Span<T>::operator[](size_type index) const
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Span<T>::data()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Span<T>::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Span<T>::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename Span<T>::size_type Span<T>::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Span<T>::clear()
|
||||
{
|
||||
m_data = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Span<T> Span<T>::slice(size_type start, size_type length)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(start <= m_size);
|
||||
if (length == ~size_type(0))
|
||||
length = m_size - start;
|
||||
ASSERT(m_size - start >= length);
|
||||
return Span(m_data + start, length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Iterators.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -11,6 +13,9 @@ namespace BAN
|
||||
{
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorSimple<char, String>;
|
||||
using const_iterator = ConstIteratorSimple<char, String>;
|
||||
static constexpr size_type sso_capacity = 15;
|
||||
|
||||
public:
|
||||
String();
|
||||
@@ -30,18 +35,26 @@ namespace BAN
|
||||
ErrorOr<void> insert(char, size_type);
|
||||
ErrorOr<void> insert(StringView, size_type);
|
||||
ErrorOr<void> append(StringView);
|
||||
ErrorOr<void> append(const String&);
|
||||
|
||||
void pop_back();
|
||||
void remove(size_type);
|
||||
void erase(size_type, size_type);
|
||||
|
||||
void clear();
|
||||
|
||||
char operator[](size_type) const;
|
||||
char& operator[](size_type);
|
||||
const_iterator begin() const { return const_iterator(data()); }
|
||||
iterator begin() { return iterator(data()); }
|
||||
const_iterator end() const { return const_iterator(data() + size()); }
|
||||
iterator end() { return iterator(data() + size()); }
|
||||
|
||||
char front() const { ASSERT(m_size > 0); return data()[0]; }
|
||||
char& front() { ASSERT(m_size > 0); return data()[0]; }
|
||||
|
||||
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||
|
||||
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
|
||||
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
|
||||
|
||||
bool operator==(const String&) const;
|
||||
bool operator==(StringView) const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
@@ -49,31 +62,48 @@ namespace BAN
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
StringView sv() const;
|
||||
StringView sv() const { return StringView(data(), size()); }
|
||||
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
size_type capacity() const;
|
||||
|
||||
char* data();
|
||||
const char* data() const;
|
||||
|
||||
private:
|
||||
ErrorOr<void> ensure_capacity(size_type);
|
||||
|
||||
ErrorOr<void> copy_impl(StringView);
|
||||
void move_impl(String&&);
|
||||
bool has_sso() const;
|
||||
|
||||
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||
|
||||
private:
|
||||
char* m_data = nullptr;
|
||||
size_type m_capacity = 0;
|
||||
size_type m_size = 0;
|
||||
struct SSOStorage
|
||||
{
|
||||
char data[sso_capacity + 1] {};
|
||||
};
|
||||
struct GeneralStorage
|
||||
{
|
||||
size_type capacity { 0 };
|
||||
char* data { nullptr };
|
||||
};
|
||||
|
||||
private:
|
||||
union {
|
||||
SSOStorage sso_storage;
|
||||
GeneralStorage general_storage;
|
||||
} m_storage { .sso_storage = SSOStorage() };
|
||||
size_type m_size : sizeof(size_type) * 8 - 1 { 0 };
|
||||
size_type m_has_sso : 1 { true };
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
String String::formatted(const char* format, const Args&... args)
|
||||
{
|
||||
String result;
|
||||
BAN::Formatter::print([&](char c){ result.push_back(c); }, format, args...);
|
||||
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/Iterators.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
@@ -10,12 +11,16 @@ namespace BAN
|
||||
{
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using const_iterator = ConstIteratorSimple<char, StringView>;
|
||||
|
||||
public:
|
||||
StringView();
|
||||
StringView(const String&);
|
||||
StringView(const char*, size_type = -1);
|
||||
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
|
||||
char operator[](size_type) const;
|
||||
|
||||
bool operator==(const String&) const;
|
||||
@@ -30,6 +35,7 @@ namespace BAN
|
||||
char back() const;
|
||||
char front() const;
|
||||
|
||||
bool contains(char) const;
|
||||
size_type count(char) const;
|
||||
|
||||
bool empty() const;
|
||||
|
||||
@@ -9,15 +9,18 @@ namespace BAN
|
||||
|
||||
struct Time
|
||||
{
|
||||
uint8_t second;
|
||||
uint8_t minute;
|
||||
uint8_t hour;
|
||||
uint8_t week_day;
|
||||
uint8_t day;
|
||||
uint32_t year;
|
||||
uint8_t month;
|
||||
int year;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
uint8_t week_day;
|
||||
};
|
||||
|
||||
uint64_t to_unix_time(const BAN::Time&);
|
||||
BAN::Time from_unix_time(uint64_t);
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace BAN
|
||||
template<typename T> struct maybe_const<true, T> { using type = const T; };
|
||||
template<bool B, typename T> using maybe_const_t = typename maybe_const<B, T>::type;
|
||||
|
||||
template<bool B, typename T1, typename T2> struct either_or { using type = T2; };
|
||||
template<typename T1, typename T2> struct either_or<true, T1, T2> { using type = T1; };
|
||||
template<bool B, typename T1, typename T2> using either_or_t = typename either_or<B, T1, T2>::type;
|
||||
|
||||
struct true_type { static constexpr bool value = true; };
|
||||
struct false_type { static constexpr bool value = false; };
|
||||
|
||||
@@ -40,6 +44,7 @@ namespace BAN
|
||||
template<typename T> struct is_lvalue_reference : false_type {};
|
||||
template<typename T> struct is_lvalue_reference<T&> : true_type {};
|
||||
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
|
||||
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
|
||||
|
||||
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
|
||||
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
|
||||
@@ -60,6 +65,21 @@ namespace BAN
|
||||
template<typename T> inline constexpr bool is_pointer_v = is_pointer<T>::value;
|
||||
template<typename T> concept pointer = is_pointer_v<T>;
|
||||
|
||||
template<typename T> struct is_const : false_type {};
|
||||
template<typename T> struct is_const<const T> : true_type {};
|
||||
template<typename T> inline constexpr bool is_const_v = is_const<T>::value;
|
||||
|
||||
template<typename T> struct is_arithmetic { static constexpr bool value = is_integral_v<T> || is_floating_point_v<T>; };
|
||||
template<typename T> inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
|
||||
template<typename T> struct is_signed<T, false> : false_type {};
|
||||
}
|
||||
template<typename T> struct is_signed : detail::is_signed<T> {};
|
||||
template<typename T> inline constexpr bool is_signed_v = is_signed<T>::value;
|
||||
|
||||
template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
|
||||
template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } };
|
||||
template<typename T> struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } };
|
||||
|
||||
@@ -3,26 +3,79 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
namespace BAN::UTF8
|
||||
{
|
||||
|
||||
static constexpr uint16_t utf8_to_codepoint(uint8_t* bytes, size_t count)
|
||||
static constexpr uint32_t invalid = 0xFFFFFFFF;
|
||||
|
||||
constexpr uint32_t byte_length(uint8_t first_byte)
|
||||
{
|
||||
if (count > 3)
|
||||
return 0xFFFF;
|
||||
if ((first_byte & 0x80) == 0x00)
|
||||
return 1;
|
||||
if ((first_byte & 0xE0) == 0xC0)
|
||||
return 2;
|
||||
if ((first_byte & 0xF0) == 0xE0)
|
||||
return 3;
|
||||
if ((first_byte & 0xF8) == 0xF0)
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < count; i++)
|
||||
constexpr uint32_t to_codepoint(uint8_t* bytes)
|
||||
{
|
||||
uint32_t length = byte_length(bytes[0]);
|
||||
|
||||
for (uint32_t i = 1; i < length; i++)
|
||||
if ((bytes[i] & 0xC0) != 0x80)
|
||||
return 0xFFFF;
|
||||
return UTF8::invalid;
|
||||
|
||||
switch (count)
|
||||
switch (length)
|
||||
{
|
||||
case 1: return bytes[0];
|
||||
case 2: return ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
||||
case 3: return ((bytes[0] & 0x1F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 0x3F);
|
||||
case 1: return ((bytes[0] & 0x80) != 0x00) ? UTF8::invalid : bytes[0];
|
||||
case 2: return ((bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
||||
case 3: return ((bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 0x3F);
|
||||
case 4: return ((bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) | ((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
|
||||
}
|
||||
|
||||
return 0xFFFF;
|
||||
return UTF8::invalid;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool from_codepoints(const T* codepoints, size_t count, char* out)
|
||||
{
|
||||
uint8_t* ptr = (uint8_t*)out;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
if (codepoints[i] < 0x80)
|
||||
{
|
||||
*ptr++ = codepoints[i];
|
||||
}
|
||||
else if (codepoints[i] < 0x800)
|
||||
{
|
||||
*ptr++ = 0xC0 | ((codepoints[i] >> 6) & 0x1F);
|
||||
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||
}
|
||||
else if (codepoints[i] < 0x10000)
|
||||
{
|
||||
*ptr++ = 0xE0 | ((codepoints[i] >> 12) & 0x0F);
|
||||
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
|
||||
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||
}
|
||||
else if (codepoints[i] < 0x110000)
|
||||
{
|
||||
*ptr++ = 0xF0 | ((codepoints[i] >> 18) & 0x07);
|
||||
*ptr++ = 0x80 | ((codepoints[i] >> 12) & 0x3F);
|
||||
*ptr++ = 0x80 | ((codepoints[i] >> 6) & 0x3F);
|
||||
*ptr++ = 0x80 | ((codepoints[i] >> 0) & 0x3F);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
98
BAN/include/BAN/UniqPtr.h
Normal file
98
BAN/include/BAN/UniqPtr.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class UniqPtr
|
||||
{
|
||||
BAN_NON_COPYABLE(UniqPtr);
|
||||
|
||||
public:
|
||||
UniqPtr() = default;
|
||||
|
||||
template<typename U>
|
||||
UniqPtr(UniqPtr<U>&& other)
|
||||
{
|
||||
m_pointer = other.m_pointer;
|
||||
other.m_pointer = nullptr;
|
||||
}
|
||||
|
||||
~UniqPtr()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
static UniqPtr adopt(T* pointer)
|
||||
{
|
||||
UniqPtr uniq;
|
||||
uniq.m_pointer = pointer;
|
||||
return uniq;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static BAN::ErrorOr<UniqPtr> create(Args&&... args)
|
||||
{
|
||||
UniqPtr uniq;
|
||||
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
|
||||
if (uniq.m_pointer == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return uniq;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
UniqPtr& operator=(UniqPtr<U>&& other)
|
||||
{
|
||||
clear();
|
||||
m_pointer = other.m_pointer;
|
||||
other.m_pointer = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& operator*()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
|
||||
T* operator->()
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
T* ptr() { return m_pointer; }
|
||||
const T* ptr() const { return m_pointer; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_pointer)
|
||||
delete m_pointer;
|
||||
m_pointer = nullptr;
|
||||
}
|
||||
|
||||
operator bool() const { return m_pointer != nullptr; }
|
||||
|
||||
private:
|
||||
T* m_pointer = nullptr;
|
||||
|
||||
template<typename U>
|
||||
friend class UniqPtr;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,144 +4,290 @@
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T1, typename T2>
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
constexpr size_t size_ref_as_ptr() { return is_lvalue_reference_v<T> ? sizeof(remove_reference_t<T>*) : sizeof(T); }
|
||||
template<typename T>
|
||||
constexpr size_t align_ref_as_ptr() { return is_lvalue_reference_v<T> ? alignof(remove_reference_t<T>*) : alignof(T); }
|
||||
|
||||
template<typename T>
|
||||
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T>(); }
|
||||
template<typename T0, typename T1, typename... Ts>
|
||||
constexpr size_t max_size_ref_as_ptr() { return size_ref_as_ptr<T0>() > size_ref_as_ptr<T1>() ? max_size_ref_as_ptr<T0, Ts...>() : max_size_ref_as_ptr<T1, Ts...>(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T>(); }
|
||||
template<typename T0, typename T1, typename... Ts>
|
||||
constexpr size_t max_align_ref_as_ptr() { return align_ref_as_ptr<T0>() > align_ref_as_ptr<T1>() ? max_align_ref_as_ptr<T0, Ts...>() : max_align_ref_as_ptr<T1, Ts...>(); }
|
||||
|
||||
template<typename T, typename T0, typename... Ts>
|
||||
constexpr size_t index()
|
||||
{
|
||||
if constexpr(is_same_v<T, T0>)
|
||||
return 0;
|
||||
else if constexpr(sizeof...(Ts) == 0)
|
||||
return 1;
|
||||
else
|
||||
return index<T, Ts...>() + 1;
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
void destruct(size_t index, uint8_t* data)
|
||||
{
|
||||
if (index == 0)
|
||||
if constexpr(!is_lvalue_reference_v<T>)
|
||||
reinterpret_cast<T*>(data)->~T();
|
||||
else;
|
||||
else if constexpr(sizeof...(Ts) > 0)
|
||||
destruct<Ts...>(index - 1, data);
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
void move_construct(size_t index, uint8_t* source, uint8_t* target)
|
||||
{
|
||||
if (index == 0)
|
||||
if constexpr(!is_lvalue_reference_v<T>)
|
||||
new (target) T(move(*reinterpret_cast<T*>(source)));
|
||||
else
|
||||
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||
else if constexpr(sizeof...(Ts) > 0)
|
||||
move_construct<Ts...>(index - 1, source, target);
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
void copy_construct(size_t index, const uint8_t* source, uint8_t* target)
|
||||
{
|
||||
if (index == 0)
|
||||
if constexpr(!is_lvalue_reference_v<T>)
|
||||
new (target) T(*reinterpret_cast<const T*>(source));
|
||||
else
|
||||
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||
else if constexpr(sizeof...(Ts) > 0)
|
||||
copy_construct<Ts...>(index - 1, source, target);
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
void move_assign(size_t index, uint8_t* source, uint8_t* target)
|
||||
{
|
||||
if (index == 0)
|
||||
if constexpr(!is_lvalue_reference_v<T>)
|
||||
*reinterpret_cast<T*>(target) = move(*reinterpret_cast<T*>(source));
|
||||
else
|
||||
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||
else if constexpr(sizeof...(Ts) > 0)
|
||||
move_assign<Ts...>(index - 1, source, target);
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
void copy_assign(size_t index, const uint8_t* source, uint8_t* target)
|
||||
{
|
||||
if (index == 0)
|
||||
if constexpr(!is_lvalue_reference_v<T>)
|
||||
*reinterpret_cast<T*>(target) = *reinterpret_cast<const T*>(source);
|
||||
else
|
||||
memcpy(target, source, sizeof(remove_reference_t<T>*));
|
||||
else if constexpr(sizeof...(Ts) > 0)
|
||||
copy_assign<Ts...>(index - 1, source, target);
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
requires (!is_const_v<Ts> && ...)
|
||||
class Variant
|
||||
{
|
||||
public:
|
||||
static_assert(!is_same_v<T1, T2>);
|
||||
private:
|
||||
template<typename T>
|
||||
static constexpr bool can_have() { return detail::index<T, Ts...>() != invalid_index(); }
|
||||
static constexpr size_t invalid_index() { return sizeof...(Ts); }
|
||||
|
||||
public:
|
||||
Variant() = default;
|
||||
|
||||
Variant(const T1& value) { set(value); }
|
||||
Variant(T1&& value) { set(move(value)); }
|
||||
Variant(const T2& value) { set(value); }
|
||||
Variant(T2&& value) { set(move(value)); }
|
||||
Variant(Variant&& other)
|
||||
: m_index(other.m_index)
|
||||
{
|
||||
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
other.clear();
|
||||
}
|
||||
|
||||
Variant(const Variant<T1, T2>& other) { *this = other; }
|
||||
Variant(Variant<T1, T2>&& other) { *this = move(other); }
|
||||
Variant(const Variant& other)
|
||||
: m_index(other.m_index)
|
||||
{
|
||||
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
}
|
||||
|
||||
~Variant() { clear(); }
|
||||
template<typename T>
|
||||
Variant(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
: m_index(detail::index<T, Ts...>())
|
||||
{
|
||||
new (m_storage) T(move(value));
|
||||
}
|
||||
|
||||
Variant<T1, T2>& operator=(const Variant<T1, T2>& other);
|
||||
Variant<T1, T2>& operator=(Variant<T1, T2>&& other);
|
||||
template<typename T>
|
||||
Variant(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
: m_index(detail::index<T, Ts...>())
|
||||
{
|
||||
new (m_storage) T(value);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool is() const;
|
||||
~Variant()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void set(U&&);
|
||||
template<typename U>
|
||||
void set(const U& value) { set(move(U(value))); }
|
||||
Variant& operator=(Variant&& other)
|
||||
{
|
||||
if (m_index == other.m_index)
|
||||
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||
else
|
||||
{
|
||||
clear();
|
||||
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
m_index = other.m_index;
|
||||
}
|
||||
other.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
const U& get() const;
|
||||
template<typename U>
|
||||
U& get();
|
||||
Variant& operator=(const Variant& other)
|
||||
{
|
||||
if (m_index == other.m_index)
|
||||
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
|
||||
else
|
||||
{
|
||||
clear();
|
||||
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||
m_index = other.m_index;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear();
|
||||
template<typename T>
|
||||
Variant& operator=(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
if (size_t index = detail::index<T, Ts...>(); index == m_index)
|
||||
get<T>() = move(value);
|
||||
else
|
||||
{
|
||||
clear();
|
||||
new (m_storage) T(move(value));
|
||||
m_index = index;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Variant& operator=(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
if (size_t index = detail::index<T, Ts...>(); index == m_index)
|
||||
get<T>() = value;
|
||||
else
|
||||
{
|
||||
clear();
|
||||
new (m_storage) T(value);
|
||||
m_index = index;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool has() const requires (can_have<T>())
|
||||
{
|
||||
return m_index == detail::index<T, Ts...>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
if (has<T>())
|
||||
get<T>() = move(value);
|
||||
else
|
||||
{
|
||||
clear();
|
||||
m_index = detail::index<T, Ts...>();
|
||||
new (m_storage) T(move(value));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(const T& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
if (has<T>())
|
||||
get<T>() = value;
|
||||
else
|
||||
{
|
||||
clear();
|
||||
m_index = detail::index<T, Ts...>();
|
||||
new (m_storage) T(value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(T value) requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||
{
|
||||
clear();
|
||||
m_index = detail::index<T, Ts...>();
|
||||
*reinterpret_cast<remove_reference_t<T>**>(m_storage) = &value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& get() requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
ASSERT(has<T>());
|
||||
return *reinterpret_cast<T*>(m_storage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& get() const requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
ASSERT(has<T>());
|
||||
return *reinterpret_cast<const T*>(m_storage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get() requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||
{
|
||||
ASSERT(has<T>());
|
||||
return **reinterpret_cast<remove_reference_t<T>**>(m_storage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T get() const requires (can_have<T>() && is_lvalue_reference_v<T>)
|
||||
{
|
||||
ASSERT(has<T>());
|
||||
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_index != invalid_index())
|
||||
{
|
||||
detail::destruct<Ts...>(m_index, m_storage);
|
||||
m_index = invalid_index();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint32_t m_size = Math::max(sizeof(T1), sizeof(T2));
|
||||
uint8_t m_storage[m_size] = {};
|
||||
uint32_t m_index = 0;
|
||||
alignas(detail::max_align_ref_as_ptr<Ts...>()) uint8_t m_storage[detail::max_size_ref_as_ptr<Ts...>()] {};
|
||||
size_t m_index { invalid_index() };
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
Variant<T1, T2>& Variant<T1, T2>::operator=(const Variant<T1, T2>& other)
|
||||
{
|
||||
clear();
|
||||
if (other.is<T1>())
|
||||
set(other.get<T1>());
|
||||
if (other.is<T2>())
|
||||
set(other.get<T2>());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
Variant<T1, T2>& Variant<T1, T2>::operator=(Variant<T1, T2>&& other)
|
||||
{
|
||||
clear();
|
||||
if (other.is<T1>())
|
||||
set(move(other.get<T1>()));
|
||||
if (other.is<T2>())
|
||||
set(move(other.get<T2>()));
|
||||
other.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
template<typename U>
|
||||
bool Variant<T1, T2>::is() const
|
||||
{
|
||||
if constexpr(is_same_v<T1, U>)
|
||||
return m_index == 1;
|
||||
if constexpr(is_same_v<T2, U>)
|
||||
return m_index == 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template<typename T1, typename T2>
|
||||
template<typename U>
|
||||
void Variant<T1, T2>::set(U&& value)
|
||||
{
|
||||
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
|
||||
clear();
|
||||
if constexpr(is_same_v<T1, U>)
|
||||
{
|
||||
new (m_storage) T1(move(value));
|
||||
m_index = 1;
|
||||
}
|
||||
if constexpr(is_same_v<T2, U>)
|
||||
{
|
||||
new (m_storage) T2(move(value));
|
||||
m_index = 2;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
template<typename U>
|
||||
const U& Variant<T1, T2>::get() const
|
||||
{
|
||||
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
|
||||
if constexpr(is_same_v<T1, U>)
|
||||
{
|
||||
ASSERT(m_index == 1);
|
||||
return *(T1*)m_storage;
|
||||
}
|
||||
if constexpr(is_same_v<T2, U>)
|
||||
{
|
||||
ASSERT(m_index == 2);
|
||||
return *(T2*)m_storage;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
template<typename U>
|
||||
U& Variant<T1, T2>::get()
|
||||
{
|
||||
static_assert(is_same_v<T1, U> || is_same_v<T2, U>);
|
||||
if constexpr(is_same_v<T1, U>)
|
||||
{
|
||||
ASSERT(m_index == 1);
|
||||
return *(T1*)m_storage;
|
||||
}
|
||||
if constexpr(is_same_v<T2, U>)
|
||||
{
|
||||
ASSERT(m_index == 2);
|
||||
return *(T2*)m_storage;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
void Variant<T1, T2>::clear()
|
||||
{
|
||||
if (is<T1>()) ((T1*)m_storage)->~T1();
|
||||
if (is<T2>()) ((T2*)m_storage)->~T2();
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Memory.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/Span.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, bool CONST>
|
||||
class VectorIterator;
|
||||
|
||||
// T must be move assignable, move constructable (and copy constructable for some functions)
|
||||
template<typename T>
|
||||
class Vector
|
||||
@@ -18,8 +17,8 @@ namespace BAN
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using value_type = T;
|
||||
using iterator = VectorIterator<T, false>;
|
||||
using const_iterator = VectorIterator<T, true>;
|
||||
using iterator = IteratorSimple<T, Vector>;
|
||||
using const_iterator = ConstIteratorSimple<T, Vector>;
|
||||
|
||||
public:
|
||||
Vector() = default;
|
||||
@@ -40,10 +39,10 @@ namespace BAN
|
||||
ErrorOr<void> insert(size_type, T&&);
|
||||
ErrorOr<void> insert(size_type, const T&);
|
||||
|
||||
iterator begin() { return iterator (m_data); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
iterator end() { return iterator (m_data + m_size); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
iterator begin() { return iterator(m_data); }
|
||||
iterator end() { return iterator(m_data + m_size); }
|
||||
const_iterator begin() const { return const_iterator(m_data); }
|
||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||
|
||||
void pop_back();
|
||||
void remove(size_type);
|
||||
@@ -54,6 +53,9 @@ namespace BAN
|
||||
|
||||
bool contains(const T&) const;
|
||||
|
||||
Span<T> span() { return Span(m_data, m_size); }
|
||||
const Span<T> span() const { return Span(m_data, m_size); }
|
||||
|
||||
const T& operator[](size_type) const;
|
||||
T& operator[](size_type);
|
||||
|
||||
@@ -62,7 +64,7 @@ namespace BAN
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
ErrorOr<void> resize(size_type);
|
||||
ErrorOr<void> resize(size_type, const T& = T());
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
@@ -79,51 +81,6 @@ namespace BAN
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<typename T, bool CONST>
|
||||
class VectorIterator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using data_type = maybe_const_t<CONST, T>;
|
||||
|
||||
public:
|
||||
VectorIterator() = default;
|
||||
template<bool C>
|
||||
VectorIterator(const VectorIterator<T, C>& other, enable_if_t<C == CONST || !C>)
|
||||
: m_data(other.m_data)
|
||||
{
|
||||
}
|
||||
|
||||
VectorIterator<T, CONST>& operator++() { m_data++; return *this; }
|
||||
VectorIterator<T, CONST>& operator--() { m_data--; return *this; }
|
||||
VectorIterator<T, CONST> operator++(int) { auto temp = *this; ++(*this); return temp; }
|
||||
VectorIterator<T, CONST> operator--(int) { auto temp = *this; --(*this); return temp; }
|
||||
|
||||
template<bool ENABLE = !CONST>
|
||||
enable_if_t<ENABLE, T&> operator*() { ASSERT(m_data); return *m_data; }
|
||||
const T& operator*() const { ASSERT(m_data); return *m_data; }
|
||||
|
||||
template<bool ENABLE = !CONST>
|
||||
enable_if_t<ENABLE, T*> operator->() { ASSERT(m_data); return m_data; }
|
||||
const T* operator->() const { ASSERT(m_data); return m_data; }
|
||||
|
||||
bool operator==(const VectorIterator<T, CONST>& other) const { return m_data == other.m_data; }
|
||||
bool operator!=(const VectorIterator<T, CONST>& other) const { return !(*this == other); }
|
||||
|
||||
operator bool() const { return m_data; }
|
||||
|
||||
private:
|
||||
VectorIterator(data_type* data) : m_data(data) { }
|
||||
|
||||
private:
|
||||
data_type* m_data = nullptr;
|
||||
|
||||
friend class Vector<T>;
|
||||
friend class VectorIterator<T, !CONST>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
Vector<T>::Vector(Vector<T>&& other)
|
||||
{
|
||||
@@ -256,7 +213,7 @@ namespace BAN
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::insert(size_type index, const T& value)
|
||||
{
|
||||
return insert(move(T(value)), index);
|
||||
return insert(index, move(T(value)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -340,7 +297,7 @@ namespace BAN
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size)
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value)
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
@@ -348,7 +305,7 @@ namespace BAN
|
||||
m_data[i].~T();
|
||||
if (size > m_size)
|
||||
for (size_type i = m_size; i < size; i++)
|
||||
new (m_data + i) T();
|
||||
new (m_data + i) T(value);
|
||||
m_size = size;
|
||||
return {};
|
||||
}
|
||||
|
||||
107
BAN/include/BAN/WeakPtr.h
Normal file
107
BAN/include/BAN/WeakPtr.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/RefPtr.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class Weakable;
|
||||
|
||||
template<typename T>
|
||||
class WeakPtr;
|
||||
|
||||
template<typename T>
|
||||
class WeakLink : public RefCounted<WeakLink<T>>
|
||||
{
|
||||
public:
|
||||
RefPtr<T> lock() { ASSERT(m_ptr); return raw_ptr(); }
|
||||
T* raw_ptr() { return m_ptr; }
|
||||
|
||||
bool valid() const { return m_ptr; }
|
||||
void invalidate() { m_ptr = nullptr; }
|
||||
|
||||
private:
|
||||
WeakLink(T* ptr) : m_ptr(ptr) {}
|
||||
|
||||
private:
|
||||
T* m_ptr;
|
||||
|
||||
friend class RefPtr<WeakLink<T>>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Weakable
|
||||
{
|
||||
public:
|
||||
~Weakable()
|
||||
{
|
||||
if (m_link)
|
||||
m_link->invalidate();
|
||||
}
|
||||
|
||||
ErrorOr<WeakPtr<T>> get_weak_ptr() const
|
||||
{
|
||||
if (!m_link)
|
||||
m_link = TRY(RefPtr<WeakLink<T>>::create((T*)this));
|
||||
return WeakPtr<T>(m_link);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable RefPtr<WeakLink<T>> m_link;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WeakPtr
|
||||
{
|
||||
public:
|
||||
WeakPtr() = default;
|
||||
WeakPtr(WeakPtr&& other) { *this = move(other); }
|
||||
WeakPtr(const WeakPtr& other) { *this = other; }
|
||||
WeakPtr(const RefPtr<T>& other) { *this = other; }
|
||||
|
||||
WeakPtr& operator=(WeakPtr&& other)
|
||||
{
|
||||
clear();
|
||||
m_link = move(other.m_link);
|
||||
return *this;
|
||||
}
|
||||
WeakPtr& operator=(const WeakPtr& other)
|
||||
{
|
||||
clear();
|
||||
m_link = other.m_link;
|
||||
return *this;
|
||||
}
|
||||
WeakPtr& operator=(const RefPtr<T>& other)
|
||||
{
|
||||
clear();
|
||||
if (other)
|
||||
m_link = MUST(other->get_weak_ptr()).move_link();
|
||||
return *this;
|
||||
}
|
||||
|
||||
RefPtr<T> lock()
|
||||
{
|
||||
if (m_link->valid())
|
||||
return m_link->lock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void clear() { m_link.clear(); }
|
||||
|
||||
bool valid() const { return m_link && m_link->valid(); }
|
||||
|
||||
private:
|
||||
WeakPtr(const RefPtr<WeakLink<T>>& link)
|
||||
: m_link(link)
|
||||
{ }
|
||||
|
||||
RefPtr<WeakLink<T>>&& move_link() { return move(m_link); }
|
||||
|
||||
private:
|
||||
RefPtr<WeakLink<T>> m_link;
|
||||
|
||||
friend class Weakable<T>;
|
||||
};
|
||||
|
||||
}
|
||||
44
CMakeLists.txt
Normal file
44
CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os")
|
||||
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os")
|
||||
endif ()
|
||||
|
||||
add_compile_options(-mno-sse -mno-sse2)
|
||||
add_compile_definitions(__enable_sse=0)
|
||||
|
||||
project(banan-os CXX C ASM)
|
||||
|
||||
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
|
||||
set(BANAN_INCLUDE ${BANAN_SYSROOT}/usr/include)
|
||||
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
|
||||
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
|
||||
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
|
||||
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(bootloader)
|
||||
add_subdirectory(BAN)
|
||||
add_subdirectory(libc)
|
||||
add_subdirectory(LibELF)
|
||||
add_subdirectory(userspace)
|
||||
|
||||
add_custom_target(sysroot
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
|
||||
COMMAND cd ${BANAN_SYSROOT} && tar xf ${BANAN_BASE_SYSROOT}
|
||||
)
|
||||
|
||||
add_custom_target(headers
|
||||
DEPENDS kernel-headers
|
||||
DEPENDS ban-headers
|
||||
DEPENDS libc-headers
|
||||
DEPENDS libelf-headers
|
||||
)
|
||||
|
||||
add_custom_target(install-sysroot
|
||||
COMMAND cd ${BANAN_SYSROOT} && tar cf ${BANAN_SYSROOT_TAR} *
|
||||
DEPENDS kernel-install
|
||||
DEPENDS ban-install
|
||||
DEPENDS libc-install
|
||||
DEPENDS userspace-install
|
||||
DEPENDS libelf-install
|
||||
)
|
||||
24
LICENCE
Normal file
24
LICENCE
Normal file
@@ -0,0 +1,24 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2023, Oskari Alaranta
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
12
LibELF/CMakeLists.txt
Normal file
12
LibELF/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(LibELF CXX)
|
||||
|
||||
add_custom_target(libelf-headers
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
|
||||
DEPENDS sysroot
|
||||
)
|
||||
|
||||
add_custom_target(libelf-install
|
||||
DEPENDS libelf-headers
|
||||
)
|
||||
406
LibELF/LibELF/ELF.cpp
Normal file
406
LibELF/LibELF/ELF.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
#include <BAN/ScopeGuard.h>
|
||||
#include <LibELF/ELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/Memory/PageTableScope.h>
|
||||
#include <kernel/Process.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#define ELF_PRINT_HEADERS 0
|
||||
|
||||
#ifdef __is_kernel
|
||||
extern uint8_t g_kernel_end[];
|
||||
using namespace Kernel;
|
||||
#endif
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
#ifdef __is_kernel
|
||||
BAN::ErrorOr<BAN::UniqPtr<ELF>> ELF::load_from_file(BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
BAN::Vector<uint8_t> buffer;
|
||||
TRY(buffer.resize(inode->size()));
|
||||
|
||||
TRY(inode->read(0, buffer.data(), inode->size()));
|
||||
|
||||
ELF* elf_ptr = new ELF(BAN::move(buffer));
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
auto elf = BAN::UniqPtr<ELF>::adopt(elf_ptr);
|
||||
TRY(elf->load());
|
||||
|
||||
return BAN::move(elf);
|
||||
}
|
||||
#else
|
||||
BAN::ErrorOr<ELF*> ELF::load_from_file(BAN::StringView file_path)
|
||||
{
|
||||
ELF* elf = nullptr;
|
||||
|
||||
{
|
||||
BAN::Vector<uint8_t> data;
|
||||
|
||||
int fd = TRY(Kernel::Process::current().open(file_path, O_RDONLY));
|
||||
BAN::ScopeGuard _([fd] { MUST(Kernel::Process::current().close(fd)); });
|
||||
|
||||
struct stat st;
|
||||
TRY(Kernel::Process::current().fstat(fd, &st));
|
||||
|
||||
TRY(data.resize(st.st_size));
|
||||
|
||||
TRY(Kernel::Process::current().read(fd, data.data(), data.size()));
|
||||
|
||||
elf = new ELF(BAN::move(data));
|
||||
ASSERT(elf);
|
||||
}
|
||||
|
||||
if (auto res = elf->load(); res.is_error())
|
||||
{
|
||||
delete elf;
|
||||
return res.error();
|
||||
}
|
||||
|
||||
return elf;
|
||||
}
|
||||
#endif
|
||||
|
||||
BAN::ErrorOr<void> ELF::load()
|
||||
{
|
||||
if (m_data.size() < EI_NIDENT)
|
||||
{
|
||||
dprintln("Too small ELF file");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_MAG0] != ELFMAG0 ||
|
||||
m_data[EI_MAG1] != ELFMAG1 ||
|
||||
m_data[EI_MAG2] != ELFMAG2 ||
|
||||
m_data[EI_MAG3] != ELFMAG3)
|
||||
{
|
||||
dprintln("Invalid ELF header");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
dprintln("Only little-endian is supported");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid ELF version");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_data[EI_CLASS] == ELFCLASS64)
|
||||
{
|
||||
if (m_data.size() <= sizeof(Elf64FileHeader))
|
||||
{
|
||||
dprintln("Too small ELF file");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto& header = file_header64();
|
||||
if (!parse_elf64_file_header(header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
for (size_t i = 0; i < header.e_phnum; i++)
|
||||
{
|
||||
auto& program_header = program_header64(i);
|
||||
if (!parse_elf64_program_header(program_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < header.e_shnum; i++)
|
||||
{
|
||||
auto& section_header = section_header64(i);
|
||||
if (!parse_elf64_section_header(section_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
else if (m_data[EI_CLASS] == ELFCLASS32)
|
||||
{
|
||||
if (m_data.size() <= sizeof(Elf32FileHeader))
|
||||
{
|
||||
dprintln("Too small ELF file");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto& header = file_header32();
|
||||
if (!parse_elf32_file_header(header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
for (size_t i = 0; i < header.e_phnum; i++)
|
||||
{
|
||||
auto& program_header = program_header32(i);
|
||||
if (!parse_elf32_program_header(program_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < header.e_shnum; i++)
|
||||
{
|
||||
auto& section_header = section_header32(i);
|
||||
if (!parse_elf32_section_header(section_header))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ELF::is_x86_32() const { return m_data[EI_CLASS] == ELFCLASS32; }
|
||||
bool ELF::is_x86_64() const { return m_data[EI_CLASS] == ELFCLASS64; }
|
||||
|
||||
/*
|
||||
|
||||
64 bit ELF
|
||||
|
||||
*/
|
||||
|
||||
const char* ELF::lookup_section_name64(uint32_t offset) const
|
||||
{
|
||||
return lookup_string64(file_header64().e_shstrndx, offset);
|
||||
}
|
||||
|
||||
const char* ELF::lookup_string64(size_t table_index, uint32_t offset) const
|
||||
{
|
||||
if (table_index == SHN_UNDEF)
|
||||
return nullptr;
|
||||
auto& section_header = section_header64(table_index);
|
||||
return (const char*)m_data.data() + section_header.sh_offset + offset;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf64_file_header(const Elf64FileHeader& header)
|
||||
{
|
||||
if (header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid ELF version");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf64_program_header(const Elf64ProgramHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
dprintln("program header");
|
||||
dprintln(" type {H}", header.p_type);
|
||||
dprintln(" flags {H}", header.p_flags);
|
||||
dprintln(" offset {H}", header.p_offset);
|
||||
dprintln(" vaddr {H}", header.p_vaddr);
|
||||
dprintln(" paddr {H}", header.p_paddr);
|
||||
dprintln(" filesz {}", header.p_filesz);
|
||||
dprintln(" memsz {}", header.p_memsz);
|
||||
dprintln(" align {}", header.p_align);
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf64_section_header(const Elf64SectionHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
if (auto* name = lookup_section_name64(header.sh_name))
|
||||
dprintln("{}", name);
|
||||
|
||||
switch (header.sh_type)
|
||||
{
|
||||
case SHT_NULL:
|
||||
dprintln(" SHT_NULL");
|
||||
break;
|
||||
case SHT_PROGBITS:
|
||||
dprintln(" SHT_PROGBITS");
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
|
||||
{
|
||||
auto& symbol = ((const Elf64Symbol*)(m_data.data() + header.sh_offset))[i];
|
||||
if (auto* name = lookup_string64(header.sh_link, symbol.st_name))
|
||||
dprintln(" {}", name);
|
||||
}
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
dprintln(" SHT_STRTAB");
|
||||
break;
|
||||
case SHT_RELA:
|
||||
dprintln(" SHT_RELA");
|
||||
break;
|
||||
case SHT_NOBITS:
|
||||
dprintln(" SHT_NOBITS");
|
||||
break;
|
||||
case SHT_REL:
|
||||
dprintln(" SHT_REL");
|
||||
break;
|
||||
case SHT_SHLIB:
|
||||
dprintln(" SHT_SHLIB");
|
||||
break;
|
||||
case SHT_DYNSYM:
|
||||
dprintln(" SHT_DYNSYM");
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Elf64FileHeader& ELF::file_header64() const
|
||||
{
|
||||
ASSERT(is_x86_64());
|
||||
return *(const Elf64FileHeader*)m_data.data();
|
||||
}
|
||||
|
||||
const Elf64ProgramHeader& ELF::program_header64(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_64());
|
||||
const auto& file_header = file_header64();
|
||||
ASSERT(index < file_header.e_phnum);
|
||||
return *(const Elf64ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index);
|
||||
}
|
||||
|
||||
const Elf64SectionHeader& ELF::section_header64(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_64());
|
||||
const auto& file_header = file_header64();
|
||||
ASSERT(index < file_header.e_shnum);
|
||||
return *(const Elf64SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
32 bit ELF
|
||||
|
||||
*/
|
||||
|
||||
const char* ELF::lookup_section_name32(uint32_t offset) const
|
||||
{
|
||||
return lookup_string32(file_header32().e_shstrndx, offset);
|
||||
}
|
||||
|
||||
const char* ELF::lookup_string32(size_t table_index, uint32_t offset) const
|
||||
{
|
||||
if (table_index == SHN_UNDEF)
|
||||
return nullptr;
|
||||
auto& section_header = section_header32(table_index);
|
||||
return (const char*)m_data.data() + section_header.sh_offset + offset;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf32_file_header(const Elf32FileHeader& header)
|
||||
{
|
||||
if (header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid ELF version");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf32_program_header(const Elf32ProgramHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
dprintln("program header");
|
||||
dprintln(" type {H}", header.p_type);
|
||||
dprintln(" flags {H}", header.p_flags);
|
||||
dprintln(" offset {H}", header.p_offset);
|
||||
dprintln(" vaddr {H}", header.p_vaddr);
|
||||
dprintln(" paddr {H}", header.p_paddr);
|
||||
dprintln(" filesz {}", header.p_filesz);
|
||||
dprintln(" memsz {}", header.p_memsz);
|
||||
dprintln(" align {}", header.p_align);
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELF::parse_elf32_section_header(const Elf32SectionHeader& header)
|
||||
{
|
||||
#if ELF_PRINT_HEADERS
|
||||
if (auto* name = lookup_section_name32(header.sh_name))
|
||||
dprintln("{}", name);
|
||||
|
||||
switch (header.sh_type)
|
||||
{
|
||||
case SHT_NULL:
|
||||
dprintln(" SHT_NULL");
|
||||
break;
|
||||
case SHT_PROGBITS:
|
||||
dprintln(" SHT_PROGBITS");
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
|
||||
{
|
||||
auto& symbol = ((const Elf32Symbol*)(m_data.data() + header.sh_offset))[i];
|
||||
if (auto* name = lookup_string32(header.sh_link, symbol.st_name))
|
||||
dprintln(" {}", name);
|
||||
}
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
dprintln(" SHT_STRTAB");
|
||||
break;
|
||||
case SHT_RELA:
|
||||
dprintln(" SHT_RELA");
|
||||
break;
|
||||
case SHT_NOBITS:
|
||||
dprintln(" SHT_NOBITS");
|
||||
break;
|
||||
case SHT_REL:
|
||||
dprintln(" SHT_REL");
|
||||
break;
|
||||
case SHT_SHLIB:
|
||||
dprintln(" SHT_SHLIB");
|
||||
break;
|
||||
case SHT_DYNSYM:
|
||||
dprintln(" SHT_DYNSYM");
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
#endif
|
||||
(void)header;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Elf32FileHeader& ELF::file_header32() const
|
||||
{
|
||||
ASSERT(is_x86_32());
|
||||
return *(const Elf32FileHeader*)m_data.data();
|
||||
}
|
||||
|
||||
const Elf32ProgramHeader& ELF::program_header32(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_32());
|
||||
const auto& file_header = file_header32();
|
||||
ASSERT(index < file_header.e_phnum);
|
||||
return *(const Elf32ProgramHeader*)(m_data.data() + file_header.e_phoff + file_header.e_phentsize * index);
|
||||
}
|
||||
|
||||
const Elf32SectionHeader& ELF::section_header32(size_t index) const
|
||||
{
|
||||
ASSERT(is_x86_32());
|
||||
const auto& file_header = file_header32();
|
||||
ASSERT(index < file_header.e_shnum);
|
||||
return *(const Elf32SectionHeader*)(m_data.data() + file_header.e_shoff + file_header.e_shentsize * index);
|
||||
}
|
||||
|
||||
}
|
||||
331
LibELF/LibELF/LoadableELF.cpp
Normal file
331
LibELF/LibELF/LoadableELF.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/CriticalScope.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <LibELF/LoadableELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
auto* elf_ptr = new LoadableELF(page_table, inode);
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
||||
TRY(elf->initialize());
|
||||
return BAN::move(elf);
|
||||
}
|
||||
|
||||
LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
||||
: m_inode(inode)
|
||||
, m_page_table(page_table)
|
||||
{
|
||||
}
|
||||
|
||||
LoadableELF::~LoadableELF()
|
||||
{
|
||||
if (!m_loaded)
|
||||
return;
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
continue;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
for (size_t i = 0; i < pages; i++)
|
||||
{
|
||||
paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
|
||||
if (paddr != 0)
|
||||
Heap::get().release_page(paddr);
|
||||
}
|
||||
m_page_table.unmap_range(start, pages * PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> LoadableELF::initialize()
|
||||
{
|
||||
if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
|
||||
{
|
||||
dprintln("Too small file");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
size_t nread = TRY(m_inode->read(0, BAN::ByteSpan::from(m_file_header)));
|
||||
ASSERT(nread == sizeof(m_file_header));
|
||||
|
||||
if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 ||
|
||||
m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
|
||||
m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
|
||||
m_file_header.e_ident[EI_MAG3] != ELFMAG3)
|
||||
{
|
||||
dprintln("Invalid magic in header");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
dprintln("Only little-endian is supported");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid version");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
#if ARCH(i386)
|
||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
#elif ARCH(x86_64)
|
||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
#endif
|
||||
{
|
||||
dprintln("Not in native format");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_file_header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_file_header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Unsupported version");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
|
||||
|
||||
TRY(m_program_headers.resize(m_file_header.e_phnum));
|
||||
for (size_t i = 0; i < m_file_header.e_phnum; i++)
|
||||
{
|
||||
TRY(m_inode->read(m_file_header.e_phoff + m_file_header.e_phentsize * i, BAN::ByteSpan::from(m_program_headers[i])));
|
||||
|
||||
const auto& pheader = m_program_headers[i];
|
||||
if (pheader.p_type != PT_NULL && pheader.p_type != PT_LOAD)
|
||||
{
|
||||
dprintln("Unsupported program header type {}", pheader.p_type);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
if (pheader.p_memsz < pheader.p_filesz)
|
||||
{
|
||||
dprintln("Invalid program header");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
vaddr_t LoadableELF::entry_point() const
|
||||
{
|
||||
return m_file_header.e_entry;
|
||||
}
|
||||
|
||||
bool LoadableELF::contains(vaddr_t address) const
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
continue;
|
||||
case PT_LOAD:
|
||||
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadableELF::is_address_space_free() const
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoadableELF::reserve_address_space()
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
ASSERT(m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
|
||||
continue;
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
// Temporarily map page as RW so kernel can write to it
|
||||
m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
m_physical_page_count++;
|
||||
|
||||
memset((void*)vaddr, 0x00, PAGE_SIZE);
|
||||
|
||||
if (vaddr / PAGE_SIZE < BAN::Math::div_round_up<size_t>(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE))
|
||||
{
|
||||
size_t vaddr_offset = 0;
|
||||
if (vaddr < program_header.p_vaddr)
|
||||
vaddr_offset = program_header.p_vaddr - vaddr;
|
||||
|
||||
size_t file_offset = 0;
|
||||
if (vaddr > program_header.p_vaddr)
|
||||
file_offset = vaddr - program_header.p_vaddr;
|
||||
|
||||
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
|
||||
TRY(m_inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes }));
|
||||
}
|
||||
|
||||
// Map page with the correct flags
|
||||
m_page_table.map_page_at(paddr, vaddr, flags);
|
||||
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
||||
{
|
||||
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
||||
|
||||
memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
|
||||
|
||||
TRY(elf->m_program_headers.resize(m_program_headers.size()));
|
||||
memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
|
||||
|
||||
elf->reserve_address_space();
|
||||
|
||||
ASSERT(&PageTable::current() == &m_page_table);
|
||||
LockGuard _(m_page_table);
|
||||
ASSERT(m_page_table.is_page_free(0));
|
||||
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
if (!(program_header.p_flags & LibELF::PF_W))
|
||||
continue;
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
|
||||
for (size_t i = 0; i < pages; i++)
|
||||
{
|
||||
if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
|
||||
continue;
|
||||
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
{
|
||||
CriticalScope _;
|
||||
PageTable::map_fast_page(paddr);
|
||||
memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
|
||||
PageTable::unmap_fast_page();
|
||||
}
|
||||
|
||||
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
|
||||
elf->m_physical_page_count++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return elf;
|
||||
}
|
||||
|
||||
}
|
||||
89
LibELF/include/LibELF/ELF.h
Normal file
89
LibELF/include/LibELF/ELF.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __is_kernel
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/VirtualRange.h>
|
||||
#endif
|
||||
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
#include <kernel/Arch.h>
|
||||
#include "Types.h"
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class ELF
|
||||
{
|
||||
public:
|
||||
#ifdef __is_kernel
|
||||
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::RefPtr<Kernel::Inode>);
|
||||
#else
|
||||
static BAN::ErrorOr<BAN::UniqPtr<ELF>> load_from_file(BAN::StringView);
|
||||
#endif
|
||||
|
||||
const Elf64FileHeader& file_header64() const;
|
||||
const Elf64ProgramHeader& program_header64(size_t) const;
|
||||
const Elf64SectionHeader& section_header64(size_t) const;
|
||||
const char* lookup_section_name64(uint32_t) const;
|
||||
const char* lookup_string64(size_t, uint32_t) const;
|
||||
#if ARCH(x86_64)
|
||||
const Elf64FileHeader& file_header_native() const { return file_header64(); }
|
||||
const Elf64ProgramHeader& program_header_native(size_t index) const { return program_header64(index); }
|
||||
const Elf64SectionHeader& section_header_native(size_t index) const { return section_header64(index); }
|
||||
const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name64(offset); }
|
||||
const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string64(table_index, offset); }
|
||||
bool is_native() const { return is_x86_64(); }
|
||||
#endif
|
||||
|
||||
const Elf32FileHeader& file_header32() const;
|
||||
const Elf32ProgramHeader& program_header32(size_t) const;
|
||||
const Elf32SectionHeader& section_header32(size_t) const;
|
||||
const char* lookup_section_name32(uint32_t) const;
|
||||
const char* lookup_string32(size_t, uint32_t) const;
|
||||
#if ARCH(i386)
|
||||
const Elf32FileHeader& file_header_native() const { return file_header32(); }
|
||||
const Elf32ProgramHeader& program_header_native(size_t index) const { return program_header32(index); }
|
||||
const Elf32SectionHeader& section_header_native(size_t index) const { return section_header32(index); }
|
||||
const char* lookup_section_name_native(uint32_t offset) const { return lookup_section_name32(offset); }
|
||||
const char* lookup_string_native(size_t table_index, uint32_t offset) const { return lookup_string32(table_index, offset); }
|
||||
bool is_native() const { return is_x86_32(); }
|
||||
#endif
|
||||
|
||||
const uint8_t* data() const { return m_data.data(); }
|
||||
|
||||
bool is_x86_32() const;
|
||||
bool is_x86_64() const;
|
||||
|
||||
private:
|
||||
//#ifdef __is_kernel
|
||||
// ELF(BAN::UniqPtr<Kernel::VirtualRange>&& storage, size_t size)
|
||||
// : m_storage(BAN::move(storage))
|
||||
// , m_data((const uint8_t*)m_storage->vaddr(), size)
|
||||
// {}
|
||||
//#else
|
||||
ELF(BAN::Vector<uint8_t>&& data)
|
||||
: m_data(BAN::move(data))
|
||||
{}
|
||||
//#endif
|
||||
BAN::ErrorOr<void> load();
|
||||
|
||||
bool parse_elf64_file_header(const Elf64FileHeader&);
|
||||
bool parse_elf64_program_header(const Elf64ProgramHeader&);
|
||||
bool parse_elf64_section_header(const Elf64SectionHeader&);
|
||||
|
||||
bool parse_elf32_file_header(const Elf32FileHeader&);
|
||||
bool parse_elf32_program_header(const Elf32ProgramHeader&);
|
||||
bool parse_elf32_section_header(const Elf32SectionHeader&);
|
||||
|
||||
private:
|
||||
//#ifdef __is_kernel
|
||||
// BAN::UniqPtr<Kernel::VirtualRange> m_storage;
|
||||
// BAN::Span<const uint8_t> m_data;
|
||||
//#else
|
||||
const BAN::Vector<uint8_t> m_data;
|
||||
//#endif
|
||||
};
|
||||
|
||||
}
|
||||
54
LibELF/include/LibELF/LoadableELF.h
Normal file
54
LibELF/include/LibELF/LoadableELF.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __is_kernel
|
||||
#error "This is kernel only header"
|
||||
#endif
|
||||
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class LoadableELF
|
||||
{
|
||||
BAN_NON_COPYABLE(LoadableELF);
|
||||
BAN_NON_MOVABLE(LoadableELF);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
||||
~LoadableELF();
|
||||
|
||||
Kernel::vaddr_t entry_point() const;
|
||||
|
||||
bool contains(Kernel::vaddr_t address) const;
|
||||
bool is_address_space_free() const;
|
||||
void reserve_address_space();
|
||||
|
||||
BAN::ErrorOr<void> load_page_to_memory(Kernel::vaddr_t address);
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> clone(Kernel::PageTable&);
|
||||
|
||||
size_t virtual_page_count() const { return m_virtual_page_count; }
|
||||
size_t physical_page_count() const { return m_physical_page_count; }
|
||||
|
||||
private:
|
||||
LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
private:
|
||||
BAN::RefPtr<Kernel::Inode> m_inode;
|
||||
Kernel::PageTable& m_page_table;
|
||||
ElfNativeFileHeader m_file_header;
|
||||
BAN::Vector<ElfNativeProgramHeader> m_program_headers;
|
||||
size_t m_virtual_page_count = 0;
|
||||
size_t m_physical_page_count = 0;
|
||||
bool m_loaded { false };
|
||||
};
|
||||
|
||||
}
|
||||
186
LibELF/include/LibELF/Types.h
Normal file
186
LibELF/include/LibELF/Types.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/Arch.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
using Elf32Addr = uint32_t;
|
||||
using Elf32Off = uint32_t;
|
||||
using Elf32Half = uint16_t;
|
||||
using Elf32Word = uint32_t;
|
||||
using Elf32Sword = int32_t;
|
||||
|
||||
struct Elf32FileHeader
|
||||
{
|
||||
unsigned char e_ident[16];
|
||||
Elf32Half e_type;
|
||||
Elf32Half e_machine;
|
||||
Elf32Word e_version;
|
||||
Elf32Addr e_entry;
|
||||
Elf32Off e_phoff;
|
||||
Elf32Off e_shoff;
|
||||
Elf32Word e_flags;
|
||||
Elf32Half e_ehsize;
|
||||
Elf32Half e_phentsize;
|
||||
Elf32Half e_phnum;
|
||||
Elf32Half e_shentsize;
|
||||
Elf32Half e_shnum;
|
||||
Elf32Half e_shstrndx;
|
||||
};
|
||||
|
||||
struct Elf32SectionHeader
|
||||
{
|
||||
Elf32Word sh_name;
|
||||
Elf32Word sh_type;
|
||||
Elf32Word sh_flags;
|
||||
Elf32Addr sh_addr;
|
||||
Elf32Off sh_offset;
|
||||
Elf32Word sh_size;
|
||||
Elf32Word sh_link;
|
||||
Elf32Word sh_info;
|
||||
Elf32Word sh_addralign;
|
||||
Elf32Word sh_entsize;
|
||||
};
|
||||
|
||||
struct Elf32Symbol
|
||||
{
|
||||
Elf32Word st_name;
|
||||
Elf32Addr st_value;
|
||||
Elf32Word st_size;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf32Half st_shndx;
|
||||
};
|
||||
|
||||
struct Elf32Relocation
|
||||
{
|
||||
Elf32Addr r_offset;
|
||||
Elf32Word r_info;
|
||||
};
|
||||
|
||||
struct Elf32RelocationA
|
||||
{
|
||||
Elf32Addr r_offset;
|
||||
Elf32Word r_info;
|
||||
Elf32Sword r_addend;
|
||||
};
|
||||
|
||||
struct Elf32ProgramHeader
|
||||
{
|
||||
Elf32Word p_type;
|
||||
Elf32Off p_offset;
|
||||
Elf32Addr p_vaddr;
|
||||
Elf32Addr p_paddr;
|
||||
Elf32Word p_filesz;
|
||||
Elf32Word p_memsz;
|
||||
Elf32Word p_flags;
|
||||
Elf32Word p_align;
|
||||
};
|
||||
|
||||
using Elf64Addr = uint64_t;
|
||||
using Elf64Off = uint64_t;
|
||||
using Elf64Half = uint16_t;
|
||||
using Elf64Word = uint32_t;
|
||||
using Elf64Sword = int32_t;
|
||||
using Elf64Xword = uint64_t;
|
||||
using Elf64Sxword = int64_t;
|
||||
|
||||
struct Elf64FileHeader
|
||||
{
|
||||
unsigned char e_ident[16];
|
||||
Elf64Half e_type;
|
||||
Elf64Half e_machine;
|
||||
Elf64Word e_version;
|
||||
Elf64Addr e_entry;
|
||||
Elf64Off e_phoff;
|
||||
Elf64Off e_shoff;
|
||||
Elf64Word e_flags;
|
||||
Elf64Half e_ehsize;
|
||||
Elf64Half e_phentsize;
|
||||
Elf64Half e_phnum;
|
||||
Elf64Half e_shentsize;
|
||||
Elf64Half e_shnum;
|
||||
Elf64Half e_shstrndx;
|
||||
};
|
||||
|
||||
struct Elf64SectionHeader
|
||||
{
|
||||
Elf64Word sh_name;
|
||||
Elf64Word sh_type;
|
||||
Elf64Xword sh_flags;
|
||||
Elf64Addr sh_addr;
|
||||
Elf64Off sh_offset;
|
||||
Elf64Xword sh_size;
|
||||
Elf64Word sh_link;
|
||||
Elf64Word sh_info;
|
||||
Elf64Xword sh_addralign;
|
||||
Elf64Xword sh_entsize;
|
||||
};
|
||||
|
||||
struct Elf64Symbol
|
||||
{
|
||||
Elf64Word st_name;
|
||||
unsigned char st_info;
|
||||
unsigned char st_other;
|
||||
Elf64Half st_shndx;
|
||||
Elf64Addr st_value;
|
||||
Elf64Xword st_size;
|
||||
};
|
||||
|
||||
struct Elf64Relocation
|
||||
{
|
||||
Elf64Addr r_offset;
|
||||
Elf64Xword r_info;
|
||||
};
|
||||
|
||||
struct Elf64RelocationA
|
||||
{
|
||||
Elf64Addr r_offset;
|
||||
Elf64Xword r_info;
|
||||
Elf64Sxword r_addend;
|
||||
};
|
||||
|
||||
struct Elf64ProgramHeader
|
||||
{
|
||||
Elf64Word p_type;
|
||||
Elf64Word p_flags;
|
||||
Elf64Off p_offset;
|
||||
Elf64Addr p_vaddr;
|
||||
Elf64Addr p_paddr;
|
||||
Elf64Xword p_filesz;
|
||||
Elf64Xword p_memsz;
|
||||
Elf64Xword p_align;
|
||||
};
|
||||
|
||||
#if ARCH(i386)
|
||||
using ElfNativeAddr = Elf32Addr;
|
||||
using ElfNativeOff = Elf32Off;
|
||||
using ElfNativeHalf = Elf32Half;
|
||||
using ElfNativeWord = Elf32Word;
|
||||
using ElfNativeSword = Elf32Sword;
|
||||
using ElfNativeFileHeader = Elf32FileHeader;
|
||||
using ElfNativeSectionHeader = Elf32SectionHeader;
|
||||
using ElfNativeSymbol = Elf32Symbol;
|
||||
using ElfNativeRelocation = Elf32Relocation;
|
||||
using ElfNativeRelocationA = Elf32RelocationA;
|
||||
using ElfNativeProgramHeader = Elf32ProgramHeader;
|
||||
#elif ARCH(x86_64)
|
||||
using ElfNativeAddr = Elf64Addr;
|
||||
using ElfNativeOff = Elf64Off;
|
||||
using ElfNativeHalf = Elf64Half;
|
||||
using ElfNativeWord = Elf64Word;
|
||||
using ElfNativeSword = Elf64Sword;
|
||||
using ElfNativeXword = Elf64Xword;
|
||||
using ElfNativeSxword = Elf64Sxword;
|
||||
using ElfNativeFileHeader = Elf64FileHeader;
|
||||
using ElfNativeSectionHeader = Elf64SectionHeader;
|
||||
using ElfNativeSymbol = Elf64Symbol;
|
||||
using ElfNativeRelocation = Elf64Relocation;
|
||||
using ElfNativeRelocationA = Elf64RelocationA;
|
||||
using ElfNativeProgramHeader = Elf64ProgramHeader;
|
||||
#endif
|
||||
|
||||
}
|
||||
140
LibELF/include/LibELF/Values.h
Normal file
140
LibELF/include/LibELF/Values.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
enum ELF_Ident
|
||||
{
|
||||
ELFMAG0 = 0x7F,
|
||||
ELFMAG1 = 'E',
|
||||
ELFMAG2 = 'L',
|
||||
ELFMAG3 = 'F',
|
||||
|
||||
ELFCLASSNONE = 0,
|
||||
ELFCLASS32 = 1,
|
||||
ELFCLASS64 = 2,
|
||||
|
||||
ELFDATANONE = 0,
|
||||
ELFDATA2LSB = 1,
|
||||
ELFDATA2MSB = 2,
|
||||
};
|
||||
|
||||
enum ELF_EI
|
||||
{
|
||||
EI_MAG0 = 0,
|
||||
EI_MAG1 = 1,
|
||||
EI_MAG2 = 2,
|
||||
EI_MAG3 = 3,
|
||||
EI_CLASS = 4,
|
||||
EI_DATA = 5,
|
||||
EI_VERSION = 6,
|
||||
EI_OSABI = 7,
|
||||
EI_ABIVERSION = 8,
|
||||
EI_NIDENT = 16,
|
||||
};
|
||||
|
||||
enum ELF_ET
|
||||
{
|
||||
ET_NONE = 0,
|
||||
ET_REL = 1,
|
||||
ET_EXEC = 2,
|
||||
ET_DYN = 3,
|
||||
ET_CORE = 4,
|
||||
ET_LOOS = 0xfe00,
|
||||
ET_HIOS = 0xfeff,
|
||||
ET_LOPROC = 0xff00,
|
||||
ET_HIPROC = 0xffff,
|
||||
};
|
||||
|
||||
enum ELF_EV
|
||||
{
|
||||
EV_NONE = 0,
|
||||
EV_CURRENT = 1,
|
||||
};
|
||||
|
||||
enum ELF_SHT
|
||||
{
|
||||
SHT_NULL = 0,
|
||||
SHT_PROGBITS = 1,
|
||||
SHT_SYMTAB = 2,
|
||||
SHT_STRTAB = 3,
|
||||
SHT_RELA = 4,
|
||||
SHT_NOBITS = 8,
|
||||
SHT_REL = 9,
|
||||
SHT_SHLIB = 10,
|
||||
SHT_DYNSYM = 11,
|
||||
SHT_LOOS = 0x60000000,
|
||||
SHT_HIOS = 0x6FFFFFFF,
|
||||
SHT_LOPROC = 0x70000000,
|
||||
SHT_HIPROC = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
enum ELF_SHF
|
||||
{
|
||||
SHF_WRITE = 0x1,
|
||||
SHF_ALLOC = 0x2,
|
||||
SHF_EXECINSTR = 0x4,
|
||||
SHF_MASKOS = 0x0F000000,
|
||||
SHF_MASKPROC = 0xF0000000,
|
||||
};
|
||||
|
||||
enum ELF_SHN
|
||||
{
|
||||
SHN_UNDEF = 0,
|
||||
SHN_LOPROC = 0xFF00,
|
||||
SHN_HIPROC = 0xFF1F,
|
||||
SHN_LOOS = 0xFF20,
|
||||
SHN_HIOS = 0xFF3F,
|
||||
SHN_ABS = 0xFFF1,
|
||||
SHN_COMMON = 0xFFF2,
|
||||
};
|
||||
|
||||
enum ELF_STB
|
||||
{
|
||||
STB_LOCAL = 0,
|
||||
STB_GLOBAL = 1,
|
||||
STB_WEAK = 2,
|
||||
STB_LOOS = 10,
|
||||
STB_HIOS = 12,
|
||||
STB_LOPROC = 13,
|
||||
STB_HIPROC = 15,
|
||||
};
|
||||
|
||||
enum ELF_STT
|
||||
{
|
||||
STT_NOTYPE = 0,
|
||||
STT_OBJECT = 1,
|
||||
STT_FUNC = 2,
|
||||
STT_SECTION = 3,
|
||||
STT_FILE = 4,
|
||||
STT_LOOS = 10,
|
||||
STT_HIOS = 12,
|
||||
STT_LOPROC = 13,
|
||||
STT_HIPROC = 15,
|
||||
};
|
||||
|
||||
enum ELF_PT
|
||||
{
|
||||
PT_NULL = 0,
|
||||
PT_LOAD = 1,
|
||||
PT_DYNAMIC = 2,
|
||||
PT_INTERP = 3,
|
||||
PT_NOTE = 4,
|
||||
PT_SHLIB = 5,
|
||||
PT_PHDR = 6,
|
||||
PT_LOOS = 0x60000000,
|
||||
PT_HIOS = 0x6FFFFFFF,
|
||||
PT_LOPROC = 0x70000000,
|
||||
PT_HIPROC = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
enum ELF_PF
|
||||
{
|
||||
PF_X = 0x1,
|
||||
PF_W = 0x2,
|
||||
PF_R = 0x4,
|
||||
PF_MASKOS = 0x00FF0000,
|
||||
PF_MASKPROC = 0xFF000000,
|
||||
};
|
||||
|
||||
}
|
||||
48
README.md
Normal file
48
README.md
Normal file
@@ -0,0 +1,48 @@
|
||||

|
||||
|
||||
# banan-os
|
||||
|
||||
This is my hobby operating system written in C++. Currently supports only x86\_64 architecture. We have a ext2 filesystem, basic ramfs, IDE disk drivers in ATA PIO mode, ATA AHCI drivers, userspace processes, executable loading from ELF format, linear VBE graphics and multithreaded processing on single core.
|
||||
|
||||

|
||||
|
||||
## Code structure
|
||||
|
||||
Each major component and library has its own subdirectory (kernel, userspace, libc, ...). Each directory contains directory *include*, which has **all** of the header files of the component. Every header is included by its absolute path.
|
||||
|
||||
## Building
|
||||
|
||||
There does not exist a complete list of needed packages for building. From the top of my head I can say that *cmake*, *ninja*, *make*, *grub*, *rsync* and emulator (*qemu* or *bochs*) are needed.
|
||||
|
||||
To build the toolchain for this os. You can run the following command.
|
||||
> ***NOTE:*** The following step has to be done only once. This might take a long time since we are compiling binutils and gcc.
|
||||
```sh
|
||||
./script/build.sh toolchain
|
||||
```
|
||||
|
||||
To build the os itself you can run one of the following commands. You will need root access for disk image creation/modification.
|
||||
```sh
|
||||
./script/build.sh qemu
|
||||
./script/build.sh qemu-nographic
|
||||
./script/build.sh qemu-debug
|
||||
./script/build.sh bochs
|
||||
```
|
||||
|
||||
You can also build the kernel or disk image without running it:
|
||||
```sh
|
||||
./script/build.sh kernel
|
||||
./script/build.sh image
|
||||
```
|
||||
|
||||
If you have corrupted your disk image or want to create new one, you can either manually delete *build/banan-os.img* and build system will automatically create you a new one or you can run the following command.
|
||||
```sh
|
||||
./script/build.sh image-full
|
||||
```
|
||||
|
||||
If you feel like ```./script/build.sh``` is too verbose, there exists a symlink _bos_ in this projects root directory. All build commands can be used with ```./bos args...``` instead.
|
||||
|
||||
I have also created shell completion script for zsh. You can either copy the file in _script/shell-completion/zsh/\_bos_ to _/usr/share/zsh/site-functions/_ or add the _script/shell-completion/zsh_ to your fpath in _.zshrc_.
|
||||
|
||||
### Contributing
|
||||
|
||||
Currently I don't accept contributions to this repository unless explicitly told otherwise. This is a learning project for me and I want to do everything myself. Feel free to fork/clone this repo and tinker with it yourself.
|
||||
BIN
assets/banan-os.png
Normal file
BIN
assets/banan-os.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
base-sysroot.tar.gz
Normal file
BIN
base-sysroot.tar.gz
Normal file
Binary file not shown.
3
bootloader/.gitignore
vendored
Normal file
3
bootloader/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
test.img
|
||||
build/
|
||||
installer/build/
|
||||
16
bootloader/CMakeLists.txt
Normal file
16
bootloader/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(bootloader ASM)
|
||||
|
||||
set(BOOTLOADER_SOURCES
|
||||
boot.S
|
||||
command_line.S
|
||||
disk.S
|
||||
ext2.S
|
||||
memory_map.S
|
||||
utils.S
|
||||
)
|
||||
|
||||
add_executable(bootloader ${BOOTLOADER_SOURCES})
|
||||
target_link_options(bootloader PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
|
||||
target_link_options(bootloader PUBLIC -nostdlib)
|
||||
84
bootloader/boot.S
Normal file
84
bootloader/boot.S
Normal file
@@ -0,0 +1,84 @@
|
||||
.code16
|
||||
|
||||
#########################################
|
||||
#
|
||||
# STAGE 1 BOOTLOADER
|
||||
#
|
||||
# its sole purpose is to load stage2 from
|
||||
# bios boot partition
|
||||
#
|
||||
#########################################
|
||||
|
||||
.section .stage1
|
||||
|
||||
.global stage1_main
|
||||
stage1_main:
|
||||
# setup segments
|
||||
movw $0, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
|
||||
# setup stack
|
||||
movw %ax, %ss
|
||||
movl $0x7C00, %esp
|
||||
|
||||
# save boot disk number
|
||||
call read_stage2_into_memory
|
||||
|
||||
jmp stage2_main
|
||||
|
||||
.global print_and_halt
|
||||
print_and_halt:
|
||||
call puts
|
||||
halt:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# STAGE 2 BOOTLOADER
|
||||
#
|
||||
#########################################
|
||||
|
||||
.section .stage2
|
||||
|
||||
stage2_main:
|
||||
# clear screen and enter 80x25 text mode
|
||||
movb $0x03, %al
|
||||
movb $0x00, %ah
|
||||
int $0x10
|
||||
|
||||
# print hello message
|
||||
movw $hello_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
call get_memory_map
|
||||
call read_user_command_line
|
||||
|
||||
call print_newline
|
||||
|
||||
movw $start_kernel_load_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
call print_memory_map
|
||||
|
||||
call find_root_disk
|
||||
call find_root_partition
|
||||
|
||||
call print_root_partition_info
|
||||
call print_newline
|
||||
|
||||
call has_ext2_filesystem
|
||||
testb %al, %al
|
||||
jz print_and_halt
|
||||
|
||||
call ext2_find_kernel
|
||||
|
||||
jmp halt
|
||||
|
||||
hello_msg:
|
||||
.asciz "This is banan-os bootloader"
|
||||
|
||||
start_kernel_load_msg:
|
||||
.asciz "Starting to load kernel"
|
||||
69
bootloader/command_line.S
Normal file
69
bootloader/command_line.S
Normal file
@@ -0,0 +1,69 @@
|
||||
.code16
|
||||
|
||||
.section .stage2
|
||||
|
||||
# fills command line buffer
|
||||
# NO REGISTERS SAVED
|
||||
.global read_user_command_line
|
||||
read_user_command_line:
|
||||
movw $command_line_enter_msg, %si
|
||||
call puts
|
||||
|
||||
movw $command_line_buffer, %di
|
||||
|
||||
.read_user_command_line_loop:
|
||||
call getc
|
||||
|
||||
cmpb $'\b', %al
|
||||
je .read_user_command_line_backspace
|
||||
|
||||
# Not sure if some BIOSes return '\n' as enter, but check it just in case
|
||||
cmpb $'\r', %al
|
||||
je .read_user_command_line_done
|
||||
cmpb $'\n', %al
|
||||
je .read_user_command_line_done
|
||||
|
||||
call isprint
|
||||
testb %al, %al
|
||||
jnz .read_user_command_line_loop
|
||||
|
||||
# put byte to buffer
|
||||
movb %al, (%di)
|
||||
incw %di
|
||||
|
||||
# print byte
|
||||
call putc
|
||||
|
||||
jmp .read_user_command_line_loop
|
||||
|
||||
.read_user_command_line_backspace:
|
||||
# don't do anything if at the beginning
|
||||
cmpw $command_line_buffer, %di
|
||||
je .read_user_command_line_loop
|
||||
|
||||
# decrement buffer pointer
|
||||
decw %di
|
||||
|
||||
# erase byte in display
|
||||
call print_backspace
|
||||
|
||||
jmp .read_user_command_line_loop
|
||||
|
||||
.read_user_command_line_done:
|
||||
# null terminate command line
|
||||
movb $0, (%di)
|
||||
|
||||
call print_newline
|
||||
|
||||
ret
|
||||
|
||||
|
||||
command_line_enter_msg:
|
||||
.asciz "cmdline: "
|
||||
|
||||
|
||||
.section .bss
|
||||
|
||||
# 100 character command line
|
||||
command_line_buffer:
|
||||
.skip 100
|
||||
521
bootloader/disk.S
Normal file
521
bootloader/disk.S
Normal file
@@ -0,0 +1,521 @@
|
||||
# FIXME: don't assume 512 byte sectors
|
||||
.set SECTOR_SIZE_SHIFT, 9
|
||||
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
|
||||
|
||||
.code16
|
||||
|
||||
.section .stage1
|
||||
|
||||
.global stage2_start
|
||||
.global stage2_end
|
||||
|
||||
# check that drive has int13 ext
|
||||
# dl: drive number
|
||||
# returns only if drive does have the extension
|
||||
drive_has_int13_ext:
|
||||
pusha
|
||||
|
||||
movb $0x41, %ah
|
||||
movw $0x55AA, %bx
|
||||
int $0x13
|
||||
jc .drive_has_int13_ext_no_int13_ext
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
.drive_has_int13_ext_no_int13_ext:
|
||||
mov $no_int13_ext_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# read sectors from disk
|
||||
# bx:eax: lba start
|
||||
# cx: lba count (has to less than 0x80)
|
||||
# dl: drive number
|
||||
# ds:di: physical address
|
||||
# returns only on success
|
||||
.global read_from_disk
|
||||
read_from_disk:
|
||||
pusha
|
||||
|
||||
call drive_has_int13_ext
|
||||
|
||||
# prepare disk read packet
|
||||
mov $disk_address_packet, %si
|
||||
movb $0x10, 0x00(%si) # packet size
|
||||
movb $0x00, 0x01(%si) # always 0
|
||||
movw %cx, 0x02(%si) # lba count
|
||||
movw %di, 0x04(%si) # offset
|
||||
movw %ds, 0x06(%si) # segment
|
||||
movl %eax, 0x08(%si) # 32 bit lower lba
|
||||
movw %bx, 0x0C(%si) # 16 bit upper lba
|
||||
movw $0, 0x0E(%si) # zero
|
||||
|
||||
# issue read command
|
||||
mov $0x42, %ah
|
||||
int $0x13
|
||||
jc .read_from_disk_failed
|
||||
|
||||
popa
|
||||
ret
|
||||
|
||||
.read_from_disk_failed:
|
||||
mov $read_from_disk_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# Reads GPT header into gpt_header buffer
|
||||
# dl: drive number
|
||||
# return:
|
||||
# ax: 1 if has GPT header, 0 otherwise
|
||||
.global read_gpt_header
|
||||
read_gpt_header:
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
pushw %di
|
||||
|
||||
xorw %bx, %bx
|
||||
movl $1, %eax
|
||||
movw $1, %cx
|
||||
movw $gpt_header, %di
|
||||
call read_from_disk
|
||||
|
||||
xorw %bx, %bx
|
||||
movw $1, %ax
|
||||
|
||||
# check if header starts with 'EFI PART'
|
||||
cmpl $0x20494645, (gpt_header + 0)
|
||||
cmovnew %bx, %ax
|
||||
cmpl $0x54524150, (gpt_header + 4)
|
||||
cmovnew %bx, %ax
|
||||
|
||||
popw %di
|
||||
popw %cx
|
||||
popw %bx
|
||||
ret
|
||||
|
||||
|
||||
# Find bios boot partition from boot drive
|
||||
# returns:
|
||||
# bx:eax: first lba
|
||||
# cx: sector count
|
||||
find_stage2_partition:
|
||||
# read boot disk GPT header
|
||||
movb (boot_disk_number), %dl
|
||||
call read_gpt_header
|
||||
|
||||
testb %al, %al
|
||||
jz .find_stage2_partition_not_gpt
|
||||
|
||||
# eax := entry_count
|
||||
movl (gpt_header + 80), %eax
|
||||
test %eax, %eax
|
||||
jz .find_stage2_partition_not_found
|
||||
|
||||
# edx:eax := eax * entry_size
|
||||
mull (gpt_header + 84)
|
||||
test %edx, %edx
|
||||
jnz .find_stage2_partition_too_big_entries
|
||||
|
||||
# FIXME: read one entry array section at a time
|
||||
|
||||
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
|
||||
movl %eax, %ecx
|
||||
shrl $SECTOR_SIZE_SHIFT, %ecx
|
||||
|
||||
# start lba
|
||||
movl (gpt_header + 72), %eax
|
||||
movw (gpt_header + 76), %bx
|
||||
|
||||
movw $gpt_entry_data, %di
|
||||
movw $bios_boot_guid, %si
|
||||
movb (boot_disk_number), %dl
|
||||
|
||||
call read_from_disk
|
||||
|
||||
# NOTE: 'only' 0xFFFF partitions supported,
|
||||
# although read will fail with more than 0x80
|
||||
movw (gpt_header + 80), %cx
|
||||
|
||||
.find_stage2_partition_loop_gpt_entries:
|
||||
pushw %cx
|
||||
movw $16, %cx
|
||||
call memcmp
|
||||
popw %cx
|
||||
|
||||
testb %al, %al
|
||||
jnz .find_stage2_partition_found
|
||||
|
||||
# add entry size to entry pointer
|
||||
addw (gpt_header + 84), %di
|
||||
|
||||
loop .find_stage2_partition_loop_gpt_entries
|
||||
|
||||
# fall through to not found case
|
||||
.find_stage2_partition_not_found:
|
||||
movw $no_bios_boot_partition_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_stage2_partition_not_gpt:
|
||||
movw $not_gpt_partition_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_stage2_partition_too_big_entries:
|
||||
movw $too_gpt_big_entries_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_stage2_partition_found:
|
||||
# first lba
|
||||
movl 32(%di), %eax
|
||||
movw 36(%di), %bx
|
||||
|
||||
# count := last lba - first lba + 1
|
||||
movl 40(%di), %ecx
|
||||
subl %eax, %ecx
|
||||
incl %ecx
|
||||
|
||||
ret
|
||||
|
||||
# reads stage2 into memory
|
||||
# dl: boot drive number
|
||||
# returns only on success
|
||||
.global read_stage2_into_memory
|
||||
read_stage2_into_memory:
|
||||
movb %dl, (boot_disk_number)
|
||||
|
||||
# push stage2 sector count
|
||||
movl $stage2_end, %eax
|
||||
subl $stage2_start, %eax
|
||||
addl $(SECTOR_SIZE - 1), %eax
|
||||
movl $SECTOR_SIZE, %ecx
|
||||
xorl %edx, %edx
|
||||
divl %ecx
|
||||
pushl %eax
|
||||
|
||||
call find_stage2_partition
|
||||
|
||||
movb (boot_disk_number), %dl
|
||||
popl %ecx # FIXME: validate that partition has enough sectors
|
||||
movw $stage2_start, %di
|
||||
call read_from_disk
|
||||
|
||||
ret
|
||||
|
||||
# 21686148-6449-6E6F-744E-656564454649
|
||||
.align 4
|
||||
bios_boot_guid:
|
||||
.long 0x21686148 # little endian
|
||||
.word 0x6449 # little endian
|
||||
.word 0x6E6F # little endian
|
||||
.word 0x4E74 # big endian
|
||||
.quad 0x494645646565 # big endian
|
||||
|
||||
boot_disk_number:
|
||||
.skip 1
|
||||
|
||||
read_from_disk_msg:
|
||||
.asciz "read error"
|
||||
|
||||
no_int13_ext_msg:
|
||||
.asciz "no INT13 ext"
|
||||
|
||||
no_bios_boot_partition_msg:
|
||||
.asciz "no bios boot"
|
||||
|
||||
too_gpt_big_entries_msg:
|
||||
.asciz "too big GPT array"
|
||||
|
||||
not_gpt_partition_msg:
|
||||
.asciz "not GPT"
|
||||
|
||||
|
||||
.section .stage2
|
||||
|
||||
# check if drive exists
|
||||
# dl: drive number
|
||||
# return:
|
||||
# al: 1 if disk is usable, 0 otherwise
|
||||
drive_exists:
|
||||
pusha
|
||||
|
||||
movb $0x48, %ah
|
||||
movw $disk_drive_parameters, %si
|
||||
movw $0x1A, (disk_drive_parameters) # set buffer size
|
||||
|
||||
int $0x13
|
||||
jc .drive_exists_nope
|
||||
|
||||
popa
|
||||
movb $1, %al
|
||||
ret
|
||||
|
||||
.drive_exists_nope:
|
||||
popa
|
||||
movb $0, %al
|
||||
ret
|
||||
|
||||
# find root disk and populate root_disk_drive_number field
|
||||
# NO REGISTERS SAVED
|
||||
.global find_root_disk
|
||||
find_root_disk:
|
||||
movb $0x80, %dl
|
||||
|
||||
.find_root_disk_loop:
|
||||
call drive_exists
|
||||
testb %al, %al
|
||||
jz .find_root_disk_not_found
|
||||
|
||||
# read GPT header
|
||||
xorw %bx, %bx
|
||||
movl $1, %eax
|
||||
movw $1, %cx
|
||||
movw $gpt_header, %di
|
||||
call read_from_disk
|
||||
|
||||
# confirm header (starts with 'EFI PART')
|
||||
cmpl $0x20494645, (gpt_header + 0)
|
||||
jne .find_root_disk_next_disk
|
||||
cmpl $0x54524150, (gpt_header + 4)
|
||||
jne .find_root_disk_next_disk
|
||||
|
||||
# compare disk GUID
|
||||
movw $root_disk_guid, %si
|
||||
movw $(gpt_header + 56), %di
|
||||
movw $16, %cx
|
||||
call memcmp
|
||||
testb %al, %al
|
||||
jz .find_root_disk_next_disk
|
||||
|
||||
movw $root_disk_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
movb %dl, (root_disk_drive_number)
|
||||
ret
|
||||
|
||||
.find_root_disk_next_disk:
|
||||
incb %dl
|
||||
jmp .find_root_disk_loop
|
||||
|
||||
.find_root_disk_not_found:
|
||||
movw $root_disk_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# finds root partition from root disk
|
||||
# fills root_partition_entry data structure
|
||||
# NOTE: assumes GPT header is in `gpt_header`
|
||||
# NO REGISTERS SAVED
|
||||
# return:
|
||||
# dl: drive number
|
||||
# ecx: sector count (capped at 0xFFFFFFFF)
|
||||
# bx:eax: first sector
|
||||
.global find_root_partition
|
||||
find_root_partition:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $16, %esp
|
||||
|
||||
# esp + 0: 8 byte entry array lba
|
||||
movl (gpt_header + 72), %eax
|
||||
movl %eax, 0(%esp)
|
||||
movl (gpt_header + 76), %eax
|
||||
movl %eax, 4(%esp)
|
||||
# FIXME: check that bits 48-63 are zero
|
||||
|
||||
# esp + 8: 4 byte entries per sector
|
||||
xorl %edx, %edx
|
||||
movl $SECTOR_SIZE, %eax
|
||||
divl (gpt_header + 84)
|
||||
movl %eax, 8(%esp)
|
||||
|
||||
# esp + 12: 4 byte entries remaining
|
||||
movl (gpt_header + 80), %eax
|
||||
testl %eax, %eax
|
||||
jz .find_root_partition_not_found
|
||||
movl %eax, 12(%esp)
|
||||
|
||||
.find_root_partition_read_entry_section:
|
||||
movl 0(%esp), %eax
|
||||
movl 4(%esp), %ebx
|
||||
movw $1, %cx
|
||||
movb (root_disk_drive_number), %dl
|
||||
movw $sector_buffer, %di
|
||||
call read_from_disk
|
||||
|
||||
# ecx: min(entries per section, entries remaining)
|
||||
movl 8(%esp), %ecx
|
||||
cmpl 12(%esp), %ecx
|
||||
jae .find_root_partition_got_entry_count
|
||||
movl 12(%esp), %ecx
|
||||
|
||||
.find_root_partition_got_entry_count:
|
||||
# update entries remaining
|
||||
subl %ecx, 12(%esp)
|
||||
|
||||
# si: entry pointer
|
||||
movw $sector_buffer, %si
|
||||
|
||||
.find_root_partition_loop_entries:
|
||||
# temporarily save cx in dx
|
||||
movw %cx, %dx
|
||||
|
||||
# check that entry is used
|
||||
movw $16, %cx
|
||||
movw $zero_guid, %di
|
||||
call memcmp
|
||||
test %al, %al
|
||||
jnz .find_root_partition_next_entry
|
||||
|
||||
# compare entry guid to root guid
|
||||
movw $16, %cx
|
||||
addw $16, %si
|
||||
movw $root_partition_guid, %di
|
||||
call memcmp
|
||||
subw $16, %si
|
||||
|
||||
testb %al, %al
|
||||
jnz .find_root_partition_found
|
||||
|
||||
.find_root_partition_next_entry:
|
||||
|
||||
# restore cx
|
||||
movw %dx, %cx
|
||||
|
||||
# entry pointer += entry size
|
||||
addw (gpt_header + 84), %si
|
||||
loop .find_root_partition_loop_entries
|
||||
|
||||
# entry not found in this sector
|
||||
|
||||
# increment 8 byte entry array lba
|
||||
incl 0(%esp)
|
||||
jnc .find_root_partition_no_overflow
|
||||
incl 4(%esp)
|
||||
|
||||
.find_root_partition_no_overflow:
|
||||
# loop to read next section if entries remaining
|
||||
cmpl $0, 12(%esp)
|
||||
jnz .find_root_partition_read_entry_section
|
||||
|
||||
.find_root_partition_not_found:
|
||||
movw $root_partition_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.find_root_partition_found:
|
||||
# copy entry to buffer
|
||||
movw $root_partition_entry, %di
|
||||
movw $128, %cx
|
||||
rep movsb
|
||||
|
||||
movw $root_partition_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
# ebx:eax := last lba
|
||||
movl (root_partition_entry + 44), %ebx
|
||||
movl (root_partition_entry + 40), %eax
|
||||
|
||||
# ebx:eax -= first lba - 1
|
||||
subl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %ecx;
|
||||
decl %ecx
|
||||
subl %ecx, %eax
|
||||
jnc .find_root_partition_count_sub_no_carry
|
||||
decl %ebx
|
||||
.find_root_partition_count_sub_no_carry:
|
||||
|
||||
# ecx: min(partition count, 0xFFFFFFFF)
|
||||
movl $0xFFFFFFFF, %edx
|
||||
movl %eax, %ecx
|
||||
testl %ebx, %ebx
|
||||
cmovnzl %edx, %ecx
|
||||
|
||||
# ebx:eax := first lba
|
||||
# FIXME: confirm ebx bits 16:31 are zero
|
||||
movl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %eax
|
||||
|
||||
movb (root_disk_drive_number), %dl
|
||||
|
||||
leavel
|
||||
ret
|
||||
|
||||
|
||||
# print information about root partition
|
||||
.global print_root_partition_info
|
||||
print_root_partition_info:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
pushw %si
|
||||
|
||||
movw $root_partition_info_start_msg, %si
|
||||
call puts;
|
||||
|
||||
movw $16, %bx
|
||||
movw $2, %cx
|
||||
movw (root_partition_entry + 38), %ax; call print_number
|
||||
movw (root_partition_entry + 36), %ax; call print_number
|
||||
movw (root_partition_entry + 34), %ax; call print_number
|
||||
movw (root_partition_entry + 32), %ax; call print_number
|
||||
|
||||
movb $'-', %al; call putc
|
||||
movb $'>', %al; call putc
|
||||
|
||||
movw (root_partition_entry + 46), %ax; call print_number
|
||||
movw (root_partition_entry + 44), %ax; call print_number
|
||||
movw (root_partition_entry + 42), %ax; call print_number
|
||||
movw (root_partition_entry + 40), %ax; call print_number
|
||||
|
||||
call print_newline
|
||||
|
||||
popw %si
|
||||
popw %cx
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
|
||||
# These will be patched during bootloader installation
|
||||
root_disk_guid:
|
||||
.ascii "root disk guid "
|
||||
root_partition_guid:
|
||||
.ascii "root part guid "
|
||||
zero_guid:
|
||||
.skip 16, 0
|
||||
|
||||
root_disk_found_msg:
|
||||
.asciz "Root disk found!"
|
||||
root_disk_not_found_msg:
|
||||
.asciz "Root disk not found"
|
||||
|
||||
root_partition_found_msg:
|
||||
.asciz "Root partition found!"
|
||||
root_partition_not_found_msg:
|
||||
.asciz "Root partition not found"
|
||||
|
||||
root_partition_info_start_msg:
|
||||
.asciz "Root partition: "
|
||||
|
||||
.section .bss
|
||||
|
||||
.align SECTOR_SIZE
|
||||
gpt_header:
|
||||
.skip SECTOR_SIZE
|
||||
gpt_entry_data:
|
||||
.skip SECTOR_SIZE
|
||||
sector_buffer:
|
||||
.skip SECTOR_SIZE
|
||||
|
||||
disk_address_packet:
|
||||
.skip 16
|
||||
|
||||
disk_drive_parameters:
|
||||
.skip 0x1A
|
||||
.skip 2 # padding
|
||||
|
||||
root_disk_drive_number:
|
||||
.skip 1
|
||||
.skip 3 # padding
|
||||
|
||||
root_partition_entry:
|
||||
.skip 128
|
||||
522
bootloader/ext2.S
Normal file
522
bootloader/ext2.S
Normal file
@@ -0,0 +1,522 @@
|
||||
# FIXME: don't assume 512 byte sectors
|
||||
.set SECTOR_SHIFT, 9
|
||||
.set SECTOR_SIZE, 1 << SECTOR_SHIFT
|
||||
|
||||
# FIXME: don't assume 1024 byte blocks
|
||||
.set EXT2_BLOCK_SHIFT, 10
|
||||
.set EXT2_BLOCK_SIZE, 1 << EXT2_BLOCK_SHIFT
|
||||
.set EXT2_SUPERBLOCK_SIZE, 264
|
||||
.set EXT2_BGD_SHIFT, 5
|
||||
.set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT
|
||||
.set EXT2_INODE_SIZE_MAX, 256
|
||||
.set EXT2_ROOT_INO, 2
|
||||
.set EXT2_GOOD_OLD_REV, 0
|
||||
|
||||
# inode types
|
||||
.set EXT2_S_IMASK, 0xF000
|
||||
.set EXT2_S_IFDIR, 0x4000
|
||||
.set EXT2_S_IFREG, 0x8000
|
||||
|
||||
# superblock offsets
|
||||
.set s_log_block_size, 24
|
||||
.set s_inodes_per_group, 40
|
||||
.set s_magic, 56
|
||||
.set s_rev_level, 76
|
||||
.set s_inode_size, 88
|
||||
|
||||
# block group descriptor offsets
|
||||
.set bg_inode_table, 8
|
||||
|
||||
# inode offsets
|
||||
.set i_mode, 0
|
||||
.set i_size, 4
|
||||
.set i_block, 40
|
||||
|
||||
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# checks whether partition contains ext2 filesystem.
|
||||
# fills ext2_superblock_buffer
|
||||
# dl: drive number
|
||||
# ecx: sector count
|
||||
# bx:eax: first sector
|
||||
# return:
|
||||
# al: 1 if is ext2, 0 otherwise
|
||||
# si: error message on error
|
||||
.global has_ext2_filesystem
|
||||
has_ext2_filesystem:
|
||||
pushl %ecx
|
||||
pushw %bx
|
||||
pushw %di
|
||||
|
||||
# fill ext2_partition_first_sector
|
||||
movw $0, (ext2_partition_first_sector + 6)
|
||||
movw %bx, (ext2_partition_first_sector + 4)
|
||||
movl %eax, (ext2_partition_first_sector + 0)
|
||||
|
||||
# fill ext2_drive_number
|
||||
movb %dl, (ext2_drive_number)
|
||||
|
||||
cmpl $3, %ecx
|
||||
jb .has_ext2_filesystem_does_not_fit
|
||||
|
||||
# one sector
|
||||
movw $1, %cx
|
||||
|
||||
# from byte offset 1024
|
||||
addl $(1024 / SECTOR_SIZE), %eax
|
||||
jnc .has_ext2_filesystem_no_overflow
|
||||
incw %bx
|
||||
.has_ext2_filesystem_no_overflow:
|
||||
|
||||
# into sector buffer
|
||||
movw $ext2_block_buffer, %di
|
||||
|
||||
call read_from_disk
|
||||
|
||||
# copy superblock to its buffer
|
||||
movw $ext2_block_buffer, %si
|
||||
movw $ext2_superblock_buffer, %di
|
||||
movw $EXT2_SUPERBLOCK_SIZE, %cx
|
||||
rep movsb
|
||||
|
||||
# verify magic
|
||||
cmpw $0xEF53, (ext2_superblock_buffer + s_magic)
|
||||
jne .has_ext2_filesystem_invalid_magic
|
||||
|
||||
# verify block size
|
||||
# verify shift fits in one byte
|
||||
movl (ext2_superblock_buffer + s_log_block_size), %ecx
|
||||
testl $0xFFFFFF00, %ecx
|
||||
jnz .has_ext2_filesystem_unsupported_block_size
|
||||
# verify 1024 << s_log_block_size == EXT2_BLOCK_SIZE
|
||||
movl $1024, %eax
|
||||
shll %cl, %eax
|
||||
cmpl $EXT2_BLOCK_SIZE, %eax
|
||||
jne .has_ext2_filesystem_unsupported_block_size
|
||||
|
||||
# fill inode size
|
||||
movl $128, %eax
|
||||
cmpl $EXT2_GOOD_OLD_REV, (ext2_superblock_buffer + s_rev_level)
|
||||
cmovnel (ext2_superblock_buffer + s_inode_size), %eax
|
||||
movl %eax, (ext2_inode_size)
|
||||
|
||||
movb $1, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_does_not_fit:
|
||||
movw $root_partition_does_not_fit_ext2_filesystem_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_invalid_magic:
|
||||
movw $root_partition_has_invalid_ext2_magic_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_unsupported_block_size:
|
||||
movw $root_partition_has_unsupported_ext2_block_size_msg, %si
|
||||
movb $0, %al
|
||||
jmp .has_ext2_filesystem_done
|
||||
|
||||
.has_ext2_filesystem_done:
|
||||
popw %di
|
||||
popw %bx
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
|
||||
# reads block in to ext2_block_buffer
|
||||
# eax: block number
|
||||
ext2_read_block:
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
pushw %cx
|
||||
pushl %edx
|
||||
pushw %di
|
||||
|
||||
# NOTE: this assumes 1024 block size
|
||||
# eax := (block * block_size) / sector_size := (eax << EXT2_BLOCK_SHIFT) >> SECTOR_SHIFT
|
||||
xorl %edx, %edx
|
||||
shll $EXT2_BLOCK_SHIFT, %eax
|
||||
shrl $SECTOR_SHIFT, %eax
|
||||
|
||||
# ebx:eax := eax + (ext2_partition_first_sector)
|
||||
movl (ext2_partition_first_sector + 4), %ebx
|
||||
addl (ext2_partition_first_sector + 0), %eax
|
||||
jnc .ext2_read_block_no_carry
|
||||
incl %ebx
|
||||
.ext2_read_block_no_carry:
|
||||
|
||||
# sectors per block
|
||||
movw $(EXT2_BLOCK_SIZE / SECTOR_SIZE), %cx
|
||||
|
||||
movw $ext2_block_buffer, %di
|
||||
|
||||
movb (ext2_drive_number), %dl
|
||||
call read_from_disk
|
||||
|
||||
popw %di
|
||||
popl %edx
|
||||
popw %cx
|
||||
popl %ebx
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
|
||||
# reads block group descrtiptor into ext2_block_group_descriptor
|
||||
# eax: block group
|
||||
ext2_read_block_group_descriptor:
|
||||
pushal
|
||||
|
||||
# eax := bgd_byte_offset := 2048 + EXT2_BGD_SIZE * eax := (eax << EXT2_BGD_SHIFT) + 2048
|
||||
shll $EXT2_BGD_SHIFT, %eax
|
||||
addl $2048, %eax
|
||||
|
||||
# eax: bgd_block := bgd_byte_offset / EXT2_BLOCK_SIZE
|
||||
# ebx: bgd_offset := bgd_byte_offset % EXT2_BLOCK_SIZE
|
||||
xorl %edx, %edx
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block
|
||||
|
||||
# esi := &ext2_block_buffer + bgd_offset := ebx + &ext2_block_buffer
|
||||
# edi := &ext2_block_group_descriptor_buffer
|
||||
movl %ebx, %esi
|
||||
addl $ext2_block_buffer, %esi
|
||||
movl $ext2_block_group_descriptor_buffer, %edi
|
||||
movw $EXT2_BGD_SIZE, %cx
|
||||
rep movsb
|
||||
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# reads inode into ext2_inode_buffer
|
||||
# eax: ino
|
||||
ext2_read_inode:
|
||||
pushal
|
||||
|
||||
# eax := block_group = (ino - 1) / s_inodes_per_group
|
||||
# ebx := inode_index = (ino - 1) % s_inodes_per_group
|
||||
xorl %edx, %edx
|
||||
decl %eax
|
||||
movl (ext2_superblock_buffer + s_inodes_per_group), %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block_group_descriptor
|
||||
|
||||
# eax := inode_table_block := (inode_index * inode_size) / EXT2_BLOCK_SIZE
|
||||
# ebx := inode_table_offset := (inode_index * inode_size) % EXT2_BLOCK_SIZE
|
||||
xorl %edx, %edx
|
||||
movl %ebx, %eax
|
||||
movl (ext2_inode_size), %ebx
|
||||
mull %ebx
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
# eax := file system block := eax + bg_inode_table
|
||||
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
|
||||
|
||||
movb (ext2_drive_number), %dl
|
||||
call ext2_read_block
|
||||
|
||||
# copy inode memory
|
||||
# esi := inode_table_offset + ext2_block_buffer := edx + ext2_block_buffer
|
||||
movl %ebx, %esi
|
||||
addl $ext2_block_buffer, %esi
|
||||
# edi := ext2_inode_buffer
|
||||
movl $ext2_inode_buffer, %edi
|
||||
# cx := inode_size
|
||||
movw (ext2_inode_size), %cx
|
||||
rep movsb
|
||||
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# gets block index from n'th data block in inode stored in ext2_inode_buffer
|
||||
# eax: data block index
|
||||
# return:
|
||||
# eax: block index
|
||||
ext2_data_block_index:
|
||||
pushl %ecx
|
||||
|
||||
# calculate max data blocks
|
||||
movl (ext2_inode_buffer + i_size), %ecx
|
||||
addl (ext2_inode_size), %ecx
|
||||
decl %ecx
|
||||
shll $EXT2_BLOCK_SHIFT, %ecx
|
||||
|
||||
# verify data block is within bounds
|
||||
cmpl %ecx, %eax
|
||||
jae .ext2_data_block_index_out_of_bounds
|
||||
|
||||
# check if this is direct block access
|
||||
cmpl $12, %eax
|
||||
jb .ext2_data_block_index_direct
|
||||
|
||||
jmp .ext2_data_block_index_unsupported
|
||||
|
||||
.ext2_data_block_index_direct:
|
||||
movl $(ext2_inode_buffer + i_block), %ecx
|
||||
addl %eax, %ecx
|
||||
movl (%ecx), %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_out_of_bounds:
|
||||
movw $ext2_data_block_index_out_of_bounds_msg, %si
|
||||
call puts; call print_newline
|
||||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_unsupported:
|
||||
movw $ext2_data_block_index_unsupported_msg, %si
|
||||
call puts; call print_newline
|
||||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_done:
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
|
||||
# find inode in inside directory inode stored in ext2_inode_buffer
|
||||
# store the found inode in ext2_inode_buffer
|
||||
# si: name string
|
||||
# cx: name length
|
||||
# return:
|
||||
# eax: ino if inode was found, 0 otherwise
|
||||
ext2_directory_find_inode:
|
||||
pushl %ebx
|
||||
pushw %cx
|
||||
pushw %dx
|
||||
pushw %si
|
||||
pushw %di
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $8, %esp
|
||||
|
||||
# 0(%esp) := name length
|
||||
movw %cx, 0(%esp)
|
||||
|
||||
# 2(%esp) := name string
|
||||
movw %si, 2(%esp)
|
||||
|
||||
# verify that the name is <= 0xFF bytes
|
||||
cmpw $0xFF, %cx
|
||||
ja .ext2_directory_find_inode_not_found
|
||||
|
||||
# ebx := max data blocks: ceil(i_size / EXT2_BLOCK_SIZE)
|
||||
movl (ext2_inode_buffer + i_size), %ebx
|
||||
addl $EXT2_BLOCK_SHIFT, %ebx
|
||||
decl %ebx
|
||||
shrl $EXT2_BLOCK_SHIFT, %ebx
|
||||
jz .ext2_directory_find_inode_not_found
|
||||
|
||||
# 4(%esp) := current block
|
||||
movl $0, 4(%esp)
|
||||
|
||||
.ext2_directory_find_inode_block_read_loop:
|
||||
# get next block index
|
||||
movl 4(%esp), %eax
|
||||
call ext2_data_block_index
|
||||
test %eax, %eax
|
||||
jz .ext2_directory_find_inode_next_block
|
||||
|
||||
# read current block
|
||||
call ext2_read_block
|
||||
|
||||
# dx := current entry pointer
|
||||
movw $ext2_block_buffer, %si
|
||||
|
||||
.ext2_directory_find_inode_loop_entries:
|
||||
# temporarily store entry pointer in dx
|
||||
movw %si, %dx
|
||||
|
||||
# check if name length matches
|
||||
# cx := name length
|
||||
movw 0(%esp), %cx
|
||||
cmpb 6(%si), %cl
|
||||
jne .ext2_directory_find_inode_next_entry
|
||||
|
||||
# si := entry name
|
||||
addw $8, %si
|
||||
|
||||
# di := asked name
|
||||
movw 2(%esp), %di
|
||||
|
||||
# check if name matches
|
||||
call memcmp
|
||||
test %al, %al
|
||||
# NOTE: dx contains entry pointer
|
||||
jnz .ext2_directory_find_inode_found
|
||||
|
||||
.ext2_directory_find_inode_next_entry:
|
||||
# restore si
|
||||
movw %dx, %si
|
||||
|
||||
# go to next entry if this block contains one
|
||||
addw 4(%si), %si
|
||||
cmpw $(ext2_block_buffer + EXT2_BLOCK_SIZE), %si
|
||||
jb .ext2_directory_find_inode_loop_entries
|
||||
|
||||
.ext2_directory_find_inode_next_block:
|
||||
incl 4(%esp)
|
||||
cmpl %ebx, 4(%esp)
|
||||
jb .ext2_directory_find_inode_block_read_loop
|
||||
|
||||
.ext2_directory_find_inode_not_found:
|
||||
movb $0, %al
|
||||
jmp .ext2_directory_find_inode_done
|
||||
|
||||
.ext2_directory_find_inode_found:
|
||||
# extract ino and read it to ext2_inode_buffer
|
||||
movw %dx, %si
|
||||
movl 0(%si), %eax
|
||||
call ext2_read_inode
|
||||
|
||||
.ext2_directory_find_inode_done:
|
||||
leavel
|
||||
popw %di
|
||||
popw %si
|
||||
popw %dx
|
||||
popw %cx
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
|
||||
# search for kernel file from filesystem
|
||||
.global ext2_find_kernel
|
||||
ext2_find_kernel:
|
||||
movl $EXT2_ROOT_INO, %eax
|
||||
call ext2_read_inode
|
||||
|
||||
movw $kernel_path, %di
|
||||
.ext2_find_kernel_loop:
|
||||
movw (%di), %si
|
||||
|
||||
# check if this list is done
|
||||
testw %si, %si
|
||||
jz .ext2_find_kernel_loop_done
|
||||
|
||||
# check that current part is directory
|
||||
movw (ext2_inode_buffer + i_mode), %ax
|
||||
andw $EXT2_S_IMASK, %ax
|
||||
cmpw $EXT2_S_IFDIR, %ax
|
||||
jne .ext2_find_kernel_part_not_dir
|
||||
|
||||
# prepare registers for directory finding
|
||||
movw 0(%si), %cx
|
||||
addw $2, %si
|
||||
|
||||
# print search path
|
||||
pushw %si
|
||||
movw $ext2_looking_for_msg, %si
|
||||
call puts
|
||||
popw %si
|
||||
call puts; call print_newline
|
||||
|
||||
# search current directory for this file
|
||||
call ext2_directory_find_inode
|
||||
testl %eax, %eax
|
||||
jz .ext2_find_kernel_part_not_found
|
||||
|
||||
# loop to next part
|
||||
addw $2, %di
|
||||
jmp .ext2_find_kernel_loop
|
||||
|
||||
.ext2_find_kernel_loop_done:
|
||||
|
||||
# check that kernel is a regular file
|
||||
movw (ext2_inode_buffer + i_mode), %ax
|
||||
andw $EXT2_S_IMASK, %ax
|
||||
cmpw $EXT2_S_IFREG, %ax
|
||||
jne .ext2_find_kernel_not_reg
|
||||
|
||||
movw $ext2_kernel_found_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
1: jmp 1b
|
||||
|
||||
ret
|
||||
|
||||
.ext2_find_kernel_part_not_dir:
|
||||
movw $ext2_part_not_dir_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.ext2_find_kernel_part_not_found:
|
||||
movw $ext2_part_not_found_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.ext2_find_kernel_not_reg:
|
||||
movw $ext2_kernel_not_reg_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
kernel_path:
|
||||
.short kernel_path1
|
||||
.short kernel_path2
|
||||
.short 0
|
||||
kernel_path1:
|
||||
.short 4
|
||||
.asciz "boot"
|
||||
kernel_path2:
|
||||
.short 15
|
||||
.asciz "banan-os.kernel"
|
||||
|
||||
|
||||
root_partition_does_not_fit_ext2_filesystem_msg:
|
||||
.asciz "Root partition is too small to contain ext2 filesystem"
|
||||
root_partition_has_invalid_ext2_magic_msg:
|
||||
.asciz "Root partition doesn't contain ext2 magic number"
|
||||
root_partition_has_unsupported_ext2_block_size_msg:
|
||||
.asciz "Root partition has unsupported ext2 block size (only 1024 supported)"
|
||||
|
||||
ext2_part_not_dir_msg:
|
||||
.asciz "inode in root path is not directory"
|
||||
ext2_part_not_found_msg:
|
||||
.asciz " not found"
|
||||
ext2_kernel_not_reg_msg:
|
||||
.asciz "kernel is not a regular file"
|
||||
ext2_kernel_found_msg:
|
||||
.asciz "kernel found!"
|
||||
|
||||
ext2_data_block_index_out_of_bounds_msg:
|
||||
.asciz "data block index out of bounds"
|
||||
ext2_data_block_index_unsupported_msg:
|
||||
.asciz "unsupported data block index"
|
||||
|
||||
ext2_looking_for_msg:
|
||||
.asciz "looking for "
|
||||
|
||||
.section .bss
|
||||
|
||||
ext2_block_buffer:
|
||||
.skip EXT2_BLOCK_SIZE
|
||||
|
||||
ext2_partition_first_sector:
|
||||
.skip 8
|
||||
|
||||
ext2_drive_number:
|
||||
.skip 1
|
||||
.skip 3 # padding
|
||||
|
||||
# NOTE: fits in 2 bytes
|
||||
ext2_inode_size:
|
||||
.skip 4
|
||||
|
||||
ext2_superblock_buffer:
|
||||
.skip EXT2_SUPERBLOCK_SIZE
|
||||
|
||||
ext2_block_group_descriptor_buffer:
|
||||
.skip EXT2_BGD_SIZE
|
||||
|
||||
ext2_inode_buffer:
|
||||
.skip EXT2_INODE_SIZE_MAX
|
||||
37
bootloader/install.sh
Executable file
37
bootloader/install.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z $BANAN_DISK_IMAGE_PATH ]]; then
|
||||
echo "You must set the BANAN_DISK_IMAGE_PATH environment variable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $BANAN_BUILD_DIR ]]; then
|
||||
echo "You must set the BANAN_BUILD_DIR environment variable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROOT_PARTITION_INDEX=2
|
||||
ROOT_PARTITION_INFO=$(fdisk -x $BANAN_DISK_IMAGE_PATH | grep "^$BANAN_DISK_IMAGE_PATH" | head -$ROOT_PARTITION_INDEX | tail -1)
|
||||
ROOT_PARTITION_GUID=$(echo $ROOT_PARTITION_INFO | cut -d' ' -f6)
|
||||
|
||||
INSTALLER_BUILD_DIR=$(dirname $(realpath $0))/installer/build
|
||||
BOOTLOADER_ELF=$BANAN_BUILD_DIR/bootloader/bootloader
|
||||
|
||||
if ! [ -f $BOOTLOADER_ELF ]; then
|
||||
echo "You must build the bootloader first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -d $INSTALLER_BUILD_DIR ]; then
|
||||
mkdir -p $INSTALLER_BUILD_DIR
|
||||
cd $INSTALLER_BUILD_DIR
|
||||
cmake ..
|
||||
fi
|
||||
|
||||
cd $INSTALLER_BUILD_DIR
|
||||
make
|
||||
|
||||
echo installing bootloader
|
||||
$INSTALLER_BUILD_DIR/x86_64-banan_os-bootloader-installer $BOOTLOADER_ELF $BANAN_DISK_IMAGE_PATH $ROOT_PARTITION_GUID
|
||||
17
bootloader/installer/CMakeLists.txt
Normal file
17
bootloader/installer/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(x86_64-banan_os-bootloader-installer CXX)
|
||||
|
||||
set(SOURCES
|
||||
crc32.cpp
|
||||
ELF.cpp
|
||||
GPT.cpp
|
||||
GUID.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(x86_64-banan_os-bootloader-installer ${SOURCES})
|
||||
target_compile_options(x86_64-banan_os-bootloader-installer PRIVATE -O2 -std=c++20)
|
||||
target_compile_definitions(x86_64-banan_os-bootloader-installer PRIVATE __arch=x86_64)
|
||||
target_include_directories(x86_64-banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../LibELF/include)
|
||||
target_include_directories(x86_64-banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../kernel/include)
|
||||
142
bootloader/installer/ELF.cpp
Normal file
142
bootloader/installer/ELF.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "ELF.h"
|
||||
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace LibELF;
|
||||
|
||||
ELFFile::ELFFile(std::string_view path)
|
||||
: m_path(path)
|
||||
{
|
||||
m_fd = open(m_path.c_str(), O_RDONLY);
|
||||
if (m_fd == -1)
|
||||
{
|
||||
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat(m_fd, &m_stat) == -1)
|
||||
{
|
||||
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ, MAP_PRIVATE, m_fd, 0);
|
||||
if (mmap_addr == MAP_FAILED)
|
||||
{
|
||||
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
|
||||
|
||||
if (!validate_elf_header())
|
||||
return;
|
||||
|
||||
m_success = true;
|
||||
}
|
||||
|
||||
ELFFile::~ELFFile()
|
||||
{
|
||||
if (m_mmap)
|
||||
munmap(m_mmap, m_stat.st_size);
|
||||
m_mmap = nullptr;
|
||||
|
||||
if (m_fd != -1)
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
const ElfNativeFileHeader& ELFFile::elf_header() const
|
||||
{
|
||||
return *reinterpret_cast<LibELF::ElfNativeFileHeader*>(m_mmap);
|
||||
}
|
||||
|
||||
bool ELFFile::validate_elf_header() const
|
||||
{
|
||||
if (m_stat.st_size < sizeof(ElfNativeFileHeader))
|
||||
{
|
||||
std::cerr << m_path << " is too small to be a ELF executable" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& elf_header = this->elf_header();
|
||||
|
||||
if (
|
||||
elf_header.e_ident[EI_MAG0] != ELFMAG0 ||
|
||||
elf_header.e_ident[EI_MAG1] != ELFMAG1 ||
|
||||
elf_header.e_ident[EI_MAG2] != ELFMAG2 ||
|
||||
elf_header.e_ident[EI_MAG3] != ELFMAG3
|
||||
)
|
||||
{
|
||||
std::cerr << m_path << " doesn't have an ELF magic number" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ARCH(x86_64)
|
||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
#elif ARCH(i386)
|
||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
#endif
|
||||
{
|
||||
std::cerr << m_path << " architecture doesn't match" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
std::cerr << m_path << " is not in little endian format" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
std::cerr << m_path << " has unsupported version" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf_header.e_type != ET_EXEC)
|
||||
{
|
||||
std::cerr << m_path << " is not an executable ELF file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const ElfNativeSectionHeader& ELFFile::section_header(std::size_t index) const
|
||||
{
|
||||
const auto& elf_header = this->elf_header();
|
||||
assert(index < elf_header.e_shnum);
|
||||
const uint8_t* section_array_start = m_mmap + elf_header.e_shoff;
|
||||
return *reinterpret_cast<const ElfNativeSectionHeader*>(section_array_start + index * elf_header.e_shentsize);
|
||||
}
|
||||
|
||||
std::string_view ELFFile::section_name(const ElfNativeSectionHeader& section_header) const
|
||||
{
|
||||
const auto& elf_header = this->elf_header();
|
||||
assert(elf_header.e_shstrndx != SHN_UNDEF);
|
||||
const auto& section_string_table = this->section_header(elf_header.e_shstrndx);
|
||||
const char* string_table_start = reinterpret_cast<const char*>(m_mmap + section_string_table.sh_offset);
|
||||
return string_table_start + section_header.sh_name;
|
||||
}
|
||||
|
||||
std::optional<std::span<const uint8_t>> ELFFile::find_section(std::string_view name) const
|
||||
{
|
||||
const auto& elf_header = this->elf_header();
|
||||
for (std::size_t i = 0; i < elf_header.e_shnum; i++)
|
||||
{
|
||||
const auto& section_header = this->section_header(i);
|
||||
auto section_name = this->section_name(section_header);
|
||||
if (section_name != name)
|
||||
continue;
|
||||
return std::span<const uint8_t>(m_mmap + section_header.sh_offset, section_header.sh_size);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
36
bootloader/installer/ELF.h
Normal file
36
bootloader/installer/ELF.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
class ELFFile
|
||||
{
|
||||
public:
|
||||
ELFFile(std::string_view path);
|
||||
~ELFFile();
|
||||
|
||||
const LibELF::ElfNativeFileHeader& elf_header() const;
|
||||
std::optional<std::span<const uint8_t>> find_section(std::string_view name) const;
|
||||
|
||||
bool success() const { return m_success; }
|
||||
|
||||
std::string_view path() const { return m_path; }
|
||||
|
||||
private:
|
||||
const LibELF::ElfNativeSectionHeader& section_header(std::size_t index) const;
|
||||
std::string_view section_name(const LibELF::ElfNativeSectionHeader&) const;
|
||||
bool validate_elf_header() const;
|
||||
|
||||
private:
|
||||
const std::string m_path;
|
||||
bool m_success { false };
|
||||
int m_fd { -1 };
|
||||
struct stat m_stat { };
|
||||
uint8_t* m_mmap { nullptr };
|
||||
};
|
||||
246
bootloader/installer/GPT.cpp
Normal file
246
bootloader/installer/GPT.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "crc32.h"
|
||||
#include "GPT.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// FIXME: don't assume 512 byte sectors
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
GPTFile::GPTFile(std::string_view path)
|
||||
: m_path(path)
|
||||
{
|
||||
m_fd = open(m_path.c_str(), O_RDWR);
|
||||
if (m_fd == -1)
|
||||
{
|
||||
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat(m_fd, &m_stat) == -1)
|
||||
{
|
||||
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
|
||||
if (mmap_addr == MAP_FAILED)
|
||||
{
|
||||
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
|
||||
|
||||
if (!validate_gpt_header())
|
||||
return;
|
||||
|
||||
m_success = true;
|
||||
}
|
||||
|
||||
GPTFile::~GPTFile()
|
||||
{
|
||||
if (m_mmap)
|
||||
munmap(m_mmap, m_stat.st_size);
|
||||
m_mmap = nullptr;
|
||||
|
||||
if (m_fd != -1)
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
MBR& GPTFile::mbr()
|
||||
{
|
||||
return *reinterpret_cast<MBR*>(m_mmap);
|
||||
}
|
||||
|
||||
const GPTHeader& GPTFile::gpt_header() const
|
||||
{
|
||||
return *reinterpret_cast<GPTHeader*>(m_mmap + SECTOR_SIZE);
|
||||
}
|
||||
|
||||
bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
|
||||
{
|
||||
auto& mbr = this->mbr();
|
||||
|
||||
if (stage1.size() > sizeof(mbr.boot_code))
|
||||
{
|
||||
std::cerr << m_path << ": can't fit " << stage1.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy boot code
|
||||
memcpy(mbr.boot_code, stage1.data(), stage1.size());
|
||||
|
||||
// setup mbr
|
||||
mbr.unique_mbr_disk_signature = 0xdeadbeef;
|
||||
mbr.unknown = 0;
|
||||
mbr.signature = 0xAA55;
|
||||
|
||||
// setup mbr partition records
|
||||
mbr.partition_records[0].boot_indicator = 0x00;
|
||||
mbr.partition_records[0].starting_chs[0] = 0x00;
|
||||
mbr.partition_records[0].starting_chs[1] = 0x02;
|
||||
mbr.partition_records[0].starting_chs[2] = 0x00;
|
||||
mbr.partition_records[0].os_type = 0xEE;
|
||||
mbr.partition_records[0].ending_chs[0] = 0xFF;
|
||||
mbr.partition_records[0].ending_chs[1] = 0xFF;
|
||||
mbr.partition_records[0].ending_chs[2] = 0xFF;
|
||||
mbr.partition_records[0].starting_lba = 1;
|
||||
mbr.partition_records[0].size_in_lba = 0xFFFFFFFF;
|
||||
memset(&mbr.partition_records[1], 0x00, sizeof(MBRPartitionRecord));
|
||||
memset(&mbr.partition_records[2], 0x00, sizeof(MBRPartitionRecord));
|
||||
memset(&mbr.partition_records[3], 0x00, sizeof(MBRPartitionRecord));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid)
|
||||
{
|
||||
if (stage2.size() < 16)
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, too small for patches" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// find GUID patch offsets
|
||||
std::size_t disk_guid_offset(-1);
|
||||
std::size_t part_guid_offset(-1);
|
||||
for (std::size_t i = 0; i < stage2.size() - 16; i++)
|
||||
{
|
||||
if (memcmp(stage2.data() + i, "root disk guid ", 16) == 0)
|
||||
{
|
||||
if (disk_guid_offset != std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable disk guids" << std::endl;
|
||||
return false;
|
||||
}
|
||||
disk_guid_offset = i;
|
||||
}
|
||||
if (memcmp(stage2.data() + i, "root part guid ", 16) == 0)
|
||||
{
|
||||
if (part_guid_offset != std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable partition guids" << std::endl;
|
||||
return false;
|
||||
}
|
||||
part_guid_offset = i;
|
||||
}
|
||||
}
|
||||
if (disk_guid_offset == std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, no patchable disk guid" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (part_guid_offset == std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, no patchable partition guid" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto partition = find_partition_with_type(bios_boot_guid);
|
||||
if (!partition.has_value())
|
||||
{
|
||||
std::cerr << m_path << ": could not find partition with type " << bios_boot_guid << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
|
||||
|
||||
if (stage2.size() > partition_size)
|
||||
{
|
||||
std::cerr << m_path << ": can't fit " << stage2.size() << " bytes of data to partition of size " << partition_size << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE;
|
||||
memcpy(partition_start, stage2.data(), stage2.size());
|
||||
|
||||
// patch GUIDs
|
||||
*reinterpret_cast<GUID*>(partition_start + disk_guid_offset) = gpt_header().disk_guid;
|
||||
*reinterpret_cast<GUID*>(partition_start + part_guid_offset) = root_partition_guid;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid)
|
||||
{
|
||||
if (!find_partition_with_guid(root_partition_guid).has_value())
|
||||
{
|
||||
std::cerr << m_path << ": no partition with GUID " << root_partition_guid << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!install_stage1(stage1))
|
||||
return false;
|
||||
if (!install_stage2(stage2, root_partition_guid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_guid(const GUID& guid) const
|
||||
{
|
||||
const auto& gpt_header = this->gpt_header();
|
||||
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
||||
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
|
||||
{
|
||||
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
|
||||
if (partition_entry.partition_guid != guid)
|
||||
continue;
|
||||
return partition_entry;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_type(const GUID& type_guid) const
|
||||
{
|
||||
const auto& gpt_header = this->gpt_header();
|
||||
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
|
||||
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
|
||||
{
|
||||
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
|
||||
if (partition_entry.type_guid != type_guid)
|
||||
continue;
|
||||
return partition_entry;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool GPTFile::validate_gpt_header() const
|
||||
{
|
||||
if (SECTOR_SIZE + m_stat.st_size < sizeof(GPTHeader))
|
||||
{
|
||||
std::cerr << m_path << " is too small to have GPT header" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto gpt_header = this->gpt_header();
|
||||
|
||||
if (std::memcmp(gpt_header.signature, "EFI PART", 8) != 0)
|
||||
{
|
||||
std::cerr << m_path << " doesn't contain GPT partition header signature" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t header_crc32 = gpt_header.header_crc32;
|
||||
|
||||
gpt_header.header_crc32 = 0;
|
||||
if (header_crc32 != crc32_checksum(reinterpret_cast<uint8_t*>(&gpt_header), gpt_header.header_size))
|
||||
{
|
||||
std::cerr << m_path << " has non-matching header crc32" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t partition_array_size = gpt_header.number_of_partition_entries * gpt_header.size_of_partition_entry;
|
||||
if (gpt_header.partition_entry_array_crc32 != crc32_checksum(m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE, partition_array_size))
|
||||
{
|
||||
std::cerr << m_path << " has non-matching partition entry crc32" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
91
bootloader/installer/GPT.h
Normal file
91
bootloader/installer/GPT.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include "GUID.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct MBRPartitionRecord
|
||||
{
|
||||
uint8_t boot_indicator;
|
||||
uint8_t starting_chs[3];
|
||||
uint8_t os_type;
|
||||
uint8_t ending_chs[3];
|
||||
uint32_t starting_lba;
|
||||
uint32_t size_in_lba;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MBR
|
||||
{
|
||||
uint8_t boot_code[440];
|
||||
uint32_t unique_mbr_disk_signature;
|
||||
uint16_t unknown;
|
||||
MBRPartitionRecord partition_records[4];
|
||||
uint16_t signature;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(MBR) == 512);
|
||||
|
||||
struct GPTPartitionEntry
|
||||
{
|
||||
GUID type_guid;
|
||||
GUID partition_guid;
|
||||
uint64_t starting_lba;
|
||||
uint64_t ending_lba;
|
||||
uint64_t attributes;
|
||||
uint16_t name[36];
|
||||
};
|
||||
static_assert(sizeof(GPTPartitionEntry) == 128);
|
||||
|
||||
struct GPTHeader
|
||||
{
|
||||
char signature[8];
|
||||
uint32_t revision;
|
||||
uint32_t header_size;
|
||||
uint32_t header_crc32;
|
||||
uint32_t reserved;
|
||||
uint64_t my_lba;
|
||||
uint64_t alternate_lba;
|
||||
uint64_t first_usable_lba;
|
||||
uint64_t last_usable_lba;
|
||||
GUID disk_guid;
|
||||
uint64_t partition_entry_lba;
|
||||
uint32_t number_of_partition_entries;
|
||||
uint32_t size_of_partition_entry;
|
||||
uint32_t partition_entry_array_crc32;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(GPTHeader) == 92);
|
||||
|
||||
class GPTFile
|
||||
{
|
||||
public:
|
||||
GPTFile(std::string_view path);
|
||||
~GPTFile();
|
||||
|
||||
bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid);
|
||||
|
||||
const GPTHeader& gpt_header() const;
|
||||
|
||||
bool success() const { return m_success; }
|
||||
|
||||
std::string_view path() const { return m_path; }
|
||||
|
||||
private:
|
||||
MBR& mbr();
|
||||
bool validate_gpt_header() const;
|
||||
std::optional<GPTPartitionEntry> find_partition_with_guid(const GUID& guid) const;
|
||||
std::optional<GPTPartitionEntry> find_partition_with_type(const GUID& type_guid) const;
|
||||
|
||||
bool install_stage1(std::span<const uint8_t> stage1);
|
||||
bool install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid);
|
||||
|
||||
private:
|
||||
const std::string m_path;
|
||||
bool m_success { false };
|
||||
int m_fd { -1 };
|
||||
struct stat m_stat { };
|
||||
uint8_t* m_mmap { nullptr };
|
||||
};
|
||||
74
bootloader/installer/GUID.cpp
Normal file
74
bootloader/installer/GUID.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "GUID.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
|
||||
std::optional<uint64_t> parse_hex(std::string_view hex_string)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (char c : hex_string)
|
||||
{
|
||||
if (!isxdigit(c))
|
||||
return {};
|
||||
|
||||
uint8_t nibble = 0;
|
||||
if ('0' <= c && c <= '9')
|
||||
nibble = c - '0';
|
||||
else if ('a' <= c && c <= 'f')
|
||||
nibble = c - 'a' + 10;
|
||||
else
|
||||
nibble = c - 'A' + 10;
|
||||
result = (result << 4) | nibble;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<GUID> GUID::from_string(std::string_view guid_string)
|
||||
{
|
||||
if (guid_string.size() != 36)
|
||||
return {};
|
||||
|
||||
if (guid_string[8] != '-' || guid_string[13] != '-' || guid_string[18] != '-' || guid_string[23] != '-')
|
||||
return {};
|
||||
|
||||
auto comp1 = parse_hex(guid_string.substr(0, 8));
|
||||
auto comp2 = parse_hex(guid_string.substr(9, 4));
|
||||
auto comp3 = parse_hex(guid_string.substr(14, 4));
|
||||
auto comp4 = parse_hex(guid_string.substr(19, 4));
|
||||
auto comp5 = parse_hex(guid_string.substr(24, 12));
|
||||
|
||||
if (!comp1.has_value() || !comp2.has_value() || !comp3.has_value() || !comp4.has_value() || !comp5.has_value())
|
||||
return {};
|
||||
|
||||
GUID result;
|
||||
result.component1 = *comp1;
|
||||
result.component2 = *comp2;
|
||||
result.component3 = *comp3;
|
||||
for (int i = 0; i < 2; i++)
|
||||
result.component45[i + 0] = *comp4 >> ((2-1) * 8 - i * 8);
|
||||
for (int i = 0; i < 6; i++)
|
||||
result.component45[i + 2] = *comp5 >> ((6-1) * 8 - i * 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GUID::operator==(const GUID& other) const
|
||||
{
|
||||
return std::memcmp(this, &other, sizeof(GUID)) == 0;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const GUID& guid)
|
||||
{
|
||||
auto flags = out.flags();
|
||||
out << std::hex << std::setfill('0');
|
||||
out << std::setw(8) << guid.component1 << '-';
|
||||
out << std::setw(4) << guid.component2 << '-';
|
||||
out << std::setw(4) << guid.component3 << '-';
|
||||
|
||||
out << std::setw(2);
|
||||
for (int i = 0; i < 2; i++) out << +guid.component45[i];
|
||||
out << '-';
|
||||
for (int i = 2; i < 8; i++) out << +guid.component45[i];
|
||||
|
||||
out.flags(flags);
|
||||
return out;
|
||||
}
|
||||
37
bootloader/installer/GUID.h
Normal file
37
bootloader/installer/GUID.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string_view>
|
||||
|
||||
struct GUID
|
||||
{
|
||||
static std::optional<GUID> from_string(std::string_view);
|
||||
|
||||
uint32_t component1;
|
||||
uint16_t component2;
|
||||
uint16_t component3;
|
||||
// last 2 components are combined so no packed needed
|
||||
uint8_t component45[8];
|
||||
|
||||
bool operator==(const GUID& other) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const GUID& guid);
|
||||
|
||||
// unused 00000000-0000-0000-0000-000000000000
|
||||
static constexpr GUID unused_guid = {
|
||||
0x00000000,
|
||||
0x0000,
|
||||
0x0000,
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
// bios boot 21686148-6449-6E6F-744E-656564454649
|
||||
static constexpr GUID bios_boot_guid = {
|
||||
0x21686148,
|
||||
0x6449,
|
||||
0x6E6F,
|
||||
{ 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 }
|
||||
};
|
||||
3
bootloader/installer/build.sh
Executable file
3
bootloader/installer/build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader
|
||||
80
bootloader/installer/crc32.cpp
Normal file
80
bootloader/installer/crc32.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "crc32.h"
|
||||
|
||||
static constexpr uint32_t crc32_table[256] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
|
||||
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
|
||||
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
|
||||
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
|
||||
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
|
||||
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
|
||||
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
|
||||
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
|
||||
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
|
||||
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
|
||||
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
|
||||
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
|
||||
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
|
||||
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
|
||||
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
|
||||
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
|
||||
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
|
||||
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
|
||||
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
|
||||
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
|
||||
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
|
||||
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
||||
};
|
||||
|
||||
uint32_t crc32_checksum(const uint8_t* data, std::size_t count)
|
||||
{
|
||||
uint32_t crc32 = 0xFFFFFFFF;
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
uint8_t index = (crc32 ^ data[i]) & 0xFF;
|
||||
crc32 = (crc32 >> 8) ^ crc32_table[index];
|
||||
}
|
||||
return crc32 ^ 0xFFFFFFFF;
|
||||
}
|
||||
6
bootloader/installer/crc32.h
Normal file
6
bootloader/installer/crc32.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);
|
||||
44
bootloader/installer/main.cpp
Normal file
44
bootloader/installer/main.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "ELF.h"
|
||||
#include "GPT.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE ROOT_PARTITION_GUID\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto root_partition_guid = GUID::from_string(argv[3]);
|
||||
if (!root_partition_guid.has_value())
|
||||
{
|
||||
std::cerr << "invalid guid '" << argv[3] << '\'' << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ELFFile bootloader(argv[1]);
|
||||
if (!bootloader.success())
|
||||
return 1;
|
||||
|
||||
auto stage1 = bootloader.find_section(".stage1"sv);
|
||||
auto stage2 = bootloader.find_section(".stage2"sv);
|
||||
if (!stage1.has_value() || !stage2.has_value())
|
||||
{
|
||||
std::cerr << bootloader.path() << " doesn't contain .stage1 and .stage2 sections" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
GPTFile disk_image(argv[2]);
|
||||
if (!disk_image.success())
|
||||
return 1;
|
||||
|
||||
if (!disk_image.install_bootloader(*stage1, *stage2, *root_partition_guid))
|
||||
return 1;
|
||||
std::cout << "bootloader installed" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
bootloader/linker.ld
Normal file
15
bootloader/linker.ld
Normal file
@@ -0,0 +1,15 @@
|
||||
ENTRY(stage1_main)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x7C00;
|
||||
.stage1 : { *(.stage1) }
|
||||
|
||||
. = ALIGN(512);
|
||||
stage2_start = .;
|
||||
.stage2 : { *(.stage2) }
|
||||
stage2_end = .;
|
||||
|
||||
. = ALIGN(512);
|
||||
.bss : { *(.bss) }
|
||||
}
|
||||
129
bootloader/memory_map.S
Normal file
129
bootloader/memory_map.S
Normal file
@@ -0,0 +1,129 @@
|
||||
.code16
|
||||
|
||||
.section .stage2
|
||||
|
||||
# fills memory map data structure
|
||||
# doesn't return on error
|
||||
# NO REGISTERS SAVED
|
||||
.global get_memory_map
|
||||
get_memory_map:
|
||||
movl $0, (memory_map_entry_count)
|
||||
|
||||
movl $0x0000E820, %eax
|
||||
movl $0x534D4150, %edx
|
||||
xorl %ebx, %ebx
|
||||
movl $20, %ecx
|
||||
movw $memory_map_entries, %di
|
||||
|
||||
clc
|
||||
int $0x15
|
||||
# If first call returs with CF set, the call failed
|
||||
jc .get_memory_map_error
|
||||
|
||||
.get_memory_map_rest:
|
||||
cmpl $0x534D4150, %eax
|
||||
jne .get_memory_map_error
|
||||
|
||||
# FIXME: don't assume BIOS to always return 20 bytes
|
||||
cmpl $20, %ecx
|
||||
jne .get_memory_map_error
|
||||
|
||||
# increment entry count
|
||||
incl (memory_map_entry_count)
|
||||
|
||||
# increment entry pointer
|
||||
addw %cx, %di
|
||||
|
||||
# BIOS can indicate end of list by 0 in ebx
|
||||
testl %ebx, %ebx
|
||||
jz .get_memory_map_done
|
||||
|
||||
movl $0x0000E820, %eax
|
||||
movl $0x534D4150, %edx
|
||||
|
||||
clc
|
||||
int $0x15
|
||||
# BIOS can indicate end of list by setting CF
|
||||
jnc .get_memory_map_rest
|
||||
|
||||
.get_memory_map_done:
|
||||
ret
|
||||
|
||||
.get_memory_map_error:
|
||||
movw $memory_map_error_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
|
||||
# print memory map from memory_map_entries
|
||||
# NO REGISTERS SAVED
|
||||
.global print_memory_map
|
||||
print_memory_map:
|
||||
movw $memory_map_msg, %si
|
||||
call puts
|
||||
call print_newline
|
||||
|
||||
movl (memory_map_entry_count), %edx
|
||||
movw $memory_map_entries, %si
|
||||
|
||||
movw $16, %bx
|
||||
movw $4, %cx
|
||||
|
||||
.loop_memory_map:
|
||||
movb $' ', %al
|
||||
call putc; call putc; call putc; call putc
|
||||
|
||||
movw 0x06(%si), %ax
|
||||
call print_number
|
||||
movw 0x04(%si), %ax
|
||||
call print_number
|
||||
movw 0x02(%si), %ax
|
||||
call print_number
|
||||
movw 0x00(%si), %ax
|
||||
call print_number
|
||||
|
||||
movb $',', %al
|
||||
call putc
|
||||
movb $' ', %al
|
||||
call putc
|
||||
|
||||
movw 0x0E(%si), %ax
|
||||
call print_number
|
||||
movw 0x0C(%si), %ax
|
||||
call print_number
|
||||
movw 0x0A(%si), %ax
|
||||
call print_number
|
||||
movw 0x08(%si), %ax
|
||||
call print_number
|
||||
|
||||
movb $',', %al
|
||||
call putc
|
||||
movb $' ', %al
|
||||
call putc
|
||||
|
||||
movw 0x12(%si), %ax
|
||||
call print_number
|
||||
movw 0x10(%si), %ax
|
||||
call print_number
|
||||
|
||||
call print_newline
|
||||
|
||||
addw $20, %si
|
||||
|
||||
decl %edx
|
||||
jnz .loop_memory_map
|
||||
|
||||
ret
|
||||
|
||||
|
||||
memory_map_msg:
|
||||
.asciz "memmap:"
|
||||
memory_map_error_msg:
|
||||
.asciz "Failed to get memory map"
|
||||
|
||||
.section .bss
|
||||
|
||||
memory_map_entry_count:
|
||||
.skip 4
|
||||
# 100 entries should be enough...
|
||||
memory_map_entries:
|
||||
.skip 20 * 100
|
||||
218
bootloader/utils.S
Normal file
218
bootloader/utils.S
Normal file
@@ -0,0 +1,218 @@
|
||||
.set SCREEN_WIDTH, 80
|
||||
.set SCREEN_HEIGHT, 25
|
||||
|
||||
.code16
|
||||
|
||||
.section .stage1
|
||||
|
||||
# prints character to screen
|
||||
# al: ascii character to print
|
||||
.global putc
|
||||
putc:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
movb $0x0E, %ah
|
||||
xorb %bh, %bh
|
||||
int $0x10
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
# prints null terminated string to screen
|
||||
# ds:si: string address
|
||||
.global puts
|
||||
puts:
|
||||
pushw %si
|
||||
pushw %bx
|
||||
pushw %ax
|
||||
|
||||
movb $0x0E, %ah
|
||||
xorb %bh, %bh
|
||||
|
||||
.puts_loop:
|
||||
lodsb
|
||||
|
||||
test %al, %al
|
||||
jz .puts_done
|
||||
|
||||
int $0x10
|
||||
jmp .puts_loop
|
||||
|
||||
.puts_done:
|
||||
popw %ax
|
||||
popw %bx
|
||||
popw %si
|
||||
ret
|
||||
|
||||
# compares memory between addresses
|
||||
# si: ptr1
|
||||
# di: ptr2
|
||||
# cx: bytes count
|
||||
# return:
|
||||
# al: 1 if equal, 0 otherwise
|
||||
.global memcmp
|
||||
memcmp:
|
||||
# NOTE: using pusha + popa to save space
|
||||
pusha
|
||||
cld
|
||||
repe cmpsb
|
||||
popa
|
||||
setzb %al
|
||||
ret
|
||||
|
||||
|
||||
.section .stage2
|
||||
|
||||
# read a character from keyboard
|
||||
# return:
|
||||
# al: ascii
|
||||
# ah: bios scan code
|
||||
.global getc
|
||||
getc:
|
||||
movb $0x00, %ah
|
||||
int $0x16
|
||||
ret
|
||||
|
||||
# prints newline to screen
|
||||
.global print_newline
|
||||
print_newline:
|
||||
pushw %ax
|
||||
movb $'\r', %al
|
||||
call putc
|
||||
movb $'\n', %al
|
||||
call putc
|
||||
pop %ax
|
||||
ret
|
||||
|
||||
# prints backspace to screen, can go back a line
|
||||
.global print_backspace
|
||||
print_backspace:
|
||||
pushw %ax
|
||||
pushw %bx
|
||||
pushw %cx
|
||||
pushw %dx
|
||||
|
||||
# get cursor position
|
||||
movb $0x03, %ah
|
||||
movb $0x00, %bh
|
||||
int $0x10
|
||||
|
||||
# don't do anyting if on first row
|
||||
testb %dh, %dh
|
||||
jz .print_backspace_done
|
||||
|
||||
# go one line up if on first column
|
||||
test %dl, %dl
|
||||
jz .print_backspace_go_line_up
|
||||
|
||||
# otherwise decrease column
|
||||
decb %dl
|
||||
jmp .print_backspace_do_print
|
||||
|
||||
.print_backspace_go_line_up:
|
||||
# decrease row and set column to the last one
|
||||
decb %dh
|
||||
movb $(SCREEN_WIDTH - 1), %dl
|
||||
|
||||
.print_backspace_do_print:
|
||||
# set cursor position
|
||||
movb $0x02, %ah
|
||||
int $0x10
|
||||
|
||||
# print 'empty' character (space)
|
||||
mov $' ', %al
|
||||
call putc
|
||||
|
||||
# set cursor position
|
||||
movb $0x02, %ah
|
||||
int $0x10
|
||||
|
||||
.print_backspace_done:
|
||||
popw %dx
|
||||
popw %cx
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
# print number to screen
|
||||
# ax: number to print
|
||||
# bx: number base
|
||||
# cx: min width (zero pads if shorter)
|
||||
.global print_number
|
||||
print_number:
|
||||
pusha
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
# save min width
|
||||
subl $4, %esp
|
||||
movw %cx, (%esp)
|
||||
|
||||
movw $print_number_buffer, %si
|
||||
xorw %cx, %cx
|
||||
|
||||
.print_number_fill_loop:
|
||||
# fill buffer with all remainders ax % bx
|
||||
xorw %dx, %dx
|
||||
divw %bx
|
||||
movb %dl, (%si)
|
||||
incw %si
|
||||
incw %cx
|
||||
testw %ax, %ax
|
||||
jnz .print_number_fill_loop
|
||||
|
||||
# check if zero pad is required
|
||||
cmpw (%esp), %cx
|
||||
jae .print_number_print_loop
|
||||
|
||||
# dx: saved number count
|
||||
# cx: zero pad count
|
||||
movw %cx, %dx
|
||||
movw (%esp), %cx
|
||||
subw %dx, %cx
|
||||
movb $'0', %al
|
||||
|
||||
.print_number_pad_zeroes:
|
||||
call putc
|
||||
loop .print_number_pad_zeroes
|
||||
|
||||
# restore number count
|
||||
movw %dx, %cx
|
||||
|
||||
.print_number_print_loop:
|
||||
decw %si
|
||||
movb (%si), %al
|
||||
cmpb $10, %al
|
||||
jae .print_number_hex
|
||||
addb $'0', %al
|
||||
jmp .print_number_do_print
|
||||
.print_number_hex:
|
||||
addb $('a' - 10), %al
|
||||
.print_number_do_print:
|
||||
call putc
|
||||
loop .print_number_print_loop
|
||||
|
||||
leavel
|
||||
popa
|
||||
ret
|
||||
|
||||
# test if character is printable ascii
|
||||
# al: character to test
|
||||
# return:
|
||||
# al: 1 if is printable, 0 otherwise
|
||||
.global isprint
|
||||
isprint:
|
||||
subb $0x20, %al
|
||||
cmpb $(0x7E - 0x20), %al
|
||||
ja .isprint_not_printable
|
||||
movb $1, %al
|
||||
ret
|
||||
.isprint_not_printable:
|
||||
movb $0, %al
|
||||
ret
|
||||
|
||||
.section .bss
|
||||
|
||||
# enough for base 2 printing
|
||||
print_number_buffer:
|
||||
.skip 16
|
||||
7
build.sh
7
build.sh
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
. ./headers.sh
|
||||
|
||||
for PROJECT in $PROJECTS; do
|
||||
(cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install)
|
||||
done
|
||||
11
clean.sh
11
clean.sh
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
. ./config.sh
|
||||
|
||||
for PROJECT in $PROJECTS; do
|
||||
(cd $PROJECT && $MAKE clean)
|
||||
done
|
||||
|
||||
rm -rf sysroot
|
||||
rm -rf isodir
|
||||
rm -rf banan-os.img
|
||||
33
config.sh
33
config.sh
@@ -1,33 +0,0 @@
|
||||
SYSTEM_HEADER_PROJECTS="libc BAN kernel"
|
||||
PROJECTS="libc BAN kernel"
|
||||
|
||||
export MAKE=${MAKE:-make}
|
||||
export HOST=${HOST:-$(./default-host.sh)}
|
||||
|
||||
export AR=${HOST}-ar
|
||||
export AS=${HOST}-as
|
||||
export CC=${HOST}-gcc
|
||||
export CXX=${HOST}-g++
|
||||
|
||||
export PREFIX=/usr
|
||||
export EXEC_PREFIX=$PREFIX
|
||||
export BOOTDIR=/boot
|
||||
export LIBDIR=$EXEC_PREFIX/lib
|
||||
export INCLUDEDIR=$PREFIX/include
|
||||
|
||||
export CFLAGS='-O2 -g'
|
||||
export CPPFLAGS='--std=c++20 -Wno-literal-suffix'
|
||||
|
||||
export UBSAN=0
|
||||
|
||||
# Configure the cross-compiler to use the desired system root.
|
||||
export SYSROOT="$(pwd)/sysroot"
|
||||
export CC="$CC --sysroot=$SYSROOT"
|
||||
export CXX="$CXX --sysroot=$SYSROOT"
|
||||
|
||||
# Work around that the -elf gcc targets doesn't have a system include directory
|
||||
# because it was configured with --without-headers rather than --with-sysroot.
|
||||
if echo "$HOST" | grep -Eq -- '-elf($|-)'; then
|
||||
export CC="$CC -isystem=$INCLUDEDIR"
|
||||
export CXX="$CXX -isystem=$INCLUDEDIR"
|
||||
fi
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
echo x86_64-elf
|
||||
67
disk.sh
67
disk.sh
@@ -1,67 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
. ./build.sh
|
||||
|
||||
DISK_NAME=banan-os.img
|
||||
DISK_SIZE=$[50 * 1024 * 1024]
|
||||
MOUNT_DIR=/mnt
|
||||
|
||||
dd if=/dev/zero of=$DISK_NAME bs=512 count=$[$DISK_SIZE / 512]
|
||||
|
||||
sed -e 's/\s*\([-\+[:alnum:]]*\).*/\1/' << EOF | fdisk $DISK_NAME
|
||||
g # gpt
|
||||
n # new partition
|
||||
1 # partition number 1
|
||||
# default (from the beginning of the disk)
|
||||
+1MiB # bios boot partiton size
|
||||
n # new partition
|
||||
2 # partition number 2
|
||||
# default (right after bios boot partition)
|
||||
# default (to the end of disk)
|
||||
t # set type
|
||||
1 # ... of partition 1
|
||||
4 # bios boot partition
|
||||
t # set type
|
||||
2 # ... of partition 2
|
||||
20 # Linux filesystem
|
||||
x # expert menu
|
||||
n # partition name
|
||||
2 # ... of partition 2
|
||||
banan-root
|
||||
r # back to main menu
|
||||
w # write changes
|
||||
EOF
|
||||
|
||||
LOOP_DEV=$(sudo losetup -f --show $DISK_NAME)
|
||||
sudo partprobe $LOOP_DEV
|
||||
|
||||
PARTITION1=${LOOP_DEV}p1
|
||||
PARTITION2=${LOOP_DEV}p2
|
||||
|
||||
sudo mkfs.ext2 $PARTITION2
|
||||
|
||||
sudo mount $PARTITION2 $MOUNT_DIR
|
||||
|
||||
sudo cp -r ${SYSROOT}/* ${MOUNT_DIR}/
|
||||
sudo mkdir -p ${MOUNT_DIR}/usr/share/
|
||||
sudo cp -r fonts ${MOUNT_DIR}/usr/share/
|
||||
|
||||
sudo grub-install --no-floppy --target=i386-pc --modules="normal ext2 multiboot" --boot-directory=${MOUNT_DIR}/boot $LOOP_DEV
|
||||
|
||||
echo -e '
|
||||
menuentry "banan-os" {
|
||||
multiboot /boot/banan-os.kernel
|
||||
}
|
||||
menuentry "banan-os (no serial)" {
|
||||
multiboot /boot/banan-os.kernel noserial
|
||||
}
|
||||
menuentry "banan-os (no apic)" {
|
||||
multiboot /boot/banan-os.kernel noapic
|
||||
}
|
||||
menuentry "banan-os (no apic, no serial)" {
|
||||
multiboot /boot/banan-os.kernel noapic noserial
|
||||
}
|
||||
' | sudo tee ${MOUNT_DIR}/boot/grub/grub.cfg
|
||||
|
||||
sudo umount $MOUNT_DIR
|
||||
sudo losetup -d $LOOP_DEV
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
. ./config.sh
|
||||
|
||||
mkdir -p "$SYSROOT"
|
||||
|
||||
for PROJECT in $SYSTEM_HEADER_PROJECTS; do
|
||||
(cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install-headers)
|
||||
done
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
. ./disk.sh
|
||||
|
||||
SIZE=$(stat -c '%s' banan-os.img | numfmt --to=iec)
|
||||
|
||||
echo Writing ${SIZE}iB
|
||||
sudo dd if=banan-os.img of=/dev/sda status=progress
|
||||
3
kernel/.gitignore
vendored
3
kernel/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
*.d
|
||||
*.kernel
|
||||
*.o
|
||||
199
kernel/CMakeLists.txt
Normal file
199
kernel/CMakeLists.txt
Normal file
@@ -0,0 +1,199 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(kernel CXX C ASM)
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
set(ELF_FORMAT elf64-x86-64)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
set(ELF_FORMAT elf32-i386)
|
||||
endif()
|
||||
|
||||
set(KERNEL_SOURCES
|
||||
font/prefs.psf.o
|
||||
kernel/ACPI.cpp
|
||||
kernel/APIC.cpp
|
||||
kernel/CPUID.cpp
|
||||
kernel/Debug.cpp
|
||||
kernel/Device/Device.cpp
|
||||
kernel/Device/NullDevice.cpp
|
||||
kernel/Device/ZeroDevice.cpp
|
||||
kernel/Errors.cpp
|
||||
kernel/Font.cpp
|
||||
kernel/FS/DevFS/FileSystem.cpp
|
||||
kernel/FS/Ext2/FileSystem.cpp
|
||||
kernel/FS/Ext2/Inode.cpp
|
||||
kernel/FS/Inode.cpp
|
||||
kernel/FS/Pipe.cpp
|
||||
kernel/FS/ProcFS/FileSystem.cpp
|
||||
kernel/FS/ProcFS/Inode.cpp
|
||||
kernel/FS/TmpFS/FileSystem.cpp
|
||||
kernel/FS/TmpFS/Inode.cpp
|
||||
kernel/FS/VirtualFileSystem.cpp
|
||||
kernel/Input/PS2Controller.cpp
|
||||
kernel/Input/PS2Keyboard.cpp
|
||||
kernel/Input/PS2Keymap.cpp
|
||||
kernel/InterruptController.cpp
|
||||
kernel/kernel.cpp
|
||||
kernel/Memory/DMARegion.cpp
|
||||
kernel/Memory/FileBackedRegion.cpp
|
||||
kernel/Memory/Heap.cpp
|
||||
kernel/Memory/kmalloc.cpp
|
||||
kernel/Memory/MemoryBackedRegion.cpp
|
||||
kernel/Memory/MemoryRegion.cpp
|
||||
kernel/Memory/PhysicalRange.cpp
|
||||
kernel/Memory/VirtualRange.cpp
|
||||
kernel/Networking/E1000.cpp
|
||||
kernel/OpenFileDescriptorSet.cpp
|
||||
kernel/Panic.cpp
|
||||
kernel/PCI.cpp
|
||||
kernel/PIC.cpp
|
||||
kernel/Process.cpp
|
||||
kernel/Scheduler.cpp
|
||||
kernel/Semaphore.cpp
|
||||
kernel/SpinLock.cpp
|
||||
kernel/SSP.cpp
|
||||
kernel/Storage/ATA/AHCI/Controller.cpp
|
||||
kernel/Storage/ATA/AHCI/Device.cpp
|
||||
kernel/Storage/ATA/ATABus.cpp
|
||||
kernel/Storage/ATA/ATAController.cpp
|
||||
kernel/Storage/ATA/ATADevice.cpp
|
||||
kernel/Storage/DiskCache.cpp
|
||||
kernel/Storage/StorageDevice.cpp
|
||||
kernel/Syscall.cpp
|
||||
kernel/Syscall.S
|
||||
kernel/Terminal/Serial.cpp
|
||||
kernel/Terminal/TTY.cpp
|
||||
kernel/Terminal/VesaTerminalDriver.cpp
|
||||
kernel/Terminal/VirtualTTY.cpp
|
||||
kernel/Thread.cpp
|
||||
kernel/Timer/HPET.cpp
|
||||
kernel/Timer/PIT.cpp
|
||||
kernel/Timer/RTC.cpp
|
||||
kernel/Timer/Timer.cpp
|
||||
icxxabi.cpp
|
||||
)
|
||||
|
||||
#set(ENABLE_KERNEL_UBSAN True)
|
||||
|
||||
if(ENABLE_KERNEL_UBSAN)
|
||||
set(KERNEL_SOURCES ${KERNEL_SOURCES} ubsan.cpp)
|
||||
endif()
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
arch/x86_64/boot.S
|
||||
arch/x86_64/GDT.cpp
|
||||
arch/x86_64/IDT.cpp
|
||||
arch/x86_64/interrupts.S
|
||||
arch/x86_64/PageTable.cpp
|
||||
arch/x86_64/Signal.S
|
||||
arch/x86_64/Thread.S
|
||||
)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
arch/i386/boot.S
|
||||
arch/i386/GDT.cpp
|
||||
arch/i386/IDT.cpp
|
||||
arch/i386/MMU.cpp
|
||||
arch/i386/SpinLock.S
|
||||
arch/i386/Thread.S
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE LAI_SOURCES
|
||||
lai/*.c
|
||||
)
|
||||
set(LAI_SOURCES
|
||||
${LAI_SOURCES}
|
||||
kernel/lai_host.cpp
|
||||
)
|
||||
|
||||
set(BAN_SOURCES
|
||||
../BAN/BAN/New.cpp
|
||||
../BAN/BAN/String.cpp
|
||||
../BAN/BAN/StringView.cpp
|
||||
../BAN/BAN/Time.cpp
|
||||
)
|
||||
|
||||
set(LIBC_SOURCES
|
||||
../libc/ctype.cpp
|
||||
../libc/string.cpp
|
||||
)
|
||||
|
||||
set(LIBELF_SOURCES
|
||||
../LibELF/LibELF/LoadableELF.cpp
|
||||
)
|
||||
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
${LAI_SOURCES}
|
||||
${BAN_SOURCES}
|
||||
${LIBC_SOURCES}
|
||||
${LIBELF_SOURCES}
|
||||
)
|
||||
|
||||
add_executable(kernel ${KERNEL_SOURCES})
|
||||
add_dependencies(kernel headers)
|
||||
|
||||
target_compile_definitions(kernel PUBLIC __is_kernel)
|
||||
target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
||||
|
||||
target_compile_options(kernel PUBLIC -O2 -g)
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
|
||||
target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
||||
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Werror=return-type -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
|
||||
|
||||
# This might not work with other toolchains
|
||||
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
|
||||
|
||||
if(ENABLE_KERNEL_UBSAN)
|
||||
target_compile_options(kernel PUBLIC -fsanitize=undefined)
|
||||
endif()
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
target_compile_options(kernel PUBLIC -mcmodel=kernel -mno-red-zone -mno-mmx)
|
||||
target_link_options(kernel PUBLIC LINKER:-z,max-page-size=4096)
|
||||
target_link_options(kernel PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/x86_64/linker.ld)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
target_link_options(kernel PUBLIC LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/i386/linker.ld)
|
||||
endif()
|
||||
|
||||
target_link_options(kernel PUBLIC -ffreestanding -nostdlib)
|
||||
|
||||
add_custom_target(kernel-headers
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
|
||||
DEPENDS sysroot
|
||||
)
|
||||
|
||||
add_custom_target(kernel-install
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/kernel ${BANAN_BOOT}/banan-os.kernel
|
||||
DEPENDS kernel
|
||||
)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
add_custom_command(
|
||||
TARGET kernel PRE_LINK
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crti.S ${COMPILE_OPTIONS}
|
||||
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CRTBEGIN} .
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CRTEND} .
|
||||
)
|
||||
|
||||
#add_custom_command(
|
||||
# TARGET kernel POST_BUILD
|
||||
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
|
||||
#)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT font/prefs.psf.o
|
||||
COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_COMPILER} <CMAKE_CXX_LINK_FLAGS> <FLAGS> <LINK_FLAGS> -o <TARGET> ${CMAKE_CURRENT_BINARY_DIR}/crti.o ${CMAKE_CURRENT_BINARY_DIR}/crtbegin.o <OBJECTS> ${CMAKE_CURRENT_BINARY_DIR}/crtend.o ${CMAKE_CURRENT_BINARY_DIR}/crtn.o -lgcc ")
|
||||
131
kernel/Makefile
131
kernel/Makefile
@@ -1,131 +0,0 @@
|
||||
DEFAULT_HOST!=../default-host.sh
|
||||
HOST?=DEFAULT_HOST
|
||||
HOSTARCH!=../target-triplet-to-arch.sh $(HOST)
|
||||
|
||||
CFLAGS?=-O2 -g
|
||||
CPPFLAGS?=
|
||||
LDFLAGS?=
|
||||
LIBS?=
|
||||
|
||||
DESTDIR?=
|
||||
PREFIX?=/usr/local
|
||||
EXEC_PREFIX?=$(PREFIX)
|
||||
BOOTDIR?=$(EXEC_PREFIX)/boot
|
||||
INCLUDEDIR?=$(PREFIX)/include
|
||||
|
||||
CFLAGS:=$(CFLAGS) -D__is_kernel -D__arch=$(HOSTARCH) -Iinclude -fstack-protector -ffreestanding -Wall -Wextra -Werror=return-type -fno-omit-frame-pointer -mno-sse -mno-sse2
|
||||
CPPFLAGS:=$(CPPFLAGS) -fno-rtti -fno-exceptions
|
||||
LDFLAGS:=$(LDFLAGS)
|
||||
LIBS:=$(LIBS) -nostdlib -lk -lbank -lgcc
|
||||
|
||||
ARCHDIR=arch/$(HOSTARCH)
|
||||
|
||||
include $(ARCHDIR)/make.config
|
||||
|
||||
CFLAGS:=$(CFLAGS) $(KERNEL_ARCH_CFLAGS)
|
||||
CPPFLAGS:=$(CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS)
|
||||
LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS)
|
||||
LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS)
|
||||
|
||||
ifeq ($(UBSAN), 1)
|
||||
CFLAGS:=$(CFLAGS) -fsanitize=undefined
|
||||
endif
|
||||
|
||||
BUILDDIR=$(abspath build)
|
||||
|
||||
KERNEL_OBJS= \
|
||||
$(KERNEL_ARCH_OBJS) \
|
||||
font/prefs.o \
|
||||
kernel/APIC.o \
|
||||
kernel/build_libc.o \
|
||||
kernel/CPUID.o \
|
||||
kernel/Debug.o \
|
||||
kernel/Font.o \
|
||||
kernel/FS/Ext2.o \
|
||||
kernel/FS/VirtualFileSystem.o \
|
||||
kernel/Input.o \
|
||||
kernel/InterruptController.o \
|
||||
kernel/kernel.o \
|
||||
kernel/kmalloc.o \
|
||||
kernel/PCI.o \
|
||||
kernel/PIC.o \
|
||||
kernel/PIT.o \
|
||||
kernel/Process.o \
|
||||
kernel/RTC.o \
|
||||
kernel/Scheduler.o \
|
||||
kernel/Serial.o \
|
||||
kernel/Shell.o \
|
||||
kernel/SpinLock.o \
|
||||
kernel/SSP.o \
|
||||
kernel/Storage/ATAController.o \
|
||||
kernel/Storage/StorageDevice.o \
|
||||
kernel/Syscall.o \
|
||||
kernel/Thread.o \
|
||||
kernel/TTY.o \
|
||||
kernel/VesaTerminalDriver.o \
|
||||
userspace/userspace.o \
|
||||
icxxabi.o \
|
||||
ubsan.o \
|
||||
|
||||
OBJS= \
|
||||
$(ARCHDIR)/crti.o \
|
||||
$(ARCHDIR)/crtbegin.o \
|
||||
$(KERNEL_OBJS) \
|
||||
$(ARCHDIR)/crtend.o \
|
||||
$(ARCHDIR)/crtn.o \
|
||||
|
||||
LINK_LIST= \
|
||||
$(LDFLAGS) \
|
||||
$(ARCHDIR)/crti.o \
|
||||
$(ARCHDIR)/crtbegin.o \
|
||||
$(KERNEL_OBJS) \
|
||||
$(LIBS) \
|
||||
$(ARCHDIR)/crtend.o \
|
||||
$(ARCHDIR)/crtn.o \
|
||||
|
||||
.PHONY: all always clean install install-headers install-kernel
|
||||
.SUFFIXES: .o .c .cpp .S .psf
|
||||
|
||||
all: banan-os.kernel
|
||||
|
||||
banan-os.kernel: always $(OBJS) $(ARCHDIR)/linker.ld
|
||||
cd $(BUILDDIR) && $(CXX) -T ../$(ARCHDIR)/linker.ld -o banan-os.kernel $(CFLAGS) $(CPPFLAGS) $(LINK_LIST)
|
||||
cd $(BUILDDIR) && grub-file --is-x86-multiboot banan-os.kernel
|
||||
|
||||
$(ARCHDIR)/crtbegin.o $(ARCHDIR)/crtend.o:
|
||||
OBJ=`$(CC) $(CFLAGS) $(LDFLAGS) -print-file-name=$(@F)` && cp "$$OBJ" $(BUILDDIR)/$@
|
||||
|
||||
.c.o:
|
||||
$(CC) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS)
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS) $(CPPFLAGS)
|
||||
|
||||
.S.o:
|
||||
$(CC) -MD -c $< -o $(BUILDDIR)/$@ $(CFLAGS)
|
||||
|
||||
.psf.o:
|
||||
objcopy -O $(ELF_FORMAT) -B i386 -I binary $< $(BUILDDIR)/$@
|
||||
|
||||
always:
|
||||
mkdir -p $(BUILDDIR)/$(ARCHDIR)
|
||||
mkdir -p $(BUILDDIR)/kernel
|
||||
mkdir -p $(BUILDDIR)/kernel/FS
|
||||
mkdir -p $(BUILDDIR)/kernel/Storage
|
||||
mkdir -p $(BUILDDIR)/userspace
|
||||
mkdir -p $(BUILDDIR)/font
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
install: install-headers install-kernel
|
||||
|
||||
install-headers:
|
||||
mkdir -p $(DESTDIR)$(INCLUDEDIR)
|
||||
cp -R --preserve=timestamps include/. $(DESTDIR)$(INCLUDEDIR)/.
|
||||
|
||||
install-kernel: banan-os.kernel
|
||||
mkdir -p $(DESTDIR)$(BOOTDIR)
|
||||
cp $(BUILDDIR)/banan-os.kernel $(DESTDIR)$(BOOTDIR)
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
@@ -109,6 +109,11 @@ namespace Kernel::GDT
|
||||
write_entry(offset, (uint32_t)s_tss, sizeof(TaskStateSegment), 0x89, 0x0);
|
||||
}
|
||||
|
||||
void set_tss_stack(uintptr_t esp)
|
||||
{
|
||||
s_tss->esp0 = esp;
|
||||
}
|
||||
|
||||
static void flush_gdt()
|
||||
{
|
||||
asm volatile("lgdt %0" :: "m"(s_gdtr));
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <BAN/Errors.h>
|
||||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/kmalloc.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
#include <kernel/Panic.h>
|
||||
#include <kernel/Scheduler.h>
|
||||
|
||||
#define INTERRUPT_HANDLER____(i, msg) \
|
||||
static void interrupt ## i () \
|
||||
@@ -132,6 +133,8 @@ found:
|
||||
// NOTE: Scheduler sends PIT eoi's
|
||||
if (irq != PIT_IRQ)
|
||||
InterruptController::get().eoi(irq);
|
||||
|
||||
Kernel::Scheduler::get().reschedule_if_idling();
|
||||
}
|
||||
|
||||
extern "C" void handle_irq_common();
|
||||
@@ -171,7 +174,18 @@ found:
|
||||
"addl $16, %esp;"
|
||||
"popw %es;"
|
||||
"popw %ds;"
|
||||
"popa;"
|
||||
|
||||
// NOTE: following instructions are same as in 'popa', except we skip eax
|
||||
// since it holds the return value of the syscall.
|
||||
"popl %edi;"
|
||||
"popl %esi;"
|
||||
"popl %ebp;"
|
||||
"addl $4, %esp;"
|
||||
"popl %ebx;"
|
||||
"popl %edx;"
|
||||
"popl %ecx;"
|
||||
"addl $4, %esp;"
|
||||
|
||||
"iret;"
|
||||
);
|
||||
|
||||
|
||||
@@ -1,136 +1,227 @@
|
||||
#include <BAN/Errors.h>
|
||||
#include <kernel/Debug.h>
|
||||
#include <kernel/MMU.h>
|
||||
#include <kernel/kmalloc.h>
|
||||
#include <kernel/Memory/MMU.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MMU_DEBUG_PRINT 0
|
||||
|
||||
// bits 31-12 set
|
||||
#define PAGE_MASK 0xfffff000
|
||||
#define PAGE_SIZE 0x00001000
|
||||
#define PAGE_MASK 0xfffff000
|
||||
#define FLAGS_MASK 0x00000fff
|
||||
|
||||
static MMU* s_instance = nullptr;
|
||||
|
||||
void MMU::intialize()
|
||||
namespace Kernel
|
||||
{
|
||||
ASSERT(s_instance == nullptr);
|
||||
s_instance = new MMU();
|
||||
}
|
||||
|
||||
MMU& MMU::get()
|
||||
{
|
||||
ASSERT(s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
static MMU* s_instance = nullptr;
|
||||
|
||||
static uint64_t* allocate_page_aligned_page()
|
||||
{
|
||||
uint64_t* page = (uint64_t*)kmalloc(PAGE_SIZE, PAGE_SIZE);
|
||||
ASSERT(page);
|
||||
ASSERT(((uintptr_t)page % PAGE_SIZE) == 0);
|
||||
memset(page, 0, PAGE_SIZE);
|
||||
return page;
|
||||
}
|
||||
|
||||
MMU::MMU()
|
||||
{
|
||||
m_highest_paging_struct = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
|
||||
ASSERT(m_highest_paging_struct);
|
||||
ASSERT(((uintptr_t)m_highest_paging_struct % 32) == 0);
|
||||
|
||||
// allocate all page directories
|
||||
for (int i = 0; i < 4; i++)
|
||||
void MMU::initialize()
|
||||
{
|
||||
uint64_t* page_directory = allocate_page_aligned_page();
|
||||
m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present;
|
||||
ASSERT(s_instance == nullptr);
|
||||
s_instance = new MMU();
|
||||
ASSERT(s_instance);
|
||||
s_instance->initialize_kernel();
|
||||
s_instance->load();
|
||||
}
|
||||
|
||||
// create and identity map first 6 MiB
|
||||
uint64_t* page_directory1 = (uint64_t*)(m_highest_paging_struct[0] & PAGE_MASK);
|
||||
for (uint64_t i = 0; i < 3; i++)
|
||||
MMU& MMU::get()
|
||||
{
|
||||
uint64_t* page_table = allocate_page_aligned_page();
|
||||
for (uint64_t j = 0; j < 512; j++)
|
||||
page_table[j] = (i << 21) | (j << 12) | Flags::ReadWrite | Flags::Present;
|
||||
|
||||
page_directory1[i] = (uint64_t)page_table | Flags::ReadWrite | Flags::Present;
|
||||
ASSERT(s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
// dont map first page (0 -> 4 KiB) so that nullptr dereference
|
||||
// causes page fault :)
|
||||
uint64_t* page_table1 = (uint64_t*)(page_directory1[0] & PAGE_MASK);
|
||||
page_table1[0] = 0;
|
||||
|
||||
// reload this new pdpt
|
||||
asm volatile("movl %0, %%cr3" :: "r"(m_highest_paging_struct));
|
||||
}
|
||||
|
||||
void MMU::allocate_page(uintptr_t address, uint8_t flags)
|
||||
{
|
||||
#if MMU_DEBUG_PRINT
|
||||
dprintln("AllocatePage(0x{8H})", address);
|
||||
#endif
|
||||
ASSERT(flags & Flags::Present);
|
||||
|
||||
address &= PAGE_MASK;
|
||||
|
||||
uint32_t pdpte = (address & 0xC0000000) >> 30;
|
||||
uint32_t pde = (address & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (address & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
static uint64_t* allocate_page_aligned_page()
|
||||
{
|
||||
uint64_t* page_table = allocate_page_aligned_page();
|
||||
page_directory[pde] = (uint64_t)page_table;
|
||||
uint64_t* page = (uint64_t*)kmalloc(PAGE_SIZE, PAGE_SIZE);
|
||||
ASSERT(page);
|
||||
ASSERT(((uintptr_t)page % PAGE_SIZE) == 0);
|
||||
memset(page, 0, PAGE_SIZE);
|
||||
return page;
|
||||
}
|
||||
page_directory[pde] |= flags;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
page_table[pte] = address | flags;
|
||||
void MMU::initialize_kernel()
|
||||
{
|
||||
m_highest_paging_struct = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
|
||||
ASSERT(m_highest_paging_struct);
|
||||
ASSERT(((uintptr_t)m_highest_paging_struct % 32) == 0);
|
||||
|
||||
// allocate all page directories
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
uint64_t* page_directory = allocate_page_aligned_page();
|
||||
m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present;
|
||||
}
|
||||
|
||||
// FIXME: We should just identity map until g_kernel_end
|
||||
|
||||
// create and identity map first 6 MiB
|
||||
uint64_t* page_directory1 = (uint64_t*)(m_highest_paging_struct[0] & PAGE_MASK);
|
||||
for (uint64_t i = 0; i < 3; i++)
|
||||
{
|
||||
uint64_t* page_table = allocate_page_aligned_page();
|
||||
for (uint64_t j = 0; j < 512; j++)
|
||||
page_table[j] = (i << 21) | (j << 12) | Flags::ReadWrite | Flags::Present;
|
||||
|
||||
page_directory1[i] = (uint64_t)page_table | Flags::ReadWrite | Flags::Present;
|
||||
}
|
||||
|
||||
// dont map first page (0 -> 4 KiB) so that nullptr dereference
|
||||
// causes page fault :)
|
||||
uint64_t* page_table1 = (uint64_t*)(page_directory1[0] & PAGE_MASK);
|
||||
page_table1[0] = 0;
|
||||
}
|
||||
|
||||
MMU::MMU()
|
||||
{
|
||||
if (s_instance == nullptr)
|
||||
return;
|
||||
|
||||
// Here we copy the s_instances paging structs since they are
|
||||
// global for every process
|
||||
|
||||
uint64_t* global_pdpt = s_instance->m_highest_paging_struct;
|
||||
|
||||
uint64_t* pdpt = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
|
||||
ASSERT(pdpt);
|
||||
|
||||
for (uint32_t pdpte = 0; pdpte < 4; pdpte++)
|
||||
{
|
||||
if (!(global_pdpt[pdpte] & Flags::Present))
|
||||
continue;
|
||||
|
||||
uint64_t* global_pd = (uint64_t*)(global_pdpt[pdpte] & PAGE_MASK);
|
||||
|
||||
uint64_t* pd = allocate_page_aligned_page();
|
||||
pdpt[pdpte] = (uint64_t)pd | (global_pdpt[pdpte] & ~PAGE_MASK);
|
||||
|
||||
for (uint32_t pde = 0; pde < 512; pde++)
|
||||
{
|
||||
if (!(global_pd[pde] & Flags::Present))
|
||||
continue;
|
||||
|
||||
uint64_t* global_pt = (uint64_t*)(global_pd[pde] & PAGE_MASK);
|
||||
|
||||
uint64_t* pt = allocate_page_aligned_page();
|
||||
pd[pde] = (uint64_t)pt | (global_pd[pde] & ~PAGE_MASK);
|
||||
|
||||
memcpy(pt, global_pt, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
m_highest_paging_struct = pdpt;
|
||||
}
|
||||
|
||||
MMU::~MMU()
|
||||
{
|
||||
uint64_t* pdpt = m_highest_paging_struct;
|
||||
for (uint32_t pdpte = 0; pdpte < 512; pdpte++)
|
||||
{
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
continue;
|
||||
uint64_t* pd = (uint64_t*)(pdpt[pdpte] & PAGE_MASK);
|
||||
for (uint32_t pde = 0; pde < 512; pde++)
|
||||
{
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
continue;
|
||||
kfree((void*)(pd[pde] & PAGE_MASK));
|
||||
}
|
||||
kfree(pd);
|
||||
}
|
||||
kfree(pdpt);
|
||||
}
|
||||
|
||||
void MMU::load()
|
||||
{
|
||||
asm volatile("movl %0, %%cr3" :: "r"(m_highest_paging_struct));
|
||||
}
|
||||
|
||||
void MMU::map_page_at(paddr_t paddr, vaddr_t vaddr, uint8_t flags)
|
||||
{
|
||||
#if MMU_DEBUG_PRINT
|
||||
dprintln("AllocatePage(0x{8H})", address);
|
||||
#endif
|
||||
ASSERT(flags & Flags::Present);
|
||||
|
||||
ASSERT(!(paddr & ~PAGE_MASK));
|
||||
ASSERT(!(vaddr & ~PAGE_MASK));
|
||||
|
||||
uint32_t pdpte = (vaddr & 0xC0000000) >> 30;
|
||||
uint32_t pde = (vaddr & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (vaddr & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
{
|
||||
uint64_t* page_table = allocate_page_aligned_page();
|
||||
page_directory[pde] = (uint64_t)page_table;
|
||||
}
|
||||
page_directory[pde] |= flags;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
page_table[pte] = paddr | flags;
|
||||
}
|
||||
|
||||
void MMU::identity_map_page(paddr_t address, uint8_t flags)
|
||||
{
|
||||
address &= PAGE_MASK;
|
||||
map_page_at(address, address, flags);
|
||||
}
|
||||
|
||||
void MMU::identity_map_range(paddr_t address, ptrdiff_t size, uint8_t flags)
|
||||
{
|
||||
paddr_t s_page = address & PAGE_MASK;
|
||||
paddr_t e_page = (address + size - 1) & PAGE_MASK;
|
||||
for (paddr_t page = s_page; page <= e_page; page += PAGE_SIZE)
|
||||
identity_map_page(page, flags);
|
||||
}
|
||||
|
||||
void MMU::unmap_page(vaddr_t address)
|
||||
{
|
||||
#if MMU_DEBUG_PRINT
|
||||
dprintln("UnAllocatePage(0x{8H})", address & PAGE_MASK);
|
||||
#endif
|
||||
|
||||
uint32_t pdpte = (address & 0xC0000000) >> 30;
|
||||
uint32_t pde = (address & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (address & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
return;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
if (!(page_table[pte] & Flags::Present))
|
||||
return;
|
||||
|
||||
page_table[pte] = 0;
|
||||
|
||||
// TODO: Unallocate the page table if this was the only allocated page
|
||||
}
|
||||
|
||||
void MMU::unmap_range(vaddr_t address, ptrdiff_t size)
|
||||
{
|
||||
uintptr_t s_page = address & PAGE_MASK;
|
||||
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
|
||||
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
|
||||
unmap_page(page);
|
||||
}
|
||||
|
||||
uint8_t MMU::get_page_flags(vaddr_t address) const
|
||||
{
|
||||
uint32_t pdpte = (address & 0xC0000000) >> 30;
|
||||
uint32_t pde = (address & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (address & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
return 0;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
if (!(page_table[pte] & Flags::Present))
|
||||
return 0;
|
||||
|
||||
return page_table[pte] & FLAGS_MASK;
|
||||
}
|
||||
|
||||
asm volatile("invlpg (%0)" :: "r"(address) : "memory");
|
||||
}
|
||||
|
||||
void MMU::allocate_range(uintptr_t address, ptrdiff_t size, uint8_t flags)
|
||||
{
|
||||
uintptr_t s_page = address & PAGE_MASK;
|
||||
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
|
||||
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
|
||||
allocate_page(page, flags);
|
||||
}
|
||||
|
||||
void MMU::unallocate_page(uintptr_t address)
|
||||
{
|
||||
#if MMU_DEBUG_PRINT
|
||||
dprintln("UnAllocatePage(0x{8H})", address & PAGE_MASK);
|
||||
#endif
|
||||
|
||||
uint32_t pdpte = (address & 0xC0000000) >> 30;
|
||||
uint32_t pde = (address & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (address & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
return;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
if (!(page_table[pte] & Flags::Present))
|
||||
return;
|
||||
|
||||
page_table[pte] = 0;
|
||||
|
||||
// TODO: Unallocate the page table if this was the only allocated page
|
||||
|
||||
asm volatile("invlpg (%0)" :: "r"(address & PAGE_MASK) : "memory");
|
||||
}
|
||||
|
||||
void MMU::unallocate_range(uintptr_t address, ptrdiff_t size)
|
||||
{
|
||||
uintptr_t s_page = address & PAGE_MASK;
|
||||
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
|
||||
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
|
||||
unallocate_page(page);
|
||||
}
|
||||
|
||||
@@ -26,3 +26,22 @@ continue_thread:
|
||||
movl 4(%esp), %esp
|
||||
movl $0, %eax
|
||||
jmp *%ecx
|
||||
|
||||
# void thread_jump_userspace(uint32_t rsp, uint32_t rip)
|
||||
.global thread_jump_userspace
|
||||
thread_jump_userspace:
|
||||
movl $0x23, %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
movl 8(%esp), %ecx
|
||||
movl 4(%esp), %esp
|
||||
|
||||
pushl $0x23
|
||||
pushl %esp
|
||||
pushfl
|
||||
pushl $0x1B
|
||||
pushl %ecx
|
||||
iret
|
||||
|
||||
@@ -28,9 +28,7 @@ g_boot_stack_bottom:
|
||||
g_boot_stack_top:
|
||||
|
||||
# 0 MiB -> 1 MiB: bootloader stuff
|
||||
# 1 MiB -> 2 MiB: kernel
|
||||
# 2 MiB -> 3 MiB: kmalloc
|
||||
# 3 MiB -> 4 Mib: kmalloc_fixed
|
||||
# 1 MiB -> : kernel
|
||||
.align 32
|
||||
boot_page_directory_pointer_table:
|
||||
.skip 4 * 8
|
||||
|
||||
23
kernel/arch/i386/crt0.S
Normal file
23
kernel/arch/i386/crt0.S
Normal file
@@ -0,0 +1,23 @@
|
||||
.section .text
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
# Set up end of the stack frame linked list.
|
||||
movl $0, %ebp
|
||||
pushl %ebp # rip=0
|
||||
pushl %ebp # rbp=0
|
||||
movl %esp, %ebp
|
||||
|
||||
# Prepare signals, memory allocation, stdio and such.
|
||||
#call initialize_standard_library
|
||||
|
||||
# Run the global constructors.
|
||||
call _init
|
||||
|
||||
# Run main
|
||||
call main
|
||||
|
||||
# Terminate the process with the exit code.
|
||||
movl %eax, %edi
|
||||
call exit
|
||||
.size _start, . - _start
|
||||
@@ -4,6 +4,7 @@ SECTIONS
|
||||
{
|
||||
. = 0x00100000;
|
||||
|
||||
g_kernel_start = .;
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.multiboot)
|
||||
@@ -11,9 +12,7 @@ SECTIONS
|
||||
}
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
g_rodata_start = .;
|
||||
*(.rodata.*)
|
||||
g_rodata_end = .;
|
||||
}
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
@@ -26,13 +25,4 @@ SECTIONS
|
||||
}
|
||||
|
||||
g_kernel_end = .;
|
||||
|
||||
. = 0x00A00000;
|
||||
|
||||
g_userspace_start = .;
|
||||
.userspace BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.userspace)
|
||||
}
|
||||
g_userspace_end = .;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
KERNEL_ARCH_CFLAGS=
|
||||
KERNEL_ARCH_CPPFLAGS=
|
||||
KERNEL_ARCH_LDFLAGS=
|
||||
KERNEL_ARCH_LIBS=
|
||||
|
||||
ELF_FORMAT=elf32-i386
|
||||
|
||||
KERNEL_ARCH_OBJS= \
|
||||
$(ARCHDIR)/boot.o \
|
||||
$(ARCHDIR)/GDT.o \
|
||||
$(ARCHDIR)/IDT.o \
|
||||
$(ARCHDIR)/MMU.o \
|
||||
$(ARCHDIR)/SpinLock.o \
|
||||
$(ARCHDIR)/Thread.o \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Array.h>
|
||||
#include <kernel/GDT.h>
|
||||
|
||||
#include <string.h>
|
||||
@@ -54,69 +54,72 @@ namespace Kernel::GDT
|
||||
uint64_t address;
|
||||
} __attribute__((packed));
|
||||
|
||||
static TaskStateSegment* s_tss = nullptr;
|
||||
static SegmentDescriptor* s_gdt = nullptr;
|
||||
static constexpr uint16_t s_tss_offset = 0x28;
|
||||
|
||||
static TaskStateSegment s_tss;
|
||||
static BAN::Array<SegmentDescriptor, 7> s_gdt; // null, kernel code, kernel data, user code, user data, tss low, tss high
|
||||
static GDTR s_gdtr;
|
||||
|
||||
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
|
||||
{
|
||||
SegmentDescriptor& desc = *(SegmentDescriptor*)((uintptr_t)s_gdt + offset);
|
||||
desc.base1 = base;
|
||||
desc.base2 = base >> 16;
|
||||
desc.base3 = base >> 24;
|
||||
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
|
||||
|
||||
desc.limit1 = limit;
|
||||
desc.limit2 = limit >> 16;
|
||||
SegmentDescriptor& desc = s_gdt[offset / sizeof(SegmentDescriptor)];
|
||||
desc.base1 = (base >> 0) & 0xFFFF;
|
||||
desc.base2 = (base >> 16) & 0xFF;
|
||||
desc.base3 = (base >> 24) & 0xFF;
|
||||
|
||||
desc.access = access;
|
||||
desc.limit1 = (limit >> 0) & 0xFFFF;
|
||||
desc.limit2 = (limit >> 16) & 0x0F;
|
||||
|
||||
desc.flags = flags;
|
||||
desc.access = access & 0xFF;
|
||||
|
||||
desc.flags = flags & 0x0F;
|
||||
}
|
||||
|
||||
static void write_tss(uint8_t offset)
|
||||
static void write_tss()
|
||||
{
|
||||
s_tss = new TaskStateSegment();
|
||||
ASSERT(s_tss);
|
||||
memset(&s_tss, 0x00, sizeof(TaskStateSegment));
|
||||
s_tss.iopb = sizeof(TaskStateSegment);
|
||||
|
||||
memset(s_tss, 0x00, sizeof(TaskStateSegment));
|
||||
s_tss->rsp0 = (uintptr_t)g_boot_stack_top;
|
||||
uint64_t base = (uint64_t)&s_tss;
|
||||
|
||||
uintptr_t base = (uintptr_t)s_tss;
|
||||
write_entry(s_tss_offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
|
||||
|
||||
write_entry(offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
|
||||
SegmentDescriptor& desc = *(SegmentDescriptor*)((uintptr_t)s_gdt + offset + 0x08);
|
||||
SegmentDescriptor& desc = s_gdt[s_tss_offset / sizeof(SegmentDescriptor) + 1];
|
||||
desc.low = base >> 32;
|
||||
desc.high = 0;
|
||||
}
|
||||
|
||||
void set_tss_stack(uintptr_t rsp)
|
||||
{
|
||||
s_tss.rsp0 = rsp;
|
||||
}
|
||||
|
||||
static void flush_gdt()
|
||||
{
|
||||
asm volatile("lgdt %0" :: "m"(s_gdtr));
|
||||
}
|
||||
|
||||
extern "C" void flush_tss(uint16_t offset)
|
||||
static void flush_tss()
|
||||
{
|
||||
asm volatile("ltr %0" :: "m"(offset));
|
||||
asm volatile("ltr %0" :: "m"(s_tss_offset));
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
constexpr uint32_t descriptor_count = 6 + 1; // tss takes 2
|
||||
s_gdt = new SegmentDescriptor[descriptor_count];
|
||||
ASSERT(s_gdt);
|
||||
|
||||
s_gdtr.address = (uint64_t)s_gdt;
|
||||
s_gdtr.size = descriptor_count * sizeof(SegmentDescriptor) - 1;
|
||||
s_gdtr.address = (uint64_t)&s_gdt;
|
||||
s_gdtr.size = s_gdt.size() * sizeof(SegmentDescriptor) - 1;
|
||||
|
||||
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
|
||||
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
|
||||
write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
|
||||
write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xA); // user code
|
||||
write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
|
||||
write_tss(0x28);
|
||||
write_tss();
|
||||
|
||||
flush_gdt();
|
||||
flush_tss(0x28);
|
||||
flush_tss();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/Errors.h>
|
||||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/kmalloc.h>
|
||||
#include <kernel/InterruptStack.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
#include <kernel/Panic.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/Scheduler.h>
|
||||
#include <kernel/Timer/PIT.h>
|
||||
|
||||
#define REGISTER_ISR_HANDLER(i) register_interrupt_handler(i, isr ## i)
|
||||
#define REGISTER_IRQ_HANDLER(i) register_interrupt_handler(IRQ_VECTOR_BASE + i, irq ## i)
|
||||
#define ISR_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
|
||||
#define IRQ_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
|
||||
|
||||
namespace IDT
|
||||
namespace Kernel::IDT
|
||||
{
|
||||
|
||||
struct Registers
|
||||
@@ -57,7 +62,68 @@ namespace IDT
|
||||
static IDTR s_idtr;
|
||||
static GateDescriptor* s_idt = nullptr;
|
||||
|
||||
static void(*s_irq_handlers[0x10])() { nullptr };
|
||||
#define X(num) 1 +
|
||||
static BAN::Array<Interruptable*, IRQ_LIST_X 0> s_interruptables;
|
||||
#undef X
|
||||
|
||||
enum ISR
|
||||
{
|
||||
DivisionError,
|
||||
Debug,
|
||||
NonMaskableInterrupt,
|
||||
Breakpoint,
|
||||
Overflow,
|
||||
BoundRangeException,
|
||||
InvalidOpcode,
|
||||
DeviceNotAvailable,
|
||||
DoubleFault,
|
||||
CoprocessorSegmentOverrun,
|
||||
InvalidTSS,
|
||||
SegmentNotPresent,
|
||||
StackSegmentFault,
|
||||
GeneralProtectionFault,
|
||||
PageFault,
|
||||
UnknownException0x0F,
|
||||
x87FloatingPointException,
|
||||
AlignmentCheck,
|
||||
MachineCheck,
|
||||
SIMDFloatingPointException,
|
||||
VirtualizationException,
|
||||
ControlProtectionException,
|
||||
UnknownException0x16,
|
||||
UnknownException0x17,
|
||||
UnknownException0x18,
|
||||
UnknownException0x19,
|
||||
UnknownException0x1A,
|
||||
UnknownException0x1B,
|
||||
HypervisorInjectionException,
|
||||
VMMCommunicationException,
|
||||
SecurityException,
|
||||
UnkownException0x1F,
|
||||
};
|
||||
|
||||
struct PageFaultError
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t raw;
|
||||
struct
|
||||
{
|
||||
uint32_t present : 1;
|
||||
uint32_t write : 1;
|
||||
uint32_t userspace : 1;
|
||||
uint32_t reserved_write : 1;
|
||||
uint32_t instruction : 1;
|
||||
uint32_t protection_key : 1;
|
||||
uint32_t shadow_stack : 1;
|
||||
uint32_t reserved1 : 8;
|
||||
uint32_t sgx_violation : 1;
|
||||
uint32_t reserved2 : 16;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
static_assert(sizeof(PageFaultError) == 4);
|
||||
|
||||
static const char* isr_exceptions[] =
|
||||
{
|
||||
@@ -95,40 +161,181 @@ namespace IDT
|
||||
"Unkown Exception 0x1F",
|
||||
};
|
||||
|
||||
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, const Registers* regs)
|
||||
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs)
|
||||
{
|
||||
Kernel::panic(
|
||||
"{} (error code: 0x{16H})\r\n"
|
||||
#if __enable_sse
|
||||
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
|
||||
if (from_userspace)
|
||||
Thread::current().save_sse();
|
||||
#endif
|
||||
|
||||
pid_t tid = Scheduler::current_tid();
|
||||
pid_t pid = tid ? Process::current().pid() : 0;
|
||||
|
||||
if (tid)
|
||||
{
|
||||
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||
|
||||
if (isr == ISR::PageFault)
|
||||
{
|
||||
// Check if stack is OOB
|
||||
auto& stack = Thread::current().stack();
|
||||
auto& istack = Thread::current().interrupt_stack();
|
||||
if (stack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= stack.vaddr() + stack.size())
|
||||
; // using normal stack
|
||||
else if (istack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= istack.vaddr() + istack.size())
|
||||
; // using interrupt stack
|
||||
else
|
||||
{
|
||||
derrorln("Stack pointer out of bounds!");
|
||||
derrorln("rsp {H}, stack {H}->{H}, istack {H}->{H}",
|
||||
interrupt_stack.rsp,
|
||||
stack.vaddr(), stack.vaddr() + stack.size(),
|
||||
istack.vaddr(), istack.vaddr() + istack.size()
|
||||
);
|
||||
Thread::current().handle_signal(SIGKILL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Try demand paging on non present pages
|
||||
PageFaultError page_fault_error;
|
||||
page_fault_error.raw = error;
|
||||
if (!page_fault_error.present)
|
||||
{
|
||||
asm volatile("sti");
|
||||
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2);
|
||||
asm volatile("cli");
|
||||
|
||||
if (!result.is_error() && result.value())
|
||||
goto done;
|
||||
|
||||
if (result.is_error())
|
||||
{
|
||||
dwarnln("Demand paging: {}", result.error());
|
||||
Thread::current().handle_signal(SIGKILL);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & PageTable::Flags::Present)
|
||||
{
|
||||
auto* machine_code = (const uint8_t*)interrupt_stack.rip;
|
||||
dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
|
||||
machine_code[0],
|
||||
machine_code[1],
|
||||
machine_code[2],
|
||||
machine_code[3],
|
||||
machine_code[4],
|
||||
machine_code[5],
|
||||
machine_code[6],
|
||||
machine_code[7]
|
||||
);
|
||||
}
|
||||
|
||||
dwarnln(
|
||||
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
|
||||
"Register dump\r\n"
|
||||
"rax=0x{16H}, rbx=0x{16H}, rcx=0x{16H}, rdx=0x{16H}\r\n"
|
||||
"rsp=0x{16H}, rbp=0x{16H}, rdi=0x{16H}, rsi=0x{16H}\r\n"
|
||||
"rip=0x{16H}, rflags=0x{16H}\r\n"
|
||||
"cr0=0x{16H}, cr2=0x{16H}, cr3=0x{16H}, cr4=0x{16H}\r\n",
|
||||
isr_exceptions[isr], error,
|
||||
"cr0=0x{16H}, cr2=0x{16H}, cr3=0x{16H}, cr4=0x{16H}",
|
||||
isr_exceptions[isr], error, pid, tid,
|
||||
regs->rax, regs->rbx, regs->rcx, regs->rdx,
|
||||
regs->rsp, regs->rbp, regs->rdi, regs->rsi,
|
||||
regs->rip, regs->rflags,
|
||||
regs->cr0, regs->cr2, regs->cr3, regs->cr4
|
||||
);
|
||||
}
|
||||
if (isr == ISR::PageFault)
|
||||
PageTable::current().debug_dump();
|
||||
Debug::dump_stack_trace();
|
||||
|
||||
extern "C" void cpp_irq_handler(uint64_t irq)
|
||||
{
|
||||
if (s_irq_handlers[irq])
|
||||
s_irq_handlers[irq]();
|
||||
if (tid && Thread::current().is_userspace())
|
||||
{
|
||||
// TODO: Confirm and fix the exception to signal mappings
|
||||
|
||||
int signal = 0;
|
||||
switch (isr)
|
||||
{
|
||||
case ISR::DeviceNotAvailable:
|
||||
case ISR::DivisionError:
|
||||
case ISR::SIMDFloatingPointException:
|
||||
case ISR::x87FloatingPointException:
|
||||
signal = SIGFPE;
|
||||
break;
|
||||
case ISR::AlignmentCheck:
|
||||
signal = SIGBUS;
|
||||
break;
|
||||
case ISR::InvalidOpcode:
|
||||
signal = SIGILL;
|
||||
break;
|
||||
case ISR::PageFault:
|
||||
signal = SIGSEGV;
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unhandled exception");
|
||||
signal = SIGABRT;
|
||||
break;
|
||||
}
|
||||
|
||||
Thread::current().handle_signal(signal);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!InterruptController::get().is_in_service(irq))
|
||||
{
|
||||
dprintln("spurious irq 0x{2H}", irq);
|
||||
return;
|
||||
}
|
||||
dprintln("no handler for irq 0x{2H}\n", irq);
|
||||
panic("Unhandled exception");
|
||||
}
|
||||
|
||||
// NOTE: Scheduler sends PIT eoi's
|
||||
if (irq != PIT_IRQ)
|
||||
ASSERT(Thread::current().state() != Thread::State::Terminated);
|
||||
|
||||
done:
|
||||
#if __enable_sse
|
||||
if (from_userspace)
|
||||
{
|
||||
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||
Thread::current().load_sse();
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
extern "C" void cpp_irq_handler(uint64_t irq, InterruptStack& interrupt_stack)
|
||||
{
|
||||
#if __enable_sse
|
||||
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
|
||||
if (from_userspace)
|
||||
Thread::current().save_sse();
|
||||
#endif
|
||||
|
||||
if (Scheduler::current_tid())
|
||||
{
|
||||
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||
}
|
||||
|
||||
if (!InterruptController::get().is_in_service(irq))
|
||||
dprintln("spurious irq 0x{2H}", irq);
|
||||
else
|
||||
{
|
||||
InterruptController::get().eoi(irq);
|
||||
if (s_interruptables[irq])
|
||||
s_interruptables[irq]->handle_irq();
|
||||
else
|
||||
dprintln("no handler for irq 0x{2H}\n", irq);
|
||||
}
|
||||
|
||||
Scheduler::get().reschedule_if_idling();
|
||||
|
||||
ASSERT(Thread::current().state() != Thread::State::Terminated);
|
||||
|
||||
#if __enable_sse
|
||||
if (from_userspace)
|
||||
{
|
||||
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||
Thread::current().load_sse();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void flush_idt()
|
||||
@@ -154,60 +361,20 @@ namespace IDT
|
||||
s_idt[index].flags = 0xEE;
|
||||
}
|
||||
|
||||
void register_irq_handler(uint8_t irq, void(*handler)())
|
||||
void register_irq_handler(uint8_t irq, Interruptable* interruptable)
|
||||
{
|
||||
s_irq_handlers[irq] = handler;
|
||||
if (irq > s_interruptables.size())
|
||||
Kernel::panic("Trying to assign handler for irq {} while only {} are supported", irq, s_interruptables.size());
|
||||
s_interruptables[irq] = interruptable;
|
||||
}
|
||||
|
||||
extern "C" void isr0();
|
||||
extern "C" void isr1();
|
||||
extern "C" void isr2();
|
||||
extern "C" void isr3();
|
||||
extern "C" void isr4();
|
||||
extern "C" void isr5();
|
||||
extern "C" void isr6();
|
||||
extern "C" void isr7();
|
||||
extern "C" void isr8();
|
||||
extern "C" void isr9();
|
||||
extern "C" void isr10();
|
||||
extern "C" void isr11();
|
||||
extern "C" void isr12();
|
||||
extern "C" void isr13();
|
||||
extern "C" void isr14();
|
||||
extern "C" void isr15();
|
||||
extern "C" void isr16();
|
||||
extern "C" void isr17();
|
||||
extern "C" void isr18();
|
||||
extern "C" void isr19();
|
||||
extern "C" void isr20();
|
||||
extern "C" void isr21();
|
||||
extern "C" void isr22();
|
||||
extern "C" void isr23();
|
||||
extern "C" void isr24();
|
||||
extern "C" void isr25();
|
||||
extern "C" void isr26();
|
||||
extern "C" void isr27();
|
||||
extern "C" void isr28();
|
||||
extern "C" void isr29();
|
||||
extern "C" void isr30();
|
||||
extern "C" void isr31();
|
||||
#define X(num) extern "C" void isr ## num();
|
||||
ISR_LIST_X
|
||||
#undef X
|
||||
|
||||
extern "C" void irq0();
|
||||
extern "C" void irq1();
|
||||
extern "C" void irq2();
|
||||
extern "C" void irq3();
|
||||
extern "C" void irq4();
|
||||
extern "C" void irq5();
|
||||
extern "C" void irq6();
|
||||
extern "C" void irq7();
|
||||
extern "C" void irq8();
|
||||
extern "C" void irq9();
|
||||
extern "C" void irq10();
|
||||
extern "C" void irq11();
|
||||
extern "C" void irq12();
|
||||
extern "C" void irq13();
|
||||
extern "C" void irq14();
|
||||
extern "C" void irq15();
|
||||
#define X(num) extern "C" void irq ## num();
|
||||
IRQ_LIST_X
|
||||
#undef X
|
||||
|
||||
extern "C" void syscall_asm();
|
||||
|
||||
@@ -220,59 +387,27 @@ namespace IDT
|
||||
s_idtr.offset = (uint64_t)s_idt;
|
||||
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
|
||||
|
||||
REGISTER_ISR_HANDLER(0);
|
||||
REGISTER_ISR_HANDLER(1);
|
||||
REGISTER_ISR_HANDLER(2);
|
||||
REGISTER_ISR_HANDLER(3);
|
||||
REGISTER_ISR_HANDLER(4);
|
||||
REGISTER_ISR_HANDLER(5);
|
||||
REGISTER_ISR_HANDLER(6);
|
||||
REGISTER_ISR_HANDLER(7);
|
||||
REGISTER_ISR_HANDLER(8);
|
||||
REGISTER_ISR_HANDLER(9);
|
||||
REGISTER_ISR_HANDLER(10);
|
||||
REGISTER_ISR_HANDLER(11);
|
||||
REGISTER_ISR_HANDLER(12);
|
||||
REGISTER_ISR_HANDLER(13);
|
||||
REGISTER_ISR_HANDLER(14);
|
||||
REGISTER_ISR_HANDLER(15);
|
||||
REGISTER_ISR_HANDLER(16);
|
||||
REGISTER_ISR_HANDLER(17);
|
||||
REGISTER_ISR_HANDLER(18);
|
||||
REGISTER_ISR_HANDLER(19);
|
||||
REGISTER_ISR_HANDLER(20);
|
||||
REGISTER_ISR_HANDLER(21);
|
||||
REGISTER_ISR_HANDLER(22);
|
||||
REGISTER_ISR_HANDLER(23);
|
||||
REGISTER_ISR_HANDLER(24);
|
||||
REGISTER_ISR_HANDLER(25);
|
||||
REGISTER_ISR_HANDLER(26);
|
||||
REGISTER_ISR_HANDLER(27);
|
||||
REGISTER_ISR_HANDLER(28);
|
||||
REGISTER_ISR_HANDLER(29);
|
||||
REGISTER_ISR_HANDLER(30);
|
||||
REGISTER_ISR_HANDLER(31);
|
||||
#define X(num) register_interrupt_handler(num, isr ## num);
|
||||
ISR_LIST_X
|
||||
#undef X
|
||||
|
||||
REGISTER_IRQ_HANDLER(0);
|
||||
REGISTER_IRQ_HANDLER(1);
|
||||
REGISTER_IRQ_HANDLER(2);
|
||||
REGISTER_IRQ_HANDLER(3);
|
||||
REGISTER_IRQ_HANDLER(4);
|
||||
REGISTER_IRQ_HANDLER(5);
|
||||
REGISTER_IRQ_HANDLER(6);
|
||||
REGISTER_IRQ_HANDLER(7);
|
||||
REGISTER_IRQ_HANDLER(8);
|
||||
REGISTER_IRQ_HANDLER(9);
|
||||
REGISTER_IRQ_HANDLER(10);
|
||||
REGISTER_IRQ_HANDLER(11);
|
||||
REGISTER_IRQ_HANDLER(12);
|
||||
REGISTER_IRQ_HANDLER(13);
|
||||
REGISTER_IRQ_HANDLER(14);
|
||||
REGISTER_IRQ_HANDLER(15);
|
||||
#define X(num) register_interrupt_handler(IRQ_VECTOR_BASE + num, irq ## num);
|
||||
IRQ_LIST_X
|
||||
#undef X
|
||||
|
||||
register_syscall_handler(0x80, syscall_asm);
|
||||
|
||||
flush_idt();
|
||||
}
|
||||
|
||||
[[noreturn]] void force_triple_fault()
|
||||
{
|
||||
// load 0 sized IDT and trigger an interrupt to force triple fault
|
||||
asm volatile("cli");
|
||||
s_idtr.size = 0;
|
||||
flush_idt();
|
||||
asm volatile("int $0x00");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user