Compare commits
885 Commits
Author | SHA1 | Date |
---|---|---|
Sinipelto | 46a31b8efa | |
Bananymous | 85a5e81224 | |
Bananymous | 7d4cdcd1fd | |
Sinipelto | d72db1f81c | |
Sinipelto | 6cfa56dcec | |
Sinipelto | f97922a2b5 | |
Sinipelto | 566724d986 | |
Sinipelto | a7b1810aa2 | |
Sinipelto | a6a5c00763 | |
Bananymous | f2397b775c | |
Bananymous | 8b81406b81 | |
Bananymous | e2515c1109 | |
Bananymous | 5293ae070d | |
Bananymous | 6e2443ca72 | |
Bananymous | a312d75bb2 | |
Bananymous | a554bd0fd8 | |
Bananymous | f0d2a211ea | |
Bananymous | 065eec430e | |
Bananymous | 5f4d81a502 | |
Bananymous | 41065d2f9a | |
Bananymous | 3daf3d53a3 | |
Bananymous | ec56e9c6f1 | |
Bananymous | 07ae1bbf34 | |
Bananymous | 03b80ed113 | |
Bananymous | 407a7b80c5 | |
Bananymous | 8b4f169d0f | |
Bananymous | 6f9b3ab5de | |
Bananymous | aa7a8124ce | |
Bananymous | a9412aa741 | |
Bananymous | 8aab3a62cc | |
Bananymous | b0b39c56ba | |
Bananymous | 055b1a2a1a | |
Bananymous | d99ef11e48 | |
Bananymous | 732eb9da41 | |
Bananymous | 8faad47843 | |
Bananymous | aa4f3046ff | |
Bananymous | b4775fbe75 | |
Bananymous | 8a5753b0fe | |
Bananymous | 1a75262b04 | |
Bananymous | 39801e51da | |
Bananymous | 6e3f176457 | |
Bananymous | 447da99f0b | |
Bananymous | a3a287f5ca | |
Bananymous | c47f6a78bc | |
Bananymous | 430a006acf | |
Bananymous | 845ed66e5e | |
Bananymous | 2191ca46bb | |
Bananymous | cec04a2858 | |
Bananymous | b87351f6d5 | |
Bananymous | 464737fbe9 | |
Bananymous | 8b4f661acb | |
Bananymous | 27963febc0 | |
Bananymous | 6d4b684219 | |
Bananymous | 670c787af3 | |
Bananymous | a0fbf18d3b | |
Bananymous | 1acc0abf2e | |
Bananymous | c20f773c5d | |
Bananymous | a46b2f43d9 | |
Bananymous | a20f8607de | |
Bananymous | af330f7b8e | |
Bananymous | e33b3bcdff | |
Bananymous | 181d139c7d | |
Bananymous | 639fd8804c | |
Bananymous | cbb2c37e00 | |
Bananymous | ab4f033385 | |
Bananymous | 1ed08f62d3 | |
Bananymous | 8164c15b6c | |
Bananymous | f9bf47ab30 | |
Bananymous | e5ffadb109 | |
Bananymous | 061d10e635 | |
Bananymous | 6d899aa6ce | |
Bananymous | 120f7329b1 | |
Bananymous | 4f25c20c97 | |
Bananymous | 5e396851f4 | |
Bananymous | a44482639d | |
Bananymous | 3bac19e518 | |
Bananymous | 4dbe15aa0e | |
Bananymous | 1c5985148c | |
Bananymous | ce2461d0e8 | |
Bananymous | 4e785a133c | |
Bananymous | 26c7aee327 | |
Bananymous | 0405461742 | |
Bananymous | 8a10853ba7 | |
Bananymous | 5d34cebeca | |
Bananymous | 3d899d2e44 | |
Bananymous | f72fdeeb59 | |
Bananymous | 382f9d9bb3 | |
Bananymous | bc1d1bf919 | |
Bananymous | f05b9a6877 | |
Bananymous | ea5ed3001e | |
Bananymous | f312c3a4d7 | |
Bananymous | 1af3ca19ab | |
Bananymous | 09fcc613c7 | |
Bananymous | 3940f53231 | |
Bananymous | 0757834176 | |
Bananymous | 3bffbe330d | |
Bananymous | 8d583c8b67 | |
Bananymous | 99bde9aa49 | |
Bananymous | 98d702ac60 | |
Bananymous | 1ec341e2dd | |
Bananymous | d09310f388 | |
Bananymous | 126edea119 | |
Bananymous | 74bfb930f2 | |
Bananymous | 091c5b6a66 | |
Bananymous | fda4a4ad24 | |
Bananymous | 8bb47aee02 | |
Bananymous | 1f794e4ac0 | |
Bananymous | e85f9ac6a1 | |
Bananymous | 6ee5576dcc | |
Bananymous | b890e2fc14 | |
Bananymous | 4f4b8ada8c | |
Bananymous | 9e4adc1264 | |
Bananymous | 7a54a088b4 | |
Bananymous | 15bb1804ef | |
Bananymous | e8890062d6 | |
Bananymous | 1e2c2fb973 | |
Bananymous | 988a4e1cd8 | |
Bananymous | adbbdf73c4 | |
Bananymous | e8d20bc653 | |
Bananymous | 00ee86920a | |
Bananymous | 51ad27ea3c | |
Bananymous | df69612bb1 | |
Bananymous | 5bfeb9f3ca | |
Bananymous | db5c24b2a5 | |
Bananymous | 781c950af6 | |
Bananymous | e2e5c31d54 | |
Bananymous | be3efb0b92 | |
Bananymous | 792bb2df1c | |
Bananymous | e01928d186 | |
Bananymous | 48980b56ab | |
Bananymous | b767317a7a | |
Bananymous | 6f8fce94a0 | |
Bananymous | 31aa157201 | |
Bananymous | 5977341610 | |
Bananymous | 76f17bd569 | |
Bananymous | 6b1b3d333c | |
Bananymous | cb65be3e33 | |
Bananymous | dafc016293 | |
Bananymous | c7b6fc950a | |
Bananymous | 45a6783c3d | |
Bananymous | 6b180da4e8 | |
Bananymous | cf4f5f64a5 | |
Bananymous | 5630f64175 | |
Bananymous | 1d61bccfc3 | |
Bananymous | f842a9255f | |
Bananymous | 72f3c378dd | |
Bananymous | 39be6ab099 | |
Bananymous | 773dcdd3a2 | |
Bananymous | f0820e6f24 | |
Bananymous | a2b5e71654 | |
Bananymous | d3e5c8e0aa | |
Bananymous | f4b901a646 | |
Bananymous | 790064d248 | |
Bananymous | ab8b77406d | |
Bananymous | 1b9e14a53b | |
Bananymous | d2cfc843e4 | |
Bananymous | 521513bed2 | |
Bananymous | 400db176d1 | |
Bananymous | 211cad03ff | |
Bananymous | 8a9816d6e0 | |
Bananymous | 03d2bf4002 | |
Bananymous | f071240b33 | |
Bananymous | 27364f64a6 | |
Bananymous | bcf62c5f2e | |
Bananymous | 4d6322ff9c | |
Bananymous | 2eef581737 | |
Bananymous | 7ce8e2d57b | |
Bananymous | e780eaa45f | |
Bananymous | 44cb0af64f | |
Bananymous | bb0989fdef | |
Bananymous | f0b6844feb | |
Bananymous | b712c70c75 | |
Bananymous | 797ca65c66 | |
Bananymous | 762b7a4276 | |
Bananymous | a511441f7e | |
Bananymous | cd61d710df | |
Bananymous | f88ad7efcd | |
Bananymous | 38320018dc | |
Bananymous | d883d212b1 | |
Bananymous | dedb2a2399 | |
Bananymous | 8604c55de8 | |
Bananymous | e949e8550c | |
Bananymous | eb5c6cf736 | |
Bananymous | 94ce2c97be | |
Bananymous | 3f164c6b82 | |
Bananymous | f953f3d3ff | |
Bananymous | 9fc75fe445 | |
Bananymous | 7a5bb6a56b | |
Bananymous | d54c6b7f6b | |
Bananymous | db5d6a7f80 | |
Bananymous | 4a92f44cf6 | |
Bananymous | 376b9f7272 | |
Bananymous | 7e9e4c47ae | |
Bananymous | 603fc200e6 | |
Bananymous | c11e84b248 | |
Bananymous | be13120554 | |
Bananymous | 9943edad5a | |
Bananymous | f4049be975 | |
Bananymous | 6cf7e01fe9 | |
Bananymous | b51d2f5295 | |
Bananymous | 49d941ad65 | |
Bananymous | a66c3bdae5 | |
Bananymous | 547eabb403 | |
Bananymous | 79851394b3 | |
Bananymous | fcdc922343 | |
Bananymous | 0b11d76576 | |
Bananymous | f7097398ca | |
Bananymous | 85b1252b9e | |
Bananymous | 1cd12b5f16 | |
Bananymous | c84b66d078 | |
Bananymous | 27adb9486b | |
Bananymous | 8d5369fafe | |
Bananymous | feafc57b63 | |
Bananymous | f9b347f9d9 | |
Bananymous | 6e1825d6b4 | |
Bananymous | ec2baeb276 | |
Bananymous | 6cb8bda6e1 | |
Bananymous | 05e57801e7 | |
Bananymous | b924c85669 | |
Bananymous | 09c1aa44d8 | |
Bananymous | 1d470fb5ba | |
Bananymous | b4e4f7a6cc | |
Bananymous | 55d30a7cc3 | |
Bananymous | b62186441b | |
Bananymous | 8caba1e774 | |
Bananymous | 7bdb428938 | |
Bananymous | 3ea707c0e7 | |
Bananymous | 18d582c6ce | |
Bananymous | 8b2bb95b81 | |
Bananymous | 2ef496a24a | |
Bananymous | c0a89e8951 | |
Bananymous | fc953df281 | |
Bananymous | fe2dca16f0 | |
Bananymous | f662aa6da2 | |
Bananymous | fee3677fb9 | |
Bananymous | 4818c6e3dd | |
Bananymous | 971eb737c1 | |
Bananymous | 9a3286ad57 | |
Bananymous | c9e09b840e | |
Bananymous | 8136248a67 | |
Bananymous | 0d67e46041 | |
Bananymous | bc1087f5a7 | |
Bananymous | 3a9c6fc51a | |
Bananymous | 7774f56ab6 | |
Bananymous | 14a608effd | |
Bananymous | 5fae3cec2a | |
Bananymous | b0c22b61ec | |
Bananymous | 82b049204d | |
Bananymous | aa59142bfa | |
Bananymous | c55bb77ff5 | |
Bananymous | 9b4e2e1e21 | |
Bananymous | 202c38a65a | |
Bananymous | 720bc418a6 | |
Bananymous | d77f455065 | |
Bananymous | 7e08f0fb66 | |
Bananymous | 9e4a87021c | |
Bananymous | 5887f6bcaa | |
Bananymous | 5d67559e33 | |
Bananymous | e631eb7a7a | |
Bananymous | 64ff4c232a | |
Bananymous | 0ec4f970f7 | |
Bananymous | afe95be42f | |
Bananymous | 14ac1c9904 | |
Bananymous | 7c11ea3694 | |
Bananymous | c1fd341698 | |
Bananymous | 0deab1be51 | |
Bananymous | 5a623adaa6 | |
Bananymous | 4363118d9d | |
Bananymous | d613da4b6c | |
Bananymous | f46f5b2050 | |
Bananymous | d9c4114b5f | |
Bananymous | ddd36af0f1 | |
Bananymous | 35fd30ee29 | |
Bananymous | 4a0652684c | |
Bananymous | 33c81f00b7 | |
Bananymous | 55714b90cd | |
Bananymous | 9b47603a1d | |
Bananymous | 2e2a913412 | |
Bananymous | 42b85dc33b | |
Bananymous | a15ffcb071 | |
Bananymous | 692b77fb8e | |
Bananymous | 044378cfa3 | |
Bananymous | f1d4d5f995 | |
Bananymous | 19d0fb6fcd | |
Bananymous | 7933265095 | |
Bananymous | d810644018 | |
Bananymous | a7bfc1c2ec | |
Bananymous | 104b2740bc | |
Bananymous | 65501837b7 | |
Bananymous | 3ed0a54847 | |
Bananymous | cbd2519b9a | |
Bananymous | e8a73f9696 | |
Bananymous | 1a0d114861 | |
Bananymous | 5c3497681e | |
Bananymous | b05cf9ef09 | |
Bananymous | a74eb357a1 | |
Bananymous | 8eb71084f0 | |
Bananymous | ef1077fd7b | |
Bananymous | f1ba5c7e0f | |
Bananymous | 97ea4986af | |
Bananymous | 25c021c15b | |
Bananymous | 2bf12a52d1 | |
Bananymous | 6ada36d3cb | |
Bananymous | 42b90ae76c | |
Bananymous | ccc61cb10c | |
Bananymous | be5b81740e | |
Bananymous | e7458ca10a | |
Bananymous | b10168eb1c | |
Bananymous | 30463c9082 | |
Bananymous | dc7391dc89 | |
Bananymous | eb98d70a0b | |
Bananymous | dcd8374b89 | |
Bananymous | 8e4216215e | |
Bananymous | 5bd7099b96 | |
Bananymous | 9a63d3b2da | |
Bananymous | bf02330db9 | |
Bananymous | 5806a6484f | |
Bananymous | 0fa5401800 | |
Bananymous | b30f4cbfb5 | |
Bananymous | ba37183c9c | |
Bananymous | 2f298a1979 | |
Bananymous | 8c282a5d83 | |
Bananymous | d34c0a5abe | |
Bananymous | 8f3348cf2b | |
Bananymous | 38c0bc7bae | |
Bananymous | 313b00b11f | |
Bananymous | 165a379c73 | |
Bananymous | a7f37236bf | |
Bananymous | 51532336b0 | |
Bananymous | 03d4b47f63 | |
Bananymous | 8b57edde6b | |
Bananymous | 778778fede | |
Bananymous | f7449c4ab9 | |
Bananymous | fd2bcc9156 | |
Bananymous | a5b1555725 | |
Bananymous | e74fdbc55b | |
Bananymous | 008c777a9f | |
Bananymous | d8a9d4a24e | |
Bananymous | bc0e1fa898 | |
Bananymous | 17f1737c9a | |
Bananymous | 868444f043 | |
Bananymous | fdae253695 | |
Bananymous | d4adcff958 | |
Bananymous | 2c59c9a3cc | |
Bananymous | 3a59a64355 | |
Bananymous | 9363c1cdaf | |
Bananymous | 198e6d7cf6 | |
Bananymous | 07ee898f4f | |
Bananymous | 6feb8a99d2 | |
Bananymous | e57c1fc9fc | |
Bananymous | a11b5ae41f | |
Bananymous | c67a7cec5b | |
Bananymous | 91f04ce250 | |
Bananymous | 926df2b276 | |
Bananymous | 9fe878bbec | |
Bananymous | 217dbca7b7 | |
Bananymous | 13852e865c | |
Bananymous | 679d47131d | |
Bananymous | 8b1bccb79b | |
Bananymous | e86e755c51 | |
Bananymous | 8ec6d4c9fc | |
Bananymous | 08cdf88586 | |
Bananymous | 6f7d97cf94 | |
Bananymous | 5e434f5131 | |
Bananymous | a152d0aac5 | |
Bananymous | 879706e6e9 | |
Bananymous | 00f1f30a08 | |
Bananymous | a5813f9ba5 | |
Bananymous | 5652af3384 | |
Bananymous | 22cd9af8cc | |
Bananymous | a9cf9bceef | |
Bananymous | 6c0f864a6e | |
Bananymous | e4509d9482 | |
Bananymous | 0f23e1f0f4 | |
Bananymous | 642a6aa4ad | |
Bananymous | 432c296b7b | |
Bananymous | b576d373c4 | |
Bananymous | 522aa8e762 | |
Bananymous | 146802fa4c | |
Bananymous | cc8af25d73 | |
Bananymous | f5f4bf58ad | |
Bananymous | 3784da0d18 | |
Bananymous | 9eb72f4392 | |
Bananymous | f7bf6d5e62 | |
Bananymous | adb14ba373 | |
Bananymous | 7391d91317 | |
Bananymous | 2149cec29f | |
Bananymous | ad756c36fc | |
Bananymous | b56316e9da | |
Bananymous | a989c44211 | |
Bananymous | 217e5f81cc | |
Bananymous | 5f2549b198 | |
Bananymous | dcd4d0daeb | |
Bananymous | faf4220b38 | |
Bananymous | 193ddaa2f6 | |
Bananymous | 46eb27883a | |
Bananymous | 2db7cdb71e | |
Bananymous | 5411c5aa4a | |
Bananymous | f8a1a10897 | |
Bananymous | adbe13938e | |
Bananymous | 4d5b14753d | |
Bananymous | ba9fa00947 | |
Bananymous | 98cedf155c | |
Bananymous | 88e3998664 | |
Bananymous | c0c0bbc1bf | |
Bananymous | 650e1b4fc5 | |
Bananymous | 6c1ada8d0a | |
Bananymous | 7d00c2670f | |
Bananymous | bca7e9a1e8 | |
Bananymous | 3748f0304f | |
Bananymous | 2576bdbd14 | |
Bananymous | e341a36287 | |
Bananymous | bba09a3cd0 | |
Bananymous | 985df3532b | |
Bananymous | 72041a52e8 | |
Bananymous | 891144dac1 | |
Bananymous | 41e7b53903 | |
Bananymous | 6b0920e8c0 | |
Bananymous | 4285729d5c | |
Bananymous | a9c10d0751 | |
Bananymous | 74c79c7eff | |
Bananymous | 9174a89971 | |
Bananymous | 5c94a583bc | |
Bananymous | 6e1fc2766f | |
Bananymous | d3bb00cb55 | |
Bananymous | 5a5656b2d3 | |
Bananymous | 1a1e584cba | |
Bananymous | 65fa05f998 | |
Bananymous | 2276fc95b8 | |
Bananymous | 1e173c178d | |
Bananymous | 773747cf9c | |
Bananymous | 4972284dde | |
Bananymous | 45789fda08 | |
Bananymous | 3b5bc63d1b | |
Bananymous | f1089e2b8a | |
Bananymous | 6d93c1eb92 | |
Bananymous | 363c325c79 | |
Bananymous | 583504ebe0 | |
Bananymous | b354b77f8b | |
Bananymous | 74af46cb4a | |
Bananymous | 19dab08275 | |
Bananymous | 3840fbf957 | |
Bananymous | 78c091f7f8 | |
Bananymous | 274ecbba78 | |
Bananymous | 683c2a68cd | |
Bananymous | ad98181069 | |
Bananymous | a549336530 | |
Bananymous | 4eb95c963d | |
Bananymous | 22caacd2a9 | |
Bananymous | af30d537da | |
Bananymous | f1bd26fb92 | |
Bananymous | 5c6bbcb62f | |
Bananymous | 21bd87bb07 | |
Bananymous | 79450df04c | |
Bananymous | 7f8b7b811e | |
Bananymous | 3c068aa0ae | |
Bananymous | 86df258365 | |
Bananymous | d99e704728 | |
Bananymous | 0d620f3e0f | |
Bananymous | 4dce0f9074 | |
Bananymous | 54f89cba33 | |
Bananymous | de88f60d1a | |
Bananymous | f7060970e6 | |
Bananymous | e7a98ac6cc | |
Bananymous | 10544db52e | |
Bananymous | 5e123031aa | |
Bananymous | 388f530edd | |
Bananymous | d354cccd37 | |
Bananymous | 714305ef56 | |
Bananymous | f83ae1e9c6 | |
Bananymous | c38e8de6b5 | |
Bananymous | 97638f7ade | |
Bananymous | 326a30d1af | |
Bananymous | 32e1473c94 | |
Bananymous | bf617036c7 | |
Bananymous | ce55422a24 | |
Bananymous | 388cc7c3bb | |
Bananymous | 37f9404d93 | |
Bananymous | 38dff41e25 | |
Bananymous | d360340b9e | |
Bananymous | 0f63cfa43f | |
Bananymous | 537780ee1e | |
Bananymous | 4ca99fcb4e | |
Bananymous | eb7ee13f43 | |
Bananymous | b2de706693 | |
Bananymous | 6a8180470d | |
Bananymous | 12d56be5cc | |
Bananymous | bb4d81a4fa | |
Bananymous | b254ade69b | |
Bananymous | ef4ebaa969 | |
Bananymous | 99f8133b91 | |
Bananymous | 51eb44bf40 | |
Bananymous | a0be415e09 | |
Bananymous | 071da18fa3 | |
Bananymous | c62e820bcf | |
Bananymous | 46c34db6cb | |
Bananymous | 25a2a4879c | |
Bananymous | 8be28012ee | |
Bananymous | 5aed186827 | |
Bananymous | 91f812e17f | |
Bananymous | f0b22c48b2 | |
Bananymous | 52c4eebd77 | |
Bananymous | 24f0d26fce | |
Bananymous | 825ec221b7 | |
Bananymous | e31080bce3 | |
Bananymous | 7a5d5cabad | |
Bananymous | f7de310889 | |
Bananymous | e209ca7c82 | |
Bananymous | ee8de77a90 | |
Bananymous | db49cbd6e2 | |
Bananymous | e001eecb7b | |
Bananymous | 7f34d00c95 | |
Bananymous | 2c18adbddd | |
Bananymous | 97c7fc42d1 | |
Bananymous | 7da0627f8e | |
Bananymous | 27cef23823 | |
Bananymous | b7fc2dc3d0 | |
Bananymous | 8af390e0f6 | |
Bananymous | 96d6453ea8 | |
Bananymous | 2b9900e56e | |
Bananymous | 86f58f60cb | |
Bananymous | 064ce568c2 | |
Bananymous | 6aff459e1c | |
Bananymous | 0b1b4d8f7e | |
Bananymous | 3fc2c3529a | |
Bananymous | b0e9ab0519 | |
Bananymous | 668517a723 | |
Bananymous | 649f08ec78 | |
Bananymous | 2f2c298c68 | |
Bananymous | 90e48970e6 | |
Bananymous | 480842a203 | |
Bananymous | 5425394880 | |
Bananymous | a365813fa9 | |
Bananymous | 9d64dbd5c2 | |
Bananymous | 30bb61a775 | |
Bananymous | 1f36ed0cf9 | |
Bananymous | d54c76f88a | |
Bananymous | cbb9f47ee5 | |
Bananymous | b68d5a5833 | |
Bananymous | 94d2090777 | |
Bananymous | e97585daf9 | |
Bananymous | 924fc2118c | |
Bananymous | 51f4c0c750 | |
Bananymous | 37b93da650 | |
Bananymous | 35e739dcdd | |
Bananymous | 8352392b38 | |
Bananymous | 413f05bfca | |
Bananymous | dc1aff58ed | |
Bananymous | 9f75d9cfe5 | |
Bananymous | a42af7e973 | |
Bananymous | 2ce244d303 | |
Bananymous | a775a920d0 | |
Bananymous | 4f84faf392 | |
Bananymous | a4cb5d8360 | |
Bananymous | da7f09cf82 | |
Bananymous | 0166af472b | |
Bananymous | 884d986bd6 | |
Bananymous | 59b807189f | |
Bananymous | fb1c7015b1 | |
Bananymous | d4123f62b2 | |
Bananymous | a3f410d1a1 | |
Bananymous | 1d19a4bffe | |
Bananymous | 271dd91292 | |
Bananymous | 9bd4d68f9c | |
Bananymous | 3c3c7826ef | |
Bananymous | 2207357b93 | |
Bananymous | 3a69768eb0 | |
Bananymous | afb29ff3ec | |
Bananymous | e6f0f891a6 | |
Bananymous | 36e5aa4683 | |
Bananymous | 7738050105 | |
Bananymous | 4bf11ec349 | |
Bananymous | d821012eed | |
Bananymous | 35c6edd989 | |
Bananymous | 633cb4f282 | |
Bananymous | 4d4d0e26a9 | |
Bananymous | feea2d4024 | |
Bananymous | 0ffd2a5c1d | |
Bananymous | 232fdcb82c | |
Bananymous | 0ccc23d544 | |
Bananymous | 789ca3db1a | |
Bananymous | cb359a05dc | |
Bananymous | 14982c137a | |
Bananymous | 0acab11620 | |
Bananymous | 02f0239016 | |
Bananymous | ab61b49aca | |
Bananymous | 4da1d6fd27 | |
Bananymous | 909e847369 | |
Bananymous | eafa09fecf | |
Bananymous | 8175348284 | |
Bananymous | b2832cb47a | |
Bananymous | 9f499991c8 | |
Bananymous | 9a416e8ae8 | |
Bananymous | 911922c6a3 | |
Bananymous | 1f2fd59ad5 | |
Bananymous | 708d401d2c | |
Bananymous | ed0dcacab3 | |
Bananymous | e86050f343 | |
Bananymous | 57f7da6ce1 | |
Bananymous | 93e6455171 | |
Bananymous | 8f38780197 | |
Bananymous | 341f7e41e5 | |
Bananymous | 265fe9c62e | |
Bananymous | 3b9d60d7cb | |
Bananymous | 278b873e89 | |
Bananymous | e640344d7a | |
Bananymous | 7151bb86a8 | |
Bananymous | 2a34391b71 | |
Bananymous | 3d95cf02f3 | |
Bananymous | dd3f34cb2c | |
Bananymous | 0c316ebfb2 | |
Bananymous | 282bf24f65 | |
Bananymous | f964f6be8d | |
Bananymous | 0202ccec5f | |
Bananymous | 636c308993 | |
Bananymous | 6fdbe6f9c2 | |
Bananymous | c19f4c019a | |
Bananymous | 83eb3dc0cb | |
Bananymous | 481c8406f3 | |
Bananymous | 0129619d9a | |
Bananymous | e0479b291d | |
Bananymous | b847d7dfd5 | |
Bananymous | 245dff8027 | |
Bananymous | fed690a7f2 | |
Bananymous | 54d981120d | |
Bananymous | f79250c4d4 | |
Bananymous | 78b62776d2 | |
Bananymous | bda4614783 | |
Bananymous | 0ab3332ad3 | |
Bananymous | 9e0abbc2f0 | |
Bananymous | 496adb61a4 | |
Bananymous | 4a4a3bf184 | |
Bananymous | f33e78882e | |
Bananymous | 0ff067bdb7 | |
Bananymous | 31ac3260ed | |
Bananymous | d82c6c2337 | |
Bananymous | 632b699475 | |
Bananymous | 85039020d3 | |
Bananymous | 1a0fdc5a44 | |
Bananymous | fb1bab7c30 | |
Bananymous | 7eb43990ad | |
Bananymous | 53f4b5a9da | |
Bananymous | 1d4a6c3a42 | |
Bananymous | 40083e4aa1 | |
Bananymous | bd929bff07 | |
Bananymous | cd4a0530fa | |
Bananymous | 273fdd2235 | |
Bananymous | b20f2e8d31 | |
Bananymous | e756cde2b1 | |
Bananymous | de18d3e64d | |
Bananymous | 441999ba9f | |
Bananymous | dd046b1ace | |
Bananymous | 926aa238ab | |
Bananymous | 01fa521a03 | |
Bananymous | f31da19266 | |
Bananymous | 48edc38817 | |
Bananymous | ac12132ac0 | |
Bananymous | 13fabcc1f1 | |
Bananymous | 67005a80be | |
Bananymous | f43bfcb398 | |
Bananymous | d5ce4c9d2c | |
Bananymous | 1cf7ef3de6 | |
Bananymous | 5248a3fe48 | |
Bananymous | 812e61ca70 | |
Bananymous | 2d0a5a9e15 | |
Bananymous | f32d594879 | |
Bananymous | c2ad76fe4f | |
Bananymous | 10d9b72da1 | |
Bananymous | 2fe9af7165 | |
Bananymous | 0deda83d05 | |
Bananymous | ff5bcd4416 | |
Bananymous | b65cd1d09b | |
Bananymous | bc35a561d3 | |
Bananymous | 06bc807e34 | |
Bananymous | 6262e41de1 | |
Bananymous | 0cb53efa01 | |
Bananymous | 4e859bedbc | |
Bananymous | f139fc2229 | |
Bananymous | e48acbb03b | |
Bananymous | d1155c968e | |
Bananymous | 88a2c60065 | |
Bananymous | 5bfcf6783e | |
Bananymous | 94f8a657f1 | |
Bananymous | 7fac2a7526 | |
Bananymous | 46dcf98fc1 | |
Bananymous | 58ce907327 | |
Bananymous | 6ecc8cac0e | |
Bananymous | bd95f17426 | |
Bananymous | 0718bea5a1 | |
Bananymous | 175f07cd2f | |
Bananymous | 7b19d6e479 | |
Bananymous | 77c83e5552 | |
Bananymous | b15deb420f | |
Bananymous | b38989d594 | |
Bananymous | 79e6de325f | |
Bananymous | 163d2e4ba8 | |
Bananymous | 4f8f3ddc29 | |
Bananymous | 82a1a29260 | |
Bananymous | 8a5608df91 | |
Bananymous | 3f1c0ec91b | |
Bananymous | 1406a75a92 | |
Bananymous | 8001493df3 | |
Bananymous | 8c1f5bfe1e | |
Bananymous | ec8b9640e2 | |
Bananymous | 4ae1332a43 | |
Bananymous | 10c884bba4 | |
Bananymous | c15f031c3f | |
Bananymous | 1b4c744974 | |
Bananymous | d9068eebb5 | |
Bananymous | 3ad0d2328d | |
Bananymous | 3f2beb4547 | |
Bananymous | be14a6c239 | |
Bananymous | 3aa0eeb4a3 | |
Bananymous | b3eeb6412f | |
Bananymous | d38470c8e2 | |
Bananymous | a159c980ee | |
Bananymous | a993d997ad | |
Bananymous | 4475e3e184 | |
Bananymous | cf0320e47d | |
Bananymous | cd03a95128 | |
Bananymous | 51e299c7e3 | |
Bananymous | 6f65453fd4 | |
Bananymous | 67e0c21e0f | |
Bananymous | 702016a6e3 | |
Bananymous | d74ce4950c | |
Bananymous | 59a682c720 | |
Bananymous | 7bd4593748 | |
Bananymous | c5b006bf19 | |
Bananymous | 115c44630d | |
Bananymous | 1dc81abca4 | |
Bananymous | 5aaf2128a8 | |
Bananymous | 6aeac17072 | |
Bananymous | 6d425182a2 | |
Bananymous | 04ac23b67c | |
Bananymous | 5494e2c125 | |
Bananymous | aba82564f5 | |
Bananymous | 93abee9c7c | |
Bananymous | 4034bef42e | |
Bananymous | 821d065eba | |
Bananymous | 2614437ba0 | |
Bananymous | 1aac3a0425 | |
Bananymous | a4568f9263 | |
Bananymous | a180e72b6f | |
Bananymous | 2de64b592d | |
Bananymous | 9c0f3dd996 | |
Bananymous | 079df39ca8 | |
Bananymous | 60a99d1d23 | |
Bananymous | fe87c08a02 | |
Bananymous | 8637959289 | |
Bananymous | 6be53668b9 | |
Bananymous | d1b7249803 | |
Bananymous | ff7c50c627 | |
Bananymous | 12779cdef8 | |
Bananymous | f5e676b2b7 | |
Bananymous | 8e5e5f819f | |
Bananymous | 370a958379 | |
Bananymous | 0ee7da92a3 | |
Bananymous | a0bd3dc54f | |
Bananymous | 809eb2fe3e | |
Bananymous | 7010d8614f | |
Bananymous | 69f13f1896 | |
Bananymous | bdaf7cddcb | |
Bananymous | 8d6db168d6 | |
Bananymous | 2fabe1949c | |
Bananymous | c660df14ec | |
Bananymous | e704968f96 | |
Bananymous | 32359df939 | |
Bananymous | 641ed23380 | |
Bananymous | 9f977488fa | |
Bananymous | ac0b22f9b9 | |
Bananymous | 7752b02fb7 | |
Bananymous | 7610670287 | |
Bananymous | 31a1b23fb7 | |
Bananymous | 91c8f9a596 | |
Bananymous | f70cd3ea77 | |
Bananymous | 5db5ff069a | |
Bananymous | b8d852ddb7 | |
Bananymous | 46eedbd1a4 | |
Bananymous | e760bafeeb | |
Bananymous | 12351d5cb6 | |
Bananymous | e84f613c4d | |
Bananymous | 5db4e5b4d5 | |
Bananymous | b00dd8d68d | |
Bananymous | abbbf7ec15 | |
Bananymous | 22c72d8c70 | |
Bananymous | d0b1457f30 | |
Bananymous | a423cd8bb3 | |
Bananymous | db076058b9 | |
Bananymous | fe10ea85db | |
Bananymous | a1100624bf | |
Bananymous | 28e1497f88 | |
Bananymous | 8d6111641e | |
Bananymous | 3ee20d1a84 | |
Bananymous | 002c2d0aca | |
Bananymous | de9f109f2a | |
Bananymous | 461a5774f8 | |
Bananymous | 914f718767 | |
Bananymous | ebfd092075 | |
Bananymous | e322826347 | |
Bananymous | 3998c5f955 | |
Bananymous | 762d22ed28 | |
Bananymous | f2362b2b78 | |
Bananymous | 471ac80420 | |
Bananymous | 4a887fc706 | |
Bananymous | e49d3c7bfe | |
Bananymous | c5b83074ac | |
Bananymous | 79090c2648 | |
Bananymous | 7a6b1c8e47 | |
Bananymous | 8988ce2766 | |
Bananymous | dcde2ae6b4 | |
Bananymous | c62849a783 | |
Bananymous | f453e8e170 | |
Bananymous | 990887891e | |
Bananymous | 5da801d12b | |
Bananymous | 3a4557d417 | |
Bananymous | 26d9a3e253 | |
Bananymous | eef3631a5a | |
Bananymous | 88ee35165f | |
Bananymous | c8f05b4a7a | |
Bananymous | c32584cca0 | |
Bananymous | 2995a36942 | |
Bananymous | c1dbafc101 | |
Bananymous | 3e8ab8271d | |
Bananymous | 5b3a00c64f | |
Bananymous | 0ce9fd8597 | |
Bananymous | c9badb5a1c | |
Bananymous | a513bc5749 | |
Bananymous | 5d5487315f | |
Bananymous | 3508df67b1 | |
Bananymous | 06ce1f0667 | |
Bananymous | f9c3ae7090 | |
Bananymous | 1fb8c211f0 | |
Bananymous | 9c7670847e | |
Bananymous | a24c2d9be2 | |
Bananymous | f4db246658 | |
Bananymous | 7f90079ea7 | |
Bananymous | f4b4987d43 | |
Bananymous | 7f88ba70d4 | |
Bananymous | ac094a48d6 | |
Bananymous | 779912d8af | |
Bananymous | f205b8e883 | |
Bananymous | f9a0412e78 | |
Bananymous | 0ef318633c | |
Bananymous | 2f8c9746e3 | |
Bananymous | 6d6bef1b04 | |
Bananymous | 3dab392296 | |
Bananymous | f8a2c60c8d | |
Bananymous | 770f7716a0 | |
Bananymous | a011c0384f | |
Bananymous | 0d356c5bbc | |
Bananymous | d67de70126 | |
Bananymous | 6f334756c5 | |
Bananymous | 310713d203 | |
Bananymous | 7d2ab53baa | |
Bananymous | 2152b8b95f | |
Bananymous | 8ac1ae1574 | |
Bananymous | 4fd21bc303 | |
Bananymous | 15037bfc7a | |
Bananymous | 5831c72aad | |
Bananymous | a063d041c9 | |
Bananymous | 3572e9794a | |
Bananymous | cef6999dc7 | |
Bananymous | 6ed9651176 | |
Bananymous | 3efbe22a1b | |
Bananymous | 96579b88cf | |
Bananymous | 2ec18855f2 | |
Bananymous | b222581d18 | |
Bananymous | a8e3ee6f19 | |
Bananymous | a083e588ba | |
Bananymous | 9b500842a0 | |
Bananymous | b21348379f | |
Bananymous | 633055293e | |
Bananymous | ae9d618803 | |
Bananymous | 9c744dfc44 | |
Bananymous | faf1b661bb | |
Bananymous | 22e45278a2 | |
Bananymous | 43f4657566 | |
Bananymous | 70f2908056 | |
Bananymous | ef381d0600 | |
Bananymous | cfa87526a7 | |
Bananymous | 39b560fde3 | |
Bananymous | 0c582b4490 | |
Bananymous | 61caf566fc | |
Bananymous | 76d5364a55 | |
Bananymous | 5224df321e |
|
@ -1,7 +1,5 @@
|
||||||
*.img
|
|
||||||
isodir
|
|
||||||
sysroot
|
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
bochsrc
|
build/
|
||||||
bx_enh_dbg.ini
|
base/
|
||||||
|
script/fakeroot-context
|
||||||
|
|
|
@ -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); }
|
|
|
@ -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/String.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/New.h>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
String::String()
|
String::String()
|
||||||
{
|
{
|
||||||
MUST(copy_impl(""sv));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(const String& other)
|
String::String(const String& other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other.sv()));
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(String&& other)
|
String::String(String&& other)
|
||||||
{
|
{
|
||||||
move_impl(move(other));
|
*this = move(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(StringView other)
|
String::String(StringView other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other));
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
String::~String()
|
String::~String()
|
||||||
{
|
{
|
||||||
BAN::deallocator(m_data);
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(const String& other)
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(String&& other)
|
String& String::operator=(String&& other)
|
||||||
{
|
{
|
||||||
BAN::deallocator(m_data);
|
clear();
|
||||||
move_impl(move(other));
|
|
||||||
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(StringView other)
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::push_back(char ch)
|
ErrorOr<void> String::push_back(char c)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(m_size + 2));
|
TRY(ensure_capacity(m_size + 1));
|
||||||
m_data[m_size] = ch;
|
data()[m_size] = c;
|
||||||
m_size++;
|
m_size++;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::insert(char ch, size_type index)
|
ErrorOr<void> String::insert(char c, size_type index)
|
||||||
{
|
{
|
||||||
ASSERT(index <= m_size);
|
ASSERT(index <= m_size);
|
||||||
TRY(ensure_capacity(m_size + 1 + 1));
|
TRY(ensure_capacity(m_size + 1));
|
||||||
memmove(m_data + index + 1, m_data + index, m_size - index);
|
memmove(data() + index + 1, data() + index, m_size - index);
|
||||||
m_data[index] = ch;
|
data()[index] = c;
|
||||||
m_size += 1;
|
m_size++;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::insert(StringView other, size_type index)
|
ErrorOr<void> String::insert(StringView str, size_type index)
|
||||||
{
|
{
|
||||||
ASSERT(index <= m_size);
|
ASSERT(index <= m_size);
|
||||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
memmove(m_data + index + other.size(), m_data + index, m_size - index);
|
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||||
memcpy(m_data + index, other.data(), other.size());
|
memcpy(data() + index, str.data(), str.size());
|
||||||
m_size += other.size();
|
m_size += str.size();
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::append(StringView other)
|
ErrorOr<void> String::append(StringView str)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
memcpy(m_data + m_size, other.data(), other.size());
|
memcpy(data() + m_size, str.data(), str.size());
|
||||||
m_size += other.size();
|
m_size += str.size();
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<void> String::append(const String& string)
|
|
||||||
{
|
|
||||||
TRY(append(string.sv()));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,159 +111,159 @@ namespace BAN
|
||||||
{
|
{
|
||||||
ASSERT(m_size > 0);
|
ASSERT(m_size > 0);
|
||||||
m_size--;
|
m_size--;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::remove(size_type index)
|
void String::remove(size_type index)
|
||||||
{
|
{
|
||||||
erase(index, 1);
|
ASSERT(index < m_size);
|
||||||
|
memcpy(data() + index, data() + index + 1, m_size - index);
|
||||||
|
m_size--;
|
||||||
|
data()[m_size] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::clear()
|
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_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);
|
if (size() != str.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)
|
|
||||||
return false;
|
return false;
|
||||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
for (size_type i = 0; i < m_size; i++)
|
||||||
}
|
if (data()[i] != str.data()[i])
|
||||||
|
|
||||||
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])
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
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';
|
TRY(ensure_capacity(new_size));
|
||||||
m_size = 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)
|
|
||||||
{
|
m_size = new_size;
|
||||||
TRY(ensure_capacity(size + 1));
|
data()[m_size] = '\0';
|
||||||
for (size_type i = m_size; i < size; i++)
|
|
||||||
m_data[i] = ch;
|
|
||||||
m_data[size] = '\0';
|
|
||||||
m_size = size;
|
|
||||||
}
|
|
||||||
m_size = size;
|
|
||||||
return {};
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::shrink_to_fit()
|
ErrorOr<void> String::shrink_to_fit()
|
||||||
{
|
{
|
||||||
size_type temp = m_capacity;
|
if (has_sso())
|
||||||
m_capacity = 0;
|
return {};
|
||||||
auto error_or = ensure_capacity(m_size);
|
|
||||||
if (error_or.is_error())
|
if (fits_in_sso())
|
||||||
{
|
{
|
||||||
m_capacity = temp;
|
char* data = m_storage.general_storage.data;
|
||||||
return error_or;
|
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 {};
|
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
|
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
|
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 {};
|
return {};
|
||||||
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
if (has_sso() && fits_in_sso(new_size))
|
||||||
void* new_data = BAN::allocator(new_cap);
|
return {};
|
||||||
|
|
||||||
|
char* new_data = (char*)allocator(new_size + 1);
|
||||||
if (new_data == nullptr)
|
if (new_data == nullptr)
|
||||||
return Error::from_errno(ENOMEM);
|
return Error::from_errno(ENOMEM);
|
||||||
if (m_data)
|
|
||||||
memcpy(new_data, m_data, m_size + 1);
|
memcpy(new_data, data(), m_size + 1);
|
||||||
BAN::deallocator(m_data);
|
|
||||||
m_data = (char*)new_data;
|
if (has_sso())
|
||||||
m_capacity = new_cap;
|
{
|
||||||
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::copy_impl(StringView other)
|
bool String::has_sso() const
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(other.size() + 1));
|
return m_has_sso;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,9 +63,38 @@ namespace BAN
|
||||||
|
|
||||||
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties)
|
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties)
|
||||||
{
|
{
|
||||||
// FIXME: Won't work while multithreading
|
size_type count = 0;
|
||||||
static char s_delim = delim;
|
{
|
||||||
return split([](char c){ return c == s_delim; }, allow_empties);
|
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)
|
ErrorOr<Vector<StringView>> StringView::split(bool(*comp)(char), bool allow_empties)
|
||||||
|
@ -116,6 +145,14 @@ namespace BAN
|
||||||
return m_data[0];
|
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
|
StringView::size_type StringView::count(char ch) const
|
||||||
{
|
{
|
||||||
size_type result = 0;
|
size_type result = 0;
|
||||||
|
@ -134,7 +171,7 @@ namespace BAN
|
||||||
{
|
{
|
||||||
return m_size;
|
return m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* StringView::data() const
|
const char* StringView::data() const
|
||||||
{
|
{
|
||||||
return m_data;
|
return m_data;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
@ -13,11 +15,18 @@ namespace BAN
|
||||||
public:
|
public:
|
||||||
using size_type = decltype(S);
|
using size_type = decltype(S);
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Array>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Array>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Array();
|
Array();
|
||||||
Array(const T&);
|
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;
|
const T& operator[](size_type) const;
|
||||||
T& operator[](size_type);
|
T& operator[](size_type);
|
||||||
|
|
||||||
|
@ -26,7 +35,13 @@ namespace BAN
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
T& front();
|
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;
|
constexpr size_type size() const;
|
||||||
|
|
||||||
|
const T* data() const { return m_data; }
|
||||||
|
T* data() { return m_data; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T m_data[S];
|
T m_data[S];
|
||||||
|
|
|
@ -1,11 +1,33 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
#if defined(__is_kernel)
|
||||||
#include <kernel/Panic.h>
|
#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")
|
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
|
||||||
#else
|
#else
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
|
#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
|
#endif
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
#include <BAN/Variant.h>
|
#include <BAN/Variant.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
#ifdef __is_kernel
|
||||||
#include <kernel/Panic.h>
|
#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
|
#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
|
#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
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
class Error
|
class Error
|
||||||
{
|
{
|
||||||
|
#ifdef __is_kernel
|
||||||
|
private:
|
||||||
|
static constexpr uint64_t kernel_error_mask = uint64_t(1) << 63;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Error from_c_string(const char* message)
|
#ifdef __is_kernel
|
||||||
|
static Error from_error_code(Kernel::ErrorCode error)
|
||||||
{
|
{
|
||||||
Error result;
|
return Error((uint64_t)error | kernel_error_mask);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
static Error from_errno(int error)
|
static Error from_errno(int error)
|
||||||
{
|
{
|
||||||
Error result;
|
return Error(error);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_error_code() const { return m_error_code; }
|
#ifdef __is_kernel
|
||||||
const char* get_message() const { return m_message; }
|
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:
|
private:
|
||||||
char m_message[128];
|
Error(uint64_t error)
|
||||||
uint8_t m_error_code;
|
: m_error_code(error)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint64_t m_error_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -76,19 +90,46 @@ namespace BAN
|
||||||
: m_data(move(error))
|
: 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>(); }
|
const Error& error() const { return m_data.template get<Error>(); }
|
||||||
Error& error() { return m_data.template get<Error>(); }
|
Error& error() { return m_data.template get<Error>(); }
|
||||||
const T& value() const { return m_data.template get<T>(); }
|
const T& value() const { return m_data.template get<T>(); }
|
||||||
T& value() { 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(); }
|
T release_value() { return move(value()); m_data.clear(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Variant<Error, T> m_data;
|
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<>
|
template<>
|
||||||
class [[nodiscard]] ErrorOr<void>
|
class [[nodiscard]] ErrorOr<void>
|
||||||
{
|
{
|
||||||
|
@ -102,12 +143,12 @@ namespace BAN
|
||||||
const Error& error() const { return m_data; }
|
const Error& error() const { return m_data; }
|
||||||
void value() { }
|
void value() { }
|
||||||
|
|
||||||
Error release_error() { return move(m_data); m_data = Error(); }
|
Error release_error() { return move(m_data); }
|
||||||
void release_value() { m_data = Error(); }
|
void release_value() { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Error m_data;
|
Error m_data { Error::from_errno(0) };
|
||||||
bool m_has_error = false;
|
bool m_has_error { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -115,11 +156,8 @@ namespace BAN
|
||||||
namespace BAN::Formatter
|
namespace BAN::Formatter
|
||||||
{
|
{
|
||||||
template<typename F>
|
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_argument(putc, error.get_message(), format);
|
||||||
print(putc, error.get_message());
|
|
||||||
else
|
|
||||||
print(putc, "{} ({})", error.get_message(), error.get_error_code());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,8 @@ namespace BAN::Formatter
|
||||||
static size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
|
static size_t parse_format_and_print_argument(F putc, const char* format, T&& arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
IMPLEMENTATION
|
IMPLEMENTATION
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -42,7 +41,7 @@ namespace BAN::Formatter
|
||||||
int fill = 0;
|
int fill = 0;
|
||||||
bool upper = false;
|
bool upper = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void print(F putc, const char* format)
|
void print(F putc, const char* format)
|
||||||
{
|
{
|
||||||
|
@ -232,7 +231,7 @@ namespace BAN::Formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
TEMPLATE SPECIALIZATIONS
|
TEMPLATE SPECIALIZATIONS
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
@ -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, 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, 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, const char* value, const ValueFormat&) { print(putc, value); }
|
||||||
|
template<typename F> void print_argument(F putc, 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); }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
template<typename, size_t> class Array;
|
template<typename, size_t> class Array;
|
||||||
template<typename> class ErrorOr;
|
template<typename> class ErrorOr;
|
||||||
template<typename> class Function;
|
template<typename> class Function;
|
||||||
|
@ -14,5 +15,6 @@ namespace BAN
|
||||||
class StringView;
|
class StringView;
|
||||||
template<typename> class Vector;
|
template<typename> class Vector;
|
||||||
template<typename> class LinkedList;
|
template<typename> class LinkedList;
|
||||||
|
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/Memory.h>
|
#include <BAN/New.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -43,10 +43,10 @@ namespace BAN
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ret operator()(Args... args)
|
Ret operator()(Args... args) const
|
||||||
{
|
{
|
||||||
ASSERT(*this);
|
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
|
operator bool() const
|
||||||
|
@ -70,7 +70,7 @@ namespace BAN
|
||||||
struct CallableBase
|
struct CallableBase
|
||||||
{
|
{
|
||||||
virtual ~CallableBase() {}
|
virtual ~CallableBase() {}
|
||||||
virtual Ret call(Args...) = 0;
|
virtual Ret call(Args...) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CallablePointer : public CallableBase
|
struct CallablePointer : public CallableBase
|
||||||
|
@ -79,7 +79,7 @@ namespace BAN
|
||||||
: m_function(function)
|
: m_function(function)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return m_function(forward<Args>(args)...);
|
return m_function(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ namespace BAN
|
||||||
, m_function(function)
|
, m_function(function)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ namespace BAN
|
||||||
, m_function(function)
|
, m_function(function)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ namespace BAN
|
||||||
: m_lambda(lambda)
|
: m_lambda(lambda)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual Ret call(Args... args) override
|
virtual Ret call(Args... args) const override
|
||||||
{
|
{
|
||||||
return m_lambda(forward<Args>(args)...);
|
return m_lambda(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ namespace BAN
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
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 };
|
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,31 @@
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template<typename Container>
|
||||||
|
class HashMapIterator;
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||||
class HashMap
|
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:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using key_type = Key;
|
using key_type = Key;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||||
|
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashMap() = default;
|
HashMap() = default;
|
||||||
|
@ -29,6 +47,11 @@ namespace BAN
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<void> emplace(const Key&, 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);
|
ErrorOr<void> reserve(size_type);
|
||||||
|
|
||||||
void remove(const Key&);
|
void remove(const Key&);
|
||||||
|
@ -42,19 +65,6 @@ namespace BAN
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
size_type size() 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:
|
private:
|
||||||
ErrorOr<void> rebucket(size_type);
|
ErrorOr<void> rebucket(size_type);
|
||||||
LinkedList<Entry>& get_bucket(const Key&);
|
LinkedList<Entry>& get_bucket(const Key&);
|
||||||
|
@ -63,6 +73,8 @@ namespace BAN
|
||||||
private:
|
private:
|
||||||
Vector<LinkedList<Entry>> m_buckets;
|
Vector<LinkedList<Entry>> m_buckets;
|
||||||
size_type m_size = 0;
|
size_type m_size = 0;
|
||||||
|
|
||||||
|
friend iterator;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
template<typename Key, typename T, typename HASH>
|
||||||
|
@ -246,4 +258,4 @@ namespace BAN
|
||||||
return m_buckets[index];
|
return m_buckets[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,8 +85,6 @@ namespace BAN
|
||||||
friend class HashSet<T, HASH>;
|
friend class HashSet<T, HASH>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
|
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
|
||||||
: m_buckets(other.m_buckets)
|
: m_buckets(other.m_buckets)
|
||||||
|
@ -231,8 +229,6 @@ namespace BAN
|
||||||
return m_buckets[index];
|
return m_buckets[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T, typename HASH>
|
template<typename T, typename HASH>
|
||||||
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BAN
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Iteration
|
||||||
|
{
|
||||||
|
Continue,
|
||||||
|
Break
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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,15 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
template<typename T, bool CONST>
|
||||||
class LinkedListIterator;
|
class LinkedListIterator;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class LinkedList
|
class LinkedList
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@ namespace BAN
|
||||||
ErrorOr<void> emplace(iterator, Args&&...);
|
ErrorOr<void> emplace(iterator, Args&&...);
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(iterator);
|
iterator remove(iterator);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
iterator begin() { return iterator(m_data, empty()); }
|
iterator begin() { return iterator(m_data, empty()); }
|
||||||
|
@ -114,8 +114,6 @@ namespace BAN
|
||||||
friend class LinkedListIterator<T, !CONST>;
|
friend class LinkedListIterator<T, !CONST>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
||||||
{
|
{
|
||||||
|
@ -197,11 +195,11 @@ namespace BAN
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void LinkedList<T>::pop_back()
|
void LinkedList<T>::pop_back()
|
||||||
{
|
{
|
||||||
return remove(m_last);
|
remove(iterator(m_last, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void LinkedList<T>::remove(iterator iter)
|
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
|
||||||
{
|
{
|
||||||
ASSERT(!empty() && iter);
|
ASSERT(!empty() && iter);
|
||||||
Node* node = iter.m_current;
|
Node* node = iter.m_current;
|
||||||
|
@ -212,6 +210,7 @@ namespace BAN
|
||||||
(prev ? prev->next : m_data) = next;
|
(prev ? prev->next : m_data) = next;
|
||||||
(next ? next->prev : m_last) = prev;
|
(next ? next->prev : m_last) = prev;
|
||||||
m_size--;
|
m_size--;
|
||||||
|
return next ? iterator(next, false) : iterator(m_last, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -292,8 +291,6 @@ namespace BAN
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
template<typename T, bool CONST>
|
||||||
template<bool C>
|
template<bool C>
|
||||||
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
|
LinkedListIterator<T, CONST>::LinkedListIterator(const LinkedListIterator<T, C>& other, enable_if_t<C == CONST || !C>*)
|
||||||
|
@ -378,7 +375,7 @@ namespace BAN
|
||||||
ASSERT(m_current);
|
ASSERT(m_current);
|
||||||
return &m_current->value;
|
return &m_current->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
template<typename T, bool CONST>
|
||||||
bool LinkedListIterator<T, CONST>::operator==(const LinkedListIterator<T, CONST>& other) const
|
bool LinkedListIterator<T, CONST>::operator==(const LinkedListIterator<T, CONST>& other) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,20 +52,53 @@ namespace BAN::Math
|
||||||
}
|
}
|
||||||
|
|
||||||
template<integral T>
|
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;
|
if (value == 0)
|
||||||
for (size_t i = 0; i < sizeof(T); i++)
|
return false;
|
||||||
result |= (T)bytes[i] << (i * 8);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<integral T>
|
template<floating_point T>
|
||||||
inline constexpr T big_endian_to_host(const uint8_t* bytes)
|
inline constexpr T log10(T value)
|
||||||
{
|
{
|
||||||
T result = 0;
|
constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
|
||||||
for (size_t i = 0; i < sizeof(T); i++)
|
T result;
|
||||||
result |= (T)bytes[i] << (8 * (sizeof(T) - i - 1));
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <BAN/Traits.h>
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -24,4 +26,7 @@ namespace BAN
|
||||||
return static_cast<T&&>(arg);
|
return static_cast<T&&>(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void* operator new(size_t, void* addr) { return addr; }
|
||||||
|
inline void* operator new[](size_t, void* addr) { return addr; }
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -14,6 +15,8 @@ namespace BAN
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
using iterator = IteratorSimple<T, Queue>;
|
||||||
|
using const_iterator = ConstIteratorSimple<T, Queue>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Queue() = default;
|
Queue() = default;
|
||||||
|
@ -32,6 +35,11 @@ namespace BAN
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
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 pop();
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/NoCopyMove.h>
|
#include <BAN/NoCopyMove.h>
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
|
||||||
#include <kernel/kmalloc.h>
|
|
||||||
#else
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace BAN
|
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>
|
template<typename T>
|
||||||
class RefCounted
|
class RefCounted
|
||||||
|
@ -68,7 +53,6 @@ namespace BAN
|
||||||
if (m_pointer)
|
if (m_pointer)
|
||||||
m_pointer->ref();
|
m_pointer->ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
~RefPtr() { clear(); }
|
~RefPtr() { clear(); }
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
|
@ -90,6 +74,10 @@ namespace BAN
|
||||||
|
|
||||||
RefPtr(const RefPtr& other) { *this = other; }
|
RefPtr(const RefPtr& other) { *this = other; }
|
||||||
RefPtr(RefPtr&& other) { *this = move(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)
|
RefPtr& operator=(const RefPtr& other)
|
||||||
{
|
{
|
||||||
|
@ -108,6 +96,25 @@ namespace BAN
|
||||||
return *this;
|
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; }
|
T* ptr() { ASSERT(!empty()); return m_pointer; }
|
||||||
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
||||||
|
|
||||||
|
@ -129,9 +136,9 @@ namespace BAN
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* m_pointer = nullptr;
|
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()
|
~ScopeGuard()
|
||||||
{
|
{
|
||||||
m_func();
|
if (m_enabled)
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
|
void disable()
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
BAN::Function<void()> m_func;
|
BAN::Function<void()> m_func;
|
||||||
|
bool m_enabled { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/ForwardList.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -11,6 +13,9 @@ namespace BAN
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
|
using iterator = IteratorSimple<char, String>;
|
||||||
|
using const_iterator = ConstIteratorSimple<char, String>;
|
||||||
|
static constexpr size_type sso_capacity = 15;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
String();
|
String();
|
||||||
|
@ -30,18 +35,26 @@ namespace BAN
|
||||||
ErrorOr<void> insert(char, size_type);
|
ErrorOr<void> insert(char, size_type);
|
||||||
ErrorOr<void> insert(StringView, size_type);
|
ErrorOr<void> insert(StringView, size_type);
|
||||||
ErrorOr<void> append(StringView);
|
ErrorOr<void> append(StringView);
|
||||||
ErrorOr<void> append(const String&);
|
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(size_type);
|
void remove(size_type);
|
||||||
void erase(size_type, size_type);
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
char operator[](size_type) const;
|
const_iterator begin() const { return const_iterator(data()); }
|
||||||
char& operator[](size_type);
|
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==(StringView) const;
|
||||||
bool operator==(const char*) const;
|
bool operator==(const char*) const;
|
||||||
|
|
||||||
|
@ -49,31 +62,48 @@ namespace BAN
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
StringView sv() const;
|
StringView sv() const { return StringView(data(), size()); }
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const { return m_size == 0; }
|
||||||
size_type size() const;
|
size_type size() const { return m_size; }
|
||||||
size_type capacity() const;
|
size_type capacity() const;
|
||||||
|
|
||||||
|
char* data();
|
||||||
const char* data() const;
|
const char* data() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> ensure_capacity(size_type);
|
ErrorOr<void> ensure_capacity(size_type);
|
||||||
|
|
||||||
ErrorOr<void> copy_impl(StringView);
|
bool has_sso() const;
|
||||||
void move_impl(String&&);
|
|
||||||
|
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||||
|
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char* m_data = nullptr;
|
struct SSOStorage
|
||||||
size_type m_capacity = 0;
|
{
|
||||||
size_type m_size = 0;
|
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>
|
template<typename... Args>
|
||||||
String String::formatted(const char* format, const Args&... args)
|
String String::formatted(const char* format, const Args&... args)
|
||||||
{
|
{
|
||||||
String result;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <BAN/ForwardList.h>
|
#include <BAN/ForwardList.h>
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
@ -10,12 +11,16 @@ namespace BAN
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
|
using const_iterator = ConstIteratorSimple<char, StringView>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StringView();
|
StringView();
|
||||||
StringView(const String&);
|
StringView(const String&);
|
||||||
StringView(const char*, size_type = -1);
|
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;
|
char operator[](size_type) const;
|
||||||
|
|
||||||
bool operator==(const String&) const;
|
bool operator==(const String&) const;
|
||||||
|
@ -30,6 +35,7 @@ namespace BAN
|
||||||
char back() const;
|
char back() const;
|
||||||
char front() const;
|
char front() const;
|
||||||
|
|
||||||
|
bool contains(char) const;
|
||||||
size_type count(char) const;
|
size_type count(char) const;
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
|
|
@ -9,15 +9,18 @@ namespace BAN
|
||||||
|
|
||||||
struct Time
|
struct Time
|
||||||
{
|
{
|
||||||
uint8_t second;
|
uint32_t year;
|
||||||
uint8_t minute;
|
|
||||||
uint8_t hour;
|
|
||||||
uint8_t week_day;
|
|
||||||
uint8_t day;
|
|
||||||
uint8_t month;
|
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
|
namespace BAN::Formatter
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T> struct remove_refenrece { using type = T; };
|
template<typename T> struct remove_refenrece { using type = T; };
|
||||||
template<typename T> struct remove_refenrece<T&> { using type = T; };
|
template<typename T> struct remove_refenrece<T&> { using type = T; };
|
||||||
template<typename T> struct remove_refenrece<T&&> { using type = T; };
|
template<typename T> struct remove_refenrece<T&&> { using type = T; };
|
||||||
|
@ -30,6 +30,10 @@ namespace BAN
|
||||||
template<typename T> struct maybe_const<true, T> { using type = const T; };
|
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 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 true_type { static constexpr bool value = true; };
|
||||||
struct false_type { static constexpr bool value = false; };
|
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 : false_type {};
|
||||||
template<typename T> struct is_lvalue_reference<T&> : true_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> 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> 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;
|
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> inline constexpr bool is_pointer_v = is_pointer<T>::value;
|
||||||
template<typename T> concept pointer = is_pointer_v<T>;
|
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 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 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; } };
|
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 <stddef.h>
|
||||||
#include <stdint.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)
|
if ((first_byte & 0x80) == 0x00)
|
||||||
return 0xFFFF;
|
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)
|
if ((bytes[i] & 0xC0) != 0x80)
|
||||||
return 0xFFFF;
|
return UTF8::invalid;
|
||||||
|
|
||||||
switch (count)
|
switch (length)
|
||||||
{
|
{
|
||||||
case 1: return bytes[0];
|
case 1: return ((bytes[0] & 0x80) != 0x00) ? UTF8::invalid : bytes[0];
|
||||||
case 2: return ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
case 2: return ((bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
||||||
case 3: return ((bytes[0] & 0x1F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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/Math.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
namespace BAN
|
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
|
class Variant
|
||||||
{
|
{
|
||||||
public:
|
private:
|
||||||
static_assert(!is_same_v<T1, T2>);
|
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() = default;
|
||||||
|
|
||||||
Variant(const T1& value) { set(value); }
|
Variant(Variant&& other)
|
||||||
Variant(T1&& value) { set(move(value)); }
|
: m_index(other.m_index)
|
||||||
Variant(const T2& value) { set(value); }
|
{
|
||||||
Variant(T2&& value) { set(move(value)); }
|
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
|
||||||
Variant(const Variant<T1, T2>& other) { *this = other; }
|
Variant(const Variant& other)
|
||||||
Variant(Variant<T1, T2>&& other) { *this = move(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);
|
template<typename T>
|
||||||
Variant<T1, T2>& operator=(Variant<T1, T2>&& other);
|
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>
|
~Variant()
|
||||||
bool is() const;
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename U>
|
Variant& operator=(Variant&& other)
|
||||||
void set(U&&);
|
{
|
||||||
template<typename U>
|
if (m_index == other.m_index)
|
||||||
void set(const U& value) { set(move(U(value))); }
|
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>
|
Variant& operator=(const Variant& other)
|
||||||
const U& get() const;
|
{
|
||||||
template<typename U>
|
if (m_index == other.m_index)
|
||||||
U& get();
|
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:
|
private:
|
||||||
static constexpr uint32_t m_size = Math::max(sizeof(T1), sizeof(T2));
|
alignas(detail::max_align_ref_as_ptr<Ts...>()) uint8_t m_storage[detail::max_size_ref_as_ptr<Ts...>()] {};
|
||||||
uint8_t m_storage[m_size] = {};
|
size_t m_index { invalid_index() };
|
||||||
uint32_t m_index = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Iterators.h>
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Memory.h>
|
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
|
#include <BAN/New.h>
|
||||||
|
#include <BAN/Span.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, bool CONST>
|
|
||||||
class VectorIterator;
|
|
||||||
|
|
||||||
// T must be move assignable, move constructable (and copy constructable for some functions)
|
// T must be move assignable, move constructable (and copy constructable for some functions)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Vector
|
class Vector
|
||||||
|
@ -18,8 +17,8 @@ namespace BAN
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using iterator = VectorIterator<T, false>;
|
using iterator = IteratorSimple<T, Vector>;
|
||||||
using const_iterator = VectorIterator<T, true>;
|
using const_iterator = ConstIteratorSimple<T, Vector>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vector() = default;
|
Vector() = default;
|
||||||
|
@ -40,10 +39,10 @@ namespace BAN
|
||||||
ErrorOr<void> insert(size_type, T&&);
|
ErrorOr<void> insert(size_type, T&&);
|
||||||
ErrorOr<void> insert(size_type, const T&);
|
ErrorOr<void> insert(size_type, const T&);
|
||||||
|
|
||||||
iterator begin() { return iterator (m_data); }
|
iterator begin() { return iterator(m_data); }
|
||||||
const_iterator begin() const { return const_iterator(m_data); }
|
iterator end() { return iterator(m_data + m_size); }
|
||||||
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); }
|
const_iterator end() const { return const_iterator(m_data + m_size); }
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(size_type);
|
void remove(size_type);
|
||||||
|
@ -54,6 +53,9 @@ namespace BAN
|
||||||
|
|
||||||
bool contains(const T&) const;
|
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;
|
const T& operator[](size_type) const;
|
||||||
T& operator[](size_type);
|
T& operator[](size_type);
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ namespace BAN
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
T& front();
|
T& front();
|
||||||
|
|
||||||
ErrorOr<void> resize(size_type);
|
ErrorOr<void> resize(size_type, const T& = T());
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
|
@ -76,54 +78,9 @@ namespace BAN
|
||||||
private:
|
private:
|
||||||
T* m_data = nullptr;
|
T* m_data = nullptr;
|
||||||
size_type m_capacity = 0;
|
size_type m_capacity = 0;
|
||||||
size_type m_size = 0;
|
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>
|
template<typename T>
|
||||||
Vector<T>::Vector(Vector<T>&& other)
|
Vector<T>::Vector(Vector<T>&& other)
|
||||||
{
|
{
|
||||||
|
@ -256,7 +213,7 @@ namespace BAN
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ErrorOr<void> Vector<T>::insert(size_type index, const T& value)
|
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>
|
template<typename T>
|
||||||
|
@ -296,7 +253,7 @@ namespace BAN
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const T& Vector<T>::operator[](size_type index) const
|
const T& Vector<T>::operator[](size_type index) const
|
||||||
{
|
{
|
||||||
|
@ -340,7 +297,7 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
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));
|
TRY(ensure_capacity(size));
|
||||||
if (size < m_size)
|
if (size < m_size)
|
||||||
|
@ -348,7 +305,7 @@ namespace BAN
|
||||||
m_data[i].~T();
|
m_data[i].~T();
|
||||||
if (size > m_size)
|
if (size > m_size)
|
||||||
for (size_type i = m_size; i < size; i++)
|
for (size_type i = m_size; i < size; i++)
|
||||||
new (m_data + i) T();
|
new (m_data + i) T(value);
|
||||||
m_size = size;
|
m_size = size;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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.
|
|
@ -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
|
||||||
|
)
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
![license](https://img.shields.io/github/license/bananymous/banan-os)
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
![screenshot from qemu running banan-os](assets/banan-os.png)
|
||||||
|
|
||||||
|
## 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.
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
add_subdirectory(bios)
|
|
@ -0,0 +1,18 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
project(bootloader ASM)
|
||||||
|
|
||||||
|
set(BOOTLOADER_SOURCES
|
||||||
|
boot.S
|
||||||
|
command_line.S
|
||||||
|
disk.S
|
||||||
|
elf.S
|
||||||
|
ext2.S
|
||||||
|
framebuffer.S
|
||||||
|
memory_map.S
|
||||||
|
utils.S
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(bootloader ${BOOTLOADER_SOURCES})
|
||||||
|
target_link_options(bootloader PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
|
||||||
|
target_link_options(bootloader PRIVATE -nostdlib)
|
|
@ -0,0 +1,170 @@
|
||||||
|
.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 enter_unreal_mode
|
||||||
|
movw $unreal_enter_msg, %si
|
||||||
|
call puts; call print_newline
|
||||||
|
|
||||||
|
call get_memory_map
|
||||||
|
call read_user_command_line
|
||||||
|
|
||||||
|
call vesa_find_video_mode
|
||||||
|
|
||||||
|
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
|
||||||
|
movl $ext2_inode_read_bytes, %esi
|
||||||
|
|
||||||
|
call elf_read_kernel_to_memory
|
||||||
|
|
||||||
|
call vesa_set_target_mode
|
||||||
|
|
||||||
|
cli
|
||||||
|
|
||||||
|
# setup protected mode
|
||||||
|
movl %cr0, %ebx
|
||||||
|
orb $1, %bl
|
||||||
|
movl %ebx, %cr0
|
||||||
|
|
||||||
|
# jump to kernel in protected mode
|
||||||
|
ljmpl $0x18, $protected_mode
|
||||||
|
|
||||||
|
|
||||||
|
.code32
|
||||||
|
protected_mode:
|
||||||
|
movw $0x10, %bx
|
||||||
|
movw %bx, %ds
|
||||||
|
movw %bx, %es
|
||||||
|
movw %bx, %fs
|
||||||
|
movw %bx, %gs
|
||||||
|
movw %bx, %ss
|
||||||
|
|
||||||
|
movl %eax, %ecx
|
||||||
|
|
||||||
|
movl $0xD3C60CFF, %eax
|
||||||
|
movl $banan_boot_info, %ebx
|
||||||
|
xorl %edx, %edx
|
||||||
|
xorl %esi, %esi
|
||||||
|
xorl %edi, %edi
|
||||||
|
|
||||||
|
jmp *%ecx
|
||||||
|
|
||||||
|
|
||||||
|
.code16
|
||||||
|
enter_unreal_mode:
|
||||||
|
cli
|
||||||
|
pushw %ds
|
||||||
|
|
||||||
|
lgdt gdtr
|
||||||
|
|
||||||
|
movl %cr0, %eax
|
||||||
|
orb $1, %al
|
||||||
|
movl %eax, %cr0
|
||||||
|
ljmpl $0x8, $.enter_unreal_mode_pmode
|
||||||
|
|
||||||
|
.enter_unreal_mode_pmode:
|
||||||
|
movw $0x10, %bx
|
||||||
|
movw %bx, %ds
|
||||||
|
|
||||||
|
andb 0xFE, %al
|
||||||
|
movl %eax, %cr0
|
||||||
|
ljmpl $0x0, $.enter_unreal_mode_unreal
|
||||||
|
|
||||||
|
.enter_unreal_mode_unreal:
|
||||||
|
popw %ds
|
||||||
|
sti
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
hello_msg:
|
||||||
|
.asciz "This is banan-os bootloader"
|
||||||
|
|
||||||
|
unreal_enter_msg:
|
||||||
|
.asciz "Entered unreal mode"
|
||||||
|
|
||||||
|
start_kernel_load_msg:
|
||||||
|
.asciz "Starting to load kernel"
|
||||||
|
|
||||||
|
gdt:
|
||||||
|
.quad 0x0000000000000000
|
||||||
|
.quad 0x00009A000000FFFF
|
||||||
|
.quad 0x00CF92000000FFFF
|
||||||
|
.quad 0x00CF9A000000FFFF
|
||||||
|
gdtr:
|
||||||
|
.short . - gdt - 1
|
||||||
|
.quad gdt
|
||||||
|
|
||||||
|
banan_boot_info:
|
||||||
|
boot_command_line:
|
||||||
|
.long command_line
|
||||||
|
boot_framebuffer:
|
||||||
|
.long framebuffer
|
||||||
|
boot_memory_map:
|
||||||
|
.long memory_map
|
|
@ -0,0 +1,83 @@
|
||||||
|
.code16
|
||||||
|
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# fills command line buffer
|
||||||
|
# NO REGISTERS SAVED
|
||||||
|
.global read_user_command_line
|
||||||
|
read_user_command_line:
|
||||||
|
# print initial command line
|
||||||
|
movw $command_line_enter_msg, %si
|
||||||
|
call puts
|
||||||
|
movw $command_line_buffer, %si
|
||||||
|
call puts
|
||||||
|
|
||||||
|
# prepare registers for input
|
||||||
|
movw $command_line_enter_msg, %si
|
||||||
|
movw $command_line_buffer, %di
|
||||||
|
.read_user_command_line_goto_end:
|
||||||
|
cmpb $0, (%di)
|
||||||
|
jz .read_user_command_line_loop
|
||||||
|
incw %di
|
||||||
|
jmp .read_user_command_line_goto_end
|
||||||
|
|
||||||
|
.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
|
||||||
|
|
||||||
|
pushw %ax
|
||||||
|
|
||||||
|
call isprint
|
||||||
|
testb %al, %al
|
||||||
|
jz .read_user_command_line_loop
|
||||||
|
|
||||||
|
popw %ax
|
||||||
|
|
||||||
|
# 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: "
|
||||||
|
|
||||||
|
.global command_line
|
||||||
|
command_line:
|
||||||
|
# 100 character command line
|
||||||
|
command_line_buffer:
|
||||||
|
.ascii "root=/dev/sda2"
|
||||||
|
.skip 100 - 28
|
|
@ -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
|
|
@ -0,0 +1,222 @@
|
||||||
|
.set SECTOR_SIZE, 512
|
||||||
|
|
||||||
|
# file header field offsets
|
||||||
|
.set e_type, 16
|
||||||
|
.set e_machine, 18
|
||||||
|
.set e_version, 20
|
||||||
|
.set e_entry, 24
|
||||||
|
.set e_phoff, 32
|
||||||
|
.set e_shoff, 40
|
||||||
|
.set e_flags, 48
|
||||||
|
.set e_ehsize, 52
|
||||||
|
.set e_phentsize, 54
|
||||||
|
.set e_phnum, 56
|
||||||
|
.set e_shentsize, 58
|
||||||
|
.set e_shnum, 60
|
||||||
|
.set e_shstrndx, 62
|
||||||
|
|
||||||
|
# e_ident offsets
|
||||||
|
.set EI_CLASS, 4
|
||||||
|
.set EI_DATA, 5
|
||||||
|
.set EI_VERSION, 6
|
||||||
|
|
||||||
|
# e_ident constants
|
||||||
|
.set ELFMAGIC, 0x464C457F
|
||||||
|
.set ELFCLASS64, 2
|
||||||
|
.set ELFDATA2LSB, 1
|
||||||
|
.set EV_CURRENT, 1
|
||||||
|
|
||||||
|
# e_type constants
|
||||||
|
.set ET_EXEC, 2
|
||||||
|
|
||||||
|
# program header field offsets
|
||||||
|
.set p_type, 0
|
||||||
|
.set p_flags, 4
|
||||||
|
.set p_offset, 8
|
||||||
|
.set p_vaddr, 16
|
||||||
|
.set p_paddr, 24
|
||||||
|
.set p_filesz, 32
|
||||||
|
.set p_memsz, 40
|
||||||
|
.set p_align, 48
|
||||||
|
|
||||||
|
# p_type constants
|
||||||
|
.set PT_NULL, 0
|
||||||
|
.set PT_LOAD, 1
|
||||||
|
|
||||||
|
.code16
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# Validate file header stored in elf_file_header
|
||||||
|
# returns only on success
|
||||||
|
elf_validate_file_header:
|
||||||
|
cmpl $ELFMAGIC, (elf_file_header)
|
||||||
|
jne .elf_validate_file_header_invalid_magic
|
||||||
|
|
||||||
|
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||||
|
jne .elf_validate_file_header_only_64bit_supported
|
||||||
|
|
||||||
|
cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA)
|
||||||
|
jne .elf_validate_file_header_only_little_endian_supported
|
||||||
|
|
||||||
|
cmpb $EV_CURRENT, (elf_file_header + EI_VERSION)
|
||||||
|
jne .elf_validate_file_header_not_current_version
|
||||||
|
|
||||||
|
cmpl $EV_CURRENT, (elf_file_header + e_version)
|
||||||
|
jne .elf_validate_file_header_not_current_version
|
||||||
|
|
||||||
|
cmpw $ET_EXEC, (elf_file_header + e_type)
|
||||||
|
jne .elf_validate_file_header_not_executable
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
.elf_validate_file_header_invalid_magic:
|
||||||
|
movw $elf_validate_file_header_invalid_magic_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.elf_validate_file_header_only_64bit_supported:
|
||||||
|
movw $elf_validate_file_header_only_64bit_supported_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.elf_validate_file_header_only_little_endian_supported:
|
||||||
|
movw $elf_validate_file_header_only_little_endian_supported_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.elf_validate_file_header_not_current_version:
|
||||||
|
movw $elf_validate_file_header_not_current_version_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.elf_validate_file_header_not_executable:
|
||||||
|
movw $elf_validate_file_header_not_executable_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
# read callback format
|
||||||
|
# eax: first byte
|
||||||
|
# ecx: byte count
|
||||||
|
# edi: buffer
|
||||||
|
# returns only on success
|
||||||
|
|
||||||
|
|
||||||
|
# reads kernel to memory
|
||||||
|
# esi: callback for reading from kernel image
|
||||||
|
# return:
|
||||||
|
# eax: kernel entry address
|
||||||
|
.global elf_read_kernel_to_memory
|
||||||
|
elf_read_kernel_to_memory:
|
||||||
|
pushal
|
||||||
|
pushl %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
subl $2, %esp
|
||||||
|
|
||||||
|
# read file header
|
||||||
|
movl $0, %eax
|
||||||
|
movl $64, %ecx
|
||||||
|
movl $elf_file_header, %edi
|
||||||
|
call *%esi
|
||||||
|
|
||||||
|
call elf_validate_file_header
|
||||||
|
|
||||||
|
cmpl $0, (elf_file_header + e_phoff + 4)
|
||||||
|
jnz .elf_read_kernel_to_memory_unsupported_offset
|
||||||
|
|
||||||
|
# current program header
|
||||||
|
movw $0, -2(%ebp)
|
||||||
|
|
||||||
|
.elf_read_kernel_to_memory_loop_program_headers:
|
||||||
|
movw -2(%ebp), %cx
|
||||||
|
cmpw (elf_file_header + e_phnum), %cx
|
||||||
|
jae .elf_read_kernel_to_memory_done
|
||||||
|
|
||||||
|
# eax := program_header_index * e_phentsize + e_phoff
|
||||||
|
xorl %eax, %eax
|
||||||
|
movw %cx, %ax
|
||||||
|
xorl %ebx, %ebx
|
||||||
|
movw (elf_file_header + e_phentsize), %bx
|
||||||
|
mull %ebx
|
||||||
|
addl (elf_file_header + e_phoff), %eax
|
||||||
|
jc .elf_read_kernel_to_memory_unsupported_offset
|
||||||
|
|
||||||
|
# setup program header size and address
|
||||||
|
movl $56, %ecx
|
||||||
|
movl $elf_program_header, %edi
|
||||||
|
|
||||||
|
# read the program header
|
||||||
|
call *%esi
|
||||||
|
|
||||||
|
# test if program header is empty
|
||||||
|
cmpl $PT_NULL, (elf_program_header + p_type)
|
||||||
|
je .elf_read_kernel_to_memory_null_program_header
|
||||||
|
|
||||||
|
# confirm that the program header is loadable
|
||||||
|
cmpl $PT_LOAD, (elf_program_header + p_type)
|
||||||
|
jne .elf_read_kernel_to_memory_not_loadable_header
|
||||||
|
|
||||||
|
# memset p_filesz -> p_memsz to 0
|
||||||
|
movl (elf_program_header + p_filesz), %ebx
|
||||||
|
|
||||||
|
movl (elf_program_header + p_vaddr), %edi
|
||||||
|
andl $0x7FFFFFFF, %edi
|
||||||
|
addl %ebx, %edi
|
||||||
|
|
||||||
|
movl (elf_program_header + p_memsz), %ecx
|
||||||
|
subl %ebx, %ecx
|
||||||
|
jz .elf_read_kernel_to_memory_memset_done
|
||||||
|
|
||||||
|
.elf_read_kernel_to_memory_memset:
|
||||||
|
movb $0, (%edi)
|
||||||
|
incl %edi
|
||||||
|
decl %ecx
|
||||||
|
jnz .elf_read_kernel_to_memory_memset
|
||||||
|
.elf_read_kernel_to_memory_memset_done:
|
||||||
|
|
||||||
|
# read file specified in program header to memory
|
||||||
|
movl (elf_program_header + p_offset), %eax
|
||||||
|
movl (elf_program_header + p_vaddr), %edi
|
||||||
|
andl $0x7FFFFFFF, %edi
|
||||||
|
movl (elf_program_header + p_filesz), %ecx
|
||||||
|
|
||||||
|
#call print_hex32; call print_newline
|
||||||
|
|
||||||
|
call *%esi
|
||||||
|
|
||||||
|
.elf_read_kernel_to_memory_null_program_header:
|
||||||
|
incw -2(%ebp)
|
||||||
|
jmp .elf_read_kernel_to_memory_loop_program_headers
|
||||||
|
|
||||||
|
.elf_read_kernel_to_memory_done:
|
||||||
|
leavel
|
||||||
|
popal
|
||||||
|
|
||||||
|
# set kernel entry address
|
||||||
|
movl (elf_file_header + e_entry), %eax
|
||||||
|
andl $0x7FFFFF, %eax
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
.elf_read_kernel_to_memory_unsupported_offset:
|
||||||
|
movw $elf_read_kernel_to_memory_unsupported_offset_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.elf_read_kernel_to_memory_not_loadable_header:
|
||||||
|
movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
elf_validate_file_header_invalid_magic_msg:
|
||||||
|
.asciz "ELF: file has invalid ELF magic"
|
||||||
|
elf_validate_file_header_only_64bit_supported_msg:
|
||||||
|
.asciz "ELF: file is not targettint 64 bit"
|
||||||
|
elf_validate_file_header_only_little_endian_supported_msg:
|
||||||
|
.asciz "ELF: file is not in little endian format"
|
||||||
|
elf_validate_file_header_not_current_version_msg:
|
||||||
|
.asciz "ELF: file is not in current ELF version"
|
||||||
|
elf_validate_file_header_not_executable_msg:
|
||||||
|
.asciz "ELF: file is not an executable"
|
||||||
|
|
||||||
|
elf_read_kernel_to_memory_unsupported_offset_msg:
|
||||||
|
.asciz "ELF: unsupported offset (only 32 bit offsets supported)"
|
||||||
|
elf_read_kernel_to_memory_not_loadable_header_msg:
|
||||||
|
.asciz "ELF: kernel contains non-loadable program header"
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
elf_file_header:
|
||||||
|
.skip 64
|
||||||
|
|
||||||
|
elf_program_header:
|
||||||
|
.skip 56
|
|
@ -0,0 +1,705 @@
|
||||||
|
# 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
|
||||||
|
# ecx := inode_size
|
||||||
|
movl (ext2_inode_size), %ecx
|
||||||
|
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 %ebx
|
||||||
|
pushl %ecx
|
||||||
|
pushl %edx
|
||||||
|
pushl %esi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
subl $12, %eax
|
||||||
|
|
||||||
|
# check if this is singly indirect block access
|
||||||
|
cmpl $(EXT2_BLOCK_SIZE / 4), %eax
|
||||||
|
jb .ext2_data_block_index_singly_indirect
|
||||||
|
subl $(EXT2_BLOCK_SIZE / 4), %eax
|
||||||
|
|
||||||
|
# check if this is doubly indirect block access
|
||||||
|
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
|
||||||
|
jb .ext2_data_block_index_doubly_indirect
|
||||||
|
subl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
|
||||||
|
|
||||||
|
# check if this is triply indirect block access
|
||||||
|
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
|
||||||
|
jb .ext2_data_block_index_triply_indirect
|
||||||
|
|
||||||
|
# otherwise this is invalid access
|
||||||
|
jmp .ext2_data_block_index_invalid
|
||||||
|
|
||||||
|
.ext2_data_block_index_direct:
|
||||||
|
movl $(ext2_inode_buffer + i_block), %esi
|
||||||
|
movl (%esi, %eax, 4), %eax
|
||||||
|
jmp .ext2_data_block_index_done
|
||||||
|
|
||||||
|
.ext2_data_block_index_singly_indirect:
|
||||||
|
movl %eax, %ebx
|
||||||
|
movl (ext2_inode_buffer + i_block + 12 * 4), %eax
|
||||||
|
movw $1, %cx
|
||||||
|
jmp .ext2_data_block_index_indirect
|
||||||
|
|
||||||
|
.ext2_data_block_index_doubly_indirect:
|
||||||
|
movl %eax, %ebx
|
||||||
|
movl (ext2_inode_buffer + i_block + 13 * 4), %eax
|
||||||
|
movw $2, %cx
|
||||||
|
jmp .ext2_data_block_index_indirect
|
||||||
|
|
||||||
|
.ext2_data_block_index_triply_indirect:
|
||||||
|
movl %eax, %ebx
|
||||||
|
movl (ext2_inode_buffer + i_block + 14 * 4), %eax
|
||||||
|
movw $3, %cx
|
||||||
|
jmp .ext2_data_block_index_indirect
|
||||||
|
|
||||||
|
# eax := current block
|
||||||
|
# ebx := index
|
||||||
|
# cx := depth
|
||||||
|
.ext2_data_block_index_indirect:
|
||||||
|
call ext2_read_block
|
||||||
|
|
||||||
|
# store depth and index
|
||||||
|
pushw %cx
|
||||||
|
pushl %ebx
|
||||||
|
|
||||||
|
cmpw $1, %cx
|
||||||
|
jbe .ext2_data_block_index_no_shift
|
||||||
|
|
||||||
|
# cl := shift
|
||||||
|
movb $(EXT2_BLOCK_SHIFT - 2), %al
|
||||||
|
decb %cl
|
||||||
|
mulb %cl
|
||||||
|
movb %al, %cl
|
||||||
|
|
||||||
|
# ebx := ebx >> cl
|
||||||
|
shrl %cl, %ebx
|
||||||
|
|
||||||
|
.ext2_data_block_index_no_shift:
|
||||||
|
# edx := index of next block
|
||||||
|
movl %ebx, %eax
|
||||||
|
xorl %edx, %edx
|
||||||
|
movl $(EXT2_BLOCK_SIZE / 4), %ebx
|
||||||
|
divl %ebx
|
||||||
|
|
||||||
|
# eax := next block
|
||||||
|
movl $ext2_block_buffer, %esi
|
||||||
|
movl (%esi, %edx, 4), %eax
|
||||||
|
|
||||||
|
# restore depth and index
|
||||||
|
popl %ebx
|
||||||
|
popw %cx
|
||||||
|
|
||||||
|
loop .ext2_data_block_index_indirect
|
||||||
|
|
||||||
|
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_invalid:
|
||||||
|
movw $ext2_data_block_index_invalid_msg, %si
|
||||||
|
call puts; call print_newline
|
||||||
|
movl $0, %eax
|
||||||
|
jmp .ext2_data_block_index_done
|
||||||
|
|
||||||
|
.ext2_data_block_index_done:
|
||||||
|
popl %esi
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
# read bytes from inode (implements read callback)
|
||||||
|
# eax: first byte
|
||||||
|
# ecx: byte count
|
||||||
|
# edi: buffer
|
||||||
|
# returns only on success
|
||||||
|
.global ext2_inode_read_bytes
|
||||||
|
ext2_inode_read_bytes:
|
||||||
|
pushal
|
||||||
|
pushl %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
subl $8, %esp
|
||||||
|
|
||||||
|
# save read info
|
||||||
|
movl %eax, 0(%esp)
|
||||||
|
movl %ecx, 4(%esp)
|
||||||
|
|
||||||
|
# check if eax % EXT2_BLOCK_SIZE != 0,
|
||||||
|
# then we need to read a partial block starting from an offset
|
||||||
|
xorl %edx, %edx
|
||||||
|
movl $EXT2_BLOCK_SIZE, %ebx
|
||||||
|
divl %ebx
|
||||||
|
testl %edx, %edx
|
||||||
|
jz .ext2_inode_read_bytes_no_partial_start
|
||||||
|
|
||||||
|
# get data block index and read block
|
||||||
|
call ext2_data_block_index
|
||||||
|
call ext2_read_block
|
||||||
|
|
||||||
|
# ecx := byte count (min(block_size - edx, remaining_bytes))
|
||||||
|
movl $EXT2_BLOCK_SIZE, %ecx
|
||||||
|
subl %edx, %ecx
|
||||||
|
cmpl %ecx, 4(%esp)
|
||||||
|
cmovbl 4(%esp), %ecx
|
||||||
|
|
||||||
|
# update remaining read info
|
||||||
|
addl %ecx, 0(%esp)
|
||||||
|
subl %ecx, 4(%esp)
|
||||||
|
|
||||||
|
# esi := start sector data (block_buffer + index * SECTOR_SIZE)
|
||||||
|
movl $ext2_block_buffer, %esi
|
||||||
|
addl %edx, %esi
|
||||||
|
|
||||||
|
# very dumb memcpy with 32 bit addresses
|
||||||
|
movl $0, %ebx
|
||||||
|
.ext2_inode_read_bytes_memcpy_partial:
|
||||||
|
movb (%esi, %ebx), %al
|
||||||
|
movb %al, (%edi, %ebx)
|
||||||
|
incl %ebx
|
||||||
|
decl %ecx
|
||||||
|
jnz .ext2_inode_read_bytes_memcpy_partial
|
||||||
|
addl %ebx, %edi
|
||||||
|
|
||||||
|
# check if all sectors are read
|
||||||
|
cmpl $0, 4(%esp)
|
||||||
|
je .ext2_inode_read_bytes_done
|
||||||
|
|
||||||
|
.ext2_inode_read_bytes_no_partial_start:
|
||||||
|
# eax := data block index (byte_start / block_size)
|
||||||
|
movl 0(%esp), %eax
|
||||||
|
shrl $(EXT2_BLOCK_SHIFT), %eax
|
||||||
|
|
||||||
|
# get data block index and read block
|
||||||
|
call ext2_data_block_index
|
||||||
|
call ext2_read_block
|
||||||
|
|
||||||
|
# calculate bytes to copy (min(block_size, remaining_bytes))
|
||||||
|
movl $EXT2_BLOCK_SIZE, %ecx
|
||||||
|
cmpl %ecx, 4(%esp)
|
||||||
|
cmovbl 4(%esp), %ecx
|
||||||
|
|
||||||
|
# update remaining read info
|
||||||
|
addl %ecx, 0(%esp)
|
||||||
|
subl %ecx, 4(%esp)
|
||||||
|
|
||||||
|
# very dumb memcpy with 32 bit addresses
|
||||||
|
movl $ext2_block_buffer, %esi
|
||||||
|
movl $0, %ebx
|
||||||
|
.ext2_inode_read_bytes_memcpy:
|
||||||
|
movb (%esi, %ebx), %al
|
||||||
|
movb %al, (%edi, %ebx)
|
||||||
|
incl %ebx
|
||||||
|
decl %ecx
|
||||||
|
jnz .ext2_inode_read_bytes_memcpy
|
||||||
|
addl %ebx, %edi
|
||||||
|
|
||||||
|
# read next block if more sectors remaining
|
||||||
|
cmpl $0, 4(%esp)
|
||||||
|
jnz .ext2_inode_read_bytes_no_partial_start
|
||||||
|
|
||||||
|
.ext2_inode_read_bytes_done:
|
||||||
|
leavel
|
||||||
|
popal
|
||||||
|
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
|
||||||
|
# returns only on success
|
||||||
|
.global ext2_find_kernel
|
||||||
|
ext2_find_kernel:
|
||||||
|
pushl %eax
|
||||||
|
pushw %cx
|
||||||
|
pushw %di
|
||||||
|
pushw %si
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
popw %si
|
||||||
|
popw %di
|
||||||
|
popw %cx
|
||||||
|
popl %eax
|
||||||
|
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_invalid_msg:
|
||||||
|
.asciz "data block index is invalid"
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,156 @@
|
||||||
|
.set TARGET_WIDTH, 800
|
||||||
|
.set TARGET_HEIGHT, 600
|
||||||
|
.set TARGET_BPP, 32
|
||||||
|
|
||||||
|
.code16
|
||||||
|
.section .stage2
|
||||||
|
|
||||||
|
# Find suitable video mode
|
||||||
|
# return:
|
||||||
|
# ax: video mode number if found, 0 otherwise
|
||||||
|
.global vesa_find_video_mode
|
||||||
|
vesa_find_video_mode:
|
||||||
|
pushw %ax
|
||||||
|
pushw %cx
|
||||||
|
pushw %di
|
||||||
|
pushl %esi
|
||||||
|
|
||||||
|
# clear target mode and frame buffer
|
||||||
|
movw $0, (vesa_target_mode)
|
||||||
|
movl $0, (framebuffer + 0)
|
||||||
|
movl $0, (framebuffer + 4)
|
||||||
|
movl $0, (framebuffer + 8)
|
||||||
|
movl $0, (framebuffer + 12)
|
||||||
|
movw $0, (framebuffer + 16)
|
||||||
|
|
||||||
|
# get vesa information
|
||||||
|
movw $0x4F00, %ax
|
||||||
|
movw $vesa_info_buffer, %di
|
||||||
|
int $0x10
|
||||||
|
cmpb $0x4F, %al; jne .vesa_unsupported
|
||||||
|
cmpb $0x00, %ah; jne .vesa_error
|
||||||
|
|
||||||
|
# confirm that response starts with 'VESA'
|
||||||
|
cmpl $0x41534556, (vesa_info_buffer)
|
||||||
|
jne .vesa_error
|
||||||
|
|
||||||
|
# confirm that version is atleast 2.0
|
||||||
|
cmpw $0x0200, (vesa_info_buffer + 0x04)
|
||||||
|
jb .vesa_unsupported_version
|
||||||
|
|
||||||
|
movl $(vesa_info_buffer + 0x0E), %esi
|
||||||
|
movl (%esi), %esi
|
||||||
|
.vesa_find_video_mode_loop_modes:
|
||||||
|
cmpw $0xFFFF, (%esi)
|
||||||
|
je .vesa_find_video_mode_loop_modes_done
|
||||||
|
|
||||||
|
# get info of next mode
|
||||||
|
movw $0x4F01, %ax
|
||||||
|
movw (%esi), %cx
|
||||||
|
movw $vesa_mode_info_buffer, %di
|
||||||
|
int $0x10
|
||||||
|
cmpb $0x4F, %al; jne .vesa_unsupported
|
||||||
|
cmpb $0x00, %ah; jne .vesa_error
|
||||||
|
|
||||||
|
# check whether in graphics mode
|
||||||
|
testb $0x10, (vesa_mode_info_buffer + 0)
|
||||||
|
jz .vesa_find_video_mode_next_mode
|
||||||
|
|
||||||
|
# compare mode's dimensions
|
||||||
|
cmpw $TARGET_WIDTH, (vesa_mode_info_buffer + 0x12)
|
||||||
|
jne .vesa_find_video_mode_next_mode
|
||||||
|
cmpw $TARGET_HEIGHT, (vesa_mode_info_buffer + 0x14)
|
||||||
|
jne .vesa_find_video_mode_next_mode
|
||||||
|
cmpb $TARGET_BPP, (vesa_mode_info_buffer + 0x19)
|
||||||
|
jne .vesa_find_video_mode_next_mode
|
||||||
|
|
||||||
|
movl (vesa_mode_info_buffer + 0x28), %esi
|
||||||
|
movl %esi, (framebuffer + 0)
|
||||||
|
movw (vesa_mode_info_buffer + 0x10), %ax
|
||||||
|
movw %ax, (framebuffer + 4)
|
||||||
|
movl $TARGET_WIDTH, (framebuffer + 8)
|
||||||
|
movl $TARGET_HEIGHT, (framebuffer + 12)
|
||||||
|
movb $TARGET_BPP, (framebuffer + 16)
|
||||||
|
movb $1, (framebuffer + 17)
|
||||||
|
|
||||||
|
movw %cx, (vesa_target_mode)
|
||||||
|
jmp .vesa_find_video_mode_loop_modes_done
|
||||||
|
|
||||||
|
.vesa_find_video_mode_next_mode:
|
||||||
|
addl $2, %esi
|
||||||
|
jmp .vesa_find_video_mode_loop_modes
|
||||||
|
|
||||||
|
.vesa_find_video_mode_loop_modes_done:
|
||||||
|
popl %esi
|
||||||
|
popw %di
|
||||||
|
popw %cx
|
||||||
|
popw %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.vesa_unsupported:
|
||||||
|
movw $vesa_unsupported_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.vesa_unsupported_version:
|
||||||
|
movw $vesa_unsupported_version_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
.vesa_error:
|
||||||
|
movw $vesa_error_msg, %si
|
||||||
|
jmp print_and_halt
|
||||||
|
|
||||||
|
|
||||||
|
# set mode found from vesa_find_video_mode. if no mode
|
||||||
|
# was found, set it to 80x25 text mode to clear the screen.
|
||||||
|
.global vesa_set_target_mode
|
||||||
|
vesa_set_target_mode:
|
||||||
|
pushw %ax
|
||||||
|
pushw %bx
|
||||||
|
|
||||||
|
movw (vesa_target_mode), %bx
|
||||||
|
testw %bx, %bx
|
||||||
|
jz .vesa_set_target_mode_generic
|
||||||
|
|
||||||
|
movw $0x4F02, %ax
|
||||||
|
orw $0x4000, %bx
|
||||||
|
int $0x10
|
||||||
|
|
||||||
|
jmp .set_video_done
|
||||||
|
|
||||||
|
.vesa_set_target_mode_generic:
|
||||||
|
movb $0x03, %al
|
||||||
|
movb $0x00, %ah
|
||||||
|
int $0x10
|
||||||
|
|
||||||
|
.set_video_done:
|
||||||
|
popw %bx
|
||||||
|
popw %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
vesa_error_msg:
|
||||||
|
.asciz "VESA error"
|
||||||
|
vesa_unsupported_msg:
|
||||||
|
.asciz "VESA unsupported"
|
||||||
|
vesa_unsupported_version_msg:
|
||||||
|
.asciz "VESA unsupported version"
|
||||||
|
vesa_success_msg:
|
||||||
|
.asciz "VESA success"
|
||||||
|
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
vesa_info_buffer:
|
||||||
|
.skip 512
|
||||||
|
|
||||||
|
vesa_mode_info_buffer:
|
||||||
|
.skip 256
|
||||||
|
|
||||||
|
vesa_target_mode:
|
||||||
|
.skip 2
|
||||||
|
|
||||||
|
.global framebuffer
|
||||||
|
framebuffer:
|
||||||
|
.skip 4 # address
|
||||||
|
.skip 4 # pitch
|
||||||
|
.skip 4 # width
|
||||||
|
.skip 4 # height
|
||||||
|
.skip 1 # bpp
|
||||||
|
.skip 1 # type
|
|
@ -0,0 +1,15 @@
|
||||||
|
ENTRY(stage1_main)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x7C00;
|
||||||
|
.stage1 : { *(.stage1) }
|
||||||
|
|
||||||
|
. = ALIGN(512);
|
||||||
|
stage2_start = .;
|
||||||
|
.stage2 : { *(.stage2) }
|
||||||
|
stage2_end = .;
|
||||||
|
|
||||||
|
. = ALIGN(512);
|
||||||
|
.bss : { *(.bss) }
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
.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
|
||||||
|
|
||||||
|
.global memory_map
|
||||||
|
memory_map:
|
||||||
|
memory_map_entry_count:
|
||||||
|
.skip 4
|
||||||
|
# 100 entries should be enough...
|
||||||
|
memory_map_entries:
|
||||||
|
.skip 20 * 100
|
|
@ -0,0 +1,280 @@
|
||||||
|
.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
|
||||||
|
|
||||||
|
# prints 8 bit hexadecimal number to screen
|
||||||
|
# al: number to print
|
||||||
|
.global print_hex8
|
||||||
|
print_hex8:
|
||||||
|
pushw %ax
|
||||||
|
pushw %bx
|
||||||
|
pushw %cx
|
||||||
|
|
||||||
|
movw $16, %bx
|
||||||
|
movw $2, %cx
|
||||||
|
andw $0xFF, %ax
|
||||||
|
call print_number
|
||||||
|
|
||||||
|
popw %cx
|
||||||
|
popw %bx
|
||||||
|
popw %ax
|
||||||
|
ret
|
||||||
|
|
||||||
|
# prints 16 bit hexadecimal number to screen
|
||||||
|
# ax: number to print
|
||||||
|
.global print_hex16
|
||||||
|
print_hex16:
|
||||||
|
pushw %bx
|
||||||
|
pushw %cx
|
||||||
|
|
||||||
|
movw $16, %bx
|
||||||
|
movw $4, %cx
|
||||||
|
call print_number
|
||||||
|
|
||||||
|
popw %cx
|
||||||
|
popw %bx
|
||||||
|
ret
|
||||||
|
|
||||||
|
# prints 32 bit hexadecimal number to screen
|
||||||
|
# eax: number to print
|
||||||
|
.global print_hex32
|
||||||
|
print_hex32:
|
||||||
|
pushl %eax
|
||||||
|
pushw %dx
|
||||||
|
|
||||||
|
movw %ax, %dx
|
||||||
|
|
||||||
|
shrl $16, %eax;
|
||||||
|
call print_hex16
|
||||||
|
|
||||||
|
movw %dx, %ax
|
||||||
|
call print_hex16
|
||||||
|
|
||||||
|
popw %dx
|
||||||
|
popl %eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
# prints 64 bit hexadecimal number to screen
|
||||||
|
# edx:eax: number to print
|
||||||
|
.global print_hex64
|
||||||
|
print_hex64:
|
||||||
|
xchgl %eax, %edx
|
||||||
|
call print_hex32
|
||||||
|
xchgl %eax, %edx
|
||||||
|
call print_hex32
|
||||||
|
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
|
|
@ -0,0 +1 @@
|
||||||
|
build/
|
|
@ -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)
|
|
@ -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 {};
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 }
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);
|
|
@ -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;
|
||||||
|
}
|
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
|
|
|
@ -1,3 +0,0 @@
|
||||||
*.d
|
|
||||||
*.kernel
|
|
||||||
*.o
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
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/BootInfo.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);
|
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()
|
static void flush_gdt()
|
||||||
{
|
{
|
||||||
asm volatile("lgdt %0" :: "m"(s_gdtr));
|
asm volatile("lgdt %0" :: "m"(s_gdtr));
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <kernel/IDT.h>
|
#include <kernel/IDT.h>
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
#include <kernel/kmalloc.h>
|
#include <kernel/Memory/kmalloc.h>
|
||||||
#include <kernel/Panic.h>
|
#include <kernel/Panic.h>
|
||||||
|
#include <kernel/Scheduler.h>
|
||||||
|
|
||||||
#define INTERRUPT_HANDLER____(i, msg) \
|
#define INTERRUPT_HANDLER____(i, msg) \
|
||||||
static void interrupt ## i () \
|
static void interrupt ## i () \
|
||||||
|
@ -132,6 +133,8 @@ found:
|
||||||
// NOTE: Scheduler sends PIT eoi's
|
// NOTE: Scheduler sends PIT eoi's
|
||||||
if (irq != PIT_IRQ)
|
if (irq != PIT_IRQ)
|
||||||
InterruptController::get().eoi(irq);
|
InterruptController::get().eoi(irq);
|
||||||
|
|
||||||
|
Kernel::Scheduler::get().reschedule_if_idling();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void handle_irq_common();
|
extern "C" void handle_irq_common();
|
||||||
|
@ -151,7 +154,7 @@ found:
|
||||||
"popa;"
|
"popa;"
|
||||||
"iret;"
|
"iret;"
|
||||||
);
|
);
|
||||||
|
|
||||||
extern "C" void syscall_asm();
|
extern "C" void syscall_asm();
|
||||||
asm(
|
asm(
|
||||||
".global syscall_asm;"
|
".global syscall_asm;"
|
||||||
|
@ -171,7 +174,18 @@ found:
|
||||||
"addl $16, %esp;"
|
"addl $16, %esp;"
|
||||||
"popw %es;"
|
"popw %es;"
|
||||||
"popw %ds;"
|
"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;"
|
"iret;"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,136 +1,227 @@
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <kernel/Debug.h>
|
#include <kernel/Debug.h>
|
||||||
#include <kernel/MMU.h>
|
#include <kernel/Memory/MMU.h>
|
||||||
#include <kernel/kmalloc.h>
|
#include <kernel/Memory/kmalloc.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define MMU_DEBUG_PRINT 0
|
#define MMU_DEBUG_PRINT 0
|
||||||
|
|
||||||
// bits 31-12 set
|
// bits 31-12 set
|
||||||
#define PAGE_MASK 0xfffff000
|
#define PAGE_MASK 0xfffff000
|
||||||
#define PAGE_SIZE 0x00001000
|
#define FLAGS_MASK 0x00000fff
|
||||||
|
|
||||||
static MMU* s_instance = nullptr;
|
namespace Kernel
|
||||||
|
|
||||||
void MMU::intialize()
|
|
||||||
{
|
{
|
||||||
ASSERT(s_instance == nullptr);
|
|
||||||
s_instance = new MMU();
|
|
||||||
}
|
|
||||||
|
|
||||||
MMU& MMU::get()
|
static MMU* s_instance = nullptr;
|
||||||
{
|
|
||||||
ASSERT(s_instance);
|
|
||||||
return *s_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t* allocate_page_aligned_page()
|
void MMU::initialize()
|
||||||
{
|
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
uint64_t* page_directory = allocate_page_aligned_page();
|
ASSERT(s_instance == nullptr);
|
||||||
m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present;
|
s_instance = new MMU();
|
||||||
|
ASSERT(s_instance);
|
||||||
|
s_instance->initialize_kernel();
|
||||||
|
s_instance->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
// create and identity map first 6 MiB
|
MMU& MMU::get()
|
||||||
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();
|
ASSERT(s_instance);
|
||||||
for (uint64_t j = 0; j < 512; j++)
|
return *s_instance;
|
||||||
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
|
static uint64_t* allocate_page_aligned_page()
|
||||||
// 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))
|
|
||||||
{
|
{
|
||||||
uint64_t* page_table = allocate_page_aligned_page();
|
uint64_t* page = (uint64_t*)kmalloc(PAGE_SIZE, PAGE_SIZE);
|
||||||
page_directory[pde] = (uint64_t)page_table;
|
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);
|
void MMU::initialize_kernel()
|
||||||
page_table[pte] = address | flags;
|
{
|
||||||
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,23 @@ continue_thread:
|
||||||
movl 8(%esp), %ecx
|
movl 8(%esp), %ecx
|
||||||
movl 4(%esp), %esp
|
movl 4(%esp), %esp
|
||||||
movl $0, %eax
|
movl $0, %eax
|
||||||
jmp *%ecx
|
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:
|
g_boot_stack_top:
|
||||||
|
|
||||||
# 0 MiB -> 1 MiB: bootloader stuff
|
# 0 MiB -> 1 MiB: bootloader stuff
|
||||||
# 1 MiB -> 2 MiB: kernel
|
# 1 MiB -> : kernel
|
||||||
# 2 MiB -> 3 MiB: kmalloc
|
|
||||||
# 3 MiB -> 4 Mib: kmalloc_fixed
|
|
||||||
.align 32
|
.align 32
|
||||||
boot_page_directory_pointer_table:
|
boot_page_directory_pointer_table:
|
||||||
.skip 4 * 8
|
.skip 4 * 8
|
||||||
|
|
|
@ -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;
|
. = 0x00100000;
|
||||||
|
|
||||||
|
g_kernel_start = .;
|
||||||
.text BLOCK(4K) : ALIGN(4K)
|
.text BLOCK(4K) : ALIGN(4K)
|
||||||
{
|
{
|
||||||
*(.multiboot)
|
*(.multiboot)
|
||||||
|
@ -11,9 +12,7 @@ SECTIONS
|
||||||
}
|
}
|
||||||
.rodata BLOCK(4K) : ALIGN(4K)
|
.rodata BLOCK(4K) : ALIGN(4K)
|
||||||
{
|
{
|
||||||
g_rodata_start = .;
|
|
||||||
*(.rodata.*)
|
*(.rodata.*)
|
||||||
g_rodata_end = .;
|
|
||||||
}
|
}
|
||||||
.data BLOCK(4K) : ALIGN(4K)
|
.data BLOCK(4K) : ALIGN(4K)
|
||||||
{
|
{
|
||||||
|
@ -26,13 +25,4 @@ SECTIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
g_kernel_end = .;
|
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 \
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue