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 |
13
.clangd
13
.clangd
|
@ -1,13 +0,0 @@
|
|||
Diagnostics:
|
||||
Suppress: target_unsupported_type
|
||||
|
||||
CompileFlags:
|
||||
Remove: [
|
||||
-fstrict-volatile-bitfields,
|
||||
-fno-tree-loop-distribute-patterns
|
||||
]
|
||||
Add: [
|
||||
-D__banan_os__,
|
||||
-D__arch__=x86_64,
|
||||
-D__x86_64__
|
||||
]
|
|
@ -0,0 +1,4 @@
|
|||
[submodule "kernel/lai"]
|
||||
path = kernel/lai
|
||||
url = https://github.com/managarm/lai.git
|
||||
ignore = untracked
|
|
@ -1,8 +0,0 @@
|
|||
# .pre-commit-config.yaml
|
||||
exclude: '.patch$'
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0 # this is optional, use `pre-commit autoupdate` to get the latest rev!
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "banan-os",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/BAN/include",
|
||||
"${workspaceFolder}/kernel/include",
|
||||
"${workspaceFolder}/userspace/libraries/*/include"
|
||||
],
|
||||
"defines": [
|
||||
"__arch=x86_64",
|
||||
"__enable_sse=1"
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++20",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
},
|
||||
{
|
||||
"name": "banan-os-kernel",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/BAN/include",
|
||||
"${workspaceFolder}/kernel/include",
|
||||
"${workspaceFolder}/userspace/libraries/*/include"
|
||||
],
|
||||
"defines": [
|
||||
"__arch=x86_64",
|
||||
"__is_kernel",
|
||||
"__enable_sse=1"
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/toolchain/local/bin/x86_64-banan_os-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++20",
|
||||
"intelliSenseMode": "linux-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"cmake.configureOnOpen": false,
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": false,
|
||||
"editor.detectIndentation": false,
|
||||
"clangd.arguments": [
|
||||
"--compile-commands-dir=${workspaceFolder}/build",
|
||||
"-header-insertion=never"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
*.a
|
||||
*.d
|
||||
*.o
|
|
@ -1,22 +0,0 @@
|
|||
#include <BAN/Assert.h>
|
||||
|
||||
#if __is_kernel
|
||||
|
||||
#include <kernel/Panic.h>
|
||||
|
||||
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
|
||||
{
|
||||
Kernel::panic_impl(location, msg);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <BAN/Debug.h>
|
||||
|
||||
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg)
|
||||
{
|
||||
derrorln("{}: {}", location, msg);
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,269 @@
|
|||
#include <BAN/String.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
String::String()
|
||||
{
|
||||
}
|
||||
|
||||
String::String(const String& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
String::String(String&& other)
|
||||
{
|
||||
*this = move(other);
|
||||
}
|
||||
|
||||
String::String(StringView other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
String::~String()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
String& String::operator=(const String& other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(String&& other)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (other.has_sso())
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
else
|
||||
{
|
||||
m_storage.general_storage = other.m_storage.general_storage;
|
||||
m_has_sso = false;
|
||||
}
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_size = 0;
|
||||
other.m_storage.sso_storage = SSOStorage();
|
||||
other.m_has_sso = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator=(StringView other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size());
|
||||
m_size = other.size();
|
||||
data()[m_size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::push_back(char c)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
data()[m_size] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::insert(char c, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
memmove(data() + index + 1, data() + index, m_size - index);
|
||||
data()[index] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::insert(StringView str, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||
memcpy(data() + index, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::append(StringView str)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memcpy(data() + m_size, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
void String::pop_back()
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void String::remove(size_type index)
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
memcpy(data() + index, data() + index + 1, m_size - index);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void String::clear()
|
||||
{
|
||||
if (!has_sso())
|
||||
{
|
||||
deallocator(m_storage.general_storage.data);
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
}
|
||||
m_size = 0;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
bool String::operator==(StringView str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool String::operator==(const char* cstr) const
|
||||
{
|
||||
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)
|
||||
{
|
||||
TRY(ensure_capacity(new_size));
|
||||
memset(data() + m_size, init_c, new_size - m_size);
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::reserve(size_type new_size)
|
||||
{
|
||||
TRY(ensure_capacity(new_size));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> String::shrink_to_fit()
|
||||
{
|
||||
if (has_sso())
|
||||
return {};
|
||||
|
||||
if (fits_in_sso())
|
||||
{
|
||||
char* data = m_storage.general_storage.data;
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
memcpy(this->data(), data, m_size + 1);
|
||||
deallocator(data);
|
||||
return {};
|
||||
}
|
||||
|
||||
GeneralStorage& storage = m_storage.general_storage;
|
||||
if (storage.capacity == m_size)
|
||||
return {};
|
||||
|
||||
char* new_data = (char*)allocator(m_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
memcpy(new_data, storage.data, m_size);
|
||||
deallocator(storage.data);
|
||||
|
||||
storage.capacity = m_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String::size_type String::capacity() const
|
||||
{
|
||||
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
|
||||
{
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
|
||||
ErrorOr<void> String::ensure_capacity(size_type new_size)
|
||||
{
|
||||
if (m_size >= new_size)
|
||||
return {};
|
||||
if (has_sso() && fits_in_sso(new_size))
|
||||
return {};
|
||||
|
||||
char* new_data = (char*)allocator(new_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
memcpy(new_data, data(), m_size + 1);
|
||||
|
||||
if (has_sso())
|
||||
{
|
||||
m_storage.general_storage = GeneralStorage();
|
||||
m_has_sso = false;
|
||||
}
|
||||
else
|
||||
deallocator(m_storage.general_storage.data);
|
||||
|
||||
auto& storage = m_storage.general_storage;
|
||||
storage.capacity = new_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool String::has_sso() const
|
||||
{
|
||||
return m_has_sso;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,180 @@
|
|||
#include <BAN/String.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
StringView::StringView()
|
||||
{ }
|
||||
|
||||
StringView::StringView(const String& other)
|
||||
: StringView(other.data(), other.size())
|
||||
{ }
|
||||
|
||||
StringView::StringView(const char* string, size_type len)
|
||||
{
|
||||
if (len == size_type(-1))
|
||||
len = strlen(string);
|
||||
m_data = string;
|
||||
m_size = len;
|
||||
}
|
||||
|
||||
char StringView::operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
bool StringView::operator==(const String& other) const
|
||||
{
|
||||
if (m_size != other.size())
|
||||
return false;
|
||||
return memcmp(m_data, other.data(), m_size) == 0;
|
||||
}
|
||||
|
||||
bool StringView::operator==(StringView other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
return false;
|
||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
||||
}
|
||||
|
||||
bool StringView::operator==(const char* other) const
|
||||
{
|
||||
if (memcmp(m_data, other, m_size))
|
||||
return false;
|
||||
return other[m_size] == '\0';
|
||||
}
|
||||
|
||||
StringView StringView::substring(size_type index, size_type len) const
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
if (len == size_type(-1))
|
||||
len = m_size - index;
|
||||
ASSERT(len <= m_size - index); // weird order to avoid overflow
|
||||
StringView result;
|
||||
result.m_data = m_data + index;
|
||||
result.m_size = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<StringView>> StringView::split(char delim, bool allow_empties)
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<StringView>> StringView::split(bool(*comp)(char), bool allow_empties)
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (comp(m_data[i]))
|
||||
{
|
||||
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 (comp(m_data[i]))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
char StringView::back() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[m_size - 1];
|
||||
}
|
||||
|
||||
char StringView::front() const
|
||||
{
|
||||
ASSERT(m_size > 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
|
||||
{
|
||||
size_type result = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StringView::empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
StringView::size_type StringView::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
const char* StringView::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +1,24 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(BAN CXX)
|
||||
|
||||
set(BAN_SOURCES
|
||||
BAN/Assert.cpp
|
||||
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})
|
||||
target_link_options(ban PRIVATE -nolibc)
|
||||
banan_link_library(ban libc)
|
||||
add_dependencies(ban headers libc-install)
|
||||
|
||||
set_target_properties(ban PROPERTIES OUTPUT_NAME libban)
|
||||
|
||||
banan_install_headers(ban)
|
||||
install(TARGETS ban OPTIONAL)
|
||||
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
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Span.h>
|
||||
|
||||
|
@ -18,7 +19,7 @@ namespace BAN
|
|||
using const_iterator = ConstIteratorSimple<T, Array>;
|
||||
|
||||
public:
|
||||
Array() = default;
|
||||
Array();
|
||||
Array(const T&);
|
||||
|
||||
iterator begin() { return iterator(m_data); }
|
||||
|
@ -43,9 +44,16 @@ namespace BAN
|
|||
T* data() { return m_data; }
|
||||
|
||||
private:
|
||||
T m_data[S] {};
|
||||
T m_data[S];
|
||||
};
|
||||
|
||||
template<typename T, size_t S>
|
||||
Array<T, S>::Array()
|
||||
{
|
||||
for (size_type i = 0; i < S; i++)
|
||||
m_data[i] = T();
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
Array<T, S>::Array(const T& value)
|
||||
{
|
||||
|
|
|
@ -1,14 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#define __ban_assert_stringify_helper(s) #s
|
||||
#define __ban_assert_stringify(s) __ban_assert_stringify_helper(s)
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#if defined(__is_kernel)
|
||||
#include <kernel/Panic.h>
|
||||
|
||||
#define ASSERT(cond) \
|
||||
(__builtin_expect(!(cond), 0) \
|
||||
? __ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT(" #cond ") failed") \
|
||||
: (void)0)
|
||||
do { \
|
||||
if (!(cond)) \
|
||||
Kernel::panic("ASSERT(" #cond ") failed"); \
|
||||
} while (false)
|
||||
|
||||
#define ASSERT_NOT_REACHED() \
|
||||
__ban_assertion_failed(__FILE__ ":" __ban_assert_stringify(__LINE__), "ASSERT_NOT_REACHED() reached")
|
||||
#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)
|
||||
|
||||
[[noreturn]] void __ban_assertion_failed(const char* location, const char* msg);
|
||||
#define ASSERT_LT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LT", <)
|
||||
#define ASSERT_LTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LTE", <=)
|
||||
#define ASSERT_GT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GT", >)
|
||||
#define ASSERT_GTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GTE", >=)
|
||||
#define ASSERT_EQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_EQ", ==)
|
||||
#define ASSERT_NEQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_NEQ", !=)
|
||||
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
|
||||
#else
|
||||
#include <assert.h>
|
||||
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
|
||||
#define ASSERT_NOT_REACHED() do { assert(false && "ASSERT_NOT_REACHED() failed"); __builtin_unreachable(); } while (false)
|
||||
#endif
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
enum MemoryOrder
|
||||
{
|
||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||
memory_order_consume = __ATOMIC_CONSUME,
|
||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
||||
memory_order_release = __ATOMIC_RELEASE,
|
||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
||||
memory_order_seq_cst = __ATOMIC_SEQ_CST,
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
|
||||
requires requires { __atomic_always_lock_free(sizeof(T), 0); }
|
||||
class Atomic
|
||||
{
|
||||
Atomic(const Atomic&) = delete;
|
||||
Atomic(Atomic&&) = delete;
|
||||
Atomic& operator=(const Atomic&) volatile = delete;
|
||||
Atomic& operator=(Atomic&&) volatile = delete;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : m_value(0) {}
|
||||
constexpr Atomic(T val) : m_value(val) {}
|
||||
|
||||
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return __atomic_load_n(&m_value, mem_order); }
|
||||
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { __atomic_store_n(&m_value, val, mem_order); }
|
||||
|
||||
inline T operator=(T val) volatile { store(val); return val; }
|
||||
|
||||
inline operator T() const volatile { return load(); }
|
||||
|
||||
inline T operator+=(T val) volatile { return __atomic_add_fetch(&m_value, val, MEM_ORDER); }
|
||||
inline T operator-=(T val) volatile { return __atomic_sub_fetch(&m_value, val, MEM_ORDER); }
|
||||
inline T operator&=(T val) volatile { return __atomic_and_fetch(&m_value, val, MEM_ORDER); }
|
||||
inline T operator^=(T val) volatile { return __atomic_xor_fetch(&m_value, val, MEM_ORDER); }
|
||||
inline T operator|=(T val) volatile { return __atomic_or_fetch(&m_value, val, MEM_ORDER); }
|
||||
|
||||
inline T operator--() volatile { return __atomic_sub_fetch(&m_value, 1, MEM_ORDER); }
|
||||
inline T operator++() volatile { return __atomic_add_fetch(&m_value, 1, MEM_ORDER); }
|
||||
|
||||
inline T operator--(int) volatile { return __atomic_fetch_sub(&m_value, 1, MEM_ORDER); }
|
||||
inline T operator++(int) volatile { return __atomic_fetch_add(&m_value, 1, MEM_ORDER); }
|
||||
|
||||
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_compare_exchange_n(&m_value, &expected, desired, false, mem_order, mem_order); }
|
||||
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_exchange_n(&m_value, desired, mem_order); };
|
||||
|
||||
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_add_fetch (&m_value, val, mem_order); }
|
||||
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_sub_fetch (&m_value, val, mem_order); }
|
||||
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_and_fetch (&m_value, val, mem_order); }
|
||||
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_xor_fetch (&m_value, val, mem_order); }
|
||||
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_or_fetch (&m_value, val, mem_order); }
|
||||
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_nand_fetch(&m_value, val, mem_order); }
|
||||
|
||||
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_add (&m_value, val, mem_order); }
|
||||
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_sub (&m_value, val, mem_order); }
|
||||
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_and (&m_value, val, mem_order); }
|
||||
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_xor (&m_value, val, mem_order); }
|
||||
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch__or (&m_value, val, mem_order); }
|
||||
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_nfetch_and(&m_value, val, mem_order); }
|
||||
|
||||
private:
|
||||
T m_value;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename To, typename From>
|
||||
constexpr To bit_cast(const From& from)
|
||||
{
|
||||
return __builtin_bit_cast(To, from);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include <BAN/Span.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
|
@ -21,56 +19,60 @@ namespace BAN
|
|||
, m_size(size)
|
||||
{ }
|
||||
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
|
||||
ByteSpanGeneral(ByteSpanGeneral& other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ByteSpanGeneral(const Span<T>& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
|
||||
template<bool C2>
|
||||
ByteSpanGeneral(const ByteSpanGeneral<C2>& other) requires(CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
template<typename T>
|
||||
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
|
||||
ByteSpanGeneral(Span<uint8_t> other)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
{ }
|
||||
ByteSpanGeneral(const Span<const uint8_t>& other) requires(CONST)
|
||||
: m_data(other.data())
|
||||
, m_size(other.size())
|
||||
{ }
|
||||
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
|
||||
ByteSpanGeneral& operator=(ByteSpanGeneral other)
|
||||
{
|
||||
m_data = other.data();
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
template<bool SRC_CONST>
|
||||
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
|
||||
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();
|
||||
other.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>)
|
||||
requires(CONST || !is_const_v<S>)
|
||||
static ByteSpanGeneral from(S& value)
|
||||
{
|
||||
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
S& as() const requires(!CONST || is_const_v<S>)
|
||||
requires(!CONST && !is_const_v<S>)
|
||||
S& as()
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(m_size >= sizeof(S));
|
||||
|
@ -78,13 +80,30 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename S>
|
||||
Span<S> as_span() const requires(!CONST || is_const_v<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));
|
||||
}
|
||||
|
||||
[[nodiscard]] ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1)) const
|
||||
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);
|
||||
|
@ -94,28 +113,25 @@ namespace BAN
|
|||
return ByteSpanGeneral(m_data + offset, length);
|
||||
}
|
||||
|
||||
value_type& operator[](size_type offset) const
|
||||
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() const { return m_data; }
|
||||
value_type* data() { return m_data; }
|
||||
const value_type* data() const { return m_data; }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_data = nullptr;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type* m_data { nullptr };
|
||||
size_type m_size { 0 };
|
||||
|
||||
friend class ByteSpanGeneral<!CONST>;
|
||||
};
|
||||
|
||||
using ByteSpan = ByteSpanGeneral<false>;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
@ -24,16 +22,13 @@ namespace BAN
|
|||
void push(const T&);
|
||||
void push(T&&);
|
||||
template<typename... Args>
|
||||
void emplace(Args&&... args) requires is_constructible_v<T, Args...>;
|
||||
void emplace(Args&&... args);
|
||||
|
||||
void pop();
|
||||
|
||||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
const T& back() const;
|
||||
T& back();
|
||||
|
||||
size_type size() const { return m_size; }
|
||||
bool empty() const { return size() == 0; }
|
||||
bool full() const { return size() == capacity(); }
|
||||
|
@ -71,7 +66,7 @@ namespace BAN
|
|||
|
||||
template<typename T, size_t S>
|
||||
template<typename... Args>
|
||||
void CircularQueue<T, S>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
void CircularQueue<T, S>::emplace(Args&&... args)
|
||||
{
|
||||
ASSERT(!full());
|
||||
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
|
||||
|
@ -101,20 +96,6 @@ namespace BAN
|
|||
return *element_at(m_first);
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T& CircularQueue<T, S>::back() const
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return *element_at((m_first + m_size - 1) % capacity());
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
T& CircularQueue<T, S>::back()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return *element_at((m_first + m_size - 1) % capacity());
|
||||
}
|
||||
|
||||
template<typename T, size_t S>
|
||||
const T* CircularQueue<T, S>::element_at(size_type index) const
|
||||
{
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#if __is_kernel
|
||||
|
||||
#include <kernel/Debug.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define __debug_putchar [](int c) { putc(c, stddbg); }
|
||||
|
||||
#define dprintln(...) \
|
||||
do { \
|
||||
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::print(__debug_putchar,"\r\n"); \
|
||||
fflush(stddbg); \
|
||||
} while (false)
|
||||
|
||||
#define dwarnln(...) \
|
||||
do { \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
|
||||
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
|
||||
fflush(stddbg); \
|
||||
} while(false)
|
||||
|
||||
#define derrorln(...) \
|
||||
do { \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
|
||||
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
|
||||
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
|
||||
fflush(stddbg); \
|
||||
} while(false)
|
||||
|
||||
#define dprintln_if(cond, ...) \
|
||||
do { \
|
||||
if constexpr(cond) \
|
||||
dprintln(__VA_ARGS__); \
|
||||
} while(false)
|
||||
|
||||
#define dwarnln_if(cond, ...) \
|
||||
do { \
|
||||
if constexpr(cond) \
|
||||
dwarnln(__VA_ARGS__); \
|
||||
} while(false)
|
||||
|
||||
#define derrorln_if(cond, ...) \
|
||||
do { \
|
||||
if constexpr(cond) \
|
||||
derrorln(__VA_ARGS__); \
|
||||
} while(false)
|
||||
|
||||
#endif
|
|
@ -45,12 +45,6 @@ namespace BAN
|
|||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T little_endian_to_host(T value)
|
||||
{
|
||||
return host_to_little_endian(value);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_big_endian(T value)
|
||||
{
|
||||
|
@ -61,28 +55,13 @@ namespace BAN
|
|||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T big_endian_to_host(T value)
|
||||
{
|
||||
return host_to_big_endian(value);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
struct LittleEndian
|
||||
{
|
||||
constexpr LittleEndian()
|
||||
: raw(0)
|
||||
{ }
|
||||
|
||||
constexpr LittleEndian(T value)
|
||||
: raw(host_to_little_endian(value))
|
||||
{ }
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_little_endian(raw);
|
||||
}
|
||||
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
@ -90,36 +69,12 @@ namespace BAN
|
|||
template<integral T>
|
||||
struct BigEndian
|
||||
{
|
||||
constexpr BigEndian()
|
||||
: raw(0)
|
||||
{ }
|
||||
|
||||
constexpr BigEndian(T value)
|
||||
: raw(host_to_big_endian(value))
|
||||
{ }
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
return host_to_big_endian(raw);
|
||||
}
|
||||
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
||||
template<integral T>
|
||||
using NetworkEndian = BigEndian<T>;
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_network_endian(T value)
|
||||
{
|
||||
return host_to_big_endian(value);
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T network_endian_to_host(T value)
|
||||
{
|
||||
return big_endian_to_host(value);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <BAN/Variant.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -13,9 +13,9 @@
|
|||
#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
|
||||
#include <BAN/Debug.h>
|
||||
#define MUST(expr) ({ auto&& e = expr; if (e.is_error()) { derrorln("MUST(" #expr "): {}", e.error()); __builtin_trap(); } e.release_value(); })
|
||||
#define MUST_REF(expr) *({ auto&& e = expr; if (e.is_error()) { derrorln("MUST(" #expr "): {}", e.error()); __builtin_trap(); } &e.release_value(); })
|
||||
#include <assert.h>
|
||||
#define MUST(expr) ({ auto&& e = expr; assert(!e.is_error()); e.release_value(); })
|
||||
#define MUST_REF(expr) *({ auto&& e = expr; assert(!e.is_error()); &e.release_value(); })
|
||||
#endif
|
||||
|
||||
#define TRY(expr) ({ auto&& e = expr; if (e.is_error()) return e.release_error(); e.release_value(); })
|
||||
|
@ -37,14 +37,7 @@ namespace BAN
|
|||
{
|
||||
return Error((uint64_t)error | kernel_error_mask);
|
||||
}
|
||||
#else
|
||||
template<size_t N>
|
||||
consteval static Error from_literal(const char (&message)[N])
|
||||
{
|
||||
return Error(message);
|
||||
}
|
||||
#endif
|
||||
|
||||
static Error from_errno(int error)
|
||||
{
|
||||
return Error(error);
|
||||
|
@ -62,43 +55,27 @@ namespace BAN
|
|||
}
|
||||
#endif
|
||||
|
||||
constexpr uint64_t get_error_code() const { return m_error_code; }
|
||||
const char* get_message() const
|
||||
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());
|
||||
#else
|
||||
if (m_message)
|
||||
return m_message;
|
||||
#endif
|
||||
if (auto* desc = strerrordesc_np(m_error_code))
|
||||
return desc;
|
||||
return "Unknown error";
|
||||
return strerror(m_error_code);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr Error(uint64_t error)
|
||||
Error(uint64_t error)
|
||||
: m_error_code(error)
|
||||
{}
|
||||
|
||||
#ifndef __is_kernel
|
||||
constexpr Error(const char* message)
|
||||
: m_message(message)
|
||||
{}
|
||||
#endif
|
||||
|
||||
uint64_t m_error_code { 0 };
|
||||
|
||||
#ifndef __is_kernel
|
||||
const char* m_message { nullptr };
|
||||
#endif
|
||||
uint64_t m_error_code;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class [[nodiscard]] ErrorOr
|
||||
{
|
||||
BAN_NON_COPYABLE(ErrorOr);
|
||||
public:
|
||||
ErrorOr(const T& value)
|
||||
: m_data(value)
|
||||
|
@ -112,14 +89,6 @@ namespace BAN
|
|||
ErrorOr(Error&& error)
|
||||
: m_data(move(error))
|
||||
{}
|
||||
ErrorOr(ErrorOr&& other)
|
||||
: m_data(move(other.m_data))
|
||||
{}
|
||||
ErrorOr& operator=(ErrorOr&& other)
|
||||
{
|
||||
m_data = move(other.m_data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_error() const { return m_data.template has<Error>(); }
|
||||
const Error& error() const { return m_data.template get<Error>(); }
|
||||
|
|
|
@ -10,19 +10,22 @@ namespace BAN::Formatter
|
|||
|
||||
struct ValueFormat;
|
||||
|
||||
template<typename F>
|
||||
static void print(F putc, const char* format);
|
||||
|
||||
template<typename F, typename Arg, typename... Args>
|
||||
static void print(F putc, const char* format, Arg&& arg, Args&&... args);
|
||||
|
||||
template<typename F, typename... Args>
|
||||
concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format)
|
||||
{
|
||||
(print_argument(putc, BAN::forward<Args>(args), format), ...);
|
||||
};
|
||||
static void println(F putc, const char* format, Args&&... args);
|
||||
|
||||
template<typename F, typename T>
|
||||
inline void print_argument(F putc, T value, const ValueFormat& format);
|
||||
static void print_argument(F putc, T value, const ValueFormat& format);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename F, typename T>
|
||||
inline 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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -36,12 +39,11 @@ namespace BAN::Formatter
|
|||
int base = 10;
|
||||
int percision = 3;
|
||||
int fill = 0;
|
||||
char fill_char = '0';
|
||||
bool upper = false;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
inline void print(F putc, const char* format)
|
||||
void print(F putc, const char* format)
|
||||
{
|
||||
while (*format)
|
||||
{
|
||||
|
@ -50,8 +52,8 @@ namespace BAN::Formatter
|
|||
}
|
||||
}
|
||||
|
||||
template<typename F, typename Arg, typename... Args> requires PrintableArguments<F, Arg, Args...>
|
||||
inline void print(F putc, const char* format, Arg&& arg, Args&&... args)
|
||||
template<typename F, typename Arg, typename... Args>
|
||||
void print(F putc, const char* format, Arg&& arg, Args&&... args)
|
||||
{
|
||||
while (*format && *format != '{')
|
||||
{
|
||||
|
@ -69,7 +71,7 @@ namespace BAN::Formatter
|
|||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
inline void println(F putc, const char* format, Args&&... args)
|
||||
void println(F putc, const char* format, Args&&... args)
|
||||
{
|
||||
print(putc, format, args...);
|
||||
putc('\n');
|
||||
|
@ -79,7 +81,7 @@ namespace BAN::Formatter
|
|||
{
|
||||
|
||||
template<typename F, typename Arg>
|
||||
inline size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
|
||||
size_t parse_format_and_print_argument(F putc, const char* format, Arg&& argument)
|
||||
{
|
||||
ValueFormat value_format;
|
||||
|
||||
|
@ -92,12 +94,6 @@ namespace BAN::Formatter
|
|||
if (!format[i] || format[i] == '}')
|
||||
break;
|
||||
|
||||
if (format[i] == ' ')
|
||||
{
|
||||
value_format.fill_char = ' ';
|
||||
i++;
|
||||
}
|
||||
|
||||
if ('0' <= format[i] && format[i] <= '9')
|
||||
{
|
||||
int fill = 0;
|
||||
|
@ -147,7 +143,7 @@ namespace BAN::Formatter
|
|||
return i + 1;
|
||||
}
|
||||
|
||||
inline char value_to_base_char(uint8_t value, int base, bool upper)
|
||||
static char value_to_base_char(uint8_t value, int base, bool upper)
|
||||
{
|
||||
if (base <= 10)
|
||||
return value + '0';
|
||||
|
@ -161,12 +157,11 @@ namespace BAN::Formatter
|
|||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
inline void print_integer(F putc, T value, const ValueFormat& format)
|
||||
void print_integer(F putc, T value, const ValueFormat& format)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
for (int i = 0; i < format.fill - 1; i++)
|
||||
putc(format.fill_char);
|
||||
for (int i = 0; i < format.fill || i < 1; i++)
|
||||
putc('0');
|
||||
return;
|
||||
}
|
||||
|
@ -193,7 +188,7 @@ namespace BAN::Formatter
|
|||
}
|
||||
|
||||
while (ptr >= buffer + sizeof(buffer) - format.fill)
|
||||
*(--ptr) = format.fill_char;
|
||||
*(--ptr) = '0';
|
||||
|
||||
if (sign)
|
||||
*(--ptr) = '-';
|
||||
|
@ -202,7 +197,7 @@ namespace BAN::Formatter
|
|||
}
|
||||
|
||||
template<typename F, typename T>
|
||||
inline void print_floating(F putc, T value, const ValueFormat& format)
|
||||
void print_floating(F putc, T value, const ValueFormat& format)
|
||||
{
|
||||
int64_t int_part = (int64_t)value;
|
||||
T frac_part = value - (T)int_part;
|
||||
|
@ -225,7 +220,7 @@ namespace BAN::Formatter
|
|||
}
|
||||
|
||||
template<typename F>
|
||||
inline void print_pointer(F putc, void* ptr, const ValueFormat& format)
|
||||
void print_pointer(F putc, void* ptr, const ValueFormat& format)
|
||||
{
|
||||
uintptr_t value = (uintptr_t)ptr;
|
||||
print(putc, "0x");
|
||||
|
@ -241,13 +236,13 @@ namespace BAN::Formatter
|
|||
|
||||
*/
|
||||
|
||||
template<typename F, integral T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||
template<typename F, floating_point T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
|
||||
template<typename F, pointer T> inline void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
||||
template<typename F, integral T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_integer(putc, value, format); }
|
||||
template<typename F, floating_point T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_floating(putc, value, format); }
|
||||
template<typename F, pointer T> void print_argument(F putc, T value, const ValueFormat& format) { detail::print_pointer(putc, (void*)value, format); }
|
||||
|
||||
template<typename F> inline void print_argument(F putc, char value, const ValueFormat&) { putc(value); }
|
||||
template<typename F> inline void print_argument(F putc, bool value, const ValueFormat&) { print(putc, value ? "true" : "false"); }
|
||||
template<typename F> inline void print_argument(F putc, const char* value, const ValueFormat&) { print(putc, value); }
|
||||
template<typename F> inline void print_argument(F putc, char* value, const ValueFormat&) { print(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, const char* value, const ValueFormat&) { print(putc, value); }
|
||||
template<typename F> void print_argument(F putc, char* value, const ValueFormat&) { print(putc, value); }
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
#include <BAN/New.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -20,19 +20,19 @@ namespace BAN
|
|||
new (m_storage) CallablePointer(function);
|
||||
}
|
||||
template<typename Own>
|
||||
Function(Ret(Own::*function)(Args...), Own& owner)
|
||||
Function(Ret(Own::*function)(Args...), Own* owner)
|
||||
{
|
||||
static_assert(sizeof(CallableMember<Own>) <= m_size);
|
||||
new (m_storage) CallableMember<Own>(function, owner);
|
||||
}
|
||||
template<typename Own>
|
||||
Function(Ret(Own::*function)(Args...) const, const Own& owner)
|
||||
Function(Ret(Own::*function)(Args...) const, const Own* owner)
|
||||
{
|
||||
static_assert(sizeof(CallableMemberConst<Own>) <= m_size);
|
||||
new (m_storage) CallableMemberConst<Own>(function, owner);
|
||||
}
|
||||
template<typename Lambda>
|
||||
Function(Lambda lambda) requires requires(Lambda lamda, Args&&... args) { { lambda(forward<Args>(args)...) } -> BAN::same_as<Ret>; }
|
||||
Function(Lambda lambda)
|
||||
{
|
||||
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
|
||||
new (m_storage) CallableLambda<Lambda>(lambda);
|
||||
|
@ -91,36 +91,36 @@ namespace BAN
|
|||
template<typename Own>
|
||||
struct CallableMember : public CallableBase
|
||||
{
|
||||
CallableMember(Ret(Own::*function)(Args...), Own& owner)
|
||||
CallableMember(Ret(Own::*function)(Args...), Own* owner)
|
||||
: m_owner(owner)
|
||||
, m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return (m_owner.*m_function)(forward<Args>(args)...);
|
||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Own& m_owner;
|
||||
Own* m_owner = nullptr;
|
||||
Ret(Own::*m_function)(Args...) = nullptr;
|
||||
};
|
||||
|
||||
template<typename Own>
|
||||
struct CallableMemberConst : public CallableBase
|
||||
{
|
||||
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own& owner)
|
||||
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own* owner)
|
||||
: m_owner(owner)
|
||||
, m_function(function)
|
||||
{ }
|
||||
|
||||
virtual Ret call(Args... args) const override
|
||||
{
|
||||
return (m_owner.*m_function)(forward<Args>(args)...);
|
||||
return (m_owner->*m_function)(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
const Own& m_owner;
|
||||
const Own* m_owner = nullptr;
|
||||
Ret(Own::*m_function)(Args...) const = nullptr;
|
||||
};
|
||||
|
||||
|
@ -141,7 +141,7 @@ namespace BAN
|
|||
};
|
||||
|
||||
private:
|
||||
static constexpr size_t m_size = sizeof(void*) * 8;
|
||||
static constexpr size_t m_size = sizeof(void*) * 5;
|
||||
alignas(CallableBase) uint8_t m_storage[m_size] { 0 };
|
||||
};
|
||||
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/String.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
struct GUID
|
||||
{
|
||||
uint32_t component1 { 0 };
|
||||
uint16_t component2 { 0 };
|
||||
uint16_t component3 { 0 };
|
||||
uint8_t component45[8] { };
|
||||
|
||||
bool operator==(const GUID& other) const
|
||||
{
|
||||
return memcmp(this, &other, sizeof(GUID)) == 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::String> to_string() const
|
||||
{
|
||||
char buffer[37];
|
||||
char* ptr = buffer;
|
||||
|
||||
const auto append_hex_nibble =
|
||||
[&ptr](uint8_t nibble)
|
||||
{
|
||||
if (nibble < 10)
|
||||
*ptr++ = '0' + nibble;
|
||||
else
|
||||
*ptr++ = 'A' + nibble - 10;
|
||||
};
|
||||
|
||||
const auto append_hex_byte =
|
||||
[&append_hex_nibble](uint8_t byte)
|
||||
{
|
||||
append_hex_nibble(byte >> 4);
|
||||
append_hex_nibble(byte & 0xF);
|
||||
};
|
||||
|
||||
append_hex_byte((component1 >> 24) & 0xFF);
|
||||
append_hex_byte((component1 >> 16) & 0xFF);
|
||||
append_hex_byte((component1 >> 8) & 0xFF);
|
||||
append_hex_byte((component1 >> 0) & 0xFF);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte((component2 >> 8) & 0xFF);
|
||||
append_hex_byte((component2 >> 0) & 0xFF);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte((component3 >> 8) & 0xFF);
|
||||
append_hex_byte((component3 >> 0) & 0xFF);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte(component45[0]);
|
||||
append_hex_byte(component45[1]);
|
||||
*ptr++ = '-';
|
||||
append_hex_byte(component45[2]);
|
||||
append_hex_byte(component45[3]);
|
||||
append_hex_byte(component45[4]);
|
||||
append_hex_byte(component45[5]);
|
||||
append_hex_byte(component45[6]);
|
||||
append_hex_byte(component45[7]);
|
||||
*ptr = '\0';
|
||||
|
||||
BAN::String guid;
|
||||
TRY(guid.append(buffer));
|
||||
return BAN::move(guid);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(GUID) == 16);
|
||||
|
||||
}
|
|
@ -7,6 +7,9 @@
|
|||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename Container>
|
||||
class HashMapIterator;
|
||||
|
||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
||||
class HashMap
|
||||
{
|
||||
|
@ -14,17 +17,11 @@ namespace BAN
|
|||
struct Entry
|
||||
{
|
||||
template<typename... Args>
|
||||
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
Entry(const Key& key, Args&&... args)
|
||||
: key(key)
|
||||
, value(forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
template<typename... Args>
|
||||
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
: key(BAN::move(key))
|
||||
, value(forward<Args>(args)...)
|
||||
{}
|
||||
|
||||
Key key;
|
||||
T value;
|
||||
};
|
||||
|
@ -45,27 +42,10 @@ namespace BAN
|
|||
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
|
||||
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
|
||||
|
||||
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); }
|
||||
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
|
||||
ErrorOr<iterator> insert(Key&& key, const T& value) { return emplace(move(key), value); }
|
||||
ErrorOr<iterator> insert(Key&& key, T&& value) { return emplace(move(key), move(value)); }
|
||||
|
||||
ErrorOr<iterator> insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); }
|
||||
ErrorOr<iterator> insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); }
|
||||
ErrorOr<iterator> insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); }
|
||||
ErrorOr<iterator> insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); }
|
||||
|
||||
ErrorOr<void> insert(const Key&, const T&);
|
||||
ErrorOr<void> insert(const Key&, T&&);
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
{ return emplace(Key(key), forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
{ return emplace_or_assign(Key(key), forward<Args>(args)...); }
|
||||
template<typename... Args>
|
||||
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, 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()); }
|
||||
|
@ -75,14 +55,11 @@ namespace BAN
|
|||
ErrorOr<void> reserve(size_type);
|
||||
|
||||
void remove(const Key&);
|
||||
void remove(iterator it);
|
||||
void clear();
|
||||
|
||||
T& operator[](const Key&);
|
||||
const T& operator[](const Key&) const;
|
||||
|
||||
iterator find(const Key& key);
|
||||
const_iterator find(const Key& key) const;
|
||||
bool contains(const Key&) const;
|
||||
|
||||
bool empty() const;
|
||||
|
@ -92,8 +69,6 @@ namespace BAN
|
|||
ErrorOr<void> rebucket(size_type);
|
||||
LinkedList<Entry>& get_bucket(const Key&);
|
||||
const LinkedList<Entry>& get_bucket(const Key&) const;
|
||||
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
|
||||
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
|
||||
|
||||
private:
|
||||
Vector<LinkedList<Entry>> m_buckets;
|
||||
|
@ -140,36 +115,29 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
template<typename... Args>
|
||||
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, const T& value)
|
||||
{
|
||||
ASSERT(!contains(key));
|
||||
TRY(rebucket(m_size + 1));
|
||||
return insert(key, move(T(value)));
|
||||
}
|
||||
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...));
|
||||
m_size++;
|
||||
|
||||
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
|
||||
template<typename Key, typename T, typename HASH>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, T&& value)
|
||||
{
|
||||
return emplace(key, move(value));
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
template<typename... Args>
|
||||
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> HashMap<Key, T, HASH>::emplace(const Key& key, Args&&... args)
|
||||
{
|
||||
if (empty())
|
||||
return emplace(move(key), forward<Args>(args)...);
|
||||
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++)
|
||||
{
|
||||
if (entry_it->key != key)
|
||||
continue;
|
||||
entry_it->value = T(forward<Args>(args)...);
|
||||
return iterator(m_buckets.end(), bucket_it, entry_it);
|
||||
}
|
||||
|
||||
return emplace(move(key), forward<Args>(args)...);
|
||||
ASSERT(!contains(key));
|
||||
TRY(rebucket(m_size + 1));
|
||||
auto& bucket = get_bucket(key);
|
||||
auto result = bucket.emplace_back(key, forward<Args>(args)...);
|
||||
if (result.is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
|
@ -182,16 +150,17 @@ namespace BAN
|
|||
template<typename Key, typename T, typename HASH>
|
||||
void HashMap<Key, T, HASH>::remove(const Key& key)
|
||||
{
|
||||
auto it = find(key);
|
||||
if (it != end())
|
||||
remove(it);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
void HashMap<Key, T, HASH>::remove(iterator it)
|
||||
if (empty()) return;
|
||||
auto& bucket = get_bucket(key);
|
||||
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||
{
|
||||
it.outer_current()->remove(it.inner_current());
|
||||
if (it->key == key)
|
||||
{
|
||||
bucket.remove(it);
|
||||
m_size--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
|
@ -209,7 +178,7 @@ namespace BAN
|
|||
for (Entry& entry : bucket)
|
||||
if (entry.key == key)
|
||||
return entry.value;
|
||||
ASSERT_NOT_REACHED();
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
|
@ -220,37 +189,18 @@ namespace BAN
|
|||
for (const Entry& entry : bucket)
|
||||
if (entry.key == key)
|
||||
return entry.value;
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::find(const Key& key)
|
||||
{
|
||||
if (empty())
|
||||
return end();
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
|
||||
if (it->key == key)
|
||||
return iterator(m_buckets.end(), bucket_it, it);
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::find(const Key& key) const
|
||||
{
|
||||
if (empty())
|
||||
return end();
|
||||
auto bucket_it = get_bucket_iterator(key);
|
||||
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
|
||||
if (it->key == key)
|
||||
return const_iterator(m_buckets.end(), bucket_it, it);
|
||||
return end();
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
bool HashMap<Key, T, HASH>::contains(const Key& key) const
|
||||
{
|
||||
return find(key) != end();
|
||||
if (empty()) return false;
|
||||
const auto& bucket = get_bucket(key);
|
||||
for (const Entry& entry : bucket)
|
||||
if (entry.key == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
|
@ -273,14 +223,18 @@ namespace BAN
|
|||
|
||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<LinkedList<Entry>> new_buckets;
|
||||
TRY(new_buckets.resize(new_bucket_count));
|
||||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
// NOTE: We have to copy the old entries to the new entries and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (auto& bucket : m_buckets)
|
||||
{
|
||||
for (auto it = bucket.begin(); it != bucket.end();)
|
||||
for (Entry& entry : bucket)
|
||||
{
|
||||
size_type new_bucket_index = HASH()(it->key) % new_buckets.size();
|
||||
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||
size_type bucket_index = HASH()(entry.key) % new_buckets.size();
|
||||
if (new_buckets[bucket_index].push_back(entry).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,29 +245,17 @@ namespace BAN
|
|||
template<typename Key, typename T, typename HASH>
|
||||
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
|
||||
{
|
||||
return *get_bucket_iterator(key);
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
|
||||
{
|
||||
return *get_bucket_iterator(key);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return next(m_buckets.begin(), index);
|
||||
}
|
||||
|
||||
template<typename Key, typename T, typename HASH>
|
||||
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
auto index = HASH()(key) % m_buckets.size();
|
||||
return next(m_buckets.begin(), index);
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/LinkedList.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
@ -11,22 +9,24 @@
|
|||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T, typename HASH>
|
||||
class HashSetIterator;
|
||||
|
||||
template<typename T, typename HASH = hash<T>>
|
||||
class HashSet
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
||||
using size_type = hash_t;
|
||||
using const_iterator = HashSetIterator<T, HASH>;
|
||||
|
||||
public:
|
||||
HashSet() = default;
|
||||
HashSet(const HashSet&);
|
||||
HashSet(HashSet&&);
|
||||
HashSet(const HashSet<T, HASH>&);
|
||||
HashSet(HashSet<T, HASH>&&);
|
||||
|
||||
HashSet& operator=(const HashSet&);
|
||||
HashSet& operator=(HashSet&&);
|
||||
HashSet<T, HASH>& operator=(const HashSet<T, HASH>&);
|
||||
HashSet<T, HASH>& operator=(HashSet<T, HASH>&&);
|
||||
|
||||
ErrorOr<void> insert(const T&);
|
||||
ErrorOr<void> insert(T&&);
|
||||
|
@ -35,10 +35,8 @@ namespace BAN
|
|||
|
||||
ErrorOr<void> reserve(size_type);
|
||||
|
||||
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()); }
|
||||
const_iterator begin() const { return const_iterator(this, m_buckets.begin()); }
|
||||
const_iterator end() const { return const_iterator(this, m_buckets.end()); }
|
||||
|
||||
bool contains(const T&) const;
|
||||
|
||||
|
@ -47,23 +45,55 @@ namespace BAN
|
|||
|
||||
private:
|
||||
ErrorOr<void> rebucket(size_type);
|
||||
LinkedList<T>& get_bucket(const T&);
|
||||
const LinkedList<T>& get_bucket(const T&) const;
|
||||
Vector<T>& get_bucket(const T&);
|
||||
const Vector<T>& get_bucket(const T&) const;
|
||||
|
||||
private:
|
||||
Vector<LinkedList<T>> m_buckets;
|
||||
Vector<Vector<T>> m_buckets;
|
||||
size_type m_size = 0;
|
||||
|
||||
friend class HashSetIterator<T, HASH>;
|
||||
};
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(const HashSet& other)
|
||||
class HashSetIterator
|
||||
{
|
||||
public:
|
||||
HashSetIterator() = default;
|
||||
HashSetIterator(const HashSetIterator<T, HASH>&);
|
||||
|
||||
HashSetIterator<T, HASH>& operator++();
|
||||
HashSetIterator<T, HASH> operator++(int);
|
||||
|
||||
const T& operator*() const;
|
||||
const T* operator->() const;
|
||||
|
||||
bool operator==(const HashSetIterator<T, HASH>&) const;
|
||||
bool operator!=(const HashSetIterator<T, HASH>&) const;
|
||||
|
||||
operator bool() const { return m_owner && m_current_bucket; }
|
||||
|
||||
private:
|
||||
HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket);
|
||||
void find_next();
|
||||
|
||||
private:
|
||||
const HashSet<T, HASH>* m_owner = nullptr;
|
||||
Vector<Vector<T>>::const_iterator m_current_bucket;
|
||||
Vector<T>::const_iterator m_current_key;
|
||||
|
||||
friend class HashSet<T, HASH>;
|
||||
};
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(const HashSet<T, HASH>& other)
|
||||
: m_buckets(other.m_buckets)
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>::HashSet(HashSet&& other)
|
||||
HashSet<T, HASH>::HashSet(HashSet<T, HASH>&& other)
|
||||
: m_buckets(move(other.m_buckets))
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
|
@ -71,7 +101,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other)
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet<T, HASH>& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = other.m_buckets;
|
||||
|
@ -80,7 +110,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other)
|
||||
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet<T, HASH>&& other)
|
||||
{
|
||||
clear();
|
||||
m_buckets = move(other.m_buckets);
|
||||
|
@ -111,12 +141,12 @@ namespace BAN
|
|||
void HashSet<T, HASH>::remove(const T& key)
|
||||
{
|
||||
if (empty()) return;
|
||||
auto& bucket = get_bucket(key);
|
||||
for (auto it = bucket.begin(); it != bucket.end(); it++)
|
||||
Vector<T>& bucket = get_bucket(key);
|
||||
for (size_type i = 0; i < bucket.size(); i++)
|
||||
{
|
||||
if (*it == key)
|
||||
if (bucket[i] == key)
|
||||
{
|
||||
bucket.remove(it);
|
||||
bucket.remove(i);
|
||||
m_size--;
|
||||
break;
|
||||
}
|
||||
|
@ -162,17 +192,20 @@ namespace BAN
|
|||
if (m_buckets.size() >= bucket_count)
|
||||
return {};
|
||||
|
||||
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<LinkedList<T>> new_buckets;
|
||||
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
|
||||
Vector<Vector<T>> new_buckets;
|
||||
if (new_buckets.resize(new_bucket_count).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
for (auto& bucket : m_buckets)
|
||||
// NOTE: We have to copy the old keys to the new keys and not move
|
||||
// since we might run out of memory half way through.
|
||||
for (Vector<T>& bucket : m_buckets)
|
||||
{
|
||||
for (auto it = bucket.begin(); it != bucket.end();)
|
||||
for (T& key : bucket)
|
||||
{
|
||||
size_type new_bucket_index = HASH()(*it) % new_buckets.size();
|
||||
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
|
||||
size_type bucket_index = HASH()(key) % new_buckets.size();
|
||||
if (new_buckets[bucket_index].push_back(key).is_error())
|
||||
return Error::from_errno(ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +214,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||
Vector<T>& HashSet<T, HASH>::get_bucket(const T& key)
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
size_type index = HASH()(key) % m_buckets.size();
|
||||
|
@ -189,11 +222,83 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||
const Vector<T>& HashSet<T, HASH>::get_bucket(const T& key) const
|
||||
{
|
||||
ASSERT(!m_buckets.empty());
|
||||
size_type index = HASH()(key) % m_buckets.size();
|
||||
return m_buckets[index];
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>& HashSetIterator<T, HASH>::operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
if (m_current_key == m_current_bucket->end())
|
||||
m_current_bucket++;
|
||||
else
|
||||
m_current_key++;
|
||||
find_next();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH> HashSetIterator<T, HASH>::operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const T& HashSetIterator<T, HASH>::operator*() const
|
||||
{
|
||||
ASSERT(m_owner && m_current_bucket && m_current_key);
|
||||
return *m_current_key;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
const T* HashSetIterator<T, HASH>::operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSetIterator<T, HASH>::operator==(const HashSetIterator<T, HASH>& other) const
|
||||
{
|
||||
if (!m_owner || m_owner != other.m_owner)
|
||||
return false;
|
||||
return m_current_bucket == other.m_current_bucket
|
||||
&& m_current_key == other.m_current_key;
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
bool HashSetIterator<T, HASH>::operator!=(const HashSetIterator<T, HASH>& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
HashSetIterator<T, HASH>::HashSetIterator(const HashSet<T, HASH>* owner, Vector<Vector<T>>::const_iterator bucket)
|
||||
: m_owner(owner)
|
||||
, m_current_bucket(bucket)
|
||||
{
|
||||
if (m_current_bucket != m_owner->m_buckets.end())
|
||||
m_current_key = m_current_bucket->begin();
|
||||
find_next();
|
||||
}
|
||||
|
||||
template<typename T, typename HASH>
|
||||
void HashSetIterator<T, HASH>::find_next()
|
||||
{
|
||||
ASSERT(m_owner && m_current_bucket);
|
||||
while (m_current_bucket != m_owner->m_buckets.end())
|
||||
{
|
||||
if (m_current_key && m_current_key != m_current_bucket->end())
|
||||
return;
|
||||
m_current_bucket++;
|
||||
m_current_key = m_current_bucket->begin();
|
||||
}
|
||||
m_current_key = typename Vector<T>::const_iterator();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Endianness.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/Hash.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
struct IPv4Address
|
||||
{
|
||||
constexpr IPv4Address(uint32_t u32_address)
|
||||
{
|
||||
raw = u32_address;
|
||||
}
|
||||
|
||||
constexpr IPv4Address(uint8_t oct1, uint8_t oct2, uint8_t oct3, uint8_t oct4)
|
||||
{
|
||||
octets[0] = oct1;
|
||||
octets[1] = oct2;
|
||||
octets[2] = oct3;
|
||||
octets[3] = oct4;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const IPv4Address& other) const
|
||||
{
|
||||
return raw == other.raw;
|
||||
}
|
||||
|
||||
constexpr IPv4Address mask(const IPv4Address& other) const
|
||||
{
|
||||
return IPv4Address(raw & other.raw);
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
uint8_t octets[4];
|
||||
uint32_t raw;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(IPv4Address) == 4);
|
||||
|
||||
template<>
|
||||
struct hash<IPv4Address>
|
||||
{
|
||||
constexpr hash_t operator()(IPv4Address ipv4) const
|
||||
{
|
||||
return hash<uint32_t>()(ipv4.raw);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const IPv4Address& ipv4, const ValueFormat&)
|
||||
{
|
||||
ValueFormat format {
|
||||
.base = 10,
|
||||
.percision = 0,
|
||||
.fill = 0,
|
||||
.upper = false,
|
||||
};
|
||||
|
||||
print_argument(putc, ipv4.octets[0], format);
|
||||
for (size_t i = 1; i < 4; i++)
|
||||
{
|
||||
putc('.');
|
||||
print_argument(putc, ipv4.octets[i], format);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,180 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename It>
|
||||
constexpr It next(It it, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
requires requires(It it, size_t n) { requires is_same_v<decltype(it + n), It>; }
|
||||
constexpr It next(It it, size_t count)
|
||||
{
|
||||
return it + count;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
constexpr It prev(It it, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
--it;
|
||||
return it;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
requires requires(It it, size_t n) { requires is_same_v<decltype(it - n), It>; }
|
||||
constexpr It prev(It it, size_t count)
|
||||
{
|
||||
return it - count;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
constexpr size_t distance(It it1, It it2)
|
||||
{
|
||||
size_t dist = 0;
|
||||
while (it1 != it2)
|
||||
{
|
||||
++it1;
|
||||
++dist;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
requires requires(It it1, It it2) { requires is_integral_v<decltype(it2 - it1)>; }
|
||||
constexpr size_t distance(It it1, It it2)
|
||||
{
|
||||
return it2 - it1;
|
||||
}
|
||||
|
||||
template<typename T, typename Container, bool CONST>
|
||||
class IteratorSimpleGeneral
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
public:
|
||||
constexpr IteratorSimpleGeneral() = default;
|
||||
IteratorSimpleGeneral() = default;
|
||||
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||
constexpr IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||
IteratorSimpleGeneral(const IteratorSimpleGeneral<T, Container, CONST2>& other)
|
||||
: m_pointer(other.m_pointer)
|
||||
, m_valid(other.m_valid)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||
enable_if_t<!CONST2, T&> operator*()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
return *m_pointer;
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||
enable_if_t<!CONST2, T*> operator->()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral& operator++()
|
||||
IteratorSimpleGeneral& operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
++m_pointer;
|
||||
return *this;
|
||||
}
|
||||
constexpr IteratorSimpleGeneral operator++(int)
|
||||
IteratorSimpleGeneral operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral& operator--()
|
||||
IteratorSimpleGeneral& operator--()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_pointer);
|
||||
--m_pointer;
|
||||
return *this;
|
||||
return --m_pointer;
|
||||
}
|
||||
constexpr IteratorSimpleGeneral operator--(int)
|
||||
IteratorSimpleGeneral operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
constexpr size_t operator-(const IteratorSimpleGeneral& other) const
|
||||
bool operator==(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this && other);
|
||||
return m_pointer - other.m_pointer;
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral operator+(size_t offset) const
|
||||
{
|
||||
return IteratorSimpleGeneral(m_pointer + offset);
|
||||
}
|
||||
|
||||
constexpr IteratorSimpleGeneral operator-(size_t offset) const
|
||||
{
|
||||
return IteratorSimpleGeneral(m_pointer - offset);
|
||||
}
|
||||
|
||||
constexpr bool operator<(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return m_pointer < other.m_pointer;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return m_pointer == other.m_pointer;
|
||||
}
|
||||
constexpr bool operator!=(const IteratorSimpleGeneral& other) const
|
||||
bool operator!=(const IteratorSimpleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this);
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
operator bool() const
|
||||
{
|
||||
return m_valid;
|
||||
return m_pointer;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
|
||||
IteratorSimpleGeneral(maybe_const_t<CONST, T>* pointer)
|
||||
: m_pointer(pointer)
|
||||
, m_valid(true)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
maybe_const_t<CONST, T>* m_pointer = nullptr;
|
||||
bool m_valid = false;
|
||||
|
||||
friend IteratorSimpleGeneral<T, Container, !CONST>;
|
||||
friend Container;
|
||||
|
@ -190,19 +102,17 @@ namespace BAN
|
|||
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>;
|
||||
|
||||
using value_type = T;
|
||||
|
||||
public:
|
||||
constexpr IteratorDoubleGeneral() = default;
|
||||
IteratorDoubleGeneral() = default;
|
||||
template<bool CONST2, typename = enable_if_t<CONST2 == CONST || CONST>>
|
||||
constexpr IteratorDoubleGeneral(const IteratorDoubleGeneral<T, OuterContainer, InnerContainer, Container, CONST2>& other)
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr const T& operator*() const
|
||||
const T& operator*() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
|
@ -210,7 +120,7 @@ namespace BAN
|
|||
return m_inner_current.operator*();
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T&> operator*()
|
||||
enable_if_t<!CONST2, T&> operator*()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
|
@ -218,7 +128,7 @@ namespace BAN
|
|||
return m_inner_current.operator*();
|
||||
}
|
||||
|
||||
constexpr const T* operator->() const
|
||||
const T* operator->() const
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
|
@ -226,7 +136,7 @@ namespace BAN
|
|||
return m_inner_current.operator->();
|
||||
}
|
||||
template<bool CONST2 = CONST>
|
||||
constexpr enable_if_t<!CONST2, T*> operator->()
|
||||
enable_if_t<!CONST2, T*> operator->()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
|
@ -234,7 +144,7 @@ namespace BAN
|
|||
return m_inner_current.operator->();
|
||||
}
|
||||
|
||||
constexpr IteratorDoubleGeneral& operator++()
|
||||
IteratorDoubleGeneral& operator++()
|
||||
{
|
||||
ASSERT(*this);
|
||||
ASSERT(m_outer_current != m_outer_end);
|
||||
|
@ -243,37 +153,37 @@ namespace BAN
|
|||
find_valid_or_end();
|
||||
return *this;
|
||||
}
|
||||
constexpr IteratorDoubleGeneral operator++(int)
|
||||
IteratorDoubleGeneral operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const IteratorDoubleGeneral& other) const
|
||||
bool operator==(const IteratorDoubleGeneral& other) const
|
||||
{
|
||||
ASSERT(*this && other);
|
||||
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;
|
||||
ASSERT(m_inner_current && other.m_inner_current);
|
||||
return m_inner_current == other.m_inner_current;
|
||||
}
|
||||
constexpr bool operator!=(const IteratorDoubleGeneral& other) const
|
||||
bool operator!=(const IteratorDoubleGeneral& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
operator bool() const
|
||||
{
|
||||
return !!m_outer_current;
|
||||
return m_outer_end && m_outer_current;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||
IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current)
|
||||
: m_outer_end(outer_end)
|
||||
, m_outer_current(outer_current)
|
||||
{
|
||||
|
@ -284,15 +194,7 @@ namespace BAN
|
|||
}
|
||||
}
|
||||
|
||||
constexpr IteratorDoubleGeneral(const OuterIterator& outer_end, const OuterIterator& outer_current, const InnerIterator& inner_current)
|
||||
: m_outer_end(outer_end)
|
||||
, m_outer_current(outer_current)
|
||||
, m_inner_current(inner_current)
|
||||
{
|
||||
find_valid_or_end();
|
||||
}
|
||||
|
||||
constexpr void find_valid_or_end()
|
||||
void find_valid_or_end()
|
||||
{
|
||||
while (m_inner_current == m_outer_current->end())
|
||||
{
|
||||
|
@ -303,9 +205,6 @@ namespace BAN
|
|||
}
|
||||
}
|
||||
|
||||
constexpr OuterIterator outer_current() { return m_outer_current; }
|
||||
constexpr InnerIterator inner_current() { return m_inner_current; }
|
||||
|
||||
private:
|
||||
OuterIterator m_outer_end;
|
||||
OuterIterator m_outer_current;
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class numeric_limits
|
||||
{
|
||||
public:
|
||||
numeric_limits() = delete;
|
||||
|
||||
static inline constexpr T max()
|
||||
{
|
||||
if constexpr(is_same_v<T, char>)
|
||||
return __SCHAR_MAX__;
|
||||
if constexpr(is_same_v<T, signed char>)
|
||||
return __SCHAR_MAX__;
|
||||
if constexpr(is_same_v<T, unsigned char>)
|
||||
return (T)__SCHAR_MAX__ * 2 + 1;
|
||||
|
||||
if constexpr(is_same_v<T, short>)
|
||||
return __SHRT_MAX__;
|
||||
if constexpr(is_same_v<T, int>)
|
||||
return __INT_MAX__;
|
||||
if constexpr(is_same_v<T, long>)
|
||||
return __LONG_MAX__;
|
||||
if constexpr(is_same_v<T, long long>)
|
||||
return __LONG_LONG_MAX__;
|
||||
|
||||
if constexpr(is_same_v<T, unsigned short>)
|
||||
return (T)__SHRT_MAX__ * 2 + 1;
|
||||
if constexpr(is_same_v<T, unsigned int>)
|
||||
return (T)__INT_MAX__ * 2 + 1;
|
||||
if constexpr(is_same_v<T, unsigned long>)
|
||||
return (T)__LONG_MAX__ * 2 + 1;
|
||||
if constexpr(is_same_v<T, unsigned long long>)
|
||||
return (T)__LONG_LONG_MAX__ * 2 + 1;
|
||||
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MAX__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MAX__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MAX__;
|
||||
}
|
||||
|
||||
static inline constexpr T min()
|
||||
{
|
||||
if constexpr(is_signed_v<T> && is_integral_v<T>)
|
||||
return -max() - 1;
|
||||
|
||||
if constexpr(is_unsigned_v<T> && is_integral_v<T>)
|
||||
return 0;
|
||||
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MIN__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MIN__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MIN__;
|
||||
}
|
||||
|
||||
static inline constexpr bool has_infinity()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_HAS_INFINITY__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_HAS_INFINITY__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_HAS_INFINITY__;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline constexpr T infinity() requires(has_infinity())
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_inff();
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_inf();
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_infl();
|
||||
}
|
||||
|
||||
static inline constexpr bool has_quiet_NaN()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_HAS_QUIET_NAN__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_HAS_QUIET_NAN__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_HAS_QUIET_NAN__;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline constexpr T quiet_NaN() requires(has_quiet_NaN())
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_nanf("");
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_nan("");
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_nanl("");
|
||||
}
|
||||
|
||||
static inline constexpr int max_exponent2()
|
||||
{
|
||||
static_assert(__FLT_RADIX__ == 2);
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MAX_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MAX_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MAX_EXP__;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline constexpr int max_exponent10()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MAX_10_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MAX_10_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MAX_10_EXP__;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline constexpr int min_exponent2()
|
||||
{
|
||||
static_assert(__FLT_RADIX__ == 2);
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MIN_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MIN_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MIN_EXP__;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline constexpr int min_exponent10()
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __FLT_MIN_10_EXP__;
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __DBL_MIN_10_EXP__;
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __LDBL_MIN_10_EXP__;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -22,11 +21,11 @@ namespace BAN
|
|||
|
||||
public:
|
||||
LinkedList() = default;
|
||||
LinkedList(const LinkedList<T>& other) requires is_copy_constructible_v<T> { *this = other; }
|
||||
LinkedList(const LinkedList<T>& other) { *this = other; }
|
||||
LinkedList(LinkedList<T>&& other) { *this = move(other); }
|
||||
~LinkedList() { clear(); }
|
||||
|
||||
LinkedList<T>& operator=(const LinkedList<T>&) requires is_copy_constructible_v<T>;
|
||||
LinkedList<T>& operator=(const LinkedList<T>&);
|
||||
LinkedList<T>& operator=(LinkedList<T>&&);
|
||||
|
||||
ErrorOr<void> push_back(const T&);
|
||||
|
@ -34,16 +33,14 @@ namespace BAN
|
|||
ErrorOr<void> insert(iterator, const T&);
|
||||
ErrorOr<void> insert(iterator, T&&);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace_back(Args&&...);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(iterator, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(iterator, Args&&...);
|
||||
|
||||
void pop_back();
|
||||
iterator remove(iterator);
|
||||
void clear();
|
||||
|
||||
iterator move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter);
|
||||
|
||||
iterator begin() { return iterator(m_data, empty()); }
|
||||
const_iterator begin() const { return const_iterator(m_data, empty()); }
|
||||
iterator end() { return iterator(m_last, true); }
|
||||
|
@ -67,11 +64,7 @@ namespace BAN
|
|||
Node* prev;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
ErrorOr<Node*> allocate_node(Args&&...) const;
|
||||
|
||||
Node* remove_node(iterator);
|
||||
void insert_node(iterator, Node*);
|
||||
ErrorOr<Node*> allocate_node() const;
|
||||
|
||||
Node* m_data = nullptr;
|
||||
Node* m_last = nullptr;
|
||||
|
@ -122,7 +115,7 @@ namespace BAN
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other) requires is_copy_constructible_v<T>
|
||||
LinkedList<T>& LinkedList<T>::operator=(const LinkedList<T>& other)
|
||||
{
|
||||
clear();
|
||||
for (const T& elem : other)
|
||||
|
@ -143,31 +136,6 @@ namespace BAN
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>::Node* LinkedList<T>::remove_node(iterator iter)
|
||||
{
|
||||
ASSERT(!empty() && iter);
|
||||
Node* node = iter.m_current;
|
||||
Node* prev = node->prev;
|
||||
Node* next = node->next;
|
||||
(prev ? prev->next : m_data) = next;
|
||||
(next ? next->prev : m_last) = prev;
|
||||
m_size--;
|
||||
return node;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LinkedList<T>::insert_node(iterator iter, Node* node)
|
||||
{
|
||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||
Node* prev = next ? next->prev : m_last;
|
||||
node->next = next;
|
||||
node->prev = prev;
|
||||
(prev ? prev->next : m_data) = node;
|
||||
(next ? next->prev : m_last) = node;
|
||||
m_size++;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> LinkedList<T>::push_back(const T& value)
|
||||
{
|
||||
|
@ -189,31 +157,44 @@ namespace BAN
|
|||
template<typename T>
|
||||
ErrorOr<void> LinkedList<T>::insert(iterator iter, T&& value)
|
||||
{
|
||||
Node* new_node = TRY(allocate_node(move(value)));
|
||||
insert_node(iter, new_node);
|
||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||
Node* prev = next ? next->prev : m_last;
|
||||
Node* new_node = TRY(allocate_node());
|
||||
new (&new_node->value) T(move(value));
|
||||
new_node->next = next;
|
||||
new_node->prev = prev;
|
||||
(prev ? prev->next : m_data) = new_node;
|
||||
(next ? next->prev : m_last) = new_node;
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args)
|
||||
{
|
||||
return emplace(end(), forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args)
|
||||
{
|
||||
Node* new_node = TRY(allocate_node(forward<Args>(args)...));
|
||||
insert_node(iter, new_node);
|
||||
Node* next = iter.m_past_end ? nullptr : iter.m_current;
|
||||
Node* prev = next ? next->prev : m_last;
|
||||
Node* new_node = TRY(allocate_node());
|
||||
new (&new_node->value) T(forward<Args>(args)...);
|
||||
new_node->next = next;
|
||||
new_node->prev = prev;
|
||||
(prev ? prev->next : m_data) = new_node;
|
||||
(next ? next->prev : m_last) = new_node;
|
||||
m_size++;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void LinkedList<T>::pop_back()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
remove(iterator(m_last, false));
|
||||
}
|
||||
|
||||
|
@ -221,10 +202,14 @@ namespace BAN
|
|||
LinkedList<T>::iterator LinkedList<T>::remove(iterator iter)
|
||||
{
|
||||
ASSERT(!empty() && iter);
|
||||
Node* node = remove_node(iter);
|
||||
Node* node = iter.m_current;
|
||||
Node* prev = node->prev;
|
||||
Node* next = node->next;
|
||||
node->value.~T();
|
||||
BAN::deallocator(node);
|
||||
(prev ? prev->next : m_data) = next;
|
||||
(next ? next->prev : m_last) = prev;
|
||||
m_size--;
|
||||
return next ? iterator(next, false) : iterator(m_last, true);
|
||||
}
|
||||
|
||||
|
@ -244,16 +229,6 @@ namespace BAN
|
|||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LinkedList<T>::iterator LinkedList<T>::move_element_to_other_linked_list(LinkedList& dest_list, iterator dest_iter, iterator src_iter)
|
||||
{
|
||||
ASSERT(!empty() && src_iter);
|
||||
Node* node = remove_node(src_iter);
|
||||
iterator ret = node->next ? iterator(node->next, false) : iterator(m_last, true);
|
||||
dest_list.insert_node(dest_iter, node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& LinkedList<T>::back() const
|
||||
{
|
||||
|
@ -308,13 +283,11 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node(Args&&... args) const
|
||||
ErrorOr<typename LinkedList<T>::Node*> LinkedList<T>::allocate_node() const
|
||||
{
|
||||
Node* node = (Node*)BAN::allocator(sizeof(Node));
|
||||
if (node == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
new (&node->value) T(forward<Args>(args)...);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
struct MACAddress
|
||||
{
|
||||
uint8_t address[6];
|
||||
|
||||
constexpr bool operator==(const MACAddress& other) const
|
||||
{
|
||||
return
|
||||
address[0] == other.address[0] &&
|
||||
address[1] == other.address[1] &&
|
||||
address[2] == other.address[2] &&
|
||||
address[3] == other.address[3] &&
|
||||
address[4] == other.address[4] &&
|
||||
address[5] == other.address[5];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
||||
template<typename F>
|
||||
void print_argument(F putc, const MACAddress& mac, const ValueFormat&)
|
||||
{
|
||||
ValueFormat format {
|
||||
.base = 16,
|
||||
.percision = 0,
|
||||
.fill = 2,
|
||||
.upper = true,
|
||||
};
|
||||
|
||||
print_argument(putc, mac.address[0], format);
|
||||
for (size_t i = 1; i < 6; i++)
|
||||
{
|
||||
putc(':');
|
||||
print_argument(putc, mac.address[i], format);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Limits.h>
|
||||
#include <BAN/Numbers.h>
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace BAN::Math
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T abs(T x)
|
||||
{
|
||||
return x < 0 ? -x : x;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T min(T a, T b)
|
||||
{
|
||||
|
@ -59,211 +52,41 @@ namespace BAN::Math
|
|||
}
|
||||
|
||||
template<integral T>
|
||||
inline constexpr bool is_power_of_two(T x)
|
||||
inline constexpr bool is_power_of_two(T value)
|
||||
{
|
||||
if (x == 0)
|
||||
if (value == 0)
|
||||
return false;
|
||||
return (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
template<BAN::integral T>
|
||||
static constexpr bool will_multiplication_overflow(T a, T b)
|
||||
{
|
||||
if (a == 0 || b == 0)
|
||||
return false;
|
||||
if ((a > 0) == (b > 0))
|
||||
return a > BAN::numeric_limits<T>::max() / b;
|
||||
else
|
||||
return a < BAN::numeric_limits<T>::min() / b;
|
||||
}
|
||||
|
||||
template<BAN::integral T>
|
||||
static constexpr bool will_addition_overflow(T a, T b)
|
||||
{
|
||||
if (a > 0 && b > 0)
|
||||
return a > BAN::numeric_limits<T>::max() - b;
|
||||
if (a < 0 && b < 0)
|
||||
return a < BAN::numeric_limits<T>::min() - b;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
requires is_same_v<T, unsigned int> || is_same_v<T, unsigned long> || is_same_v<T, unsigned long long>
|
||||
inline constexpr T ilog2(T x)
|
||||
{
|
||||
if constexpr(is_same_v<T, unsigned int>)
|
||||
return sizeof(T) * 8 - __builtin_clz(x) - 1;
|
||||
if constexpr(is_same_v<T, unsigned long>)
|
||||
return sizeof(T) * 8 - __builtin_clzl(x) - 1;
|
||||
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T floor(T x)
|
||||
inline constexpr T log2(T value)
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_floorf(x);
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_floor(x);
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_floorl(x);
|
||||
T result;
|
||||
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"((T)1.0) : "st(1)");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T ceil(T x)
|
||||
inline constexpr T log10(T value)
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_ceilf(x);
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_ceil(x);
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_ceill(x);
|
||||
constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
|
||||
T result;
|
||||
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
|
||||
return result;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T round(T x)
|
||||
inline constexpr T log(T value, T base)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return x;
|
||||
if (x > (T)0.0)
|
||||
return floor<T>(x + (T)0.5);
|
||||
return ceil<T>(x - (T)0.5);
|
||||
return log2(value) / log2(base);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T trunc(T x)
|
||||
inline constexpr T pow(T base, T exp)
|
||||
{
|
||||
if constexpr(is_same_v<T, float>)
|
||||
return __builtin_truncf(x);
|
||||
if constexpr(is_same_v<T, double>)
|
||||
return __builtin_trunc(x);
|
||||
if constexpr(is_same_v<T, long double>)
|
||||
return __builtin_truncl(x);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T rint(T x)
|
||||
{
|
||||
asm("frndint" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T fmod(T a, T b)
|
||||
{
|
||||
asm(
|
||||
"1:"
|
||||
"fprem;"
|
||||
"fnstsw %%ax;"
|
||||
"testb $4, %%ah;"
|
||||
"jne 1b;"
|
||||
: "+t"(a)
|
||||
: "u"(b)
|
||||
: "ax"
|
||||
);
|
||||
return a;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
static T modf(T x, T* iptr)
|
||||
{
|
||||
const T frac = BAN::Math::fmod<T>(x, 1);
|
||||
*iptr = x - frac;
|
||||
return frac;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T frexp(T num, int* exp)
|
||||
{
|
||||
if (num == 0.0)
|
||||
{
|
||||
*exp = 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
T _exp;
|
||||
asm("fxtract" : "+t"(num), "=u"(_exp));
|
||||
*exp = (int)_exp + 1;
|
||||
return num / (T)2.0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T copysign(T x, T y)
|
||||
{
|
||||
if ((x < (T)0.0) != (y < (T)0.0))
|
||||
x = -x;
|
||||
return x;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T fyl2x(T x, T y)
|
||||
{
|
||||
asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log(T x)
|
||||
{
|
||||
return detail::fyl2x<T>(x, numbers::ln2_v<T>);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log2(T x)
|
||||
{
|
||||
return detail::fyl2x<T>(x, 1.0);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T log10(T x)
|
||||
{
|
||||
return detail::fyl2x<T>(x, numbers::lg2_v<T>);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T logb(T x)
|
||||
{
|
||||
static_assert(FLT_RADIX == 2);
|
||||
return log2<T>(x);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T exp2(T x)
|
||||
{
|
||||
if (abs(x) <= (T)1.0)
|
||||
{
|
||||
asm("f2xm1" : "+t"(x));
|
||||
return x + (T)1.0;
|
||||
}
|
||||
|
||||
asm(
|
||||
"fld1;"
|
||||
"fld %%st(1);"
|
||||
"fprem;"
|
||||
"f2xm1;"
|
||||
"faddp;"
|
||||
"fscale;"
|
||||
"fstp %%st(1);"
|
||||
: "+t"(x)
|
||||
);
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T exp(T x)
|
||||
{
|
||||
return exp2<T>(x * numbers::log2e_v<T>);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T pow(T x, T y)
|
||||
{
|
||||
asm(
|
||||
T result;
|
||||
asm volatile(
|
||||
"fyl2x;"
|
||||
"fld1;"
|
||||
"fld %%st(1);"
|
||||
|
@ -271,173 +94,12 @@ namespace BAN::Math
|
|||
"f2xm1;"
|
||||
"faddp;"
|
||||
"fscale;"
|
||||
: "+t"(x), "+u"(y)
|
||||
"fxch %%st(1);"
|
||||
"fstp %%st;"
|
||||
: "=t"(result)
|
||||
: "0"(base), "u"(exp)
|
||||
);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T scalbn(T x, int n)
|
||||
{
|
||||
asm("fscale" : "+t"(x) : "u"(static_cast<T>(n)));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T ldexp(T x, int y)
|
||||
{
|
||||
const bool exp_sign = y < 0;
|
||||
if (exp_sign)
|
||||
y = -y;
|
||||
|
||||
T exp = (T)1.0;
|
||||
T mult = (T)2.0;
|
||||
while (y)
|
||||
{
|
||||
if (y & 1)
|
||||
exp *= mult;
|
||||
mult *= mult;
|
||||
y >>= 1;
|
||||
}
|
||||
|
||||
if (exp_sign)
|
||||
exp = (T)1.0 / exp;
|
||||
|
||||
return x * exp;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T sqrt(T x)
|
||||
{
|
||||
asm("fsqrt" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T cbrt(T value)
|
||||
{
|
||||
if (value == 0.0)
|
||||
return 0.0;
|
||||
return pow<T>(value, 1.0 / 3.0);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T sin(T x)
|
||||
{
|
||||
asm("fsin" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T cos(T x)
|
||||
{
|
||||
asm("fcos" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr void sincos(T x, T& sin, T& cos)
|
||||
{
|
||||
asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T tan(T x)
|
||||
{
|
||||
T one, ret;
|
||||
asm(
|
||||
"fptan"
|
||||
: "=t"(one), "=u"(ret)
|
||||
: "0"(x)
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T atan2(T y, T x)
|
||||
{
|
||||
asm(
|
||||
"fpatan"
|
||||
: "+t"(x)
|
||||
: "u"(y)
|
||||
: "st(1)"
|
||||
);
|
||||
return x;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T atan(T x)
|
||||
{
|
||||
return atan2<T>(x, 1.0);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T asin(T x)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return (T)0.0;
|
||||
if (x == (T)1.0)
|
||||
return numbers::pi_v<T> / (T)2.0;
|
||||
if (x == (T)-1.0)
|
||||
return -numbers::pi_v<T> / (T)2.0;
|
||||
return (T)2.0 * atan<T>(x / (T(1.0) + sqrt<T>((T)1.0 - x * x)));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T acos(T x)
|
||||
{
|
||||
if (x == (T)0.0)
|
||||
return numbers::pi_v<T> / (T)2.0;
|
||||
if (x == (T)1.0)
|
||||
return (T)0.0;
|
||||
if (x == (T)-1.0)
|
||||
return numbers::pi_v<T>;
|
||||
return (T)2.0 * atan<T>(sqrt<T>((T)1.0 - x * x) / ((T)1.0 + x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T sinh(T x)
|
||||
{
|
||||
return (exp<T>(x) - exp<T>(-x)) / (T)2.0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T cosh(T x)
|
||||
{
|
||||
return (exp<T>(x) + exp<T>(-x)) / (T)2.0;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T tanh(T x)
|
||||
{
|
||||
const T exp_px = exp<T>(x);
|
||||
const T exp_nx = exp<T>(-x);
|
||||
return (exp_px - exp_nx) / (exp_px + exp_nx);
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T asinh(T x)
|
||||
{
|
||||
return log<T>(x + sqrt<T>(x * x + (T)1.0));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T acosh(T x)
|
||||
{
|
||||
return log<T>(x + sqrt<T>(x * x - (T)1.0));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T atanh(T x)
|
||||
{
|
||||
return (T)0.5 * log<T>(((T)1.0 + x) / ((T)1.0 - x));
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T hypot(T x, T y)
|
||||
{
|
||||
return sqrt<T>(x * x + y * y);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -27,3 +27,6 @@ namespace BAN
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
|
@ -1,28 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Traits.h>
|
||||
|
||||
namespace BAN::numbers
|
||||
{
|
||||
|
||||
template<floating_point T> inline constexpr T e_v = 2.71828182845904523536;
|
||||
template<floating_point T> inline constexpr T log2e_v = 1.44269504088896340736;
|
||||
template<floating_point T> inline constexpr T lge_v = 0.43429448190325182765;
|
||||
template<floating_point T> inline constexpr T lg2_v = 0.30102999566398119521;
|
||||
template<floating_point T> inline constexpr T ln2_v = 0.69314718055994530942;
|
||||
template<floating_point T> inline constexpr T ln10_v = 2.30258509299404568402;
|
||||
template<floating_point T> inline constexpr T pi_v = 3.14159265358979323846;
|
||||
template<floating_point T> inline constexpr T sqrt2_v = 1.41421356237309504880;
|
||||
template<floating_point T> inline constexpr T sqrt3_v = 1.73205080756887729353;
|
||||
|
||||
inline constexpr double e = e_v<double>;
|
||||
inline constexpr double log2e = log2e_v<double>;
|
||||
inline constexpr double lge = lge_v<double>;
|
||||
inline constexpr double lg2 = lge_v<double>;
|
||||
inline constexpr double ln2 = ln2_v<double>;
|
||||
inline constexpr double ln10 = ln10_v<double>;
|
||||
inline constexpr double pi = pi_v<double>;
|
||||
inline constexpr double sqrt2 = sqrt2_v<double>;
|
||||
inline constexpr double sqrt3 = sqrt3_v<double>;
|
||||
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <BAN/Assert.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -13,48 +12,48 @@ namespace BAN
|
|||
class Optional
|
||||
{
|
||||
public:
|
||||
constexpr Optional();
|
||||
constexpr Optional(Optional&&);
|
||||
constexpr Optional(const Optional&);
|
||||
constexpr Optional(const T&);
|
||||
constexpr Optional(T&&);
|
||||
Optional();
|
||||
Optional(Optional&&);
|
||||
Optional(const Optional&);
|
||||
Optional(const T&);
|
||||
Optional(T&&);
|
||||
template<typename... Args>
|
||||
Optional(Args&&...);
|
||||
|
||||
~Optional();
|
||||
|
||||
constexpr Optional& operator=(Optional&&);
|
||||
constexpr Optional& operator=(const Optional&);
|
||||
Optional& operator=(Optional&&);
|
||||
Optional& operator=(const Optional&);
|
||||
|
||||
template<typename... Args>
|
||||
constexpr Optional& emplace(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
Optional& emplace(Args&&...);
|
||||
|
||||
constexpr T* operator->();
|
||||
constexpr const T* operator->() const;
|
||||
T* operator->();
|
||||
const T* operator->() const;
|
||||
|
||||
constexpr T& operator*();
|
||||
constexpr const T& operator*() const;
|
||||
T& operator*();
|
||||
const T& operator*() const;
|
||||
|
||||
constexpr bool has_value() const;
|
||||
bool has_value() const;
|
||||
|
||||
constexpr T release_value();
|
||||
constexpr T& value();
|
||||
constexpr const T& value() const;
|
||||
constexpr T& value_or(T&);
|
||||
constexpr const T& value_or(const T&) const;
|
||||
T release_value();
|
||||
T& value();
|
||||
const T& value() const;
|
||||
|
||||
constexpr void clear();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
alignas(T) uint8_t m_storage[sizeof(T)] {};
|
||||
alignas(T) uint8_t m_storage[sizeof(T)];
|
||||
bool m_has_value { false };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional()
|
||||
Optional<T>::Optional()
|
||||
: m_has_value(false)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(Optional<T>&& other)
|
||||
Optional<T>::Optional(Optional<T>&& other)
|
||||
: m_has_value(other.has_value())
|
||||
{
|
||||
if (other.has_value())
|
||||
|
@ -62,7 +61,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(const Optional<T>& other)
|
||||
Optional<T>::Optional(const Optional<T>& other)
|
||||
: m_has_value(other.has_value())
|
||||
{
|
||||
if (other.has_value())
|
||||
|
@ -70,19 +69,27 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(const T& value)
|
||||
Optional<T>::Optional(const T& value)
|
||||
: m_has_value(true)
|
||||
{
|
||||
new (m_storage) T(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>::Optional(T&& value)
|
||||
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()
|
||||
{
|
||||
|
@ -90,7 +97,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||
Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
|
@ -100,18 +107,18 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||
Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||
{
|
||||
clear();
|
||||
m_has_value = other.has_value();
|
||||
if (other.has_value())
|
||||
if (other.has_value)
|
||||
new (m_storage) T(other.value());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
constexpr Optional<T>& Optional<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
Optional<T>& Optional<T>::emplace(Args&&... args)
|
||||
{
|
||||
clear();
|
||||
m_has_value = true;
|
||||
|
@ -120,41 +127,41 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T* Optional<T>::operator->()
|
||||
T* Optional<T>::operator->()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return &value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T* Optional<T>::operator->() const
|
||||
const T* Optional<T>::operator->() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return &value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::operator*()
|
||||
T& Optional<T>::operator*()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::operator*() const
|
||||
const T& Optional<T>::operator*() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool Optional<T>::has_value() const
|
||||
bool Optional<T>::has_value() const
|
||||
{
|
||||
return m_has_value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T Optional<T>::release_value()
|
||||
T Optional<T>::release_value()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
T released_value = move(value());
|
||||
|
@ -164,37 +171,21 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::value()
|
||||
T& Optional<T>::value()
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return *reinterpret_cast<T*>(&m_storage);
|
||||
return (T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::value() const
|
||||
const T& Optional<T>::value() const
|
||||
{
|
||||
ASSERT(has_value());
|
||||
return *reinterpret_cast<const T*>(&m_storage);
|
||||
return (const T&)m_storage;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& Optional<T>::value_or(T& empty)
|
||||
{
|
||||
if (!has_value())
|
||||
return empty;
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& Optional<T>::value_or(const T& empty) const
|
||||
{
|
||||
if (!has_value())
|
||||
return empty;
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void Optional<T>::clear()
|
||||
void Optional<T>::clear()
|
||||
{
|
||||
if (m_has_value)
|
||||
value().~T();
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#if __has_include(<new>)
|
||||
#include <new>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
|
||||
inline void* operator new(size_t, void* addr) { return addr; }
|
||||
inline void* operator new[](size_t, void* addr) { return addr; }
|
||||
#endif
|
|
@ -5,7 +5,6 @@
|
|||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -31,7 +30,7 @@ namespace BAN
|
|||
ErrorOr<void> push(T&&);
|
||||
ErrorOr<void> push(const T&);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(Args&&...);
|
||||
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
@ -45,7 +44,6 @@ namespace BAN
|
|||
void clear();
|
||||
|
||||
bool empty() const;
|
||||
size_type capacity() const;
|
||||
size_type size() const;
|
||||
|
||||
const T& front() const;
|
||||
|
@ -131,7 +129,7 @@ namespace BAN
|
|||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> Queue<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> Queue<T>::emplace(Args&&... args)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
new (m_data + m_size) T(forward<Args>(args)...);
|
||||
|
@ -187,12 +185,6 @@ namespace BAN
|
|||
return m_size == 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename Queue<T>::size_type Queue<T>::capacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename Queue<T>::size_type Queue<T>::size() const
|
||||
{
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Atomic.h>
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/NoCopyMove.h>
|
||||
|
@ -23,36 +22,24 @@ namespace BAN
|
|||
|
||||
void ref() const
|
||||
{
|
||||
uint32_t old = m_ref_count.fetch_add(1, MemoryOrder::memory_order_relaxed);
|
||||
ASSERT(old > 0);
|
||||
}
|
||||
|
||||
bool try_ref() const
|
||||
{
|
||||
uint32_t expected = m_ref_count.load(MemoryOrder::memory_order_relaxed);
|
||||
for (;;)
|
||||
{
|
||||
if (expected == 0)
|
||||
return false;
|
||||
if (m_ref_count.compare_exchange(expected, expected + 1, MemoryOrder::memory_order_acquire))
|
||||
return true;
|
||||
}
|
||||
ASSERT(m_ref_count > 0);
|
||||
m_ref_count++;
|
||||
}
|
||||
|
||||
void unref() const
|
||||
{
|
||||
uint32_t old = m_ref_count.fetch_sub(1);
|
||||
ASSERT(old > 0);
|
||||
if (old == 1)
|
||||
delete static_cast<const T*>(this);
|
||||
ASSERT(m_ref_count > 0);
|
||||
m_ref_count--;
|
||||
if (m_ref_count == 0)
|
||||
delete (const T*)this;
|
||||
}
|
||||
|
||||
protected:
|
||||
RefCounted() = default;
|
||||
virtual ~RefCounted() { ASSERT(m_ref_count == 0); }
|
||||
~RefCounted() { ASSERT(m_ref_count == 0); }
|
||||
|
||||
private:
|
||||
mutable Atomic<uint32_t> m_ref_count = 1;
|
||||
mutable uint32_t m_ref_count = 1;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -76,9 +63,8 @@ namespace BAN
|
|||
return ptr;
|
||||
}
|
||||
|
||||
// NOTE: don't use is_constructible_v<T, Args...> as RefPtr<T> is allowed with friends
|
||||
template<typename... Args>
|
||||
static ErrorOr<RefPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
|
||||
static ErrorOr<RefPtr> create(Args&&... args)
|
||||
{
|
||||
T* pointer = new T(forward<Args>(args)...);
|
||||
if (pointer == nullptr)
|
||||
|
@ -138,11 +124,8 @@ namespace BAN
|
|||
T* operator->() { return ptr(); }
|
||||
const T* operator->() const { return ptr(); }
|
||||
|
||||
bool operator==(RefPtr other) const { return m_pointer == other.m_pointer; }
|
||||
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
|
||||
|
||||
bool empty() const { return m_pointer == nullptr; }
|
||||
explicit operator bool() const { return m_pointer; }
|
||||
operator bool() const { return m_pointer; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Swap.h>
|
||||
#include <BAN/Traits.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
namespace BAN::sort
|
||||
{
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void exchange_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
for (It lhs = begin; lhs != end; ++lhs)
|
||||
for (It rhs = next(lhs, 1); rhs != end; ++rhs)
|
||||
if (!comp(*lhs, *rhs))
|
||||
swap(*lhs, *rhs);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename Comp>
|
||||
It partition(It begin, It end, Comp comp)
|
||||
{
|
||||
It pivot = prev(end, 1);
|
||||
|
||||
It it1 = begin;
|
||||
for (It it2 = begin; it2 != pivot; ++it2)
|
||||
{
|
||||
if (comp(*it2, *pivot))
|
||||
{
|
||||
swap(*it1, *it2);
|
||||
++it1;
|
||||
}
|
||||
}
|
||||
|
||||
swap(*it1, *pivot);
|
||||
|
||||
return it1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void quick_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
if (distance(begin, end) <= 1)
|
||||
return;
|
||||
It mid = detail::partition(begin, end, comp);
|
||||
quick_sort(begin, mid, comp);
|
||||
quick_sort(++mid, end, comp);
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void insertion_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
if (distance(begin, end) <= 1)
|
||||
return;
|
||||
for (It it1 = next(begin, 1); it1 != end; ++it1)
|
||||
{
|
||||
typename It::value_type x = move(*it1);
|
||||
It it2 = it1;
|
||||
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
|
||||
*it2 = move(*prev(it2, 1));
|
||||
*it2 = move(x);
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename Comp>
|
||||
void push_heap(It begin, size_t hole_index, size_t top_index, typename It::value_type value, Comp comp)
|
||||
{
|
||||
size_t parent = (hole_index - 1) / 2;
|
||||
while (hole_index > top_index && comp(*next(begin, parent), value))
|
||||
{
|
||||
*next(begin, hole_index) = move(*next(begin, parent));
|
||||
hole_index = parent;
|
||||
parent = (hole_index - 1) / 2;
|
||||
}
|
||||
*next(begin, hole_index) = move(value);
|
||||
}
|
||||
|
||||
template<typename It, typename Comp>
|
||||
void adjust_heap(It begin, size_t hole_index, size_t len, typename It::value_type value, Comp comp)
|
||||
{
|
||||
const size_t top_index = hole_index;
|
||||
size_t child = hole_index;
|
||||
while (child < (len - 1) / 2)
|
||||
{
|
||||
child = 2 * (child + 1);
|
||||
if (comp(*next(begin, child), *next(begin, child - 1)))
|
||||
child--;
|
||||
*next(begin, hole_index) = move(*next(begin, child));
|
||||
hole_index = child;
|
||||
}
|
||||
if (len % 2 == 0 && child == (len - 2) / 2)
|
||||
{
|
||||
child = 2 * (child + 1);
|
||||
*next(begin, hole_index) = move(*next(begin, child - 1));
|
||||
hole_index = child - 1;
|
||||
}
|
||||
push_heap(begin, hole_index, top_index, move(value), comp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void make_heap(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return;
|
||||
|
||||
size_t parent = (len - 2) / 2;
|
||||
while (true)
|
||||
{
|
||||
detail::adjust_heap(begin, parent, len, move(*next(begin, parent)), comp);
|
||||
|
||||
if (parent == 0)
|
||||
break;
|
||||
|
||||
parent--;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void sort_heap(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return;
|
||||
|
||||
size_t last = len;
|
||||
while (last > 1)
|
||||
{
|
||||
last--;
|
||||
typename It::value_type x = move(*next(begin, last));
|
||||
*next(begin, last) = move(*begin);
|
||||
detail::adjust_heap(begin, 0, last, move(x), comp);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void heap_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
make_heap(begin, end, comp);
|
||||
sort_heap(begin, end, comp);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename It, typename Comp>
|
||||
void intro_sort_impl(It begin, It end, size_t max_depth, Comp comp)
|
||||
{
|
||||
if (distance(begin, end) <= 16)
|
||||
return insertion_sort(begin, end, comp);
|
||||
if (max_depth == 0)
|
||||
return heap_sort(begin, end, comp);
|
||||
It mid = detail::partition(begin, end, comp);
|
||||
intro_sort_impl(begin, mid, max_depth - 1, comp);
|
||||
intro_sort_impl(++mid, end, max_depth - 1, comp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void intro_sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return;
|
||||
detail::intro_sort_impl(begin, end, 2 * Math::ilog2(len), comp);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<unsigned_integral T>
|
||||
consteval T lsb_index(T value)
|
||||
{
|
||||
for (T result = 0;; result++)
|
||||
if (value & (1 << result))
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename It, size_t radix = 256>
|
||||
requires is_unsigned_v<typename It::value_type> && (radix > 0 && (radix & (radix - 1)) == 0)
|
||||
BAN::ErrorOr<void> radix_sort(It begin, It end)
|
||||
{
|
||||
using value_type = typename It::value_type;
|
||||
|
||||
const size_t len = distance(begin, end);
|
||||
if (len <= 1)
|
||||
return {};
|
||||
|
||||
Vector<value_type> temp;
|
||||
TRY(temp.resize(len));
|
||||
|
||||
Vector<size_t> counts;
|
||||
TRY(counts.resize(radix));
|
||||
|
||||
constexpr size_t mask = radix - 1;
|
||||
constexpr size_t shift = detail::lsb_index(radix);
|
||||
|
||||
for (size_t s = 0; s < sizeof(value_type) * 8; s += shift)
|
||||
{
|
||||
for (auto& cnt : counts)
|
||||
cnt = 0;
|
||||
for (It it = begin; it != end; ++it)
|
||||
counts[(*it >> s) & mask]++;
|
||||
|
||||
for (size_t i = 0; i < radix - 1; i++)
|
||||
counts[i + 1] += counts[i];
|
||||
|
||||
for (It it = end; it != begin;)
|
||||
{
|
||||
--it;
|
||||
temp[--counts[(*it >> s) & mask]] = *it;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < temp.size(); j++)
|
||||
*next(begin, j) = temp[j];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename It, typename Comp = less<typename It::value_type>>
|
||||
void sort(It begin, It end, Comp comp = {})
|
||||
{
|
||||
return intro_sort(begin, end, comp);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,75 +14,114 @@ namespace BAN
|
|||
public:
|
||||
using value_type = T;
|
||||
using size_type = size_t;
|
||||
using iterator = IteratorSimple<value_type, Span>;
|
||||
using const_iterator = ConstIteratorSimple<value_type, Span>;
|
||||
|
||||
private:
|
||||
template<typename S>
|
||||
static inline constexpr bool can_init_from_v = is_same_v<value_type, const S> || is_same_v<value_type, S>;
|
||||
using iterator = IteratorSimple<T, Span>;
|
||||
using const_iterator = ConstIteratorSimple<T, Span>;
|
||||
|
||||
public:
|
||||
Span() = default;
|
||||
Span(value_type* data, size_type size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{ }
|
||||
|
||||
Span(T*, size_type);
|
||||
Span(Span<T>&);
|
||||
template<typename S>
|
||||
Span(const Span<S>& other) requires can_init_from_v<S>
|
||||
: m_data(other.m_data)
|
||||
, m_size(other.m_size)
|
||||
{ }
|
||||
template<typename S>
|
||||
Span(Span<S>&& other) requires can_init_from_v<S>
|
||||
: m_data(other.m_data)
|
||||
, m_size(other.m_size)
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
Span& operator=(const Span<S>& other) requires can_init_from_v<S>
|
||||
{
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
template<typename S>
|
||||
Span& operator=(Span<S>&& other) requires can_init_from_v<S>
|
||||
{
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
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); }
|
||||
|
||||
value_type& operator[](size_type index) const
|
||||
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];
|
||||
}
|
||||
|
||||
value_type* data() const
|
||||
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;
|
||||
}
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
template<typename T>
|
||||
const T* Span<T>::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
void clear()
|
||||
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;
|
||||
}
|
||||
|
||||
Span slice(size_type start, size_type length = ~size_type(0)) const
|
||||
template<typename T>
|
||||
Span<T> Span<T>::slice(size_type start, size_type length)
|
||||
{
|
||||
ASSERT(m_data);
|
||||
ASSERT(start <= m_size);
|
||||
|
@ -92,13 +131,4 @@ namespace BAN
|
|||
return Span(m_data + start, length);
|
||||
}
|
||||
|
||||
Span<const value_type> as_const() const { return *this; }
|
||||
|
||||
private:
|
||||
value_type* m_data = nullptr;
|
||||
size_type m_size = 0;
|
||||
|
||||
friend class Span<const value_type>;
|
||||
};
|
||||
|
||||
}
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
#include <BAN/Errors.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/StringView.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -19,130 +18,28 @@ namespace BAN
|
|||
static constexpr size_type sso_capacity = 15;
|
||||
|
||||
public:
|
||||
String() {}
|
||||
String(const String& other) { *this = other; }
|
||||
String(String&& other) { *this = move(other); }
|
||||
String(StringView other) { *this = other; }
|
||||
~String() { clear(); }
|
||||
String();
|
||||
String(const String&);
|
||||
String(String&&);
|
||||
String(StringView);
|
||||
~String();
|
||||
|
||||
template<typename... Args>
|
||||
static BAN::ErrorOr<String> formatted(const char* format, Args&&... args)
|
||||
{
|
||||
size_type length = 0;
|
||||
BAN::Formatter::print([&](char) { length++; }, format, BAN::forward<Args>(args)...);
|
||||
static String formatted(const char* format, const Args&... args);
|
||||
|
||||
String result;
|
||||
TRY(result.reserve(length));
|
||||
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, BAN::forward<Args>(args)...);
|
||||
String& operator=(const String&);
|
||||
String& operator=(String&&);
|
||||
String& operator=(StringView);
|
||||
|
||||
return result;
|
||||
}
|
||||
ErrorOr<void> push_back(char);
|
||||
ErrorOr<void> insert(char, size_type);
|
||||
ErrorOr<void> insert(StringView, size_type);
|
||||
ErrorOr<void> append(StringView);
|
||||
|
||||
String& operator=(const String& other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
m_size = other.size();
|
||||
return *this;
|
||||
}
|
||||
void pop_back();
|
||||
void remove(size_type);
|
||||
|
||||
String& operator=(String&& other)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (other.has_sso())
|
||||
memcpy(data(), other.data(), other.size() + 1);
|
||||
else
|
||||
{
|
||||
m_storage.general_storage = other.m_storage.general_storage;
|
||||
m_has_sso = false;
|
||||
}
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_size = 0;
|
||||
other.m_storage.sso_storage = SSOStorage();
|
||||
other.m_has_sso = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& operator=(StringView other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
memcpy(data(), other.data(), other.size());
|
||||
m_size = other.size();
|
||||
data()[m_size] = '\0';
|
||||
return *this;
|
||||
}
|
||||
|
||||
ErrorOr<void> push_back(char c)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
data()[m_size] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> insert(char c, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
memmove(data() + index + 1, data() + index, m_size - index);
|
||||
data()[index] = c;
|
||||
m_size++;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> insert(StringView str, size_type index)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||
memcpy(data() + index, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> append(StringView str)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + str.size()));
|
||||
memcpy(data() + m_size, str.data(), str.size());
|
||||
m_size += str.size();
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void remove(size_type index)
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
memmove(data() + index, data() + index + 1, m_size - index);
|
||||
m_size--;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (!has_sso())
|
||||
{
|
||||
deallocator(m_storage.general_storage.data);
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
}
|
||||
m_size = 0;
|
||||
data()[m_size] = '\0';
|
||||
}
|
||||
void clear();
|
||||
|
||||
const_iterator begin() const { return const_iterator(data()); }
|
||||
iterator begin() { return iterator(data()); }
|
||||
|
@ -158,151 +55,26 @@ namespace BAN
|
|||
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& str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator==(StringView) const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
bool operator==(StringView str) const
|
||||
{
|
||||
if (size() != str.size())
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (data()[i] != str.data()[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const char* cstr) const
|
||||
{
|
||||
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> resize(size_type new_size, char init_c = '\0')
|
||||
{
|
||||
if (m_size == new_size)
|
||||
return {};
|
||||
|
||||
// expanding
|
||||
if (m_size < new_size)
|
||||
{
|
||||
TRY(ensure_capacity(new_size));
|
||||
memset(data() + m_size, init_c, new_size - m_size);
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
m_size = new_size;
|
||||
data()[m_size] = '\0';
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> reserve(size_type new_size)
|
||||
{
|
||||
TRY(ensure_capacity(new_size));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> shrink_to_fit()
|
||||
{
|
||||
if (has_sso())
|
||||
return {};
|
||||
|
||||
if (fits_in_sso())
|
||||
{
|
||||
char* data = m_storage.general_storage.data;
|
||||
m_storage.sso_storage = SSOStorage();
|
||||
m_has_sso = true;
|
||||
memcpy(this->data(), data, m_size + 1);
|
||||
deallocator(data);
|
||||
return {};
|
||||
}
|
||||
|
||||
GeneralStorage& storage = m_storage.general_storage;
|
||||
if (storage.capacity == m_size)
|
||||
return {};
|
||||
|
||||
char* new_data = (char*)allocator(m_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
memcpy(new_data, storage.data, m_size);
|
||||
deallocator(storage.data);
|
||||
|
||||
storage.capacity = m_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> resize(size_type, char = '\0');
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
StringView sv() const { return StringView(data(), size()); }
|
||||
|
||||
bool empty() const { return m_size == 0; }
|
||||
size_type size() const { return m_size; }
|
||||
size_type capacity() const;
|
||||
|
||||
size_type capacity() const
|
||||
{
|
||||
if (has_sso())
|
||||
return sso_capacity;
|
||||
return m_storage.general_storage.capacity;
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
if (has_sso())
|
||||
return m_storage.sso_storage.data;
|
||||
return m_storage.general_storage.data;
|
||||
}
|
||||
char* data();
|
||||
const char* data() const;
|
||||
|
||||
private:
|
||||
ErrorOr<void> ensure_capacity(size_type new_size)
|
||||
{
|
||||
if (m_size >= new_size)
|
||||
return {};
|
||||
if (has_sso() && fits_in_sso(new_size))
|
||||
return {};
|
||||
ErrorOr<void> ensure_capacity(size_type);
|
||||
|
||||
char* new_data = (char*)allocator(new_size + 1);
|
||||
if (new_data == nullptr)
|
||||
return Error::from_errno(ENOMEM);
|
||||
|
||||
if (m_size)
|
||||
memcpy(new_data, data(), m_size + 1);
|
||||
|
||||
if (has_sso())
|
||||
{
|
||||
m_storage.general_storage = GeneralStorage();
|
||||
m_has_sso = false;
|
||||
}
|
||||
else
|
||||
deallocator(m_storage.general_storage.data);
|
||||
|
||||
auto& storage = m_storage.general_storage;
|
||||
storage.capacity = new_size;
|
||||
storage.data = new_data;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool has_sso() const { return m_has_sso; }
|
||||
bool has_sso() const;
|
||||
|
||||
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||
|
@ -327,6 +99,14 @@ namespace BAN
|
|||
size_type m_has_sso : 1 { true };
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
String String::formatted(const char* format, const Args&... args)
|
||||
{
|
||||
String result;
|
||||
BAN::Formatter::print([&](char c){ MUST(result.push_back(c)); }, format, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
struct hash<String>
|
||||
{
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/ForwardList.h>
|
||||
#include <BAN/Hash.h>
|
||||
#include <BAN/Formatter.h>
|
||||
#include <BAN/Iterators.h>
|
||||
#include <BAN/Optional.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -17,229 +14,43 @@ namespace BAN
|
|||
using const_iterator = ConstIteratorSimple<char, StringView>;
|
||||
|
||||
public:
|
||||
constexpr StringView() {}
|
||||
constexpr StringView(const char* string, size_type len = -1)
|
||||
{
|
||||
if (len == size_type(-1))
|
||||
len = strlen(string);
|
||||
m_data = string;
|
||||
m_size = len;
|
||||
}
|
||||
StringView();
|
||||
StringView(const String&);
|
||||
StringView(const char*, size_type = -1);
|
||||
|
||||
constexpr const_iterator begin() const { return const_iterator(m_data); }
|
||||
constexpr const_iterator end() const { return const_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); }
|
||||
|
||||
constexpr char operator[](size_type index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return m_data[index];
|
||||
}
|
||||
char operator[](size_type) const;
|
||||
|
||||
constexpr bool operator==(StringView other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
return false;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] != other.m_data[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool operator==(const String&) const;
|
||||
bool operator==(StringView) const;
|
||||
bool operator==(const char*) const;
|
||||
|
||||
constexpr bool operator==(const char* other) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] != other[i])
|
||||
return false;
|
||||
return other[m_size] == '\0';
|
||||
}
|
||||
StringView substring(size_type, size_type = -1) const;
|
||||
|
||||
constexpr StringView substring(size_type index, size_type len = -1) const
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
if (len == size_type(-1))
|
||||
len = m_size - index;
|
||||
ASSERT(len <= m_size - index); // weird order to avoid overflow
|
||||
StringView result;
|
||||
result.m_data = m_data + index;
|
||||
result.m_size = len;
|
||||
return result;
|
||||
}
|
||||
ErrorOr<Vector<StringView>> split(char, bool = false);
|
||||
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool = false);
|
||||
|
||||
ErrorOr<Vector<StringView>> split(char delim, bool allow_empties = false) const
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_data[i] == delim)
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
count++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start != m_size)
|
||||
count++;
|
||||
}
|
||||
char back() const;
|
||||
char front() const;
|
||||
|
||||
Vector<StringView> result;
|
||||
TRY(result.reserve(count));
|
||||
bool contains(char) const;
|
||||
size_type count(char) const;
|
||||
|
||||
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 || (start == m_size && allow_empties))
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
bool empty() const;
|
||||
size_type size() const;
|
||||
|
||||
ErrorOr<Vector<StringView>> split(bool(*comp)(char), bool allow_empties = false) const
|
||||
{
|
||||
size_type count = 0;
|
||||
{
|
||||
size_type start = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
{
|
||||
if (comp(m_data[i]))
|
||||
{
|
||||
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 (comp(m_data[i]))
|
||||
{
|
||||
if (allow_empties || start != i)
|
||||
TRY(result.push_back(this->substring(start, i - start)));
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
if (start < m_size || (start == m_size && allow_empties))
|
||||
TRY(result.push_back(this->substring(start)));
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr char back() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[m_size - 1];
|
||||
}
|
||||
|
||||
constexpr char front() const
|
||||
{
|
||||
ASSERT(m_size > 0);
|
||||
return m_data[0];
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> find(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> find(bool(*comp)(char)) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (comp(m_data[i]))
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> rfind(char ch) const
|
||||
{
|
||||
for (size_type i = m_size; i > 0; i--)
|
||||
if (m_data[i - 1] == ch)
|
||||
return i - 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<size_type> rfind(bool(*comp)(char)) const
|
||||
{
|
||||
for (size_type i = m_size; i > 0; i--)
|
||||
if (comp(m_data[i - 1]))
|
||||
return i - 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
constexpr bool starts_with(BAN::StringView target) const
|
||||
{
|
||||
if (target.size() > m_size)
|
||||
return false;
|
||||
for (size_type i = 0; i < target.size(); i++)
|
||||
if (m_data[i] != target[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool contains(char ch) const
|
||||
{
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr size_type count(char ch) const
|
||||
{
|
||||
size_type result = 0;
|
||||
for (size_type i = 0; i < m_size; i++)
|
||||
if (m_data[i] == ch)
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr bool empty() const { return m_size == 0; }
|
||||
constexpr size_type size() const { return m_size; }
|
||||
constexpr const char* data() const { return m_data; }
|
||||
const char* data() const;
|
||||
|
||||
private:
|
||||
const char* m_data = nullptr;
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<StringView>
|
||||
{
|
||||
hash_t operator()(StringView string) const
|
||||
{
|
||||
constexpr hash_t FNV_offset_basis = 0x811c9dc5;
|
||||
constexpr hash_t FNV_prime = 0x01000193;
|
||||
|
||||
hash_t hash = FNV_offset_basis;
|
||||
for (StringView::size_type i = 0; i < string.size(); i++)
|
||||
{
|
||||
hash *= FNV_prime;
|
||||
hash ^= (uint8_t)string[i];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
inline constexpr BAN::StringView operator""_sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
||||
inline BAN::StringView operator""sv(const char* str, BAN::StringView::size_type len) { return BAN::StringView(str, len); }
|
||||
|
||||
namespace BAN::Formatter
|
||||
{
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Move.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
void swap(T& lhs, T& rhs)
|
||||
{
|
||||
T tmp = move(lhs);
|
||||
lhs = move(rhs);
|
||||
rhs = move(tmp);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,10 +3,10 @@
|
|||
namespace BAN
|
||||
{
|
||||
|
||||
template<typename T> struct remove_reference { using type = T; };
|
||||
template<typename T> struct remove_reference<T&> { using type = T; };
|
||||
template<typename T> struct remove_reference<T&&> { using type = T; };
|
||||
template<typename T> using remove_reference_t = typename remove_reference<T>::type;
|
||||
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> using remove_reference_t = typename remove_refenrece<T>::type;
|
||||
|
||||
template<typename T> struct remove_const { using type = T; };
|
||||
template<typename T> struct remove_const<const T> { using type = T; };
|
||||
|
@ -34,33 +34,18 @@ namespace BAN
|
|||
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;
|
||||
|
||||
template<typename T, T V> struct integral_constant { static constexpr T value = V; };
|
||||
template<typename T, T V > inline constexpr T integral_constant_v = integral_constant<T, V>::value;
|
||||
using true_type = integral_constant<bool, true>;
|
||||
using false_type = integral_constant<bool, false>;
|
||||
struct true_type { static constexpr bool value = true; };
|
||||
struct false_type { static constexpr bool value = false; };
|
||||
|
||||
template<typename T, typename S> struct is_same : false_type {};
|
||||
template<typename T> struct is_same<T, T> : true_type {};
|
||||
template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value;
|
||||
template<typename T, typename S> concept same_as = BAN::is_same_v<T, S>;
|
||||
|
||||
template<typename T> struct is_lvalue_reference : false_type {};
|
||||
template<typename T> struct is_lvalue_reference<T&> : true_type {};
|
||||
template<typename T> inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
|
||||
template<typename T> concept lvalue_reference = is_lvalue_reference_v<T>;
|
||||
|
||||
template<typename T, typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(T, Args...); };
|
||||
template<typename T, typename... Args> inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
|
||||
|
||||
template<typename T> struct is_default_constructible { static constexpr bool value = is_constructible_v<T>; };
|
||||
template<typename T> inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_copy_constructible { static constexpr bool value = is_constructible_v<T, const T&>; };
|
||||
template<typename T> inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
|
||||
|
||||
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
|
||||
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
|
||||
|
||||
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> concept integral = is_integral_v<T>;
|
||||
|
@ -87,57 +72,13 @@ namespace BAN
|
|||
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;
|
||||
|
||||
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
|
||||
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
|
||||
|
||||
template<typename T> struct is_pod { static constexpr bool value = __is_pod(T); };
|
||||
template<typename T> inline constexpr bool is_pod_v = is_pod<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, bool = is_arithmetic_v<T>> struct is_unsigned { static constexpr bool value = T(0) < T(-1); };
|
||||
template<typename T> struct is_unsigned<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> concept signed_integral = is_signed_v<T> && is_integral_v<T>;
|
||||
|
||||
template<typename T> struct is_unsigned : detail::is_unsigned<T> {};
|
||||
template<typename T> inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
|
||||
template<typename T> concept unsigned_integral = is_unsigned_v<T> && is_integral_v<T>;
|
||||
|
||||
#define __BAN_TRAITS_MAKE_UNSIGNED_CV(__type) \
|
||||
template<> struct make_unsigned<__type> { using type = unsigned __type; }; \
|
||||
template<> struct make_unsigned<const __type> { using type = unsigned const __type; }; \
|
||||
template<> struct make_unsigned<volatile __type> { using type = unsigned volatile __type; }; \
|
||||
template<> struct make_unsigned<const volatile __type> { using type = unsigned const volatile __type; };
|
||||
|
||||
template<typename T> requires is_arithmetic_v<T> struct make_unsigned { using type = T; };
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(char)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(short)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(int)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(long)
|
||||
__BAN_TRAITS_MAKE_UNSIGNED_CV(long long)
|
||||
template<typename T> using make_unsigned_t = typename make_unsigned<T>::type;
|
||||
#undef __BAN_TRAITS_MAKE_UNSIGNED_CV
|
||||
|
||||
#define __BAN_TRAITS_MAKE_SIGNED_CV(__type) \
|
||||
template<> struct make_signed<unsigned __type> { using type = __type; }; \
|
||||
template<> struct make_signed<unsigned const __type> { using type = const __type; }; \
|
||||
template<> struct make_signed<unsigned volatile __type> { using type = volatile __type; }; \
|
||||
template<> struct make_signed<unsigned const volatile __type> { using type = const volatile __type; };
|
||||
|
||||
template<typename T> requires is_arithmetic_v<T> struct make_signed { using type = T; };
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(char)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(short)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(int)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(long)
|
||||
__BAN_TRAITS_MAKE_SIGNED_CV(long long)
|
||||
template<typename T> using make_signed_t = typename make_signed<T>::type;
|
||||
#undef __BAN_TRAITS_MAKE_SIGNED_CV
|
||||
|
||||
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; } };
|
||||
|
|
|
@ -21,21 +21,20 @@ namespace BAN::UTF8
|
|||
return 0;
|
||||
}
|
||||
|
||||
template<typename T> requires (sizeof(T) == 1)
|
||||
constexpr uint32_t to_codepoint(const T* bytes)
|
||||
constexpr uint32_t to_codepoint(uint8_t* bytes)
|
||||
{
|
||||
uint32_t length = byte_length(bytes[0]);
|
||||
|
||||
for (uint32_t i = 1; i < length; i++)
|
||||
if (((uint8_t)bytes[i] & 0xC0) != 0x80)
|
||||
if ((bytes[i] & 0xC0) != 0x80)
|
||||
return UTF8::invalid;
|
||||
|
||||
switch (length)
|
||||
{
|
||||
case 1: return (((uint8_t)bytes[0] & 0x80) != 0x00) ? UTF8::invalid : (uint8_t)bytes[0];
|
||||
case 2: return (((uint8_t)bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x1F) << 6) | ((uint8_t)bytes[1] & 0x3F);
|
||||
case 3: return (((uint8_t)bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x0F) << 12) | (((uint8_t)bytes[1] & 0x3F) << 6) | ((uint8_t)bytes[2] & 0x3F);
|
||||
case 4: return (((uint8_t)bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : (((uint8_t)bytes[0] & 0x07) << 18) | (((uint8_t)bytes[1] & 0x3F) << 12) | (((uint8_t)bytes[2] & 0x3F) << 6) | ((uint8_t)bytes[3] & 0x3F);
|
||||
case 1: return ((bytes[0] & 0x80) != 0x00) ? UTF8::invalid : bytes[0];
|
||||
case 2: return ((bytes[0] & 0xE0) != 0xC0) ? UTF8::invalid : ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
||||
case 3: return ((bytes[0] & 0xF0) != 0xE0) ? UTF8::invalid : ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) | (bytes[2] & 0x3F);
|
||||
case 4: return ((bytes[0] & 0xF8) != 0xF0) ? UTF8::invalid : ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) | ((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
|
||||
}
|
||||
|
||||
return UTF8::invalid;
|
||||
|
|
|
@ -33,9 +33,8 @@ namespace BAN
|
|||
return uniq;
|
||||
}
|
||||
|
||||
// NOTE: don't use is_constructible_v<T, Args...> as UniqPtr<T> is allowed with friends
|
||||
template<typename... Args>
|
||||
static BAN::ErrorOr<UniqPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
|
||||
static BAN::ErrorOr<UniqPtr> create(Args&&... args)
|
||||
{
|
||||
UniqPtr uniq;
|
||||
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <BAN/Assert.h>
|
||||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -43,10 +42,9 @@ namespace BAN
|
|||
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
|
||||
|
@ -216,14 +214,6 @@ namespace BAN
|
|||
return m_index == detail::index<T, Ts...>();
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void emplace(Args&&... args) requires (can_have<T>() && is_constructible_v<T, Args...>)
|
||||
{
|
||||
clear();
|
||||
m_index = detail::index<T, Ts...>();
|
||||
new (m_storage) T(BAN::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(T&& value) requires (can_have<T>() && !is_lvalue_reference_v<T>)
|
||||
{
|
||||
|
@ -286,16 +276,6 @@ namespace BAN
|
|||
return **reinterpret_cast<const remove_reference_t<T>**>(m_storage);
|
||||
}
|
||||
|
||||
bool has_value() const
|
||||
{
|
||||
return m_index != invalid_index();
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return has_value();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_index != invalid_index())
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
#include <BAN/Math.h>
|
||||
#include <BAN/Move.h>
|
||||
#include <BAN/New.h>
|
||||
#include <BAN/PlacementNew.h>
|
||||
#include <BAN/Span.h>
|
||||
#include <BAN/Swap.h>
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
@ -35,9 +33,9 @@ namespace BAN
|
|||
ErrorOr<void> push_back(T&&);
|
||||
ErrorOr<void> push_back(const T&);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace_back(Args&&...);
|
||||
template<typename... Args>
|
||||
ErrorOr<void> emplace(size_type, Args&&...) requires is_constructible_v<T, Args...>;
|
||||
ErrorOr<void> emplace(size_type, Args&&...);
|
||||
ErrorOr<void> insert(size_type, T&&);
|
||||
ErrorOr<void> insert(size_type, const T&);
|
||||
|
||||
|
@ -66,10 +64,7 @@ namespace BAN
|
|||
const T& front() const;
|
||||
T& front();
|
||||
|
||||
void reverse();
|
||||
|
||||
ErrorOr<void> resize(size_type) requires is_default_constructible_v<T>;
|
||||
ErrorOr<void> resize(size_type, const T&) requires is_copy_constructible_v<T>;
|
||||
ErrorOr<void> resize(size_type, const T& = T());
|
||||
ErrorOr<void> reserve(size_type);
|
||||
ErrorOr<void> shrink_to_fit();
|
||||
|
||||
|
@ -141,13 +136,10 @@ namespace BAN
|
|||
template<typename T>
|
||||
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
for (size_type i = 0; i < BAN::Math::min(size(), other.size()); i++)
|
||||
m_data[i] = other.m_data[i];
|
||||
for (size_type i = size(); i < other.size(); i++)
|
||||
for (size_type i = 0; i < other.size(); i++)
|
||||
new (m_data + i) T(other[i]);
|
||||
for (size_type i = other.size(); i < size(); i++)
|
||||
m_data[i].~T();
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
|
@ -169,7 +161,7 @@ namespace BAN
|
|||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> Vector<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> Vector<T>::emplace_back(Args&&... args)
|
||||
{
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
new (m_data + m_size) T(forward<Args>(args)...);
|
||||
|
@ -179,7 +171,7 @@ namespace BAN
|
|||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args) requires is_constructible_v<T, Args...>
|
||||
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args)
|
||||
{
|
||||
ASSERT(index <= m_size);
|
||||
TRY(ensure_capacity(m_size + 1));
|
||||
|
@ -305,28 +297,7 @@ namespace BAN
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void Vector<T>::reverse()
|
||||
{
|
||||
for (size_type i = 0; i < m_size / 2; i++)
|
||||
BAN::swap(m_data[i], m_data[m_size - i - 1]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size) requires is_default_constructible_v<T>
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
for (size_type i = size; i < m_size; i++)
|
||||
m_data[i].~T();
|
||||
if (size > m_size)
|
||||
for (size_type i = m_size; i < size; i++)
|
||||
new (m_data + i) T();
|
||||
m_size = size;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value) requires is_copy_constructible_v<T>
|
||||
ErrorOr<void> Vector<T>::resize(size_type size, const T& value)
|
||||
{
|
||||
TRY(ensure_capacity(size));
|
||||
if (size < m_size)
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
#include <BAN/RefPtr.h>
|
||||
|
||||
#if __is_kernel
|
||||
#include <kernel/Lock/SpinLock.h>
|
||||
#endif
|
||||
|
||||
namespace BAN
|
||||
{
|
||||
|
||||
|
@ -15,37 +11,22 @@ namespace BAN
|
|||
template<typename T>
|
||||
class WeakPtr;
|
||||
|
||||
// FIXME: Write this without using locks...
|
||||
template<typename T>
|
||||
class WeakLink : public RefCounted<WeakLink<T>>
|
||||
{
|
||||
public:
|
||||
RefPtr<T> try_lock()
|
||||
{
|
||||
#if __is_kernel
|
||||
Kernel::SpinLockGuard _(m_weak_lock);
|
||||
#endif
|
||||
if (m_ptr && m_ptr->try_ref())
|
||||
return RefPtr<T>::adopt(m_ptr);
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<T> lock() { ASSERT(m_ptr); return raw_ptr(); }
|
||||
T* raw_ptr() { return m_ptr; }
|
||||
|
||||
bool valid() const { return m_ptr; }
|
||||
void invalidate()
|
||||
{
|
||||
#if __is_kernel
|
||||
Kernel::SpinLockGuard _(m_weak_lock);
|
||||
#endif
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
void invalidate() { m_ptr = nullptr; }
|
||||
|
||||
private:
|
||||
WeakLink(T* ptr) : m_ptr(ptr) {}
|
||||
|
||||
private:
|
||||
T* m_ptr;
|
||||
#if __is_kernel
|
||||
Kernel::SpinLock m_weak_lock;
|
||||
#endif
|
||||
|
||||
friend class RefPtr<WeakLink<T>>;
|
||||
};
|
||||
|
||||
|
@ -53,7 +34,7 @@ namespace BAN
|
|||
class Weakable
|
||||
{
|
||||
public:
|
||||
virtual ~Weakable()
|
||||
~Weakable()
|
||||
{
|
||||
if (m_link)
|
||||
m_link->invalidate();
|
||||
|
@ -101,8 +82,8 @@ namespace BAN
|
|||
|
||||
RefPtr<T> lock()
|
||||
{
|
||||
if (m_link)
|
||||
return m_link->try_lock();
|
||||
if (m_link->valid())
|
||||
return m_link->lock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -110,8 +91,6 @@ namespace BAN
|
|||
|
||||
bool valid() const { return m_link && m_link->valid(); }
|
||||
|
||||
explicit operator bool() const { return valid(); }
|
||||
|
||||
private:
|
||||
WeakPtr(const RefPtr<WeakLink<T>>& link)
|
||||
: m_link(link)
|
||||
|
|
|
@ -4,54 +4,41 @@ 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_ETC ${BANAN_SYSROOT}/usr/etc)
|
||||
set(BANAN_SHARE ${BANAN_SYSROOT}/usr/share)
|
||||
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
|
||||
|
||||
set(CMAKE_INSTALL_BINDIR ${BANAN_BIN})
|
||||
set(CMAKE_INSTALL_SBINDIR ${BANAN_BIN})
|
||||
set(CMAKE_INSTALL_LIBDIR ${BANAN_LIB})
|
||||
set(CMAKE_INSTALL_INCLUDEDIR ${BANAN_INCLUDE})
|
||||
set(CMAKE_INSTALL_SYSCONF ${BANAN_ETC})
|
||||
set(CMAKE_INSTALL_MESSAGE NEVER)
|
||||
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY True)
|
||||
|
||||
set(CMAKE_STATIC_LIBRARY_PREFIX "")
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
set(BUILD_SHARED_LIBS True)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
|
||||
# include headers of ${library} to ${target}
|
||||
function(banan_include_headers target library)
|
||||
target_include_directories(${target} PRIVATE $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
|
||||
endfunction()
|
||||
|
||||
# include headers and link ${library} to ${target}
|
||||
function(banan_link_library target library)
|
||||
target_link_libraries(${target} PRIVATE ${library})
|
||||
banan_include_headers(${target} ${library})
|
||||
endfunction()
|
||||
|
||||
# add install step for all header files of target
|
||||
function(banan_install_headers target)
|
||||
file(GLOB_RECURSE headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/include *.h)
|
||||
foreach(header ${headers})
|
||||
get_filename_component(subdirectory ${header} DIRECTORY)
|
||||
install(FILES include/${header} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${subdirectory})
|
||||
endforeach()
|
||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
endfunction()
|
||||
|
||||
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,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 };
|
||||
};
|
||||
|
||||
}
|
|
@ -80,16 +80,6 @@ namespace LibELF
|
|||
Elf32Word p_align;
|
||||
};
|
||||
|
||||
struct Elf32Dynamic
|
||||
{
|
||||
Elf32Sword d_tag;
|
||||
union
|
||||
{
|
||||
Elf32Word d_val;
|
||||
Elf32Addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
using Elf64Addr = uint64_t;
|
||||
using Elf64Off = uint64_t;
|
||||
using Elf64Half = uint16_t;
|
||||
|
@ -165,17 +155,7 @@ namespace LibELF
|
|||
Elf64Xword p_align;
|
||||
};
|
||||
|
||||
struct Elf64Dynamic
|
||||
{
|
||||
Elf64Sxword d_tag;
|
||||
union
|
||||
{
|
||||
Elf64Xword d_val;
|
||||
Elf64Addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
#if ARCH(i686)
|
||||
#if ARCH(i386)
|
||||
using ElfNativeAddr = Elf32Addr;
|
||||
using ElfNativeOff = Elf32Off;
|
||||
using ElfNativeHalf = Elf32Half;
|
||||
|
@ -187,7 +167,6 @@ namespace LibELF
|
|||
using ElfNativeRelocation = Elf32Relocation;
|
||||
using ElfNativeRelocationA = Elf32RelocationA;
|
||||
using ElfNativeProgramHeader = Elf32ProgramHeader;
|
||||
using ElfNativeDynamic = Elf32Dynamic;
|
||||
#elif ARCH(x86_64)
|
||||
using ElfNativeAddr = Elf64Addr;
|
||||
using ElfNativeOff = Elf64Off;
|
||||
|
@ -202,7 +181,6 @@ namespace LibELF
|
|||
using ElfNativeRelocation = Elf64Relocation;
|
||||
using ElfNativeRelocationA = Elf64RelocationA;
|
||||
using ElfNativeProgramHeader = Elf64ProgramHeader;
|
||||
using ElfNativeDynamic = Elf64Dynamic;
|
||||
#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,
|
||||
};
|
||||
|
||||
}
|
117
README.md
117
README.md
|
@ -1,73 +1,8 @@
|
|||
[![](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fbananymous.com%2Fbanan-os%2Ftokei.json&query=%24.lines&label=total%20lines)](https://git.bananymous.com/Bananymous/banan-os)
|
||||
[![](https://img.shields.io/github/commit-activity/m/Bananymous/banan-os)](https://git.bananymous.com/Bananymous/banan-os)
|
||||
[![](https://img.shields.io/github/license/bananymous/banan-os)](https://git.bananymous.com/Bananymous/banan-os/src/branch/main/LICENSE)
|
||||
[![](https://img.shields.io/discord/1242165176032297040?logo=discord&label=discord)](https://discord.gg/ehjGySwYdK)
|
||||
![license](https://img.shields.io/github/license/bananymous/banan-os)
|
||||
|
||||
# banan-os
|
||||
|
||||
This is my hobby operating system written in C++. Currently supports x86\_64 and i686 architectures.
|
||||
|
||||
You can find a live demo [here](https://bananymous.com/banan-os)
|
||||
|
||||
If you want to try out DOOM, you should first enter the GUI environment using the `start-gui` command. Then you can run `doom` in the GUI terminal.
|
||||
|
||||
### Features
|
||||
|
||||
#### General
|
||||
- [x] Ring3 userspace
|
||||
- [x] SMP (multiprocessing)
|
||||
- [x] Linear framebuffer (VESA and GOP)
|
||||
- [x] Network stack
|
||||
- [x] ELF executable loading
|
||||
- [x] AML interpreter (partial)
|
||||
- [x] Basic graphical environment
|
||||
- [x] Terminal emulator
|
||||
- [x] Status bar
|
||||
- [ ] Program launcher
|
||||
- [ ] Some nice apps
|
||||
- [x] ELF dynamic linking
|
||||
- [x] copy-on-write memory
|
||||
- [x] file mappings
|
||||
- [ ] anonymous mappings
|
||||
|
||||
#### Drivers
|
||||
- [x] NVMe disks
|
||||
- [x] ATA (IDE, SATA) disks
|
||||
- [x] E1000 and E1000E NICs
|
||||
- [x] RTL8111/8168/8211/8411 NICs
|
||||
- [x] PS2 keyboard (all scancode sets)
|
||||
- [x] PS2 mouse
|
||||
- [x] USB
|
||||
- [x] Keyboard
|
||||
- [x] Mouse
|
||||
- [x] Mass storage
|
||||
- [ ] Hubs
|
||||
- [ ] ...
|
||||
- [ ] virtio devices (network, storage)
|
||||
|
||||
#### Network
|
||||
- [x] ARP
|
||||
- [x] ICMP
|
||||
- [x] IPv4
|
||||
- [x] UDP
|
||||
- [x] TCP (partial and buggy)
|
||||
- [x] Unix domain sockets
|
||||
- [ ] SSL
|
||||
|
||||
#### Filesystems
|
||||
- [x] Virtual filesystem
|
||||
- [x] Ext2
|
||||
- [x] FAT12/16/32
|
||||
- [x] Dev
|
||||
- [x] Ram
|
||||
- [x] Proc
|
||||
- [ ] Sys
|
||||
- [ ] 9P
|
||||
|
||||
#### Bootloader support
|
||||
- [x] GRUB
|
||||
- [x] Custom BIOS bootloader
|
||||
- [ ] Custom UEFI bootloader
|
||||
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)
|
||||
|
||||
|
@ -77,59 +12,37 @@ Each major component and library has its own subdirectory (kernel, userspace, li
|
|||
|
||||
## Building
|
||||
|
||||
### Needed packages
|
||||
|
||||
#### apt (tested on ubuntu 22.04)
|
||||
```# apt install build-essential git ninja-build texinfo bison flex libgmp-dev libmpfr-dev libmpc-dev parted qemu-system-x86 cpu-checker```
|
||||
|
||||
#### pacman
|
||||
```# pacman -S --needed base-devel git wget cmake ninja parted qemu-system-x86```
|
||||
|
||||
|
||||
### Compilation
|
||||
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
|
||||
./bos toolchain
|
||||
./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
|
||||
./bos qemu
|
||||
./bos qemu-nographic
|
||||
./bos qemu-debug
|
||||
./bos bochs
|
||||
./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
|
||||
./bos kernel
|
||||
./bos image
|
||||
./script/build.sh kernel
|
||||
./script/build.sh image
|
||||
```
|
||||
|
||||
To build for other architectures set environment variable BANAN\_ARCH=*arch* (e.g. BANAN\_ARCH=i686).
|
||||
|
||||
To change the bootloader you can set environment variable BANAN\_BOOTLOADER; supported values are BANAN (my custom bootloader) and GRUB.
|
||||
|
||||
To run with UEFI set environment variable BANAN\_UEFI\_BOOT=1. You will also have to set OVMF\_PATH to the correct OVMF (default */usr/share/ovmf/x64/OVMF.fd*).
|
||||
|
||||
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
|
||||
./bos image-full
|
||||
./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
|
||||
### Contributing
|
||||
|
||||
As the upstream is hosted on my server https://git.bananymous.com/Bananymous/banan-os, merging contributions is not as trivial as it would be on GitHub. You can still send PRs in GitHub in which case I should be able to download the diff and apply it manually. If you want, I can also provide you an account to my git server. In this case please contact me ([email](mailto:oskari.alaranta@bananymous.com), [discord](https://discord.gg/ehjGySwYdK)).
|
||||
|
||||
As this is mostly a learning experience for me, I would appreciate if you first contacted me about adding new features (email, discord, issue, ...). If you send a PR about something I was planning on doing myself and you didn't ask me, I will probably just close it. Bug fixes are always welcome!
|
||||
|
||||
Commit message should be formatted followingly
|
||||
|
||||
1. First line is of the form "_Subject: Description_", where _Subject_ tells the area touched (Kernel, Shell, BuildSystem, ...) and _Description_ is brief description of the change done. First line should fit fully in 72 characters.
|
||||
2. Body of the message should further describe the change and reasoning behind the change.
|
||||
|
||||
All commits should pass the pre-commit hook defined in _.pre-commit-config.yaml_. For instructions on how to setup pre-commit, please see https://pre-commit.com/#install.
|
||||
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.
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.26)
|
|||
project(bootloader ASM)
|
||||
|
||||
set(BOOTLOADER_SOURCES
|
||||
a20_line.S
|
||||
boot.S
|
||||
command_line.S
|
||||
disk.S
|
||||
|
@ -15,6 +14,5 @@ set(BOOTLOADER_SOURCES
|
|||
)
|
||||
|
||||
add_executable(bootloader ${BOOTLOADER_SOURCES})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_options(bootloader PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
|
||||
target_link_options(bootloader PRIVATE -nostdlib)
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
.code16
|
||||
.section .stage2
|
||||
|
||||
# checks whether A20 line is enabled or disabled
|
||||
# return
|
||||
# ax: 1 if enabled, 0 otherwise
|
||||
check_a20:
|
||||
pushf
|
||||
pushw %si
|
||||
pushw %di
|
||||
pushw %ds
|
||||
pushw %es
|
||||
|
||||
cli
|
||||
|
||||
xorw %ax, %ax
|
||||
movw %ax, %es
|
||||
notw %ax
|
||||
movw %ax, %ds
|
||||
|
||||
movw $0x0500, %di
|
||||
movw $0x0510, %si
|
||||
|
||||
movb %es:(%di), %al
|
||||
pushw %ax
|
||||
|
||||
movb %ds:(%si), %al
|
||||
pushw %ax
|
||||
|
||||
movb $0x00, %es:(%di)
|
||||
movb $0xFF, %ds:(%si)
|
||||
|
||||
cmpb $0xFF, %es:(%di)
|
||||
|
||||
pop %ax
|
||||
movb %al, %ds:(%si)
|
||||
|
||||
pop %ax
|
||||
movb %al, %es:(%di)
|
||||
|
||||
movw $0, %ax
|
||||
je .check_a20_done
|
||||
|
||||
movw $1, %ax
|
||||
|
||||
.check_a20_done:
|
||||
popw %es
|
||||
popw %ds
|
||||
popw %di
|
||||
popw %si
|
||||
popf
|
||||
ret
|
||||
|
||||
|
||||
# Try to enable A20 using PS2 controller
|
||||
enable_a20_ps2:
|
||||
pushf
|
||||
pushw %ax
|
||||
|
||||
cli
|
||||
|
||||
# disable first port
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xAD, %al
|
||||
outb %al, $0x64
|
||||
|
||||
# read controller output
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xD0, %al
|
||||
outb %al, $0x64
|
||||
|
||||
call .enable_a20_ps2_wait2
|
||||
inb $0x60, %al
|
||||
pushw %ax
|
||||
|
||||
# write controller output
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xD1, %al
|
||||
outb %al, $0x64
|
||||
|
||||
call .enable_a20_ps2_wait1
|
||||
popw %ax
|
||||
orw $2, %ax
|
||||
outb %al, $0x60
|
||||
|
||||
# enable first port
|
||||
call .enable_a20_ps2_wait1
|
||||
movb $0xAE, %al
|
||||
outb %al, $0x64
|
||||
|
||||
call .enable_a20_ps2_wait1
|
||||
|
||||
popw %ax
|
||||
popf
|
||||
ret
|
||||
|
||||
.enable_a20_ps2_wait1:
|
||||
inb $0x64, %al
|
||||
test $2, %al
|
||||
jnz .enable_a20_ps2_wait1
|
||||
ret
|
||||
|
||||
.enable_a20_ps2_wait2:
|
||||
inb $0x64, %al
|
||||
test $1, %al
|
||||
jnz .enable_a20_ps2_wait1
|
||||
ret
|
||||
|
||||
|
||||
# Check if A20 line is disabled. If it is, try to enable it
|
||||
.global enable_a20
|
||||
enable_a20:
|
||||
pushw %ax
|
||||
pushw %si
|
||||
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
movw $a20_line_disabled_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
# Try to enable A20 line using bios interrupt
|
||||
movw $0x2401, %ax
|
||||
int $0x15
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
# Try to enable A20 line using ps2 controller
|
||||
call enable_a20_ps2
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
# Try to enable A20 line using fast A20 gate
|
||||
inb $0x92, %al
|
||||
testb $2, %al
|
||||
jnz .enable_a20_fast_done
|
||||
orb $2, %al
|
||||
outb %al, $0x92
|
||||
.enable_a20_fast_done:
|
||||
|
||||
call check_a20
|
||||
testw %ax, %ax
|
||||
jnz .enable_a20_done
|
||||
|
||||
movw $a20_could_not_enable_msg, %si
|
||||
call print_and_halt
|
||||
|
||||
.enable_a20_done:
|
||||
movw $a20_line_enabled_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
popw %si
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
a20_line_disabled_msg:
|
||||
.asciz "A20 line disabled. Trying to enable it"
|
||||
|
||||
a20_line_enabled_msg:
|
||||
.asciz "A20 line enabled"
|
||||
|
||||
a20_could_not_enable_msg:
|
||||
.asciz "Could not enable A20 line"
|
|
@ -1,5 +1,3 @@
|
|||
.include "common.S"
|
||||
|
||||
.code16
|
||||
|
||||
#########################################
|
||||
|
@ -15,10 +13,12 @@
|
|||
|
||||
.global stage1_main
|
||||
stage1_main:
|
||||
# setup segments and stack
|
||||
xorw %ax, %ax
|
||||
# setup segments
|
||||
movw $0, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
|
||||
# setup stack
|
||||
movw %ax, %ss
|
||||
movl $0x7C00, %esp
|
||||
|
||||
|
@ -53,19 +53,15 @@ stage2_main:
|
|||
movw $hello_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
lgdt gdtr
|
||||
|
||||
call enter_unreal_mode
|
||||
movw $unreal_enter_msg, %si
|
||||
call puts; call print_newline
|
||||
|
||||
call enable_a20
|
||||
|
||||
call get_memory_map
|
||||
|
||||
call print_newline
|
||||
call read_user_command_line
|
||||
|
||||
call vesa_find_video_mode
|
||||
|
||||
call print_newline
|
||||
|
||||
movw $start_kernel_load_msg, %si
|
||||
|
@ -88,36 +84,36 @@ stage2_main:
|
|||
|
||||
call elf_read_kernel_to_memory
|
||||
|
||||
call vesa_set_video_mode
|
||||
call vesa_set_target_mode
|
||||
|
||||
cli
|
||||
|
||||
# kernel entry point
|
||||
movl %eax, %ecx
|
||||
|
||||
# setup kernel parameters
|
||||
movl $0xD3C60CFF, %eax
|
||||
movl $banan_boot_info, %ebx
|
||||
|
||||
# setup protected mode
|
||||
movl %cr0, %edx
|
||||
orb $1, %dl
|
||||
movl %edx, %cr0
|
||||
movl %cr0, %ebx
|
||||
orb $1, %bl
|
||||
movl %ebx, %cr0
|
||||
|
||||
# jump to kernel in protected mode
|
||||
ljmpl $0x18, $protected_mode
|
||||
|
||||
# jump to protected mode
|
||||
ljmpl $GDT_CODE32, $protected_mode
|
||||
|
||||
.code32
|
||||
protected_mode:
|
||||
# setup protected mode segments
|
||||
movw $GDT_DATA32, %dx
|
||||
movw %dx, %ds
|
||||
movw %dx, %es
|
||||
movw %dx, %fs
|
||||
movw %dx, %gs
|
||||
movw %dx, %ss
|
||||
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
|
||||
|
||||
# jump to kernel entry
|
||||
jmp *%ecx
|
||||
|
||||
|
||||
|
@ -126,18 +122,20 @@ enter_unreal_mode:
|
|||
cli
|
||||
pushw %ds
|
||||
|
||||
lgdt gdtr
|
||||
|
||||
movl %cr0, %eax
|
||||
orb $1, %al
|
||||
movl %eax, %cr0
|
||||
ljmpl $GDT_CODE16, $.enter_unreal_mode_pmode
|
||||
ljmpl $0x8, $.enter_unreal_mode_pmode
|
||||
|
||||
.enter_unreal_mode_pmode:
|
||||
movw $GDT_DATA32, %bx
|
||||
movw $0x10, %bx
|
||||
movw %bx, %ds
|
||||
|
||||
andb $0xFE, %al
|
||||
andb 0xFE, %al
|
||||
movl %eax, %cr0
|
||||
ljmpl $0x00, $.enter_unreal_mode_unreal
|
||||
ljmpl $0x0, $.enter_unreal_mode_unreal
|
||||
|
||||
.enter_unreal_mode_unreal:
|
||||
popw %ds
|
||||
|
@ -145,8 +143,6 @@ enter_unreal_mode:
|
|||
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
hello_msg:
|
||||
.asciz "This is banan-os bootloader"
|
||||
|
||||
|
@ -158,12 +154,12 @@ start_kernel_load_msg:
|
|||
|
||||
gdt:
|
||||
.quad 0x0000000000000000
|
||||
.quad 0x008F9A000000FFFF # 16-bit code
|
||||
.quad 0x00CF92000000FFFF # 32-bit data
|
||||
.quad 0x00CF9A000000FFFF # 32-bit code
|
||||
.quad 0x00009A000000FFFF
|
||||
.quad 0x00CF92000000FFFF
|
||||
.quad 0x00CF9A000000FFFF
|
||||
gdtr:
|
||||
.short . - gdt - 1
|
||||
.long gdt
|
||||
.quad gdt
|
||||
|
||||
banan_boot_info:
|
||||
boot_command_line:
|
||||
|
@ -172,5 +168,3 @@ banan_boot_info:
|
|||
.long framebuffer
|
||||
boot_memory_map:
|
||||
.long memory_map
|
||||
boot_kernel_paddr:
|
||||
.long 0
|
||||
|
|
|
@ -26,8 +26,6 @@ read_user_command_line:
|
|||
|
||||
cmpb $'\b', %al
|
||||
je .read_user_command_line_backspace
|
||||
cmpb $0x7F, %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
|
||||
|
@ -74,8 +72,6 @@ read_user_command_line:
|
|||
ret
|
||||
|
||||
|
||||
.section .data
|
||||
|
||||
command_line_enter_msg:
|
||||
.asciz "cmdline: "
|
||||
|
||||
|
@ -84,4 +80,4 @@ command_line:
|
|||
# 100 character command line
|
||||
command_line_buffer:
|
||||
.ascii "root=/dev/sda2"
|
||||
.skip 100 - (. - command_line_buffer)
|
||||
.skip 100 - 28
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.set GDT_CODE16, 0x08
|
||||
.set GDT_DATA32, 0x10
|
||||
.set GDT_CODE32, 0x18
|
|
@ -41,7 +41,7 @@ read_from_disk:
|
|||
call drive_has_int13_ext
|
||||
|
||||
# prepare disk read packet
|
||||
movw $disk_address_packet, %si
|
||||
mov $disk_address_packet, %si
|
||||
movb $0x10, 0x00(%si) # packet size
|
||||
movb $0x00, 0x01(%si) # always 0
|
||||
movw %cx, 0x02(%si) # lba count
|
||||
|
@ -389,8 +389,10 @@ find_root_partition:
|
|||
|
||||
# increment 8 byte entry array lba
|
||||
incl 0(%esp)
|
||||
adcl $0, 4(%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
|
||||
|
@ -414,10 +416,12 @@ find_root_partition:
|
|||
|
||||
# ebx:eax -= first lba - 1
|
||||
subl (root_partition_entry + 36), %ebx
|
||||
movl (root_partition_entry + 32), %ecx
|
||||
movl (root_partition_entry + 32), %ecx;
|
||||
decl %ecx
|
||||
subl %ecx, %eax
|
||||
sbbl $0, %ebx
|
||||
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
|
||||
|
@ -470,7 +474,6 @@ print_root_partition_info:
|
|||
popw %ax
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
# These will be patched during bootloader installation
|
||||
root_disk_guid:
|
||||
|
|
|
@ -5,26 +5,15 @@
|
|||
.set e_machine, 18
|
||||
.set e_version, 20
|
||||
.set e_entry, 24
|
||||
|
||||
.set e32_phoff, 28
|
||||
.set e32_shoff, 32
|
||||
.set e32_flags, 36
|
||||
.set e32_ehsize, 40
|
||||
.set e32_phentsize, 42
|
||||
.set e32_phnum, 44
|
||||
.set e32_shentsize, 46
|
||||
.set e32_shnum, 48
|
||||
.set e32_shstrndx, 50
|
||||
|
||||
.set e64_phoff, 32
|
||||
.set e64_shoff, 40
|
||||
.set e64_flags, 48
|
||||
.set e64_ehsize, 52
|
||||
.set e64_phentsize, 54
|
||||
.set e64_phnum, 56
|
||||
.set e64_shentsize, 58
|
||||
.set e64_shnum, 60
|
||||
.set e64_shstrndx, 62
|
||||
.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
|
||||
|
@ -33,7 +22,6 @@
|
|||
|
||||
# e_ident constants
|
||||
.set ELFMAGIC, 0x464C457F
|
||||
.set ELFCLASS32, 1
|
||||
.set ELFCLASS64, 2
|
||||
.set ELFDATA2LSB, 1
|
||||
.set EV_CURRENT, 1
|
||||
|
@ -43,30 +31,18 @@
|
|||
|
||||
# program header field offsets
|
||||
.set p_type, 0
|
||||
|
||||
.set p32_offset, 4
|
||||
.set p32_vaddr, 8
|
||||
.set p32_paddr, 12
|
||||
.set p32_filesz, 16
|
||||
.set p32_memsz, 20
|
||||
.set p32_flags, 24
|
||||
.set p32_align, 28
|
||||
|
||||
.set p64_flags, 4
|
||||
.set p64_offset, 8
|
||||
.set p64_vaddr, 16
|
||||
.set p64_paddr, 24
|
||||
.set p64_filesz, 32
|
||||
.set p64_memsz, 40
|
||||
.set p64_align, 48
|
||||
.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
|
||||
|
||||
# mask for entry point and segment loading
|
||||
.set LOAD_MASK, 0x07FFFFFF
|
||||
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
|
@ -76,12 +52,8 @@ elf_validate_file_header:
|
|||
cmpl $ELFMAGIC, (elf_file_header)
|
||||
jne .elf_validate_file_header_invalid_magic
|
||||
|
||||
cmpb $ELFCLASS32, (elf_file_header + EI_CLASS)
|
||||
je .elf_validate_file_header_class_valid
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
je .elf_validate_file_header_class_valid
|
||||
jmp .elf_validate_file_header_invalid_class
|
||||
.elf_validate_file_header_class_valid:
|
||||
jne .elf_validate_file_header_only_64bit_supported
|
||||
|
||||
cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA)
|
||||
jne .elf_validate_file_header_only_little_endian_supported
|
||||
|
@ -100,8 +72,8 @@ elf_validate_file_header:
|
|||
.elf_validate_file_header_invalid_magic:
|
||||
movw $elf_validate_file_header_invalid_magic_msg, %si
|
||||
jmp print_and_halt
|
||||
.elf_validate_file_header_invalid_class:
|
||||
movw $elf_validate_file_header_invalid_class_msg, %si
|
||||
.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
|
||||
|
@ -113,59 +85,6 @@ elf_validate_file_header:
|
|||
movw $elf_validate_file_header_not_executable_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
# reads memory specified by 32 bit elf_program_header to memory
|
||||
elf_read_program_header32_to_memory:
|
||||
pushal
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
# memset p_filesz -> p_memsz to 0
|
||||
movl (elf_program_header + p32_filesz), %ebx
|
||||
movl (elf_program_header + p32_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
addl %ebx, %edi
|
||||
movl (elf_program_header + p32_memsz), %ecx
|
||||
subl %ebx, %ecx
|
||||
xorb %al, %al; call memset32
|
||||
|
||||
# read file specified in program header to memory
|
||||
movl (elf_program_header + p32_offset), %eax
|
||||
movl (elf_program_header + p32_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
movl (elf_program_header + p32_filesz), %ecx
|
||||
call *%esi
|
||||
|
||||
leavel
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# reads memory specified by 64 bit elf_program_header to memory
|
||||
elf_read_program_header64_to_memory:
|
||||
pushal
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
# memset p_filesz -> p_memsz to 0
|
||||
movl (elf_program_header + p64_filesz), %ebx
|
||||
movl (elf_program_header + p64_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
addl %ebx, %edi
|
||||
movl (elf_program_header + p64_memsz), %ecx
|
||||
subl %ebx, %ecx
|
||||
xorb %al, %al; call memset32
|
||||
|
||||
# read file specified in program header to memory
|
||||
movl (elf_program_header + p64_offset), %eax
|
||||
movl (elf_program_header + p64_vaddr), %edi
|
||||
andl $LOAD_MASK, %edi
|
||||
movl (elf_program_header + p64_filesz), %ecx
|
||||
call *%esi
|
||||
|
||||
leavel
|
||||
popal
|
||||
ret
|
||||
|
||||
|
||||
# read callback format
|
||||
# eax: first byte
|
||||
|
@ -185,72 +104,42 @@ elf_read_kernel_to_memory:
|
|||
movl %esp, %ebp
|
||||
subl $2, %esp
|
||||
|
||||
# read start of file header
|
||||
# read file header
|
||||
movl $0, %eax
|
||||
movl $24, %ecx
|
||||
movl $64, %ecx
|
||||
movl $elf_file_header, %edi
|
||||
call *%esi
|
||||
|
||||
call elf_validate_file_header
|
||||
|
||||
# determine file header size
|
||||
movl $52, %ecx
|
||||
movl $64, %edx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovel %edx, %ecx
|
||||
|
||||
# read full file header
|
||||
movl $0, %eax
|
||||
movl $elf_file_header, %edi
|
||||
call *%esi
|
||||
|
||||
# verify that e_phoff fits in 32 bits
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
jne .elf_read_kernel_to_memory_valid_offset
|
||||
cmpl $0, (elf_file_header + e64_phoff + 4)
|
||||
cmpl $0, (elf_file_header + e_phoff + 4)
|
||||
jnz .elf_read_kernel_to_memory_unsupported_offset
|
||||
.elf_read_kernel_to_memory_valid_offset:
|
||||
|
||||
# read architecture phentsize and phnum to fixed locations
|
||||
movw (elf_file_header + e32_phentsize), %ax
|
||||
movw (elf_file_header + e32_phnum), %bx
|
||||
movl (elf_file_header + e32_phoff), %ecx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovew (elf_file_header + e64_phentsize), %ax
|
||||
cmovew (elf_file_header + e64_phnum), %bx
|
||||
cmovel (elf_file_header + e64_phoff), %ecx
|
||||
movw %ax, (elf_file_header_phentsize)
|
||||
movw %bx, (elf_file_header_phnum)
|
||||
movl %ecx, (elf_file_header_phoff)
|
||||
|
||||
# current program header
|
||||
movw $0, -2(%ebp)
|
||||
|
||||
.elf_read_kernel_to_memory_loop_program_headers:
|
||||
movw -2(%ebp), %cx
|
||||
cmpw (elf_file_header_phnum), %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_phentsize), %bx
|
||||
movw (elf_file_header + e_phentsize), %bx
|
||||
mull %ebx
|
||||
addl (elf_file_header_phoff), %eax
|
||||
addl (elf_file_header + e_phoff), %eax
|
||||
jc .elf_read_kernel_to_memory_unsupported_offset
|
||||
|
||||
# determine program header size
|
||||
movl $32, %ecx
|
||||
movl $56, %edx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovel %edx, %ecx
|
||||
|
||||
# read program header
|
||||
# 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 NULL header
|
||||
# test if program header is empty
|
||||
cmpl $PT_NULL, (elf_program_header + p_type)
|
||||
je .elf_read_kernel_to_memory_null_program_header
|
||||
|
||||
|
@ -258,12 +147,33 @@ elf_read_kernel_to_memory:
|
|||
cmpl $PT_LOAD, (elf_program_header + p_type)
|
||||
jne .elf_read_kernel_to_memory_not_loadable_header
|
||||
|
||||
# read program header to memory
|
||||
movl $elf_read_program_header32_to_memory, %eax
|
||||
movl $elf_read_program_header64_to_memory, %ebx
|
||||
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
|
||||
cmovel %ebx, %eax
|
||||
call *%eax
|
||||
# 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)
|
||||
|
@ -275,7 +185,7 @@ elf_read_kernel_to_memory:
|
|||
|
||||
# set kernel entry address
|
||||
movl (elf_file_header + e_entry), %eax
|
||||
andl $LOAD_MASK, %eax
|
||||
andl $0x7FFFFF, %eax
|
||||
|
||||
ret
|
||||
|
||||
|
@ -286,12 +196,11 @@ elf_read_kernel_to_memory:
|
|||
movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.section .data
|
||||
|
||||
elf_validate_file_header_invalid_magic_msg:
|
||||
.asciz "ELF: file has invalid ELF magic"
|
||||
elf_validate_file_header_invalid_class_msg:
|
||||
.asciz "ELF: file has invalid ELF class"
|
||||
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:
|
||||
|
@ -309,12 +218,5 @@ elf_read_kernel_to_memory_not_loadable_header_msg:
|
|||
elf_file_header:
|
||||
.skip 64
|
||||
|
||||
elf_file_header_phentsize:
|
||||
.skip 2
|
||||
elf_file_header_phnum:
|
||||
.skip 2
|
||||
elf_file_header_phoff:
|
||||
.skip 4 # NOTE: only 32 bit offsets are supported
|
||||
|
||||
elf_program_header:
|
||||
.skip 56
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
.set SECTOR_SHIFT, 9
|
||||
.set SECTOR_SIZE, 1 << SECTOR_SHIFT
|
||||
|
||||
.set EXT2_MAX_BLOCK_SIZE, 4096
|
||||
# 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
|
||||
|
@ -16,7 +18,6 @@
|
|||
.set EXT2_S_IFREG, 0x8000
|
||||
|
||||
# superblock offsets
|
||||
.set s_first_data_block, 20
|
||||
.set s_log_block_size, 24
|
||||
.set s_inodes_per_group, 40
|
||||
.set s_magic, 56
|
||||
|
@ -65,7 +66,9 @@ has_ext2_filesystem:
|
|||
|
||||
# from byte offset 1024
|
||||
addl $(1024 / SECTOR_SIZE), %eax
|
||||
adcw $0, %bx
|
||||
jnc .has_ext2_filesystem_no_overflow
|
||||
incw %bx
|
||||
.has_ext2_filesystem_no_overflow:
|
||||
|
||||
# into sector buffer
|
||||
movw $ext2_block_buffer, %di
|
||||
|
@ -87,16 +90,11 @@ has_ext2_filesystem:
|
|||
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_MAX_BLOCK_SIZE
|
||||
# verify 1024 << s_log_block_size == EXT2_BLOCK_SIZE
|
||||
movl $1024, %eax
|
||||
shll %cl, %eax
|
||||
cmpl $EXT2_MAX_BLOCK_SIZE, %eax
|
||||
ja .has_ext2_filesystem_unsupported_block_size
|
||||
|
||||
# fill block size and shift
|
||||
movl %eax, (ext2_block_size)
|
||||
addl $10, %ecx
|
||||
movl %ecx, (ext2_block_shift)
|
||||
cmpl $EXT2_BLOCK_SIZE, %eax
|
||||
jne .has_ext2_filesystem_unsupported_block_size
|
||||
|
||||
# fill inode size
|
||||
movl $128, %eax
|
||||
|
@ -132,23 +130,38 @@ has_ext2_filesystem:
|
|||
# reads block in to ext2_block_buffer
|
||||
# eax: block number
|
||||
ext2_read_block:
|
||||
pushal
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
pushw %cx
|
||||
pushl %edx
|
||||
pushw %di
|
||||
|
||||
# ecx := sectors_per_block := block_size / sector_size
|
||||
movl (ext2_block_size), %ecx
|
||||
shrl $SECTOR_SHIFT, %ecx
|
||||
# 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 := block * sectors_per_block + (ext2_partition_first_sector)
|
||||
xorl %ebx, %ebx
|
||||
mull %ecx
|
||||
# ebx:eax := eax + (ext2_partition_first_sector)
|
||||
movl (ext2_partition_first_sector + 4), %ebx
|
||||
addl (ext2_partition_first_sector + 0), %eax
|
||||
adcl (ext2_partition_first_sector + 4), %ebx
|
||||
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
|
||||
|
||||
popal
|
||||
popw %di
|
||||
popl %edx
|
||||
popw %cx
|
||||
popl %ebx
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
|
||||
|
@ -157,23 +170,15 @@ ext2_read_block:
|
|||
ext2_read_block_group_descriptor:
|
||||
pushal
|
||||
|
||||
# ebx := bgd_block_byte_offset := (s_first_data_block + 1) * block_size
|
||||
# := (s_first_data_block + 1) << ext2_block_shift
|
||||
movl (ext2_superblock_buffer + s_first_data_block), %ebx
|
||||
incl %ebx
|
||||
movb (ext2_block_shift), %cl
|
||||
shll %cl, %ebx
|
||||
# eax := bgd_byte_offset := 2048 + EXT2_BGD_SIZE * eax := (eax << EXT2_BGD_SHIFT) + 2048
|
||||
shll $EXT2_BGD_SHIFT, %eax
|
||||
addl $2048, %eax
|
||||
|
||||
# eax := bgd_byte_offset := bgd_block_byte_offset + EXT2_BGD_SIZE * block_group;
|
||||
# := bgd_block_byte_offset + (block_group << EXT2_BGD_SHIFT)
|
||||
movb $EXT2_BGD_SHIFT, %cl
|
||||
shll %cl, %eax
|
||||
addl %ebx, %eax
|
||||
|
||||
# eax: bgd_block := bgd_byte_offset / block_size
|
||||
# ebx: bgd_offset := bgd_byte_offset % block_size
|
||||
# eax: bgd_block := bgd_byte_offset / EXT2_BLOCK_SIZE
|
||||
# ebx: bgd_offset := bgd_byte_offset % EXT2_BLOCK_SIZE
|
||||
xorl %edx, %edx
|
||||
divl (ext2_block_size)
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
call ext2_read_block
|
||||
|
@ -199,19 +204,23 @@ ext2_read_inode:
|
|||
# ebx := inode_index = (ino - 1) % s_inodes_per_group
|
||||
xorl %edx, %edx
|
||||
decl %eax
|
||||
divl (ext2_superblock_buffer + s_inodes_per_group)
|
||||
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) / block_size
|
||||
# ebx := inode_table_offset := (inode_index * inode_size) % block_size
|
||||
# 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
|
||||
mull (ext2_inode_size)
|
||||
divl (ext2_block_size)
|
||||
movl (ext2_inode_size), %ebx
|
||||
mull %ebx
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
movl %edx, %ebx
|
||||
|
||||
# eax := filesystem_block := eax + bg_inode_table
|
||||
# eax := file system block := eax + bg_inode_table
|
||||
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
|
||||
|
||||
movb (ext2_drive_number), %dl
|
||||
|
@ -227,10 +236,6 @@ ext2_read_inode:
|
|||
movl (ext2_inode_size), %ecx
|
||||
rep movsb
|
||||
|
||||
# reset indirect cache to zero
|
||||
movl $0, (ext2_inode_indirect_number)
|
||||
|
||||
.ext2_read_inode_done:
|
||||
popal
|
||||
ret
|
||||
|
||||
|
@ -244,19 +249,15 @@ ext2_data_block_index:
|
|||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
|
||||
# ebx := max_data_blocks := (file_size + block_size - 1) / block_size
|
||||
# := (i_size + ext2_block_size - 1) >> ext2_block_shift
|
||||
# cl := ext2_block_shift
|
||||
movl (ext2_inode_buffer + i_size), %ebx
|
||||
addl (ext2_block_size), %ebx
|
||||
decl %ebx
|
||||
movb (ext2_block_shift), %cl
|
||||
shrl %cl, %ebx
|
||||
# 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 %ebx, %eax
|
||||
cmpl %ecx, %eax
|
||||
jae .ext2_data_block_index_out_of_bounds
|
||||
|
||||
# check if this is direct block access
|
||||
|
@ -264,26 +265,18 @@ ext2_data_block_index:
|
|||
jb .ext2_data_block_index_direct
|
||||
subl $12, %eax
|
||||
|
||||
# cl := indices_per_block_shift := ext2_block_shift - 2
|
||||
# ebx := comp
|
||||
subb $2, %cl
|
||||
movl $1, %ebx
|
||||
shll %cl, %ebx
|
||||
|
||||
# check if this is singly indirect block access
|
||||
cmpl %ebx, %eax
|
||||
cmpl $(EXT2_BLOCK_SIZE / 4), %eax
|
||||
jb .ext2_data_block_index_singly_indirect
|
||||
subl %ebx, %eax
|
||||
shll %cl, %ebx
|
||||
subl $(EXT2_BLOCK_SIZE / 4), %eax
|
||||
|
||||
# check if this is doubly indirect block access
|
||||
cmpl %ebx, %eax
|
||||
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
|
||||
jb .ext2_data_block_index_doubly_indirect
|
||||
subl %ebx, %eax
|
||||
shll %cl, %ebx
|
||||
subl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
|
||||
|
||||
# check if this is triply indirect block access
|
||||
cmpl %ebx, %eax
|
||||
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
|
||||
|
@ -316,28 +309,6 @@ ext2_data_block_index:
|
|||
# ebx := index
|
||||
# cx := depth
|
||||
.ext2_data_block_index_indirect:
|
||||
# edx := cache index := (index & ~(block_size / 4 - 1)) | depth
|
||||
# := (index & -(block_size >> 2)) | depth
|
||||
movl (ext2_block_size), %edx
|
||||
shrl $2, %edx
|
||||
negl %edx
|
||||
andl %ebx, %edx
|
||||
orw %cx, %dx
|
||||
|
||||
# check whether this block is already cached
|
||||
cmpl $0, (ext2_inode_indirect_number)
|
||||
je .ext2_data_block_index_indirect_no_cache
|
||||
cmpl %edx, (ext2_inode_indirect_number)
|
||||
je .ext2_data_block_index_indirect_cached
|
||||
|
||||
.ext2_data_block_index_indirect_no_cache:
|
||||
# update cache block number, will be cached when found
|
||||
movl %edx, (ext2_inode_indirect_number)
|
||||
|
||||
# eax := current block
|
||||
# ebx := index
|
||||
# cx := depth
|
||||
.ext2_data_block_index_indirect_loop:
|
||||
call ext2_read_block
|
||||
|
||||
# store depth and index
|
||||
|
@ -348,21 +319,20 @@ ext2_data_block_index:
|
|||
jbe .ext2_data_block_index_no_shift
|
||||
|
||||
# cl := shift
|
||||
movb (ext2_block_shift), %al
|
||||
subb $2, %al
|
||||
movb $(EXT2_BLOCK_SHIFT - 2), %al
|
||||
decb %cl
|
||||
mulb %cl
|
||||
movb %al, %cl
|
||||
|
||||
# ebx := ebx >> shift
|
||||
# ebx := ebx >> cl
|
||||
shrl %cl, %ebx
|
||||
|
||||
.ext2_data_block_index_no_shift:
|
||||
# edx := index of next block (ebx & (block_size / 4 - 1))
|
||||
movl (ext2_block_size), %edx
|
||||
shrl $2, %edx
|
||||
decl %edx
|
||||
andl %ebx, %edx
|
||||
# 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
|
||||
|
@ -372,13 +342,7 @@ ext2_data_block_index:
|
|||
popl %ebx
|
||||
popw %cx
|
||||
|
||||
loop .ext2_data_block_index_indirect_loop
|
||||
|
||||
# cache last read block
|
||||
movw $ext2_block_buffer, %si
|
||||
movw $ext2_inode_indirect_buffer, %di
|
||||
movw (ext2_block_size), %cx
|
||||
rep movsb
|
||||
loop .ext2_data_block_index_indirect
|
||||
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
|
@ -394,16 +358,7 @@ ext2_data_block_index:
|
|||
movl $0, %eax
|
||||
jmp .ext2_data_block_index_done
|
||||
|
||||
.ext2_data_block_index_indirect_cached:
|
||||
movl $ext2_inode_indirect_buffer, %esi
|
||||
movl (ext2_block_size), %edx
|
||||
shrl $2, %edx
|
||||
decl %edx
|
||||
andl %edx, %ebx
|
||||
movl (%esi, %ebx, 4), %eax
|
||||
|
||||
.ext2_data_block_index_done:
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %edx
|
||||
popl %ecx
|
||||
|
@ -419,7 +374,6 @@ ext2_data_block_index:
|
|||
.global ext2_inode_read_bytes
|
||||
ext2_inode_read_bytes:
|
||||
pushal
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $8, %esp
|
||||
|
@ -428,11 +382,11 @@ ext2_inode_read_bytes:
|
|||
movl %eax, 0(%esp)
|
||||
movl %ecx, 4(%esp)
|
||||
|
||||
# eax := first_byte / block_size
|
||||
# edx := first_byte % block_size
|
||||
# when edx == 0, no partial read needed
|
||||
# check if eax % EXT2_BLOCK_SIZE != 0,
|
||||
# then we need to read a partial block starting from an offset
|
||||
xorl %edx, %edx
|
||||
divl (ext2_block_size)
|
||||
movl $EXT2_BLOCK_SIZE, %ebx
|
||||
divl %ebx
|
||||
testl %edx, %edx
|
||||
jz .ext2_inode_read_bytes_no_partial_start
|
||||
|
||||
|
@ -441,7 +395,7 @@ ext2_inode_read_bytes:
|
|||
call ext2_read_block
|
||||
|
||||
# ecx := byte count (min(block_size - edx, remaining_bytes))
|
||||
movl (ext2_block_size), %ecx
|
||||
movl $EXT2_BLOCK_SIZE, %ecx
|
||||
subl %edx, %ecx
|
||||
cmpl %ecx, 4(%esp)
|
||||
cmovbl 4(%esp), %ecx
|
||||
|
@ -454,7 +408,15 @@ ext2_inode_read_bytes:
|
|||
movl $ext2_block_buffer, %esi
|
||||
addl %edx, %esi
|
||||
|
||||
call memcpy32
|
||||
# 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)
|
||||
|
@ -463,15 +425,14 @@ ext2_inode_read_bytes:
|
|||
.ext2_inode_read_bytes_no_partial_start:
|
||||
# eax := data block index (byte_start / block_size)
|
||||
movl 0(%esp), %eax
|
||||
movb (ext2_block_shift), %cl
|
||||
shrl %cl, %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
|
||||
movl $EXT2_BLOCK_SIZE, %ecx
|
||||
cmpl %ecx, 4(%esp)
|
||||
cmovbl 4(%esp), %ecx
|
||||
|
||||
|
@ -479,8 +440,16 @@ ext2_inode_read_bytes:
|
|||
addl %ecx, 0(%esp)
|
||||
subl %ecx, 4(%esp)
|
||||
|
||||
# very dumb memcpy with 32 bit addresses
|
||||
movl $ext2_block_buffer, %esi
|
||||
call memcpy32
|
||||
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)
|
||||
|
@ -519,12 +488,11 @@ ext2_directory_find_inode:
|
|||
cmpw $0xFF, %cx
|
||||
ja .ext2_directory_find_inode_not_found
|
||||
|
||||
# ebx := max data blocks: ceil(i_size / block_size)
|
||||
# ebx := max data blocks: ceil(i_size / EXT2_BLOCK_SIZE)
|
||||
movl (ext2_inode_buffer + i_size), %ebx
|
||||
addl (ext2_block_size), %ebx
|
||||
addl $EXT2_BLOCK_SHIFT, %ebx
|
||||
decl %ebx
|
||||
movb (ext2_block_shift), %cl
|
||||
shrl %cl, %ebx
|
||||
shrl $EXT2_BLOCK_SHIFT, %ebx
|
||||
jz .ext2_directory_find_inode_not_found
|
||||
|
||||
# 4(%esp) := current block
|
||||
|
@ -571,9 +539,7 @@ ext2_directory_find_inode:
|
|||
|
||||
# go to next entry if this block contains one
|
||||
addw 4(%si), %si
|
||||
movw $ext2_block_buffer, %di
|
||||
addw (ext2_block_size), %di
|
||||
cmpw %di, %si
|
||||
cmpw $(ext2_block_buffer + EXT2_BLOCK_SIZE), %si
|
||||
jb .ext2_directory_find_inode_loop_entries
|
||||
|
||||
.ext2_directory_find_inode_next_block:
|
||||
|
@ -582,7 +548,7 @@ ext2_directory_find_inode:
|
|||
jb .ext2_directory_find_inode_block_read_loop
|
||||
|
||||
.ext2_directory_find_inode_not_found:
|
||||
xorb %al, %al
|
||||
movb $0, %al
|
||||
jmp .ext2_directory_find_inode_done
|
||||
|
||||
.ext2_directory_find_inode_found:
|
||||
|
@ -676,7 +642,6 @@ ext2_find_kernel:
|
|||
movw $ext2_kernel_not_reg_msg, %si
|
||||
jmp print_and_halt
|
||||
|
||||
.section .data
|
||||
|
||||
kernel_path:
|
||||
.short kernel_path1
|
||||
|
@ -689,12 +654,13 @@ 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 (1 KiB, 2 KiB and 4 KiB are supported)"
|
||||
.asciz "Root partition has unsupported ext2 block size (only 1024 supported)"
|
||||
|
||||
ext2_part_not_dir_msg:
|
||||
.asciz "inode in root path is not directory"
|
||||
|
@ -715,14 +681,8 @@ ext2_looking_for_msg:
|
|||
|
||||
.section .bss
|
||||
|
||||
.align SECTOR_SIZE
|
||||
ext2_block_buffer:
|
||||
.skip EXT2_MAX_BLOCK_SIZE
|
||||
|
||||
ext2_inode_indirect_buffer:
|
||||
.skip EXT2_MAX_BLOCK_SIZE
|
||||
ext2_inode_indirect_number:
|
||||
.skip 4
|
||||
.skip EXT2_BLOCK_SIZE
|
||||
|
||||
ext2_partition_first_sector:
|
||||
.skip 8
|
||||
|
@ -734,10 +694,6 @@ ext2_drive_number:
|
|||
# NOTE: fits in 2 bytes
|
||||
ext2_inode_size:
|
||||
.skip 4
|
||||
ext2_block_size:
|
||||
.skip 4
|
||||
ext2_block_shift:
|
||||
.skip 4
|
||||
|
||||
ext2_superblock_buffer:
|
||||
.skip EXT2_SUPERBLOCK_SIZE
|
||||
|
|
|
@ -1,67 +1,19 @@
|
|||
.set TARGET_WIDTH, 800
|
||||
.set TARGET_HEIGHT, 600
|
||||
.set TARGET_BPP, 32
|
||||
|
||||
.code16
|
||||
.section .stage2
|
||||
|
||||
# kernel framebuffer information format
|
||||
# .align 8
|
||||
# .long 0xBABAB007
|
||||
# .long -(0xBABAB007 + width + height + bpp)
|
||||
# .long width (2 bytes used, 4 bytes for ease of calculation)
|
||||
# .long height (2 bytes used, 4 bytes for ease of calculation)
|
||||
# .long bpp (1 bytes used, 4 bytes for ease of calculation)
|
||||
|
||||
# scan memory 0x100000 -> 0x200000 for framebuffer information
|
||||
# Find suitable video mode
|
||||
# return:
|
||||
# ax: target width
|
||||
# bx: target height
|
||||
# cx: target bpp
|
||||
vesa_scan_kernel_image:
|
||||
pushl %edx
|
||||
pushl %esi
|
||||
|
||||
movl $0x100000, %esi
|
||||
|
||||
.vesa_scan_kernel_image_loop:
|
||||
# check magic
|
||||
cmpl $0xBABAB007, (%esi)
|
||||
jne .vesa_scan_kernel_image_next_addr
|
||||
|
||||
# check checksum
|
||||
movl 0x00(%esi), %edx
|
||||
addl 0x04(%esi), %edx
|
||||
addl 0x08(%esi), %edx
|
||||
addl 0x0C(%esi), %edx
|
||||
addl 0x10(%esi), %edx
|
||||
testl %edx, %edx
|
||||
jnz .vesa_scan_kernel_image_next_addr
|
||||
|
||||
# set return registers
|
||||
movw 0x08(%esi), %ax
|
||||
movw 0x0C(%esi), %bx
|
||||
movw 0x10(%esi), %cx
|
||||
jmp .vesa_scan_kernel_image_done
|
||||
|
||||
.vesa_scan_kernel_image_next_addr:
|
||||
addl $8, %esi
|
||||
cmpl $0x200000, %esi
|
||||
jb .vesa_scan_kernel_image_loop
|
||||
|
||||
# zero out return registers
|
||||
xorw %ax, %ax
|
||||
xorw %bx, %bx
|
||||
xorw %cx, %cx
|
||||
|
||||
.vesa_scan_kernel_image_done:
|
||||
popl %esi
|
||||
popl %edx
|
||||
ret
|
||||
|
||||
# Find suitable video mode and save it in (vesa_target_mode)
|
||||
# ax: video mode number if found, 0 otherwise
|
||||
.global vesa_find_video_mode
|
||||
vesa_find_video_mode:
|
||||
pushal
|
||||
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
subl $6, %esp
|
||||
pushw %ax
|
||||
pushw %cx
|
||||
pushw %di
|
||||
pushl %esi
|
||||
|
||||
# clear target mode and frame buffer
|
||||
movw $0, (vesa_target_mode)
|
||||
|
@ -71,19 +23,10 @@ vesa_find_video_mode:
|
|||
movl $0, (framebuffer + 12)
|
||||
movw $0, (framebuffer + 16)
|
||||
|
||||
call vesa_scan_kernel_image
|
||||
testw %ax, %ax
|
||||
jz .vesa_find_video_mode_loop_modes_done
|
||||
|
||||
# save arguments in stack
|
||||
movw %ax, -2(%ebp)
|
||||
movw %bx, -4(%ebp)
|
||||
movw %cx, -6(%ebp)
|
||||
|
||||
# get vesa information
|
||||
movw $0x4F00, %ax
|
||||
movw $vesa_info_buffer, %di
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
int $0x10
|
||||
cmpb $0x4F, %al; jne .vesa_unsupported
|
||||
cmpb $0x00, %ah; jne .vesa_error
|
||||
|
||||
|
@ -95,7 +38,8 @@ vesa_find_video_mode:
|
|||
cmpw $0x0200, (vesa_info_buffer + 0x04)
|
||||
jb .vesa_unsupported_version
|
||||
|
||||
movl (vesa_info_buffer + 0x0E), %esi
|
||||
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
|
||||
|
@ -104,7 +48,7 @@ vesa_find_video_mode:
|
|||
movw $0x4F01, %ax
|
||||
movw (%esi), %cx
|
||||
movw $vesa_mode_info_buffer, %di
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
int $0x10
|
||||
cmpb $0x4F, %al; jne .vesa_unsupported
|
||||
cmpb $0x00, %ah; jne .vesa_error
|
||||
|
||||
|
@ -113,25 +57,22 @@ vesa_find_video_mode:
|
|||
jz .vesa_find_video_mode_next_mode
|
||||
|
||||
# compare mode's dimensions
|
||||
movw -2(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x12)
|
||||
cmpw $TARGET_WIDTH, (vesa_mode_info_buffer + 0x12)
|
||||
jne .vesa_find_video_mode_next_mode
|
||||
movw -4(%ebp), %ax; cmpw %ax, (vesa_mode_info_buffer + 0x14)
|
||||
cmpw $TARGET_HEIGHT, (vesa_mode_info_buffer + 0x14)
|
||||
jne .vesa_find_video_mode_next_mode
|
||||
movb -6(%ebp), %al; cmpb %al, (vesa_mode_info_buffer + 0x19)
|
||||
cmpb $TARGET_BPP, (vesa_mode_info_buffer + 0x19)
|
||||
jne .vesa_find_video_mode_next_mode
|
||||
|
||||
# set address, pitch, type
|
||||
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)
|
||||
|
||||
# set width, height, bpp
|
||||
movw -2(%ebp), %ax; movw %ax, (framebuffer + 8)
|
||||
movw -4(%ebp), %ax; movw %ax, (framebuffer + 12)
|
||||
movw -6(%ebp), %ax; movb %al, (framebuffer + 16)
|
||||
|
||||
movw %cx, (vesa_target_mode)
|
||||
jmp .vesa_find_video_mode_loop_modes_done
|
||||
|
||||
|
@ -140,8 +81,10 @@ vesa_find_video_mode:
|
|||
jmp .vesa_find_video_mode_loop_modes
|
||||
|
||||
.vesa_find_video_mode_loop_modes_done:
|
||||
leavel
|
||||
popal
|
||||
popl %esi
|
||||
popw %di
|
||||
popw %cx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
.vesa_unsupported:
|
||||
|
@ -155,37 +98,33 @@ vesa_find_video_mode:
|
|||
jmp print_and_halt
|
||||
|
||||
|
||||
# scan for video mode in kernel memory and set the correct one.
|
||||
# when video mode is not found or does not exists,
|
||||
# set it to 80x25 text mode to clear the screen.
|
||||
.global vesa_set_video_mode
|
||||
vesa_set_video_mode:
|
||||
# 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
|
||||
|
||||
call vesa_find_video_mode
|
||||
|
||||
movw (vesa_target_mode), %bx
|
||||
testw %bx, %bx
|
||||
jz .vesa_set_target_mode_generic
|
||||
|
||||
movw $0x4F02, %ax
|
||||
orw $0x4000, %bx
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
int $0x10
|
||||
|
||||
jmp .set_video_done
|
||||
|
||||
.vesa_set_target_mode_generic:
|
||||
movb $0x03, %al
|
||||
movb $0x00, %ah
|
||||
pushl %ebp; int $0x10; popl %ebp # BOCHS doesn't seem to reserve ebp
|
||||
int $0x10
|
||||
|
||||
.set_video_done:
|
||||
popw %bx
|
||||
popw %ax
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
vesa_error_msg:
|
||||
.asciz "VESA error"
|
||||
|
@ -208,7 +147,6 @@ vesa_target_mode:
|
|||
.skip 2
|
||||
|
||||
.global framebuffer
|
||||
.align 8
|
||||
framebuffer:
|
||||
.skip 4 # address
|
||||
.skip 4 # pitch
|
||||
|
|
|
@ -8,8 +8,6 @@ SECTIONS
|
|||
. = ALIGN(512);
|
||||
stage2_start = .;
|
||||
.stage2 : { *(.stage2) }
|
||||
. = ALIGN(512);
|
||||
.data : { *(.data) }
|
||||
stage2_end = .;
|
||||
|
||||
. = ALIGN(512);
|
||||
|
|
|
@ -114,7 +114,6 @@ print_memory_map:
|
|||
|
||||
ret
|
||||
|
||||
.section .data
|
||||
|
||||
memory_map_msg:
|
||||
.asciz "memmap:"
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
.include "common.S"
|
||||
|
||||
.set SCREEN_WIDTH, 80
|
||||
.set SCREEN_HEIGHT, 25
|
||||
|
||||
|
@ -275,127 +273,6 @@ isprint:
|
|||
movb $0, %al
|
||||
ret
|
||||
|
||||
|
||||
# memset with 32 bit registers
|
||||
# edi: destination address
|
||||
# ecx: bytes count
|
||||
# al: value to set
|
||||
# return:
|
||||
# edi: destination address + bytes count
|
||||
# ecx: 0
|
||||
# other: preserved
|
||||
.global memset32
|
||||
memset32:
|
||||
testl %ecx, %ecx
|
||||
jz .memset32_done
|
||||
|
||||
pushf; cli
|
||||
pushw %es
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
pushl %edx
|
||||
|
||||
movl %cr0, %ebx
|
||||
orb $1, %bl
|
||||
movl %ebx, %cr0
|
||||
|
||||
ljmpl $GDT_CODE32, $.memset32_pmode32
|
||||
|
||||
.code32
|
||||
.memset32_pmode32:
|
||||
movw $GDT_DATA32, %dx
|
||||
movw %dx, %es
|
||||
|
||||
movl %ecx, %edx
|
||||
|
||||
andl $3, %ecx
|
||||
rep stosb %es:(%edi)
|
||||
|
||||
movl %edx, %ecx
|
||||
shrl $2, %ecx
|
||||
|
||||
movb %al, %ah
|
||||
movw %ax, %dx
|
||||
shll $16, %eax
|
||||
movw %dx, %ax
|
||||
rep stosl %es:(%edi)
|
||||
|
||||
ljmpl $GDT_CODE16, $.memset32_pmode16
|
||||
|
||||
.code16
|
||||
.memset32_pmode16:
|
||||
andb $0xFE, %bl
|
||||
movl %ebx, %cr0
|
||||
ljmpl $0x00, $.memset32_rmode16
|
||||
|
||||
.memset32_rmode16:
|
||||
popl %edx
|
||||
popl %ebx
|
||||
popl %eax
|
||||
popw %es
|
||||
popf
|
||||
|
||||
.memset32_done:
|
||||
ret
|
||||
|
||||
# memcpy with 32 bit registers
|
||||
# esi: source address
|
||||
# edi: destination address
|
||||
# ecx: bytes count
|
||||
# return:
|
||||
# esi: source address + bytes count
|
||||
# edi: destination address + bytes count
|
||||
# ecx: 0
|
||||
# other: preserved
|
||||
.global memcpy32
|
||||
memcpy32:
|
||||
testl %ecx, %ecx
|
||||
jz .memcpy32_done
|
||||
|
||||
pushf; cli
|
||||
pushw %ds
|
||||
pushw %es
|
||||
pushl %ebx
|
||||
pushl %edx
|
||||
|
||||
movl %cr0, %ebx
|
||||
orb $1, %bl
|
||||
movl %ebx, %cr0
|
||||
|
||||
ljmpl $GDT_CODE32, $.memcpy32_pmode32
|
||||
|
||||
.code32
|
||||
.memcpy32_pmode32:
|
||||
movw $GDT_DATA32, %dx
|
||||
movw %dx, %ds
|
||||
movw %dx, %es
|
||||
|
||||
movl %ecx, %edx
|
||||
andl $3, %ecx
|
||||
rep movsb %ds:(%esi), %es:(%edi)
|
||||
|
||||
movl %edx, %ecx
|
||||
shrl $2, %ecx
|
||||
rep movsl %ds:(%esi), %es:(%edi)
|
||||
|
||||
ljmpl $GDT_CODE16, $.memcpy32_pmode16
|
||||
|
||||
.code16
|
||||
.memcpy32_pmode16:
|
||||
andb $0xFE, %bl
|
||||
movl %ebx, %cr0
|
||||
ljmpl $0x00, $.memcpy32_rmode16
|
||||
|
||||
.memcpy32_rmode16:
|
||||
popl %edx
|
||||
popl %ebx
|
||||
popw %es
|
||||
popw %ds
|
||||
popf
|
||||
|
||||
.memcpy32_done:
|
||||
ret
|
||||
|
||||
.section .bss
|
||||
|
||||
# enough for base 2 printing
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
if (NOT DEFINED ENV{BANAN_ARCH})
|
||||
message(FATAL_ERROR "environment variable BANAN_ARCH not defined")
|
||||
endif ()
|
||||
set(BANAN_ARCH $ENV{BANAN_ARCH})
|
||||
|
||||
project(banan_os-bootloader-installer CXX)
|
||||
project(x86_64-banan_os-bootloader-installer CXX)
|
||||
|
||||
set(SOURCES
|
||||
crc32.cpp
|
||||
|
@ -15,8 +10,8 @@ set(SOURCES
|
|||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(banan_os-bootloader-installer ${SOURCES})
|
||||
target_compile_options(banan_os-bootloader-installer PRIVATE -O2 -std=c++20)
|
||||
target_compile_definitions(banan_os-bootloader-installer PRIVATE __arch=${BANAN_ARCH})
|
||||
target_include_directories(banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../userspace/libraries/LibELF/include)
|
||||
target_include_directories(banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../kernel/include)
|
||||
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)
|
||||
|
|
|
@ -81,7 +81,7 @@ bool ELFFile::validate_elf_header() const
|
|||
|
||||
#if ARCH(x86_64)
|
||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
#elif ARCH(i686)
|
||||
#elif ARCH(i386)
|
||||
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
#endif
|
||||
{
|
||||
|
|
|
@ -99,33 +99,33 @@ bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid)
|
||||
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid)
|
||||
{
|
||||
if (data.size() < 16)
|
||||
if (stage2.size() < 16)
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, too small for patches" << std::endl;
|
||||
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 < data.size() - 16; i++)
|
||||
for (std::size_t i = 0; i < stage2.size() - 16; i++)
|
||||
{
|
||||
if (memcmp(data.data() + i, "root disk guid ", 16) == 0)
|
||||
if (memcmp(stage2.data() + i, "root disk guid ", 16) == 0)
|
||||
{
|
||||
if (disk_guid_offset != std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, multiple patchable disk guids" << std::endl;
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable disk guids" << std::endl;
|
||||
return false;
|
||||
}
|
||||
disk_guid_offset = i;
|
||||
}
|
||||
if (memcmp(data.data() + i, "root part guid ", 16) == 0)
|
||||
if (memcmp(stage2.data() + i, "root part guid ", 16) == 0)
|
||||
{
|
||||
if (part_guid_offset != std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, multiple patchable partition guids" << std::endl;
|
||||
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable partition guids" << std::endl;
|
||||
return false;
|
||||
}
|
||||
part_guid_offset = i;
|
||||
|
@ -133,15 +133,16 @@ bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const ui
|
|||
}
|
||||
if (disk_guid_offset == std::size_t(-1))
|
||||
{
|
||||
std::cerr << m_path << ": contains invalid .data section, no patchable disk guid" << std::endl;
|
||||
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 .data section, no patchable partition guid" << std::endl;
|
||||
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())
|
||||
{
|
||||
|
@ -151,28 +152,23 @@ bool GPTFile::install_stage2(std::span<const uint8_t> stage2, std::span<const ui
|
|||
|
||||
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
|
||||
|
||||
std::size_t data_offset = stage2.size();
|
||||
if (std::size_t rem = data_offset % 512)
|
||||
data_offset += 512 - rem;
|
||||
|
||||
if (data_offset + data.size() > partition_size)
|
||||
if (stage2.size() > partition_size)
|
||||
{
|
||||
std::cerr << m_path << ": can't fit " << stage2.size() + data.size() << " bytes of data to partition of size " << partition_size << std::endl;
|
||||
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());
|
||||
memcpy(partition_start + data_offset, data.data(), data.size());
|
||||
|
||||
// patch GUIDs
|
||||
*reinterpret_cast<GUID*>(partition_start + data_offset + disk_guid_offset) = gpt_header().disk_guid;
|
||||
*reinterpret_cast<GUID*>(partition_start + data_offset + part_guid_offset) = root_partition_guid;
|
||||
*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, std::span<const uint8_t> data, const GUID& root_partition_guid)
|
||||
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())
|
||||
{
|
||||
|
@ -181,7 +177,7 @@ bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<cons
|
|||
}
|
||||
if (!install_stage1(stage1))
|
||||
return false;
|
||||
if (!install_stage2(stage2, data, root_partition_guid))
|
||||
if (!install_stage2(stage2, root_partition_guid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public:
|
|||
GPTFile(std::string_view path);
|
||||
~GPTFile();
|
||||
|
||||
bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, std::span<const uint8_t> data, const GUID& root_partition_guid);
|
||||
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;
|
||||
|
||||
|
@ -80,7 +80,7 @@ private:
|
|||
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, std::span<const uint8_t> data, const GUID& root_partition_guid);
|
||||
bool install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid);
|
||||
|
||||
private:
|
||||
const std::string m_path;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader
|
|
@ -26,10 +26,9 @@ int main(int argc, char** argv)
|
|||
|
||||
auto stage1 = bootloader.find_section(".stage1"sv);
|
||||
auto stage2 = bootloader.find_section(".stage2"sv);
|
||||
auto data = bootloader.find_section(".data"sv);
|
||||
if (!stage1.has_value() || !stage2.has_value() || !data.has_value())
|
||||
if (!stage1.has_value() || !stage2.has_value())
|
||||
{
|
||||
std::cerr << bootloader.path() << " doesn't contain .stage1, .stage2 and .data sections" << std::endl;
|
||||
std::cerr << bootloader.path() << " doesn't contain .stage1 and .stage2 sections" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -37,7 +36,7 @@ int main(int argc, char** argv)
|
|||
if (!disk_image.success())
|
||||
return 1;
|
||||
|
||||
if (!disk_image.install_bootloader(*stage1, *stage2, *data, *root_partition_guid))
|
||||
if (!disk_image.install_bootloader(*stage1, *stage2, *root_partition_guid))
|
||||
return 1;
|
||||
std::cout << "bootloader installed" << std::endl;
|
||||
|
||||
|
|
|
@ -1,35 +1,28 @@
|
|||
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/ACPI.cpp
|
||||
kernel/ACPI/AML.cpp
|
||||
kernel/ACPI/AML/Field.cpp
|
||||
kernel/ACPI/AML/Integer.cpp
|
||||
kernel/ACPI/AML/NamedObject.cpp
|
||||
kernel/ACPI/AML/Namespace.cpp
|
||||
kernel/ACPI/AML/Node.cpp
|
||||
kernel/ACPI/AML/Package.cpp
|
||||
kernel/ACPI/AML/Register.cpp
|
||||
kernel/ACPI/AML/Scope.cpp
|
||||
kernel/ACPI/AML/String.cpp
|
||||
kernel/ACPI.cpp
|
||||
kernel/APIC.cpp
|
||||
kernel/BootInfo.cpp
|
||||
kernel/CPUID.cpp
|
||||
kernel/Credentials.cpp
|
||||
kernel/Debug.cpp
|
||||
kernel/Device/DebugDevice.cpp
|
||||
kernel/Device/Device.cpp
|
||||
kernel/Device/FramebufferDevice.cpp
|
||||
kernel/Device/NullDevice.cpp
|
||||
kernel/Device/RandomDevice.cpp
|
||||
kernel/Device/ZeroDevice.cpp
|
||||
kernel/ELF.cpp
|
||||
kernel/Errors.cpp
|
||||
kernel/Font.cpp
|
||||
kernel/FS/DevFS/FileSystem.cpp
|
||||
kernel/FS/Ext2/FileSystem.cpp
|
||||
kernel/FS/Ext2/Inode.cpp
|
||||
kernel/FS/FAT/FileSystem.cpp
|
||||
kernel/FS/FAT/Inode.cpp
|
||||
kernel/FS/FileSystem.cpp
|
||||
kernel/FS/Inode.cpp
|
||||
kernel/FS/Pipe.cpp
|
||||
kernel/FS/ProcFS/FileSystem.cpp
|
||||
|
@ -37,15 +30,9 @@ set(KERNEL_SOURCES
|
|||
kernel/FS/TmpFS/FileSystem.cpp
|
||||
kernel/FS/TmpFS/Inode.cpp
|
||||
kernel/FS/VirtualFileSystem.cpp
|
||||
kernel/GDT.cpp
|
||||
kernel/IDT.cpp
|
||||
kernel/Input/InputDevice.cpp
|
||||
kernel/Input/PS2/Controller.cpp
|
||||
kernel/Input/PS2/Device.cpp
|
||||
kernel/Input/PS2/Keyboard.cpp
|
||||
kernel/Input/PS2/Keymap.cpp
|
||||
kernel/Input/PS2/Mouse.cpp
|
||||
kernel/Interruptable.cpp
|
||||
kernel/Input/PS2Controller.cpp
|
||||
kernel/Input/PS2Keyboard.cpp
|
||||
kernel/Input/PS2Keymap.cpp
|
||||
kernel/InterruptController.cpp
|
||||
kernel/kernel.cpp
|
||||
kernel/Memory/DMARegion.cpp
|
||||
|
@ -55,29 +42,16 @@ set(KERNEL_SOURCES
|
|||
kernel/Memory/MemoryBackedRegion.cpp
|
||||
kernel/Memory/MemoryRegion.cpp
|
||||
kernel/Memory/PhysicalRange.cpp
|
||||
kernel/Memory/SharedMemoryObject.cpp
|
||||
kernel/Memory/VirtualRange.cpp
|
||||
kernel/Networking/ARPTable.cpp
|
||||
kernel/Networking/E1000/E1000.cpp
|
||||
kernel/Networking/E1000/E1000E.cpp
|
||||
kernel/Networking/IPv4Layer.cpp
|
||||
kernel/Networking/NetworkInterface.cpp
|
||||
kernel/Networking/NetworkLayer.cpp
|
||||
kernel/Networking/NetworkManager.cpp
|
||||
kernel/Networking/NetworkSocket.cpp
|
||||
kernel/Networking/RTL8169/RTL8169.cpp
|
||||
kernel/Networking/TCPSocket.cpp
|
||||
kernel/Networking/UDPSocket.cpp
|
||||
kernel/Networking/UNIX/Socket.cpp
|
||||
kernel/Networking/E1000.cpp
|
||||
kernel/OpenFileDescriptorSet.cpp
|
||||
kernel/Panic.cpp
|
||||
kernel/PCI.cpp
|
||||
kernel/PIC.cpp
|
||||
kernel/Process.cpp
|
||||
kernel/Processor.cpp
|
||||
kernel/Random.cpp
|
||||
kernel/Scheduler.cpp
|
||||
kernel/ThreadBlocker.cpp
|
||||
kernel/Semaphore.cpp
|
||||
kernel/SpinLock.cpp
|
||||
kernel/SSP.cpp
|
||||
kernel/Storage/ATA/AHCI/Controller.cpp
|
||||
kernel/Storage/ATA/AHCI/Device.cpp
|
||||
|
@ -85,36 +59,22 @@ set(KERNEL_SOURCES
|
|||
kernel/Storage/ATA/ATAController.cpp
|
||||
kernel/Storage/ATA/ATADevice.cpp
|
||||
kernel/Storage/DiskCache.cpp
|
||||
kernel/Storage/NVMe/Controller.cpp
|
||||
kernel/Storage/NVMe/Namespace.cpp
|
||||
kernel/Storage/NVMe/Queue.cpp
|
||||
kernel/Storage/Partition.cpp
|
||||
kernel/Storage/SCSI.cpp
|
||||
kernel/Storage/StorageDevice.cpp
|
||||
kernel/Syscall.cpp
|
||||
kernel/Terminal/FramebufferTerminal.cpp
|
||||
kernel/Terminal/PseudoTerminal.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
|
||||
kernel/USB/Device.cpp
|
||||
kernel/USB/HID/HIDDriver.cpp
|
||||
kernel/USB/HID/Keyboard.cpp
|
||||
kernel/USB/HID/Mouse.cpp
|
||||
kernel/USB/MassStorage/MassStorageDriver.cpp
|
||||
kernel/USB/MassStorage/SCSIDevice.cpp
|
||||
kernel/USB/USBManager.cpp
|
||||
kernel/USB/XHCI/Controller.cpp
|
||||
kernel/USB/XHCI/Device.cpp
|
||||
icxxabi.cpp
|
||||
)
|
||||
|
||||
set(ENABLE_KERNEL_UBSAN False)
|
||||
#set(ENABLE_KERNEL_UBSAN True)
|
||||
|
||||
if(ENABLE_KERNEL_UBSAN)
|
||||
set(KERNEL_SOURCES ${KERNEL_SOURCES} ubsan.cpp)
|
||||
|
@ -124,97 +84,100 @@ 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/Syscall.S
|
||||
arch/x86_64/Thread.S
|
||||
)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i386")
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
arch/i686/boot.S
|
||||
arch/i686/interrupts.S
|
||||
arch/i686/PageTable.cpp
|
||||
arch/i686/Signal.S
|
||||
arch/i686/Syscall.S
|
||||
arch/i686/Thread.S
|
||||
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/Assert.cpp
|
||||
../BAN/BAN/New.cpp
|
||||
../BAN/BAN/String.cpp
|
||||
../BAN/BAN/StringView.cpp
|
||||
../BAN/BAN/Time.cpp
|
||||
)
|
||||
|
||||
set(KLIBC_SOURCES
|
||||
klibc/ctype.cpp
|
||||
klibc/string.cpp
|
||||
set(LIBC_SOURCES
|
||||
../libc/ctype.cpp
|
||||
../libc/string.cpp
|
||||
)
|
||||
|
||||
set(LIBFONT_SOURCES
|
||||
../userspace/libraries/LibFont/Font.cpp
|
||||
../userspace/libraries/LibFont/PSF.cpp
|
||||
)
|
||||
|
||||
set(LIBINPUT_SOURCE
|
||||
../userspace/libraries/LibInput/KeyboardLayout.cpp
|
||||
../userspace/libraries/LibInput/KeyEvent.cpp
|
||||
set(LIBELF_SOURCES
|
||||
../LibELF/LibELF/LoadableELF.cpp
|
||||
)
|
||||
|
||||
set(KERNEL_SOURCES
|
||||
${KERNEL_SOURCES}
|
||||
${LAI_SOURCES}
|
||||
${BAN_SOURCES}
|
||||
${KLIBC_SOURCES}
|
||||
${LIBFONT_SOURCES}
|
||||
${LIBINPUT_SOURCE}
|
||||
${LIBC_SOURCES}
|
||||
${LIBELF_SOURCES}
|
||||
)
|
||||
|
||||
add_executable(kernel ${KERNEL_SOURCES})
|
||||
add_dependencies(kernel headers)
|
||||
|
||||
target_compile_definitions(kernel PRIVATE __is_kernel)
|
||||
target_compile_definitions(kernel PRIVATE __arch=${BANAN_ARCH})
|
||||
target_compile_definitions(kernel PUBLIC __is_kernel)
|
||||
target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
||||
|
||||
target_compile_options(kernel PRIVATE
|
||||
-O2 -g
|
||||
-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.
|
||||
-fstack-protector
|
||||
-ffreestanding
|
||||
-fno-omit-frame-pointer
|
||||
-fstrict-volatile-bitfields
|
||||
-mgeneral-regs-only
|
||||
-Wall -Wextra -Werror -Wstack-usage=1024
|
||||
)
|
||||
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)
|
||||
|
||||
# C++ specific
|
||||
target_compile_options(kernel PRIVATE
|
||||
-Wno-literal-suffix
|
||||
-Wno-invalid-offsetof
|
||||
-fno-rtti
|
||||
-fno-exceptions
|
||||
)
|
||||
# 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 PRIVATE -fsanitize=undefined)
|
||||
target_compile_options(kernel PUBLIC -fsanitize=undefined)
|
||||
endif()
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
target_compile_options(kernel PRIVATE -mcmodel=kernel -mno-red-zone)
|
||||
target_link_options(kernel PRIVATE LINKER:-z,max-page-size=4096)
|
||||
target_link_options(kernel PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/x86_64/linker.ld)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
target_link_options(kernel PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/arch/i686/linker.ld)
|
||||
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 PRIVATE -ffreestanding -nostdlib -orphan-handling=error)
|
||||
target_link_options(kernel PUBLIC -ffreestanding -nostdlib)
|
||||
|
||||
get_target_property(KERNEL_COMPILE_OPTIONS kernel COMPILE_OPTIONS)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${KERNEL_COMPILE_OPTIONS} -print-file-name=crtend.o OUTPUT_VARIABLE CRTEND OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
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
|
||||
|
@ -229,22 +192,6 @@ add_custom_command(
|
|||
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
|
||||
#)
|
||||
|
||||
banan_include_headers(kernel ban)
|
||||
banan_include_headers(kernel libc)
|
||||
banan_include_headers(kernel libfont)
|
||||
banan_include_headers(kernel libelf)
|
||||
banan_include_headers(kernel libinput)
|
||||
|
||||
banan_install_headers(kernel)
|
||||
set_target_properties(kernel PROPERTIES OUTPUT_NAME banan-os.kernel)
|
||||
install(TARGETS kernel DESTINATION ${BANAN_BOOT} OPTIONAL)
|
||||
|
||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
set(ELF_FORMAT elf64-x86-64)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
set(ELF_FORMAT elf32-i386)
|
||||
endif()
|
||||
|
||||
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
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
#include <BAN/Assert.h>
|
||||
#include <kernel/GDT.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern "C" uintptr_t g_boot_stack_top[0];
|
||||
|
||||
namespace Kernel::GDT
|
||||
{
|
||||
|
||||
struct TaskStateSegment
|
||||
{
|
||||
uint16_t link;
|
||||
uint16_t reserved1;
|
||||
uint32_t esp0;
|
||||
uint16_t ss0;
|
||||
uint16_t reserved2;
|
||||
uint32_t esp1;
|
||||
uint16_t ss1;
|
||||
uint16_t reserved3;
|
||||
uint32_t esp2;
|
||||
uint16_t ss2;
|
||||
uint16_t reserved4;
|
||||
uint32_t cr3;
|
||||
uint32_t eip;
|
||||
uint32_t eflags;
|
||||
uint32_t eax;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t ebx;
|
||||
uint32_t esp;
|
||||
uint32_t ebp;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint16_t es;
|
||||
uint16_t reserved5;
|
||||
uint16_t cs;
|
||||
uint16_t reserved6;
|
||||
uint16_t ss;
|
||||
uint16_t reserved7;
|
||||
uint16_t ds;
|
||||
uint16_t reserved8;
|
||||
uint16_t fs;
|
||||
uint16_t reserved9;
|
||||
uint16_t gs;
|
||||
uint16_t reserved10;
|
||||
uint16_t ldtr;
|
||||
uint16_t reserved11;
|
||||
uint16_t reserved12;
|
||||
uint16_t iopb;
|
||||
uint32_t ssp;
|
||||
} __attribute__((packed));
|
||||
|
||||
union SegmentDescriptor
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint16_t limit1;
|
||||
uint16_t base1;
|
||||
uint8_t base2;
|
||||
uint8_t access;
|
||||
uint8_t limit2 : 4;
|
||||
uint8_t flags : 4;
|
||||
uint8_t base3;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t low;
|
||||
uint32_t high;
|
||||
} __attribute__((packed));
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
struct GDTR
|
||||
{
|
||||
uint16_t size;
|
||||
uint32_t address;
|
||||
} __attribute__((packed));
|
||||
|
||||
static TaskStateSegment* s_tss = nullptr;
|
||||
static SegmentDescriptor* s_gdt = nullptr;
|
||||
static GDTR s_gdtr;
|
||||
|
||||
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
|
||||
{
|
||||
SegmentDescriptor& desc = *(SegmentDescriptor*)((uintptr_t)s_gdt + offset);
|
||||
desc.base1 = base;
|
||||
desc.base2 = base >> 16;
|
||||
desc.base3 = base >> 24;
|
||||
|
||||
desc.limit1 = limit;
|
||||
desc.limit2 = limit >> 16;
|
||||
|
||||
desc.access = access;
|
||||
|
||||
desc.flags = flags;
|
||||
}
|
||||
|
||||
static void write_tss(uint8_t offset)
|
||||
{
|
||||
s_tss = new TaskStateSegment();
|
||||
ASSERT(s_tss);
|
||||
|
||||
memset(s_tss, 0x00, sizeof(TaskStateSegment));
|
||||
s_tss->ss0 = 0x10;
|
||||
s_tss->esp0 = (uintptr_t)g_boot_stack_top;
|
||||
|
||||
write_entry(offset, (uint32_t)s_tss, sizeof(TaskStateSegment), 0x89, 0x0);
|
||||
}
|
||||
|
||||
void set_tss_stack(uintptr_t esp)
|
||||
{
|
||||
s_tss->esp0 = esp;
|
||||
}
|
||||
|
||||
static void flush_gdt()
|
||||
{
|
||||
asm volatile("lgdt %0" :: "m"(s_gdtr));
|
||||
}
|
||||
|
||||
extern "C" void flush_tss(uint16_t offset)
|
||||
{
|
||||
asm volatile("ltr %0" :: "m"(offset));
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
constexpr uint32_t descriptor_count = 6;
|
||||
s_gdt = new SegmentDescriptor[descriptor_count];
|
||||
ASSERT(s_gdt);
|
||||
|
||||
s_gdtr.address = (uint64_t)s_gdt;
|
||||
s_gdtr.size = descriptor_count * sizeof(SegmentDescriptor) - 1;
|
||||
|
||||
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
|
||||
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xC); // kernel code
|
||||
write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
|
||||
write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xC); // user code
|
||||
write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
|
||||
write_tss(0x28);
|
||||
|
||||
flush_gdt();
|
||||
flush_tss(0x28);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
#include <BAN/Errors.h>
|
||||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
#include <kernel/Panic.h>
|
||||
#include <kernel/Scheduler.h>
|
||||
|
||||
#define INTERRUPT_HANDLER____(i, msg) \
|
||||
static void interrupt ## i () \
|
||||
{ \
|
||||
uint32_t eax, ebx, ecx, edx; \
|
||||
uint32_t esp, ebp; \
|
||||
uint32_t cr0, cr2, cr3, cr4; \
|
||||
asm volatile("":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx)); \
|
||||
asm volatile("movl %%esp, %%eax":"=a"(esp)); \
|
||||
asm volatile("movl %%ebp, %%eax":"=a"(ebp)); \
|
||||
asm volatile("movl %%cr0, %%eax":"=a"(cr0)); \
|
||||
asm volatile("movl %%cr2, %%eax":"=a"(cr2)); \
|
||||
asm volatile("movl %%cr3, %%eax":"=a"(cr3)); \
|
||||
asm volatile("movl %%cr4, %%eax":"=a"(cr4)); \
|
||||
Kernel::panic(msg "\r\nRegister dump\r\n" \
|
||||
"eax=0x{8H}, ebx=0x{8H}, ecx=0x{8H}, edx=0x{8H}\r\n" \
|
||||
"esp=0x{8H}, ebp=0x{8H}\r\n" \
|
||||
"CR0=0x{8H}, CR2=0x{8H}, CR3=0x{8H}, CR4=0x{8H}\r\n", \
|
||||
eax, ebx, ecx, edx, esp, ebp, cr0, cr2, cr3, cr4); \
|
||||
}
|
||||
|
||||
#define INTERRUPT_HANDLER_ERR(i, msg) \
|
||||
static void interrupt ## i () \
|
||||
{ \
|
||||
uint32_t eax, ebx, ecx, edx; \
|
||||
uint32_t esp, ebp; \
|
||||
uint32_t cr0, cr2, cr3, cr4; \
|
||||
uint32_t error_code; \
|
||||
asm volatile("":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx)); \
|
||||
asm volatile("movl %%esp, %%eax":"=a"(esp)); \
|
||||
asm volatile("movl %%ebp, %%eax":"=a"(ebp)); \
|
||||
asm volatile("movl %%cr0, %%eax":"=a"(cr0)); \
|
||||
asm volatile("movl %%cr2, %%eax":"=a"(cr2)); \
|
||||
asm volatile("movl %%cr3, %%eax":"=a"(cr3)); \
|
||||
asm volatile("movl %%cr4, %%eax":"=a"(cr4)); \
|
||||
asm volatile("popl %%eax":"=a"(error_code)); \
|
||||
Kernel::panic(msg " (error code: 0x{8H})\r\n" \
|
||||
"Register dump\r\n" \
|
||||
"eax=0x{8H}, ebx=0x{8H}, ecx=0x{8H}, edx=0x{8H}\r\n" \
|
||||
"esp=0x{8H}, ebp=0x{8H}\r\n" \
|
||||
"CR0=0x{8H}, CR2=0x{8H}, CR3=0x{8H}, CR4=0x{8H}\r\n", \
|
||||
eax, ebx, ecx, edx, esp, ebp, cr0, cr2, cr3, cr4, error_code); \
|
||||
}
|
||||
|
||||
#define REGISTER_HANDLER(i) register_interrupt_handler(i, interrupt ## i)
|
||||
|
||||
namespace IDT
|
||||
{
|
||||
|
||||
struct GateDescriptor
|
||||
{
|
||||
uint16_t offset1;
|
||||
uint16_t selector;
|
||||
uint8_t reserved : 5;
|
||||
uint8_t zero1 : 3;
|
||||
uint8_t type : 4;
|
||||
uint8_t zero2 : 1;
|
||||
uint8_t DPL : 2;
|
||||
uint8_t present : 1;
|
||||
uint16_t offset2;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct IDTR
|
||||
{
|
||||
uint16_t size;
|
||||
void* offset;
|
||||
} __attribute((packed));
|
||||
|
||||
static IDTR s_idtr;
|
||||
static GateDescriptor* s_idt = nullptr;
|
||||
|
||||
static void(*s_irq_handlers[16])() { nullptr };
|
||||
|
||||
INTERRUPT_HANDLER____(0x00, "Division Error")
|
||||
INTERRUPT_HANDLER____(0x01, "Debug")
|
||||
INTERRUPT_HANDLER____(0x02, "Non-maskable Interrupt")
|
||||
INTERRUPT_HANDLER____(0x03, "Breakpoint")
|
||||
INTERRUPT_HANDLER____(0x04, "Overflow")
|
||||
INTERRUPT_HANDLER____(0x05, "Bound Range Exception")
|
||||
INTERRUPT_HANDLER____(0x06, "Invalid Opcode")
|
||||
INTERRUPT_HANDLER____(0x07, "Device Not Available")
|
||||
INTERRUPT_HANDLER_ERR(0x08, "Double Fault")
|
||||
INTERRUPT_HANDLER____(0x09, "Coprocessor Segment Overrun")
|
||||
INTERRUPT_HANDLER_ERR(0x0A, "Invalid TSS")
|
||||
INTERRUPT_HANDLER_ERR(0x0B, "Segment Not Present")
|
||||
INTERRUPT_HANDLER_ERR(0x0C, "Stack-Segment Fault")
|
||||
INTERRUPT_HANDLER_ERR(0x0D, "General Protection Fault")
|
||||
INTERRUPT_HANDLER_ERR(0x0E, "Page Fault")
|
||||
INTERRUPT_HANDLER____(0x0F, "Unknown Exception 0x0F")
|
||||
INTERRUPT_HANDLER____(0x10, "x87 Floating-Point Exception")
|
||||
INTERRUPT_HANDLER_ERR(0x11, "Alignment Check")
|
||||
INTERRUPT_HANDLER____(0x12, "Machine Check")
|
||||
INTERRUPT_HANDLER____(0x13, "SIMD Floating-Point Exception")
|
||||
INTERRUPT_HANDLER____(0x14, "Virtualization Exception")
|
||||
INTERRUPT_HANDLER_ERR(0x15, "Control Protection Exception")
|
||||
INTERRUPT_HANDLER____(0x16, "Unknown Exception 0x16")
|
||||
INTERRUPT_HANDLER____(0x17, "Unknown Exception 0x17")
|
||||
INTERRUPT_HANDLER____(0x18, "Unknown Exception 0x18")
|
||||
INTERRUPT_HANDLER____(0x19, "Unknown Exception 0x19")
|
||||
INTERRUPT_HANDLER____(0x1A, "Unknown Exception 0x1A")
|
||||
INTERRUPT_HANDLER____(0x1B, "Unknown Exception 0x1B")
|
||||
INTERRUPT_HANDLER____(0x1C, "Hypervisor Injection Exception")
|
||||
INTERRUPT_HANDLER_ERR(0x1D, "VMM Communication Exception")
|
||||
INTERRUPT_HANDLER_ERR(0x1E, "Security Exception")
|
||||
INTERRUPT_HANDLER____(0x1F, "Unkown Exception 0x1F")
|
||||
|
||||
extern "C" void handle_irq()
|
||||
{
|
||||
uint8_t irq;
|
||||
for (uint32_t i = 0; i < 16; i++)
|
||||
{
|
||||
if (InterruptController::get().is_in_service(i))
|
||||
{
|
||||
irq = i;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
dprintln("Spurious irq");
|
||||
return;
|
||||
found:
|
||||
if (s_irq_handlers[irq])
|
||||
s_irq_handlers[irq]();
|
||||
else
|
||||
dprintln("no handler for irq 0x{2H}\n", irq);
|
||||
|
||||
// NOTE: Scheduler sends PIT eoi's
|
||||
if (irq != PIT_IRQ)
|
||||
InterruptController::get().eoi(irq);
|
||||
|
||||
Kernel::Scheduler::get().reschedule_if_idling();
|
||||
}
|
||||
|
||||
extern "C" void handle_irq_common();
|
||||
asm(
|
||||
".globl handle_irq_common;"
|
||||
"handle_irq_common:"
|
||||
"pusha;"
|
||||
"pushw %ds;"
|
||||
"pushw %es;"
|
||||
"pushw %ss;"
|
||||
"pushw %ss;"
|
||||
"popw %ds;"
|
||||
"popw %es;"
|
||||
"call handle_irq;"
|
||||
"popw %es;"
|
||||
"popw %ds;"
|
||||
"popa;"
|
||||
"iret;"
|
||||
);
|
||||
|
||||
extern "C" void syscall_asm();
|
||||
asm(
|
||||
".global syscall_asm;"
|
||||
"syscall_asm:"
|
||||
"pusha;"
|
||||
"pushw %ds;"
|
||||
"pushw %es;"
|
||||
"pushw %ss;"
|
||||
"pushw %ss;"
|
||||
"popw %ds;"
|
||||
"popw %es;"
|
||||
"pushl %edx;"
|
||||
"pushl %ecx;"
|
||||
"pushl %ebx;"
|
||||
"pushl %eax;"
|
||||
"call cpp_syscall_handler;"
|
||||
"addl $16, %esp;"
|
||||
"popw %es;"
|
||||
"popw %ds;"
|
||||
|
||||
// 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;"
|
||||
);
|
||||
|
||||
static void flush_idt()
|
||||
{
|
||||
asm volatile("lidt %0"::"m"(s_idtr));
|
||||
}
|
||||
|
||||
static void register_interrupt_handler(uint8_t index, void(*f)())
|
||||
{
|
||||
GateDescriptor& descriptor = s_idt[index];
|
||||
descriptor.offset1 = (uint32_t)f & 0xFFFF;
|
||||
descriptor.selector = 0x08;
|
||||
descriptor.type = 0xE;
|
||||
descriptor.DPL = 0;
|
||||
descriptor.present = 1;
|
||||
descriptor.offset2 = (uint32_t)f >> 16;
|
||||
}
|
||||
|
||||
void register_irq_handler(uint8_t irq, void(*f)())
|
||||
{
|
||||
s_irq_handlers[irq] = f;
|
||||
register_interrupt_handler(IRQ_VECTOR_BASE + irq, handle_irq_common);
|
||||
flush_idt();
|
||||
}
|
||||
|
||||
void register_syscall_handler(uint8_t offset, void(*handler)())
|
||||
{
|
||||
register_interrupt_handler(offset, handler);
|
||||
s_idt[offset].DPL = 3;
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
constexpr size_t idt_size = 0x100 * sizeof(GateDescriptor);
|
||||
|
||||
s_idt = (GateDescriptor*)kmalloc(idt_size);
|
||||
ASSERT(s_idt);
|
||||
memset(s_idt, 0x00, idt_size);
|
||||
|
||||
s_idtr.offset = s_idt;
|
||||
s_idtr.size = idt_size - 1;
|
||||
|
||||
REGISTER_HANDLER(0x00);
|
||||
REGISTER_HANDLER(0x01);
|
||||
REGISTER_HANDLER(0x02);
|
||||
REGISTER_HANDLER(0x03);
|
||||
REGISTER_HANDLER(0x04);
|
||||
REGISTER_HANDLER(0x05);
|
||||
REGISTER_HANDLER(0x06);
|
||||
REGISTER_HANDLER(0x07);
|
||||
REGISTER_HANDLER(0x08);
|
||||
REGISTER_HANDLER(0x09);
|
||||
REGISTER_HANDLER(0x0A);
|
||||
REGISTER_HANDLER(0x0B);
|
||||
REGISTER_HANDLER(0x0C);
|
||||
REGISTER_HANDLER(0x0D);
|
||||
REGISTER_HANDLER(0x0E);
|
||||
REGISTER_HANDLER(0x0F);
|
||||
REGISTER_HANDLER(0x10);
|
||||
REGISTER_HANDLER(0x11);
|
||||
REGISTER_HANDLER(0x12);
|
||||
REGISTER_HANDLER(0x13);
|
||||
REGISTER_HANDLER(0x14);
|
||||
REGISTER_HANDLER(0x15);
|
||||
REGISTER_HANDLER(0x16);
|
||||
REGISTER_HANDLER(0x17);
|
||||
REGISTER_HANDLER(0x18);
|
||||
REGISTER_HANDLER(0x19);
|
||||
REGISTER_HANDLER(0x1A);
|
||||
REGISTER_HANDLER(0x1B);
|
||||
REGISTER_HANDLER(0x1C);
|
||||
REGISTER_HANDLER(0x1D);
|
||||
REGISTER_HANDLER(0x1E);
|
||||
REGISTER_HANDLER(0x1F);
|
||||
|
||||
register_syscall_handler(0x80, syscall_asm);
|
||||
|
||||
flush_idt();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
#include <BAN/Errors.h>
|
||||
#include <kernel/Debug.h>
|
||||
#include <kernel/Memory/MMU.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MMU_DEBUG_PRINT 0
|
||||
|
||||
// bits 31-12 set
|
||||
#define PAGE_MASK 0xfffff000
|
||||
#define FLAGS_MASK 0x00000fff
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static MMU* s_instance = nullptr;
|
||||
|
||||
void MMU::initialize()
|
||||
{
|
||||
ASSERT(s_instance == nullptr);
|
||||
s_instance = new MMU();
|
||||
ASSERT(s_instance);
|
||||
s_instance->initialize_kernel();
|
||||
s_instance->load();
|
||||
}
|
||||
|
||||
MMU& MMU::get()
|
||||
{
|
||||
ASSERT(s_instance);
|
||||
return *s_instance;
|
||||
}
|
||||
|
||||
static uint64_t* allocate_page_aligned_page()
|
||||
{
|
||||
uint64_t* page = (uint64_t*)kmalloc(PAGE_SIZE, PAGE_SIZE);
|
||||
ASSERT(page);
|
||||
ASSERT(((uintptr_t)page % PAGE_SIZE) == 0);
|
||||
memset(page, 0, PAGE_SIZE);
|
||||
return page;
|
||||
}
|
||||
|
||||
void MMU::initialize_kernel()
|
||||
{
|
||||
m_highest_paging_struct = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
|
||||
ASSERT(m_highest_paging_struct);
|
||||
ASSERT(((uintptr_t)m_highest_paging_struct % 32) == 0);
|
||||
|
||||
// allocate all page directories
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
uint64_t* page_directory = allocate_page_aligned_page();
|
||||
m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present;
|
||||
}
|
||||
|
||||
// FIXME: We should just identity map until g_kernel_end
|
||||
|
||||
// create and identity map first 6 MiB
|
||||
uint64_t* page_directory1 = (uint64_t*)(m_highest_paging_struct[0] & PAGE_MASK);
|
||||
for (uint64_t i = 0; i < 3; i++)
|
||||
{
|
||||
uint64_t* page_table = allocate_page_aligned_page();
|
||||
for (uint64_t j = 0; j < 512; j++)
|
||||
page_table[j] = (i << 21) | (j << 12) | Flags::ReadWrite | Flags::Present;
|
||||
|
||||
page_directory1[i] = (uint64_t)page_table | Flags::ReadWrite | Flags::Present;
|
||||
}
|
||||
|
||||
// dont map first page (0 -> 4 KiB) so that nullptr dereference
|
||||
// causes page fault :)
|
||||
uint64_t* page_table1 = (uint64_t*)(page_directory1[0] & PAGE_MASK);
|
||||
page_table1[0] = 0;
|
||||
}
|
||||
|
||||
MMU::MMU()
|
||||
{
|
||||
if (s_instance == nullptr)
|
||||
return;
|
||||
|
||||
// Here we copy the s_instances paging structs since they are
|
||||
// global for every process
|
||||
|
||||
uint64_t* global_pdpt = s_instance->m_highest_paging_struct;
|
||||
|
||||
uint64_t* pdpt = (uint64_t*)kmalloc(sizeof(uint64_t) * 4, 32);
|
||||
ASSERT(pdpt);
|
||||
|
||||
for (uint32_t pdpte = 0; pdpte < 4; pdpte++)
|
||||
{
|
||||
if (!(global_pdpt[pdpte] & Flags::Present))
|
||||
continue;
|
||||
|
||||
uint64_t* global_pd = (uint64_t*)(global_pdpt[pdpte] & PAGE_MASK);
|
||||
|
||||
uint64_t* pd = allocate_page_aligned_page();
|
||||
pdpt[pdpte] = (uint64_t)pd | (global_pdpt[pdpte] & ~PAGE_MASK);
|
||||
|
||||
for (uint32_t pde = 0; pde < 512; pde++)
|
||||
{
|
||||
if (!(global_pd[pde] & Flags::Present))
|
||||
continue;
|
||||
|
||||
uint64_t* global_pt = (uint64_t*)(global_pd[pde] & PAGE_MASK);
|
||||
|
||||
uint64_t* pt = allocate_page_aligned_page();
|
||||
pd[pde] = (uint64_t)pt | (global_pd[pde] & ~PAGE_MASK);
|
||||
|
||||
memcpy(pt, global_pt, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
m_highest_paging_struct = pdpt;
|
||||
}
|
||||
|
||||
MMU::~MMU()
|
||||
{
|
||||
uint64_t* pdpt = m_highest_paging_struct;
|
||||
for (uint32_t pdpte = 0; pdpte < 512; pdpte++)
|
||||
{
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
continue;
|
||||
uint64_t* pd = (uint64_t*)(pdpt[pdpte] & PAGE_MASK);
|
||||
for (uint32_t pde = 0; pde < 512; pde++)
|
||||
{
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
continue;
|
||||
kfree((void*)(pd[pde] & PAGE_MASK));
|
||||
}
|
||||
kfree(pd);
|
||||
}
|
||||
kfree(pdpt);
|
||||
}
|
||||
|
||||
void MMU::load()
|
||||
{
|
||||
asm volatile("movl %0, %%cr3" :: "r"(m_highest_paging_struct));
|
||||
}
|
||||
|
||||
void MMU::map_page_at(paddr_t paddr, vaddr_t vaddr, uint8_t flags)
|
||||
{
|
||||
#if MMU_DEBUG_PRINT
|
||||
dprintln("AllocatePage(0x{8H})", address);
|
||||
#endif
|
||||
ASSERT(flags & Flags::Present);
|
||||
|
||||
ASSERT(!(paddr & ~PAGE_MASK));
|
||||
ASSERT(!(vaddr & ~PAGE_MASK));
|
||||
|
||||
uint32_t pdpte = (vaddr & 0xC0000000) >> 30;
|
||||
uint32_t pde = (vaddr & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (vaddr & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
{
|
||||
uint64_t* page_table = allocate_page_aligned_page();
|
||||
page_directory[pde] = (uint64_t)page_table;
|
||||
}
|
||||
page_directory[pde] |= flags;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
page_table[pte] = paddr | flags;
|
||||
}
|
||||
|
||||
void MMU::identity_map_page(paddr_t address, uint8_t flags)
|
||||
{
|
||||
address &= PAGE_MASK;
|
||||
map_page_at(address, address, flags);
|
||||
}
|
||||
|
||||
void MMU::identity_map_range(paddr_t address, ptrdiff_t size, uint8_t flags)
|
||||
{
|
||||
paddr_t s_page = address & PAGE_MASK;
|
||||
paddr_t e_page = (address + size - 1) & PAGE_MASK;
|
||||
for (paddr_t page = s_page; page <= e_page; page += PAGE_SIZE)
|
||||
identity_map_page(page, flags);
|
||||
}
|
||||
|
||||
void MMU::unmap_page(vaddr_t address)
|
||||
{
|
||||
#if MMU_DEBUG_PRINT
|
||||
dprintln("UnAllocatePage(0x{8H})", address & PAGE_MASK);
|
||||
#endif
|
||||
|
||||
uint32_t pdpte = (address & 0xC0000000) >> 30;
|
||||
uint32_t pde = (address & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (address & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
return;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
if (!(page_table[pte] & Flags::Present))
|
||||
return;
|
||||
|
||||
page_table[pte] = 0;
|
||||
|
||||
// TODO: Unallocate the page table if this was the only allocated page
|
||||
}
|
||||
|
||||
void MMU::unmap_range(vaddr_t address, ptrdiff_t size)
|
||||
{
|
||||
uintptr_t s_page = address & PAGE_MASK;
|
||||
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
|
||||
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
|
||||
unmap_page(page);
|
||||
}
|
||||
|
||||
uint8_t MMU::get_page_flags(vaddr_t address) const
|
||||
{
|
||||
uint32_t pdpte = (address & 0xC0000000) >> 30;
|
||||
uint32_t pde = (address & 0x3FE00000) >> 21;
|
||||
uint32_t pte = (address & 0x001FF000) >> 12;
|
||||
|
||||
uint64_t* page_directory = (uint64_t*)(m_highest_paging_struct[pdpte] & PAGE_MASK);
|
||||
if (!(page_directory[pde] & Flags::Present))
|
||||
return 0;
|
||||
|
||||
uint64_t* page_table = (uint64_t*)(page_directory[pde] & PAGE_MASK);
|
||||
if (!(page_table[pte] & Flags::Present))
|
||||
return 0;
|
||||
|
||||
return page_table[pte] & FLAGS_MASK;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
.global spinlock_lock_asm
|
||||
spinlock_lock_asm:
|
||||
movl 4(%esp), %eax
|
||||
lock; btsl $0, (%eax)
|
||||
jnc .done
|
||||
.retry:
|
||||
pause
|
||||
testl $1, (%eax)
|
||||
jne .retry
|
||||
lock; btsl $0, (%eax)
|
||||
jc .retry
|
||||
.done:
|
||||
ret
|
||||
|
||||
.global spinlock_unlock_asm
|
||||
spinlock_unlock_asm:
|
||||
movl 4(%esp), %eax
|
||||
movl $0, (%eax)
|
||||
ret
|
|
@ -0,0 +1,47 @@
|
|||
# uint32_t read_rip()
|
||||
.global read_rip
|
||||
read_rip:
|
||||
popl %eax
|
||||
jmp *%eax
|
||||
|
||||
exit_thread_trampoline:
|
||||
addl $4, %esp
|
||||
pushl (%esp)
|
||||
ret
|
||||
|
||||
# void start_thread(uint32_t esp, uint32_t eip)
|
||||
.global start_thread
|
||||
start_thread:
|
||||
movl 8(%esp), %ecx
|
||||
movl 4(%esp), %esp
|
||||
movl $0, %ebp
|
||||
pushl $exit_thread_trampoline
|
||||
sti
|
||||
jmp *%ecx
|
||||
|
||||
# void continue_thread(uint32_t rsp, uint32_t rip)
|
||||
.global continue_thread
|
||||
continue_thread:
|
||||
movl 8(%esp), %ecx
|
||||
movl 4(%esp), %esp
|
||||
movl $0, %eax
|
||||
jmp *%ecx
|
||||
|
||||
# void thread_jump_userspace(uint32_t rsp, uint32_t rip)
|
||||
.global thread_jump_userspace
|
||||
thread_jump_userspace:
|
||||
movl $0x23, %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
movl 8(%esp), %ecx
|
||||
movl 4(%esp), %esp
|
||||
|
||||
pushl $0x23
|
||||
pushl %esp
|
||||
pushfl
|
||||
pushl $0x1B
|
||||
pushl %ecx
|
||||
iret
|
|
@ -0,0 +1,182 @@
|
|||
# Declare constants for the multiboot header
|
||||
.set ALIGN, 1<<0 # align loaded modules on page boundaries
|
||||
.set MEMINFO, 1<<1 # provide memory map
|
||||
.set VIDEOINFO, 1<<2 # provide video info
|
||||
.set MB_FLAGS, ALIGN | MEMINFO | VIDEOINFO # this is the Multiboot 'flag' field
|
||||
.set MB_MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
|
||||
.set MB_CHECKSUM, -(MB_MAGIC + MB_FLAGS) #checksum of above, to prove we are multiboot
|
||||
|
||||
# Multiboot header
|
||||
.section .multiboot, "aw"
|
||||
.align 4
|
||||
.long MB_MAGIC
|
||||
.long MB_FLAGS
|
||||
.long MB_CHECKSUM
|
||||
.skip 20
|
||||
|
||||
.long 0
|
||||
.long 800
|
||||
.long 600
|
||||
.long 32
|
||||
|
||||
.section .bss, "aw", @nobits
|
||||
# Create stack
|
||||
.global g_boot_stack_bottom
|
||||
g_boot_stack_bottom:
|
||||
.skip 16384
|
||||
.global g_boot_stack_top
|
||||
g_boot_stack_top:
|
||||
|
||||
# 0 MiB -> 1 MiB: bootloader stuff
|
||||
# 1 MiB -> : kernel
|
||||
.align 32
|
||||
boot_page_directory_pointer_table:
|
||||
.skip 4 * 8
|
||||
.align 4096
|
||||
boot_page_directory1:
|
||||
.skip 512 * 8
|
||||
|
||||
.global g_kernel_cmdline
|
||||
g_kernel_cmdline:
|
||||
.skip 4096
|
||||
|
||||
.global g_multiboot_info
|
||||
g_multiboot_info:
|
||||
.skip 4
|
||||
.global g_multiboot_magic
|
||||
g_multiboot_magic:
|
||||
.skip 4
|
||||
|
||||
.section .text
|
||||
|
||||
boot_gdt:
|
||||
.quad 0x0000000000000000 # null
|
||||
.quad 0x00CF9A000000FFFF # kernel code
|
||||
.quad 0x00CF92000000FFFF # kernel data
|
||||
boot_gdtr:
|
||||
.short . - boot_gdt - 1
|
||||
.long boot_gdt
|
||||
|
||||
has_cpuid:
|
||||
pushfl
|
||||
pushfl
|
||||
xorl $0x00200000, (%esp)
|
||||
popfl
|
||||
pushfl
|
||||
popl %eax
|
||||
xorl (%esp), %eax
|
||||
popfl
|
||||
testl $0x00200000, %eax
|
||||
ret
|
||||
|
||||
has_pae:
|
||||
movl $0, %eax
|
||||
cpuid
|
||||
testl $(1 << 6), %edx
|
||||
ret
|
||||
|
||||
has_sse:
|
||||
movl $1, %eax
|
||||
cpuid
|
||||
testl $(1 << 25), %edx
|
||||
ret
|
||||
|
||||
check_requirements:
|
||||
call has_cpuid
|
||||
jz .exit
|
||||
call has_pae
|
||||
jz .exit
|
||||
call has_sse
|
||||
jz .exit
|
||||
ret
|
||||
.exit:
|
||||
jmp system_halt
|
||||
|
||||
copy_kernel_commandline:
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
movl g_multiboot_info, %esi
|
||||
addl $16, %esi
|
||||
movl (%esi), %esi
|
||||
movl $1024, %ecx
|
||||
movl $g_kernel_cmdline, %edi
|
||||
rep movsl
|
||||
popl %edi
|
||||
popl %esi
|
||||
ret
|
||||
|
||||
enable_sse:
|
||||
movl %cr0, %eax
|
||||
andw $0xFFFB, %ax
|
||||
orw $0x0002, %ax
|
||||
movl %eax, %cr0
|
||||
movl %cr4, %eax
|
||||
orw $0x0600, %ax
|
||||
movl %eax, %cr4
|
||||
ret
|
||||
|
||||
initialize_paging:
|
||||
# identity map first 6 MiB
|
||||
movl $(0x00000000 + 0x83), boot_page_directory1 + 0
|
||||
movl $(0x00200000 + 0x83), boot_page_directory1 + 8
|
||||
movl $(0x00400000 + 0x83), boot_page_directory1 + 16
|
||||
movl $(boot_page_directory1 + 0x01), boot_page_directory_pointer_table
|
||||
|
||||
# enable PAE
|
||||
movl %cr4, %ecx
|
||||
orl $0x20, %ecx
|
||||
movl %ecx, %cr4
|
||||
|
||||
# set address of paging structures
|
||||
movl $boot_page_directory_pointer_table, %ecx
|
||||
movl %ecx, %cr3
|
||||
|
||||
# enable paging
|
||||
movl %cr0, %ecx
|
||||
orl $0x80000000, %ecx
|
||||
movl %ecx, %cr0
|
||||
|
||||
ret
|
||||
|
||||
initialize_gdt:
|
||||
lgdt boot_gdtr
|
||||
|
||||
# flush gdt
|
||||
movw $0x10, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
jmp $0x08, $flush
|
||||
flush:
|
||||
ret
|
||||
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
# Initialize stack and multiboot info
|
||||
movl $g_boot_stack_top, %esp
|
||||
movl %eax, g_multiboot_magic
|
||||
movl %ebx, g_multiboot_info
|
||||
|
||||
call copy_kernel_commandline
|
||||
call check_requirements
|
||||
call enable_sse
|
||||
|
||||
call initialize_paging
|
||||
call initialize_gdt
|
||||
|
||||
call _init
|
||||
|
||||
# call to the kernel itself (clear ebp for stacktrace)
|
||||
xorl %ebp, %ebp
|
||||
call kernel_main
|
||||
|
||||
call _fini
|
||||
|
||||
system_halt:
|
||||
xchgw %bx, %bx
|
||||
cli
|
||||
1: hlt
|
||||
jmp 1b
|
|
@ -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
|
|
@ -1,9 +1,9 @@
|
|||
/* i686 crti.s */
|
||||
/* x86 crti.s */
|
||||
.section .init
|
||||
.global _init
|
||||
.type _init, @function
|
||||
_init:
|
||||
pushl %ebp
|
||||
push %ebp
|
||||
movl %esp, %ebp
|
||||
/* gcc will nicely put the contents of crtbegin.o's .init section here. */
|
||||
|
||||
|
@ -11,6 +11,6 @@ _init:
|
|||
.global _fini
|
||||
.type _fini, @function
|
||||
_fini:
|
||||
pushl %ebp
|
||||
push %ebp
|
||||
movl %esp, %ebp
|
||||
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */
|
|
@ -1,4 +1,4 @@
|
|||
/* i686 crtn.s */
|
||||
/* x86 crtn.s */
|
||||
.section .init
|
||||
/* gcc will nicely put the contents of crtend.o's .init section here. */
|
||||
popl %ebp
|
|
@ -0,0 +1,28 @@
|
|||
ENTRY (_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x00100000;
|
||||
|
||||
g_kernel_start = .;
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.multiboot)
|
||||
*(.text)
|
||||
}
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.rodata.*)
|
||||
}
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
g_kernel_end = .;
|
||||
}
|
|
@ -1,682 +0,0 @@
|
|||
#include <kernel/BootInfo.h>
|
||||
#include <kernel/CPUID.h>
|
||||
#include <kernel/Lock/SpinLock.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
||||
extern uint8_t g_kernel_start[];
|
||||
extern uint8_t g_kernel_end[];
|
||||
|
||||
extern uint8_t g_kernel_execute_start[];
|
||||
extern uint8_t g_kernel_execute_end[];
|
||||
|
||||
extern uint8_t g_kernel_writable_start[];
|
||||
extern uint8_t g_kernel_writable_end[];
|
||||
|
||||
extern uint8_t g_userspace_start[];
|
||||
extern uint8_t g_userspace_end[];
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
SpinLock PageTable::s_fast_page_lock;
|
||||
|
||||
static PageTable* s_kernel = nullptr;
|
||||
static bool s_has_nxe = false;
|
||||
static bool s_has_pge = false;
|
||||
static bool s_has_pat = false;
|
||||
|
||||
static paddr_t s_global_pdpte = 0;
|
||||
|
||||
static inline PageTable::flags_t parse_flags(uint64_t entry)
|
||||
{
|
||||
using Flags = PageTable::Flags;
|
||||
|
||||
PageTable::flags_t result = 0;
|
||||
if (s_has_nxe && !(entry & (1ull << 63)))
|
||||
result |= Flags::Execute;
|
||||
if (entry & Flags::Reserved)
|
||||
result |= Flags::Reserved;
|
||||
if (entry & Flags::UserSupervisor)
|
||||
result |= Flags::UserSupervisor;
|
||||
if (entry & Flags::ReadWrite)
|
||||
result |= Flags::ReadWrite;
|
||||
if (entry & Flags::Present)
|
||||
result |= Flags::Present;
|
||||
return result;
|
||||
}
|
||||
|
||||
void PageTable::initialize_pre_heap()
|
||||
{
|
||||
if (CPUID::has_nxe())
|
||||
s_has_nxe = true;
|
||||
|
||||
if (CPUID::has_pge())
|
||||
s_has_pge = true;
|
||||
|
||||
if (CPUID::has_pat())
|
||||
s_has_pat = true;
|
||||
|
||||
ASSERT(s_kernel == nullptr);
|
||||
s_kernel = new PageTable();
|
||||
ASSERT(s_kernel);
|
||||
|
||||
s_kernel->initialize_kernel();
|
||||
s_kernel->initial_load();
|
||||
}
|
||||
|
||||
void PageTable::initialize_post_heap()
|
||||
{
|
||||
// NOTE: this is no-op as our 32 bit target does not use hhdm
|
||||
}
|
||||
|
||||
void PageTable::initial_load()
|
||||
{
|
||||
if (s_has_nxe)
|
||||
{
|
||||
asm volatile(
|
||||
"movl $0xC0000080, %%ecx;"
|
||||
"rdmsr;"
|
||||
"orl $0x800, %%eax;"
|
||||
"wrmsr"
|
||||
::: "eax", "ecx", "edx", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
if (s_has_pge)
|
||||
{
|
||||
asm volatile(
|
||||
"movl %%cr4, %%eax;"
|
||||
"orl $0x80, %%eax;"
|
||||
"movl %%eax, %%cr4;"
|
||||
::: "eax"
|
||||
);
|
||||
}
|
||||
|
||||
if (s_has_pat)
|
||||
{
|
||||
asm volatile(
|
||||
"movl $0x277, %%ecx;"
|
||||
"rdmsr;"
|
||||
"movw $0x0401, %%dx;"
|
||||
"wrmsr;"
|
||||
::: "eax", "ecx", "edx", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
// enable write protect
|
||||
asm volatile(
|
||||
"movl %%cr0, %%eax;"
|
||||
"orl $0x10000, %%eax;"
|
||||
"movl %%eax, %%cr0;"
|
||||
::: "rax"
|
||||
);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
PageTable& PageTable::kernel()
|
||||
{
|
||||
ASSERT(s_kernel);
|
||||
return *s_kernel;
|
||||
}
|
||||
|
||||
bool PageTable::is_valid_pointer(uintptr_t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t* allocate_zeroed_page_aligned_page()
|
||||
{
|
||||
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
|
||||
ASSERT(page);
|
||||
memset(page, 0, PAGE_SIZE);
|
||||
return (uint64_t*)page;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static paddr_t V2P(const T vaddr)
|
||||
{
|
||||
return (vaddr_t)vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static vaddr_t P2V(const T paddr)
|
||||
{
|
||||
return (paddr_t)paddr - g_boot_info.kernel_paddr + KERNEL_OFFSET;
|
||||
}
|
||||
|
||||
void PageTable::initialize_kernel()
|
||||
{
|
||||
ASSERT(s_global_pdpte == 0);
|
||||
s_global_pdpte = V2P(allocate_zeroed_page_aligned_page());
|
||||
|
||||
map_kernel_memory();
|
||||
|
||||
prepare_fast_page();
|
||||
|
||||
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
|
||||
ASSERT((vaddr_t)g_kernel_start % PAGE_SIZE == 0);
|
||||
map_range_at(
|
||||
V2P(g_kernel_start),
|
||||
(vaddr_t)g_kernel_start,
|
||||
g_kernel_end - g_kernel_start,
|
||||
Flags::Present
|
||||
);
|
||||
|
||||
// Map executable kernel memory as executable
|
||||
map_range_at(
|
||||
V2P(g_kernel_execute_start),
|
||||
(vaddr_t)g_kernel_execute_start,
|
||||
g_kernel_execute_end - g_kernel_execute_start,
|
||||
Flags::Execute | Flags::Present
|
||||
);
|
||||
|
||||
// Map writable kernel memory as writable
|
||||
map_range_at(
|
||||
V2P(g_kernel_writable_start),
|
||||
(vaddr_t)g_kernel_writable_start,
|
||||
g_kernel_writable_end - g_kernel_writable_start,
|
||||
Flags::ReadWrite | Flags::Present
|
||||
);
|
||||
|
||||
// Map userspace memory
|
||||
map_range_at(
|
||||
V2P(g_userspace_start),
|
||||
(vaddr_t)g_userspace_start,
|
||||
g_userspace_end - g_userspace_start,
|
||||
Flags::Execute | Flags::UserSupervisor | Flags::Present
|
||||
);
|
||||
}
|
||||
|
||||
void PageTable::prepare_fast_page()
|
||||
{
|
||||
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
|
||||
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
ASSERT(pdpt[pdpte] & Flags::Present);
|
||||
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte]) & PAGE_ADDR_MASK);
|
||||
ASSERT(!(pd[pde] & Flags::Present));
|
||||
pd[pde] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
|
||||
}
|
||||
|
||||
void PageTable::map_fast_page(paddr_t paddr)
|
||||
{
|
||||
ASSERT(s_kernel);
|
||||
ASSERT(paddr);
|
||||
|
||||
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
||||
|
||||
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
|
||||
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
|
||||
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(s_kernel->m_highest_paging_struct));
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
|
||||
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
|
||||
|
||||
ASSERT(!(pt[pte] & Flags::Present));
|
||||
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
|
||||
|
||||
invalidate(fast_page(), false);
|
||||
}
|
||||
|
||||
void PageTable::unmap_fast_page()
|
||||
{
|
||||
ASSERT(s_kernel);
|
||||
|
||||
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
||||
|
||||
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
|
||||
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
|
||||
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(s_kernel->m_highest_paging_struct));
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
|
||||
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
|
||||
|
||||
ASSERT(pt[pte] & Flags::Present);
|
||||
pt[pte] = 0;
|
||||
|
||||
invalidate(fast_page(), false);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
|
||||
{
|
||||
SpinLockGuard _(s_kernel->m_lock);
|
||||
PageTable* page_table = new PageTable;
|
||||
if (page_table == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
page_table->map_kernel_memory();
|
||||
return page_table;
|
||||
}
|
||||
|
||||
void PageTable::map_kernel_memory()
|
||||
{
|
||||
ASSERT(s_kernel);
|
||||
ASSERT(s_global_pdpte);
|
||||
|
||||
ASSERT(m_highest_paging_struct == 0);
|
||||
m_highest_paging_struct = V2P(kmalloc(32, 32, true));
|
||||
ASSERT(m_highest_paging_struct);
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
pdpt[0] = 0;
|
||||
pdpt[1] = 0;
|
||||
pdpt[2] = 0;
|
||||
pdpt[3] = s_global_pdpte | Flags::Present;
|
||||
static_assert(KERNEL_OFFSET == 0xC0000000);
|
||||
}
|
||||
|
||||
PageTable::~PageTable()
|
||||
{
|
||||
if (m_highest_paging_struct == 0)
|
||||
return;
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
|
||||
for (uint32_t pdpte = 0; pdpte < 3; pdpte++)
|
||||
{
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
continue;
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
|
||||
for (uint32_t pde = 0; pde < 512; pde++)
|
||||
{
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
continue;
|
||||
kfree(reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK)));
|
||||
}
|
||||
kfree(pd);
|
||||
}
|
||||
kfree(pdpt);
|
||||
}
|
||||
|
||||
void PageTable::load()
|
||||
{
|
||||
SpinLockGuard _(m_lock);
|
||||
ASSERT(m_highest_paging_struct < 0x100000000);
|
||||
const uint32_t pdpt_lo = m_highest_paging_struct;
|
||||
asm volatile("movl %0, %%cr3" :: "r"(pdpt_lo));
|
||||
Processor::set_current_page_table(this);
|
||||
}
|
||||
|
||||
void PageTable::invalidate(vaddr_t vaddr, bool send_smp_message)
|
||||
{
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
asm volatile("invlpg (%0)" :: "r"(vaddr) : "memory");
|
||||
|
||||
if (send_smp_message)
|
||||
{
|
||||
Processor::broadcast_smp_message({
|
||||
.type = Processor::SMPMessage::Type::FlushTLB,
|
||||
.flush_tlb = {
|
||||
.vaddr = vaddr,
|
||||
.page_count = 1
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void PageTable::unmap_page(vaddr_t vaddr, bool send_smp_message)
|
||||
{
|
||||
ASSERT(vaddr);
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
ASSERT(vaddr != fast_page());
|
||||
if (vaddr >= KERNEL_OFFSET)
|
||||
ASSERT(vaddr >= (vaddr_t)g_kernel_start);
|
||||
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
|
||||
Kernel::panic("unmapping {8H}, kernel: {}", vaddr, this == s_kernel);
|
||||
|
||||
const uint64_t pdpte = (vaddr >> 30) & 0x1FF;
|
||||
const uint64_t pde = (vaddr >> 21) & 0x1FF;
|
||||
const uint64_t pte = (vaddr >> 12) & 0x1FF;
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
if (is_page_free(vaddr))
|
||||
Kernel::panic("trying to unmap unmapped page 0x{H}", vaddr);
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
|
||||
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
|
||||
|
||||
pt[pte] = 0;
|
||||
invalidate(vaddr, send_smp_message);
|
||||
}
|
||||
|
||||
void PageTable::unmap_range(vaddr_t vaddr, size_t size)
|
||||
{
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
|
||||
size_t page_count = range_page_count(vaddr, size);
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
for (vaddr_t page = 0; page < page_count; page++)
|
||||
unmap_page(vaddr + page * PAGE_SIZE, false);
|
||||
|
||||
Processor::broadcast_smp_message({
|
||||
.type = Processor::SMPMessage::Type::FlushTLB,
|
||||
.flush_tlb = {
|
||||
.vaddr = vaddr,
|
||||
.page_count = page_count
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type, bool send_smp_message)
|
||||
{
|
||||
ASSERT(vaddr);
|
||||
ASSERT(vaddr != fast_page());
|
||||
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
|
||||
Kernel::panic("mapping {8H} to {8H}, kernel: {}", paddr, vaddr, this == s_kernel);
|
||||
|
||||
ASSERT(paddr % PAGE_SIZE == 0);
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
ASSERT(flags & Flags::Used);
|
||||
|
||||
const uint64_t pdpte = (vaddr >> 30) & 0x1FF;
|
||||
const uint64_t pde = (vaddr >> 21) & 0x1FF;
|
||||
const uint64_t pte = (vaddr >> 12) & 0x1FF;
|
||||
|
||||
uint64_t extra_flags = 0;
|
||||
if (s_has_pge && vaddr >= KERNEL_OFFSET) // Map kernel memory as global
|
||||
extra_flags |= 1ull << 8;
|
||||
if (s_has_nxe && !(flags & Flags::Execute))
|
||||
extra_flags |= 1ull << 63;
|
||||
if (flags & Flags::Reserved)
|
||||
extra_flags |= Flags::Reserved;
|
||||
|
||||
if (memory_type == MemoryType::Uncached)
|
||||
extra_flags |= (1ull << 4);
|
||||
if (s_has_pat && memory_type == MemoryType::WriteCombining)
|
||||
extra_flags |= (1ull << 7);
|
||||
if (s_has_pat && memory_type == MemoryType::WriteThrough)
|
||||
extra_flags |= (1ull << 7) | (1ull << 3);
|
||||
|
||||
// NOTE: we add present here, since it has to be available in higher level structures
|
||||
flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present;
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
pdpt[pdpte] = V2P(allocate_zeroed_page_aligned_page()) | Flags::Present;
|
||||
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
|
||||
if ((pd[pde] & uwr_flags) != uwr_flags)
|
||||
{
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
pd[pde] = V2P(allocate_zeroed_page_aligned_page());
|
||||
pd[pde] |= uwr_flags;
|
||||
}
|
||||
|
||||
if (!(flags & Flags::Present))
|
||||
uwr_flags &= ~Flags::Present;
|
||||
|
||||
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
|
||||
pt[pte] = paddr | uwr_flags | extra_flags;
|
||||
|
||||
invalidate(vaddr, send_smp_message);
|
||||
}
|
||||
|
||||
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
|
||||
{
|
||||
ASSERT(vaddr);
|
||||
ASSERT(paddr % PAGE_SIZE == 0);
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
|
||||
size_t page_count = range_page_count(vaddr, size);
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
for (size_t page = 0; page < page_count; page++)
|
||||
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type, false);
|
||||
|
||||
Processor::broadcast_smp_message({
|
||||
.type = Processor::SMPMessage::Type::FlushTLB,
|
||||
.flush_tlb = {
|
||||
.vaddr = vaddr,
|
||||
.page_count = page_count
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t PageTable::get_page_data(vaddr_t vaddr) const
|
||||
{
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
|
||||
const uint64_t pdpte = (vaddr >> 30) & 0x1FF;
|
||||
const uint64_t pde = (vaddr >> 21) & 0x1FF;
|
||||
const uint64_t pte = (vaddr >> 12) & 0x1FF;
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
uint64_t* pdpt = (uint64_t*)P2V(m_highest_paging_struct);
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
return 0;
|
||||
|
||||
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
return 0;
|
||||
|
||||
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
|
||||
if (!(pt[pte] & Flags::Used))
|
||||
return 0;
|
||||
|
||||
return pt[pte];
|
||||
}
|
||||
|
||||
PageTable::flags_t PageTable::get_page_flags(vaddr_t vaddr) const
|
||||
{
|
||||
return parse_flags(get_page_data(vaddr));
|
||||
}
|
||||
|
||||
paddr_t PageTable::physical_address_of(vaddr_t vaddr) const
|
||||
{
|
||||
uint64_t page_data = get_page_data(vaddr);
|
||||
return (page_data & PAGE_ADDR_MASK) & ~(1ull << 63);
|
||||
}
|
||||
|
||||
bool PageTable::is_page_free(vaddr_t vaddr) const
|
||||
{
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
return !(get_page_flags(vaddr) & Flags::Used);
|
||||
}
|
||||
|
||||
bool PageTable::is_range_free(vaddr_t vaddr, size_t size) const
|
||||
{
|
||||
vaddr_t s_page = vaddr / PAGE_SIZE;
|
||||
vaddr_t e_page = BAN::Math::div_round_up<vaddr_t>(vaddr + size, PAGE_SIZE);
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
for (vaddr_t page = s_page; page < e_page; page++)
|
||||
if (!is_page_free(page * PAGE_SIZE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageTable::reserve_page(vaddr_t vaddr, bool only_free)
|
||||
{
|
||||
SpinLockGuard _(m_lock);
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
if (only_free && !is_page_free(vaddr))
|
||||
return false;
|
||||
map_page_at(0, vaddr, Flags::Reserved);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PageTable::reserve_range(vaddr_t vaddr, size_t bytes, bool only_free)
|
||||
{
|
||||
if (size_t rem = bytes % PAGE_SIZE)
|
||||
bytes += PAGE_SIZE - rem;
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
if (only_free && !is_range_free(vaddr, bytes))
|
||||
return false;
|
||||
for (size_t offset = 0; offset < bytes; offset += PAGE_SIZE)
|
||||
reserve_page(vaddr + offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address)
|
||||
{
|
||||
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_end)
|
||||
first_address = (vaddr_t)g_kernel_end;
|
||||
if (size_t rem = first_address % PAGE_SIZE)
|
||||
first_address += PAGE_SIZE - rem;
|
||||
if (size_t rem = last_address % PAGE_SIZE)
|
||||
last_address -= rem;
|
||||
|
||||
const uint32_t s_pdpte = (first_address >> 30) & 0x1FF;
|
||||
const uint32_t s_pde = (first_address >> 21) & 0x1FF;
|
||||
const uint32_t s_pte = (first_address >> 12) & 0x1FF;
|
||||
|
||||
const uint32_t e_pdpte = (last_address >> 30) & 0x1FF;
|
||||
const uint32_t e_pde = (last_address >> 21) & 0x1FF;
|
||||
const uint32_t e_pte = (last_address >> 12) & 0x1FF;
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
// Try to find free page that can be mapped without
|
||||
// allocations (page table with unused entries)
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
for (uint32_t pdpte = s_pdpte; pdpte < 4; pdpte++)
|
||||
{
|
||||
if (pdpte > e_pdpte)
|
||||
break;
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
continue;
|
||||
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
|
||||
for (uint32_t pde = s_pde; pde < 512; pde++)
|
||||
{
|
||||
if (pdpte == e_pdpte && pde > e_pde)
|
||||
break;
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
continue;
|
||||
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
|
||||
for (uint32_t pte = s_pte; pte < 512; pte++)
|
||||
{
|
||||
if (pdpte == e_pdpte && pde == e_pde && pte >= e_pte)
|
||||
break;
|
||||
if (!(pt[pte] & Flags::Used))
|
||||
{
|
||||
vaddr_t vaddr = 0;
|
||||
vaddr |= (vaddr_t)pdpte << 30;
|
||||
vaddr |= (vaddr_t)pde << 21;
|
||||
vaddr |= (vaddr_t)pte << 12;
|
||||
ASSERT(reserve_page(vaddr));
|
||||
return vaddr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find any free page
|
||||
for (vaddr_t vaddr = first_address; vaddr < last_address; vaddr += PAGE_SIZE)
|
||||
{
|
||||
if (is_page_free(vaddr))
|
||||
{
|
||||
ASSERT(reserve_page(vaddr));
|
||||
return vaddr;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
vaddr_t PageTable::reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address)
|
||||
{
|
||||
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_start)
|
||||
first_address = (vaddr_t)g_kernel_start;
|
||||
if (size_t rem = first_address % PAGE_SIZE)
|
||||
first_address += PAGE_SIZE - rem;
|
||||
if (size_t rem = last_address % PAGE_SIZE)
|
||||
last_address -= rem;
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
for (vaddr_t vaddr = first_address; vaddr < last_address;)
|
||||
{
|
||||
bool valid { true };
|
||||
for (size_t page = 0; page < page_count; page++)
|
||||
{
|
||||
if (!is_page_free(vaddr + page * PAGE_SIZE))
|
||||
{
|
||||
vaddr += (page + 1) * PAGE_SIZE;
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
ASSERT(reserve_range(vaddr, page_count * PAGE_SIZE));
|
||||
return vaddr;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
static void dump_range(vaddr_t start, vaddr_t end, PageTable::flags_t flags)
|
||||
{
|
||||
if (start == 0)
|
||||
return;
|
||||
dprintln("{}-{}: {}{}{}{}",
|
||||
(void*)(start), (void*)(end - 1),
|
||||
flags & PageTable::Flags::Execute ? 'x' : '-',
|
||||
flags & PageTable::Flags::UserSupervisor ? 'u' : '-',
|
||||
flags & PageTable::Flags::ReadWrite ? 'w' : '-',
|
||||
flags & PageTable::Flags::Present ? 'r' : '-'
|
||||
);
|
||||
}
|
||||
|
||||
void PageTable::debug_dump()
|
||||
{
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
flags_t flags = 0;
|
||||
vaddr_t start = 0;
|
||||
|
||||
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
|
||||
for (uint32_t pdpte = 0; pdpte < 4; pdpte++)
|
||||
{
|
||||
if (!(pdpt[pdpte] & Flags::Present))
|
||||
{
|
||||
dump_range(start, (pdpte << 30), flags);
|
||||
start = 0;
|
||||
continue;
|
||||
}
|
||||
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
|
||||
for (uint64_t pde = 0; pde < 512; pde++)
|
||||
{
|
||||
if (!(pd[pde] & Flags::Present))
|
||||
{
|
||||
dump_range(start, (pdpte << 30) | (pde << 21), flags);
|
||||
start = 0;
|
||||
continue;
|
||||
}
|
||||
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
|
||||
for (uint64_t pte = 0; pte < 512; pte++)
|
||||
{
|
||||
if (parse_flags(pt[pte]) != flags)
|
||||
{
|
||||
dump_range(start, (pdpte << 30) | (pde << 21) | (pte << 12), flags);
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (!(pt[pte] & Flags::Used))
|
||||
continue;
|
||||
|
||||
if (start == 0)
|
||||
{
|
||||
flags = parse_flags(pt[pte]);
|
||||
start = (pdpte << 30) | (pde << 21) | (pte << 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
.section .userspace, "ax"
|
||||
|
||||
// stack contains
|
||||
// return address
|
||||
// signal number
|
||||
// signal handler
|
||||
|
||||
.global signal_trampoline
|
||||
signal_trampoline:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
pusha
|
||||
|
||||
movl 40(%esp), %edi
|
||||
movl 36(%esp), %eax
|
||||
|
||||
// align stack to 16 bytes
|
||||
movl %esp, %ebx
|
||||
andl $0x0F, %ebx
|
||||
subl %ebx, %esp
|
||||
|
||||
subl $12, %esp
|
||||
pushl %edi
|
||||
call *%eax
|
||||
addl $16, %esp
|
||||
|
||||
// restore stack
|
||||
addl %ebx, %esp
|
||||
popa
|
||||
|
||||
leave
|
||||
addl $8, %esp
|
||||
|
||||
ret
|
|
@ -1,92 +0,0 @@
|
|||
// arguments in EAX, EBX, ECX, EDX, ESI, EDI
|
||||
.global asm_syscall_handler
|
||||
asm_syscall_handler:
|
||||
# save segment registers
|
||||
pushw %ds
|
||||
pushw %es
|
||||
pushw %fs
|
||||
pushw %gs
|
||||
|
||||
# save general purpose registers
|
||||
pushl %ebx
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %ebp
|
||||
|
||||
# align stack
|
||||
movl %esp, %ebp
|
||||
subl $15, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
|
||||
# push arguments
|
||||
subl $4, %esp
|
||||
pushl %ebp
|
||||
addl $32, (%esp)
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %edx
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
pushl %eax
|
||||
|
||||
# load kernel segments
|
||||
movw $0x10, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw $0x28, %ax
|
||||
movw %ax, %gs
|
||||
|
||||
call cpp_syscall_handler
|
||||
|
||||
movl %ebp, %esp
|
||||
|
||||
# restore general purpose registers
|
||||
popl %ebp
|
||||
popl %esi
|
||||
popl %edi
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %ebx
|
||||
|
||||
# restore segment registers
|
||||
popw %gs
|
||||
popw %fs
|
||||
popw %es
|
||||
popw %ds
|
||||
|
||||
iret
|
||||
|
||||
.global sys_fork_trampoline
|
||||
sys_fork_trampoline:
|
||||
pushl %ebp
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
|
||||
call read_ip
|
||||
testl %eax, %eax
|
||||
jz .reload_stack
|
||||
|
||||
movl %esp, %ebx
|
||||
|
||||
subl $8, %esp
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
call sys_fork
|
||||
addl $16, %esp
|
||||
|
||||
.done:
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
.reload_stack:
|
||||
call get_thread_start_sp
|
||||
movl %eax, %esp
|
||||
xorl %eax, %eax
|
||||
jmp .done
|
|
@ -1,69 +0,0 @@
|
|||
# uint32_t read_ip()
|
||||
.global read_ip
|
||||
read_ip:
|
||||
popl %eax
|
||||
jmp *%eax
|
||||
|
||||
# void start_kernel_thread()
|
||||
.global start_kernel_thread
|
||||
start_kernel_thread:
|
||||
call get_thread_start_sp
|
||||
movl %eax, %esp
|
||||
|
||||
# STACK LAYOUT
|
||||
# on_exit arg
|
||||
# on_exit func
|
||||
# entry arg
|
||||
# entry func
|
||||
|
||||
movl 4(%esp), %edi
|
||||
movl 0(%esp), %esi
|
||||
|
||||
subl $12, %esp
|
||||
pushl %edi
|
||||
sti
|
||||
call *%esi
|
||||
addl $16, %esp
|
||||
|
||||
movl 12(%esp), %edi
|
||||
movl 8(%esp), %esi
|
||||
|
||||
subl $12, %esp
|
||||
pushl %edi
|
||||
call *%esi
|
||||
addl $16, %esp
|
||||
|
||||
|
||||
.global start_userspace_thread
|
||||
start_userspace_thread:
|
||||
call get_thread_start_sp
|
||||
movl %eax, %esp
|
||||
|
||||
# STACK LAYOUT
|
||||
# entry
|
||||
# argc
|
||||
# argv
|
||||
# envp
|
||||
# userspace stack
|
||||
|
||||
call get_userspace_thread_stack_top
|
||||
|
||||
movw $(0x20 | 3), %bx
|
||||
movw %bx, %ds
|
||||
movw %bx, %es
|
||||
movw %bx, %fs
|
||||
movw %bx, %gs
|
||||
xorw %bx, %bx
|
||||
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %ebx
|
||||
|
||||
pushl $(0x20 | 3)
|
||||
pushl %eax
|
||||
pushl $0x202
|
||||
pushl $(0x18 | 3)
|
||||
pushl %ebx
|
||||
iret
|
|
@ -1,316 +0,0 @@
|
|||
.set PG_PRESENT, 1<<0
|
||||
.set PG_READ_WRITE, 1<<1
|
||||
.set PG_PAGE_SIZE, 1<<7
|
||||
|
||||
.set FB_WIDTH, 800
|
||||
.set FB_HEIGHT, 600
|
||||
.set FB_BPP, 32
|
||||
|
||||
#define KERNEL_OFFSET 0xC0000000
|
||||
#define V2P(vaddr) ((vaddr) - KERNEL_OFFSET)
|
||||
|
||||
.code32
|
||||
|
||||
# multiboot2 header
|
||||
.section .multiboot, "aw"
|
||||
.align 8
|
||||
multiboot2_start:
|
||||
.long 0xE85250D6
|
||||
.long 0
|
||||
.long multiboot2_end - multiboot2_start
|
||||
.long -(0xE85250D6 + (multiboot2_end - multiboot2_start))
|
||||
|
||||
# framebuffer tag
|
||||
.align 8
|
||||
.short 5
|
||||
.short 0
|
||||
.long 20
|
||||
.long FB_WIDTH
|
||||
.long FB_HEIGHT
|
||||
.long FB_BPP
|
||||
|
||||
# legacy start
|
||||
.align 8
|
||||
.short 3
|
||||
.short 0
|
||||
.long 12
|
||||
.long V2P(_start)
|
||||
|
||||
.align 8
|
||||
.short 0
|
||||
.short 0
|
||||
.long 8
|
||||
multiboot2_end:
|
||||
|
||||
.section .bananboot, "aw"
|
||||
.align 8
|
||||
bananboot_start:
|
||||
.long 0xBABAB007
|
||||
.long -(0xBABAB007 + FB_WIDTH + FB_HEIGHT + FB_BPP)
|
||||
.long FB_WIDTH
|
||||
.long FB_HEIGHT
|
||||
.long FB_BPP
|
||||
bananboot_end:
|
||||
|
||||
.section .bss, "aw", @nobits
|
||||
.align 4096
|
||||
boot_stack_bottom:
|
||||
.skip 4096 * 4
|
||||
boot_stack_top:
|
||||
|
||||
.global g_kernel_cmdline
|
||||
g_kernel_cmdline:
|
||||
.skip 4096
|
||||
|
||||
bootloader_magic:
|
||||
.skip 8
|
||||
bootloader_info:
|
||||
.skip 8
|
||||
|
||||
.section .data
|
||||
|
||||
# Map first GiB to 0x00000000 and 0xC0000000
|
||||
.align 32
|
||||
boot_pdpt:
|
||||
.long V2P(boot_pd) + (PG_PRESENT)
|
||||
.long 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.long V2P(boot_pd) + (PG_PRESENT)
|
||||
.long 0
|
||||
.align 4096
|
||||
boot_pd:
|
||||
.set i, 0
|
||||
.rept 512
|
||||
.long V2P(boot_pts) + i + (PG_READ_WRITE | PG_PRESENT)
|
||||
.long 0
|
||||
.set i, i + 0x1000
|
||||
.endr
|
||||
boot_pts:
|
||||
.set i, 0
|
||||
.rept 512
|
||||
.rept 512
|
||||
.long i + (PG_READ_WRITE | PG_PRESENT)
|
||||
.long 0
|
||||
.set i, i + 0x1000
|
||||
.endr
|
||||
.endr
|
||||
|
||||
boot_gdt:
|
||||
.quad 0x0000000000000000 # null descriptor
|
||||
.quad 0x00CF9A000000FFFF # kernel code
|
||||
.quad 0x00CF92000000FFFF # kernel data
|
||||
boot_gdtr:
|
||||
.short . - boot_gdt - 1
|
||||
.long V2P(boot_gdt)
|
||||
|
||||
.global g_ap_startup_done
|
||||
g_ap_startup_done:
|
||||
.byte 0
|
||||
.global g_ap_running_count
|
||||
g_ap_running_count:
|
||||
.byte 0
|
||||
.global g_ap_stack_loaded
|
||||
g_ap_stack_loaded:
|
||||
.byte 0
|
||||
|
||||
.section .text
|
||||
|
||||
has_cpuid:
|
||||
pushfl
|
||||
pushfl
|
||||
xorl $0x00200000, (%esp)
|
||||
popfl
|
||||
pushfl
|
||||
popl %eax
|
||||
xorl (%esp), %eax
|
||||
popfl
|
||||
testl $0x00200000, %eax
|
||||
ret
|
||||
|
||||
has_pae:
|
||||
movl $0, %eax
|
||||
cpuid
|
||||
testl $(1 << 6), %edx
|
||||
ret
|
||||
|
||||
has_sse:
|
||||
movl $1, %eax
|
||||
cpuid
|
||||
testl $(1 << 25), %edx
|
||||
ret
|
||||
|
||||
check_requirements:
|
||||
call has_cpuid
|
||||
jz .exit
|
||||
call has_pae
|
||||
jz .exit
|
||||
call has_sse
|
||||
jz .exit
|
||||
ret
|
||||
.exit:
|
||||
jmp system_halt
|
||||
|
||||
enable_sse:
|
||||
movl %cr0, %eax
|
||||
andw $0xFFFB, %ax
|
||||
orw $0x0002, %ax
|
||||
movl %eax, %cr0
|
||||
movl %cr4, %eax
|
||||
orw $0x0600, %ax
|
||||
movl %eax, %cr4
|
||||
ret
|
||||
|
||||
initialize_paging:
|
||||
# enable PAE
|
||||
movl %cr4, %ecx
|
||||
orl $(1 << 5), %ecx
|
||||
movl %ecx, %cr4
|
||||
|
||||
# load page tables
|
||||
movl $V2P(boot_pdpt), %ecx
|
||||
movl %ecx, %cr3
|
||||
|
||||
# enable paging
|
||||
movl %cr0, %ecx
|
||||
orl $(1 << 31), %ecx
|
||||
movl %ecx, %cr0
|
||||
|
||||
ret
|
||||
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
cli; cld
|
||||
|
||||
# save bootloader magic and info
|
||||
movl %eax, V2P(bootloader_magic)
|
||||
movl %ebx, V2P(bootloader_info)
|
||||
|
||||
# load boot stack
|
||||
movl $V2P(boot_stack_top), %esp
|
||||
|
||||
# load boot GDT
|
||||
lgdt V2P(boot_gdtr)
|
||||
ljmpl $0x08, $V2P(gdt_flush)
|
||||
gdt_flush:
|
||||
# set correct segment registers
|
||||
movw $0x10, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %ss
|
||||
movw %ax, %es
|
||||
|
||||
# do processor initialization
|
||||
call check_requirements
|
||||
call enable_sse
|
||||
call initialize_paging
|
||||
|
||||
# load higher half stack pointer
|
||||
movl $boot_stack_top, %esp
|
||||
|
||||
# jump to higher half
|
||||
leal higher_half, %ecx
|
||||
jmp *%ecx
|
||||
|
||||
higher_half:
|
||||
# call global constuctors
|
||||
call _init
|
||||
|
||||
movl $g_init_array_start, %ebx
|
||||
jmp 2f
|
||||
1: call *(%ebx)
|
||||
addl $4, %ebx
|
||||
2: cmpl $g_init_array_end, %ebx
|
||||
jne 1b
|
||||
|
||||
# call to the kernel itself (clear ebp for stacktrace)
|
||||
xorl %ebp, %ebp
|
||||
|
||||
subl $8, %esp
|
||||
pushl bootloader_info
|
||||
pushl bootloader_magic
|
||||
call kernel_main
|
||||
addl $16, %esp
|
||||
|
||||
# call global destructors
|
||||
call _fini
|
||||
|
||||
system_halt:
|
||||
xchgw %bx, %bx
|
||||
cli
|
||||
1: hlt
|
||||
jmp 1b
|
||||
|
||||
|
||||
#define AP_V2P(vaddr) ((vaddr) - ap_trampoline + 0xF000)
|
||||
|
||||
.section .ap_init, "ax"
|
||||
|
||||
.code16
|
||||
.global ap_trampoline
|
||||
ap_trampoline:
|
||||
jmp 1f
|
||||
|
||||
.align 8
|
||||
ap_stack_ptr:
|
||||
.skip 4
|
||||
ap_stack_loaded:
|
||||
.skip 1
|
||||
|
||||
1: cli; cld
|
||||
ljmpl $0x00, $AP_V2P(ap_cs_clear)
|
||||
|
||||
ap_cs_clear:
|
||||
# load ap gdt and enter protected mode
|
||||
lgdt AP_V2P(ap_gdtr)
|
||||
movl %cr0, %eax
|
||||
orb $1, %al
|
||||
movl %eax, %cr0
|
||||
ljmpl $0x08, $AP_V2P(ap_protected_mode)
|
||||
|
||||
.code32
|
||||
ap_protected_mode:
|
||||
movw $0x10, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %ss
|
||||
movw %ax, %es
|
||||
|
||||
movl AP_V2P(ap_stack_ptr), %esp
|
||||
movb $1, AP_V2P(ap_stack_loaded)
|
||||
|
||||
leal V2P(enable_sse), %ecx; call *%ecx
|
||||
leal V2P(initialize_paging), %ecx; call *%ecx
|
||||
|
||||
# load boot gdt and enter long mode
|
||||
lgdt V2P(boot_gdtr)
|
||||
ljmpl $0x08, $AP_V2P(ap_flush_gdt)
|
||||
|
||||
ap_flush_gdt:
|
||||
# move stack pointer to higher half
|
||||
movl %esp, %esp
|
||||
addl $KERNEL_OFFSET, %esp
|
||||
|
||||
# jump to higher half
|
||||
leal ap_higher_half, %ecx
|
||||
jmp *%ecx
|
||||
|
||||
ap_higher_half:
|
||||
# clear rbp for stacktrace
|
||||
xorl %ebp, %ebp
|
||||
|
||||
1: pause
|
||||
cmpb $0, g_ap_startup_done
|
||||
jz 1b
|
||||
|
||||
lock incb g_ap_running_count
|
||||
|
||||
call ap_main
|
||||
jmp system_halt
|
||||
|
||||
ap_gdt:
|
||||
.quad 0x0000000000000000 # null descriptor
|
||||
.quad 0x00CF9A000000FFFF # 32 bit code
|
||||
.quad 0x00CF92000000FFFF # 32 bit data
|
||||
ap_gdtr:
|
||||
.short . - ap_gdt - 1
|
||||
.long ap_gdt
|
|
@ -1,396 +0,0 @@
|
|||
.macro push_userspace
|
||||
pushw %gs
|
||||
pushw %fs
|
||||
pushw %es
|
||||
pushw %ds
|
||||
pushal
|
||||
.endm
|
||||
|
||||
.macro load_kernel_segments
|
||||
movw $0x10, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
|
||||
movw $0x28, %ax
|
||||
movw %ax, %gs
|
||||
.endm
|
||||
|
||||
.macro pop_userspace
|
||||
popal
|
||||
popw %ds
|
||||
popw %es
|
||||
popw %fs
|
||||
popw %gs
|
||||
.endm
|
||||
|
||||
isr_stub:
|
||||
push_userspace
|
||||
load_kernel_segments
|
||||
|
||||
movl %cr0, %eax; pushl %eax
|
||||
movl %cr2, %eax; pushl %eax
|
||||
movl %cr3, %eax; pushl %eax
|
||||
movl %cr4, %eax; pushl %eax
|
||||
|
||||
movl %esp, %eax // register ptr
|
||||
leal 64(%esp), %ebx // interrupt stack ptr
|
||||
movl 60(%esp), %ecx // error code
|
||||
movl 56(%esp), %edx // isr number
|
||||
|
||||
movl %esp, %ebp
|
||||
subl $15, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
call cpp_isr_handler
|
||||
|
||||
movl %ebp, %esp
|
||||
addl $16, %esp
|
||||
|
||||
pop_userspace
|
||||
addl $8, %esp
|
||||
iret
|
||||
|
||||
irq_stub:
|
||||
push_userspace
|
||||
load_kernel_segments
|
||||
|
||||
movl 40(%esp), %eax # interrupt number
|
||||
|
||||
movl %esp, %ebp
|
||||
subl $15, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
|
||||
subl $12, %esp
|
||||
pushl %eax
|
||||
call cpp_irq_handler
|
||||
|
||||
movl %ebp, %esp
|
||||
|
||||
pop_userspace
|
||||
addl $8, %esp
|
||||
iret
|
||||
|
||||
.global asm_yield_handler
|
||||
asm_yield_handler:
|
||||
# This can only be called from kernel, so no segment saving is needed
|
||||
pushal
|
||||
|
||||
movl %esp, %eax # interrupt registers ptr
|
||||
leal 32(%esp), %ebx # interrupt stack ptr
|
||||
|
||||
movl %esp, %ebp
|
||||
subl $15, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
|
||||
subl $8, %esp
|
||||
pushl %eax
|
||||
pushl %ebx
|
||||
call cpp_yield_handler
|
||||
|
||||
movl %ebp, %esp
|
||||
|
||||
popal
|
||||
iret
|
||||
|
||||
.global asm_ipi_handler
|
||||
asm_ipi_handler:
|
||||
push_userspace
|
||||
load_kernel_segments
|
||||
|
||||
movl %esp, %ebp
|
||||
subl $15, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
|
||||
call cpp_ipi_handler
|
||||
|
||||
movl %ebp, %esp
|
||||
|
||||
pop_userspace
|
||||
iret
|
||||
|
||||
|
||||
.global asm_timer_handler
|
||||
asm_timer_handler:
|
||||
push_userspace
|
||||
load_kernel_segments
|
||||
|
||||
movl %esp, %ebp
|
||||
subl $15, %esp
|
||||
andl $0xFFFFFFF0, %esp
|
||||
|
||||
call cpp_timer_handler
|
||||
|
||||
movl %ebp, %esp
|
||||
|
||||
pop_userspace
|
||||
iret
|
||||
|
||||
.macro isr n
|
||||
.global isr\n
|
||||
isr\n:
|
||||
pushl $0
|
||||
pushl $\n
|
||||
jmp isr_stub
|
||||
.endm
|
||||
|
||||
.macro isr_err n
|
||||
.global isr\n
|
||||
isr\n:
|
||||
pushl $\n
|
||||
jmp isr_stub
|
||||
.endm
|
||||
|
||||
.macro irq n
|
||||
.global irq\n
|
||||
irq\n:
|
||||
pushl $0
|
||||
pushl $\n
|
||||
jmp irq_stub
|
||||
.endm
|
||||
|
||||
isr 0
|
||||
isr 1
|
||||
isr 2
|
||||
isr 3
|
||||
isr 4
|
||||
isr 5
|
||||
isr 6
|
||||
isr 7
|
||||
isr_err 8
|
||||
isr 9
|
||||
isr_err 10
|
||||
isr_err 11
|
||||
isr_err 12
|
||||
isr_err 13
|
||||
isr_err 14
|
||||
isr 15
|
||||
isr 16
|
||||
isr_err 17
|
||||
isr 18
|
||||
isr 19
|
||||
isr 20
|
||||
isr 21
|
||||
isr 22
|
||||
isr 23
|
||||
isr 24
|
||||
isr 25
|
||||
isr 26
|
||||
isr 27
|
||||
isr 28
|
||||
isr 29
|
||||
isr 30
|
||||
isr 31
|
||||
|
||||
irq 0
|
||||
irq 1
|
||||
irq 2
|
||||
irq 3
|
||||
irq 4
|
||||
irq 5
|
||||
irq 6
|
||||
irq 7
|
||||
irq 8
|
||||
irq 9
|
||||
irq 10
|
||||
irq 11
|
||||
irq 12
|
||||
irq 13
|
||||
irq 14
|
||||
irq 15
|
||||
irq 16
|
||||
irq 17
|
||||
irq 18
|
||||
irq 19
|
||||
irq 20
|
||||
irq 21
|
||||
irq 22
|
||||
irq 23
|
||||
irq 24
|
||||
irq 25
|
||||
irq 26
|
||||
irq 27
|
||||
irq 28
|
||||
irq 29
|
||||
irq 30
|
||||
irq 31
|
||||
irq 32
|
||||
irq 33
|
||||
irq 34
|
||||
irq 35
|
||||
irq 36
|
||||
irq 37
|
||||
irq 38
|
||||
irq 39
|
||||
irq 40
|
||||
irq 41
|
||||
irq 42
|
||||
irq 43
|
||||
irq 44
|
||||
irq 45
|
||||
irq 46
|
||||
irq 47
|
||||
irq 48
|
||||
irq 49
|
||||
irq 50
|
||||
irq 51
|
||||
irq 52
|
||||
irq 53
|
||||
irq 54
|
||||
irq 55
|
||||
irq 56
|
||||
irq 57
|
||||
irq 58
|
||||
irq 59
|
||||
irq 60
|
||||
irq 61
|
||||
irq 62
|
||||
irq 63
|
||||
irq 64
|
||||
irq 65
|
||||
irq 66
|
||||
irq 67
|
||||
irq 68
|
||||
irq 69
|
||||
irq 70
|
||||
irq 71
|
||||
irq 72
|
||||
irq 73
|
||||
irq 74
|
||||
irq 75
|
||||
irq 76
|
||||
irq 77
|
||||
irq 78
|
||||
irq 79
|
||||
irq 80
|
||||
irq 81
|
||||
irq 82
|
||||
irq 83
|
||||
irq 84
|
||||
irq 85
|
||||
irq 86
|
||||
irq 87
|
||||
irq 88
|
||||
irq 89
|
||||
irq 90
|
||||
irq 91
|
||||
irq 92
|
||||
irq 93
|
||||
irq 94
|
||||
irq 95
|
||||
irq 96
|
||||
irq 97
|
||||
irq 98
|
||||
irq 99
|
||||
irq 100
|
||||
irq 101
|
||||
irq 102
|
||||
irq 103
|
||||
irq 104
|
||||
irq 105
|
||||
irq 106
|
||||
irq 107
|
||||
irq 108
|
||||
irq 109
|
||||
irq 110
|
||||
irq 111
|
||||
irq 112
|
||||
irq 113
|
||||
irq 114
|
||||
irq 115
|
||||
irq 116
|
||||
irq 117
|
||||
irq 118
|
||||
irq 119
|
||||
irq 120
|
||||
irq 121
|
||||
irq 122
|
||||
irq 123
|
||||
irq 124
|
||||
irq 125
|
||||
irq 126
|
||||
irq 127
|
||||
irq 128
|
||||
irq 129
|
||||
irq 130
|
||||
irq 131
|
||||
irq 132
|
||||
irq 133
|
||||
irq 134
|
||||
irq 135
|
||||
irq 136
|
||||
irq 137
|
||||
irq 138
|
||||
irq 139
|
||||
irq 140
|
||||
irq 141
|
||||
irq 142
|
||||
irq 143
|
||||
irq 144
|
||||
irq 145
|
||||
irq 146
|
||||
irq 147
|
||||
irq 148
|
||||
irq 149
|
||||
irq 150
|
||||
irq 151
|
||||
irq 152
|
||||
irq 153
|
||||
irq 154
|
||||
irq 155
|
||||
irq 156
|
||||
irq 157
|
||||
irq 158
|
||||
irq 159
|
||||
irq 160
|
||||
irq 161
|
||||
irq 162
|
||||
irq 163
|
||||
irq 164
|
||||
irq 165
|
||||
irq 166
|
||||
irq 167
|
||||
irq 168
|
||||
irq 169
|
||||
irq 170
|
||||
irq 171
|
||||
irq 172
|
||||
irq 173
|
||||
irq 174
|
||||
irq 175
|
||||
irq 176
|
||||
irq 177
|
||||
irq 178
|
||||
irq 179
|
||||
irq 180
|
||||
irq 181
|
||||
irq 182
|
||||
irq 183
|
||||
irq 184
|
||||
irq 185
|
||||
irq 186
|
||||
irq 187
|
||||
irq 188
|
||||
irq 189
|
||||
irq 190
|
||||
irq 191
|
||||
irq 192
|
||||
irq 193
|
||||
irq 194
|
||||
irq 195
|
||||
irq 196
|
||||
irq 197
|
||||
irq 198
|
||||
irq 199
|
||||
irq 200
|
||||
irq 201
|
||||
irq 202
|
||||
irq 203
|
||||
irq 204
|
||||
irq 205
|
||||
irq 206
|
||||
irq 207
|
|
@ -1,51 +0,0 @@
|
|||
ENTRY (_start)
|
||||
|
||||
KERNEL_OFFSET = 0xC0000000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x00100000 + KERNEL_OFFSET;
|
||||
|
||||
g_kernel_start = .;
|
||||
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_OFFSET)
|
||||
{
|
||||
g_kernel_execute_start = .;
|
||||
*(.multiboot)
|
||||
*(.bananboot)
|
||||
*(.text.*)
|
||||
}
|
||||
.userspace ALIGN(4K) : AT(ADDR(.userspace) - KERNEL_OFFSET)
|
||||
{
|
||||
g_userspace_start = .;
|
||||
*(.userspace)
|
||||
g_userspace_end = .;
|
||||
g_kernel_execute_end = .;
|
||||
}
|
||||
.ap_init ALIGN(4K) : AT(ADDR(.ap_init) - KERNEL_OFFSET)
|
||||
{
|
||||
g_ap_init_addr = .;
|
||||
*(.ap_init)
|
||||
}
|
||||
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET)
|
||||
{
|
||||
*(.rodata.*)
|
||||
}
|
||||
.init_array ALIGN(4K) : AT(ADDR(.init_array) - KERNEL_OFFSET)
|
||||
{
|
||||
g_init_array_start = .;
|
||||
*(.init_array)
|
||||
g_init_array_end = .;
|
||||
}
|
||||
.data ALIGN(4K) : AT(ADDR(.data) - KERNEL_OFFSET)
|
||||
{
|
||||
g_kernel_writable_start = .;
|
||||
*(.data)
|
||||
}
|
||||
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
g_kernel_writable_end = .;
|
||||
}
|
||||
g_kernel_end = .;
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <kernel/GDT.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern "C" uintptr_t g_boot_stack_top[0];
|
||||
|
||||
namespace Kernel::GDT
|
||||
{
|
||||
|
||||
struct TaskStateSegment
|
||||
{
|
||||
uint32_t reserved1;
|
||||
uint64_t rsp0;
|
||||
uint64_t rsp1;
|
||||
uint64_t rsp2;
|
||||
uint64_t reserved2;
|
||||
uint64_t ist1;
|
||||
uint64_t ist2;
|
||||
uint64_t ist3;
|
||||
uint64_t ist4;
|
||||
uint64_t ist5;
|
||||
uint64_t ist6;
|
||||
uint64_t ist7;
|
||||
uint64_t reserved3;
|
||||
uint16_t reserved4;
|
||||
uint16_t iopb;
|
||||
} __attribute__((packed));
|
||||
|
||||
union SegmentDescriptor
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint16_t limit1;
|
||||
uint16_t base1;
|
||||
uint8_t base2;
|
||||
uint8_t access;
|
||||
uint8_t limit2 : 4;
|
||||
uint8_t flags : 4;
|
||||
uint8_t base3;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t low;
|
||||
uint32_t high;
|
||||
} __attribute__((packed));
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
struct GDTR
|
||||
{
|
||||
uint16_t size;
|
||||
uint64_t address;
|
||||
} __attribute__((packed));
|
||||
|
||||
static constexpr uint16_t s_tss_offset = 0x28;
|
||||
|
||||
static TaskStateSegment s_tss;
|
||||
static BAN::Array<SegmentDescriptor, 7> s_gdt; // null, kernel code, kernel data, user code, user data, tss low, tss high
|
||||
static GDTR s_gdtr;
|
||||
|
||||
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
|
||||
{
|
||||
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
|
||||
|
||||
SegmentDescriptor& desc = s_gdt[offset / sizeof(SegmentDescriptor)];
|
||||
desc.base1 = (base >> 0) & 0xFFFF;
|
||||
desc.base2 = (base >> 16) & 0xFF;
|
||||
desc.base3 = (base >> 24) & 0xFF;
|
||||
|
||||
desc.limit1 = (limit >> 0) & 0xFFFF;
|
||||
desc.limit2 = (limit >> 16) & 0x0F;
|
||||
|
||||
desc.access = access & 0xFF;
|
||||
|
||||
desc.flags = flags & 0x0F;
|
||||
}
|
||||
|
||||
static void write_tss()
|
||||
{
|
||||
memset(&s_tss, 0x00, sizeof(TaskStateSegment));
|
||||
s_tss.iopb = sizeof(TaskStateSegment);
|
||||
|
||||
uint64_t base = (uint64_t)&s_tss;
|
||||
|
||||
write_entry(s_tss_offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
|
||||
|
||||
SegmentDescriptor& desc = s_gdt[s_tss_offset / sizeof(SegmentDescriptor) + 1];
|
||||
desc.low = base >> 32;
|
||||
desc.high = 0;
|
||||
}
|
||||
|
||||
void set_tss_stack(uintptr_t rsp)
|
||||
{
|
||||
s_tss.rsp0 = rsp;
|
||||
}
|
||||
|
||||
static void flush_gdt()
|
||||
{
|
||||
asm volatile("lgdt %0" :: "m"(s_gdtr));
|
||||
}
|
||||
|
||||
static void flush_tss()
|
||||
{
|
||||
asm volatile("ltr %0" :: "m"(s_tss_offset));
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
s_gdtr.address = (uint64_t)&s_gdt;
|
||||
s_gdtr.size = s_gdt.size() * sizeof(SegmentDescriptor) - 1;
|
||||
|
||||
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
|
||||
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
|
||||
write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
|
||||
write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xA); // user code
|
||||
write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
|
||||
write_tss();
|
||||
|
||||
flush_gdt();
|
||||
flush_tss();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <BAN/Errors.h>
|
||||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/InterruptStack.h>
|
||||
#include <kernel/Memory/kmalloc.h>
|
||||
#include <kernel/Panic.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/Scheduler.h>
|
||||
#include <kernel/Timer/PIT.h>
|
||||
|
||||
#define ISR_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
|
||||
#define IRQ_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
|
||||
|
||||
namespace Kernel::IDT
|
||||
{
|
||||
|
||||
struct Registers
|
||||
{
|
||||
uint64_t rsp;
|
||||
uint64_t rip;
|
||||
uint64_t rflags;
|
||||
uint64_t cr4;
|
||||
uint64_t cr3;
|
||||
uint64_t cr2;
|
||||
uint64_t cr0;
|
||||
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rsi;
|
||||
uint64_t rdi;
|
||||
uint64_t rbp;
|
||||
uint64_t rdx;
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t rax;
|
||||
};
|
||||
|
||||
struct GateDescriptor
|
||||
{
|
||||
uint16_t offset1;
|
||||
uint16_t selector;
|
||||
uint8_t IST;
|
||||
uint8_t flags;
|
||||
uint16_t offset2;
|
||||
uint32_t offset3;
|
||||
uint32_t reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct IDTR
|
||||
{
|
||||
uint16_t size;
|
||||
uint64_t offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
static IDTR s_idtr;
|
||||
static GateDescriptor* s_idt = nullptr;
|
||||
|
||||
#define X(num) 1 +
|
||||
static BAN::Array<Interruptable*, IRQ_LIST_X 0> s_interruptables;
|
||||
#undef X
|
||||
|
||||
enum ISR
|
||||
{
|
||||
DivisionError,
|
||||
Debug,
|
||||
NonMaskableInterrupt,
|
||||
Breakpoint,
|
||||
Overflow,
|
||||
BoundRangeException,
|
||||
InvalidOpcode,
|
||||
DeviceNotAvailable,
|
||||
DoubleFault,
|
||||
CoprocessorSegmentOverrun,
|
||||
InvalidTSS,
|
||||
SegmentNotPresent,
|
||||
StackSegmentFault,
|
||||
GeneralProtectionFault,
|
||||
PageFault,
|
||||
UnknownException0x0F,
|
||||
x87FloatingPointException,
|
||||
AlignmentCheck,
|
||||
MachineCheck,
|
||||
SIMDFloatingPointException,
|
||||
VirtualizationException,
|
||||
ControlProtectionException,
|
||||
UnknownException0x16,
|
||||
UnknownException0x17,
|
||||
UnknownException0x18,
|
||||
UnknownException0x19,
|
||||
UnknownException0x1A,
|
||||
UnknownException0x1B,
|
||||
HypervisorInjectionException,
|
||||
VMMCommunicationException,
|
||||
SecurityException,
|
||||
UnkownException0x1F,
|
||||
};
|
||||
|
||||
struct PageFaultError
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t raw;
|
||||
struct
|
||||
{
|
||||
uint32_t present : 1;
|
||||
uint32_t write : 1;
|
||||
uint32_t userspace : 1;
|
||||
uint32_t reserved_write : 1;
|
||||
uint32_t instruction : 1;
|
||||
uint32_t protection_key : 1;
|
||||
uint32_t shadow_stack : 1;
|
||||
uint32_t reserved1 : 8;
|
||||
uint32_t sgx_violation : 1;
|
||||
uint32_t reserved2 : 16;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
static_assert(sizeof(PageFaultError) == 4);
|
||||
|
||||
static const char* isr_exceptions[] =
|
||||
{
|
||||
"Division Error",
|
||||
"Debug",
|
||||
"Non-maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Overflow",
|
||||
"Bound Range Exception",
|
||||
"Invalid Opcode",
|
||||
"Device Not Available",
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Invalid TSS",
|
||||
"Segment Not Present",
|
||||
"Stack-Segment Fault",
|
||||
"General Protection Fault",
|
||||
"Page Fault",
|
||||
"Unknown Exception 0x0F",
|
||||
"x87 Floating-Point Exception",
|
||||
"Alignment Check",
|
||||
"Machine Check",
|
||||
"SIMD Floating-Point Exception",
|
||||
"Virtualization Exception",
|
||||
"Control Protection Exception",
|
||||
"Unknown Exception 0x16",
|
||||
"Unknown Exception 0x17",
|
||||
"Unknown Exception 0x18",
|
||||
"Unknown Exception 0x19",
|
||||
"Unknown Exception 0x1A",
|
||||
"Unknown Exception 0x1B",
|
||||
"Hypervisor Injection Exception",
|
||||
"VMM Communication Exception",
|
||||
"Security Exception",
|
||||
"Unkown Exception 0x1F",
|
||||
};
|
||||
|
||||
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs)
|
||||
{
|
||||
#if __enable_sse
|
||||
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
|
||||
if (from_userspace)
|
||||
Thread::current().save_sse();
|
||||
#endif
|
||||
|
||||
pid_t tid = Scheduler::current_tid();
|
||||
pid_t pid = tid ? Process::current().pid() : 0;
|
||||
|
||||
if (tid)
|
||||
{
|
||||
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||
|
||||
if (isr == ISR::PageFault)
|
||||
{
|
||||
// Check if stack is OOB
|
||||
auto& stack = Thread::current().stack();
|
||||
auto& istack = Thread::current().interrupt_stack();
|
||||
if (stack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= stack.vaddr() + stack.size())
|
||||
; // using normal stack
|
||||
else if (istack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= istack.vaddr() + istack.size())
|
||||
; // using interrupt stack
|
||||
else
|
||||
{
|
||||
derrorln("Stack pointer out of bounds!");
|
||||
derrorln("rsp {H}, stack {H}->{H}, istack {H}->{H}",
|
||||
interrupt_stack.rsp,
|
||||
stack.vaddr(), stack.vaddr() + stack.size(),
|
||||
istack.vaddr(), istack.vaddr() + istack.size()
|
||||
);
|
||||
Thread::current().handle_signal(SIGKILL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Try demand paging on non present pages
|
||||
PageFaultError page_fault_error;
|
||||
page_fault_error.raw = error;
|
||||
if (!page_fault_error.present)
|
||||
{
|
||||
asm volatile("sti");
|
||||
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2);
|
||||
asm volatile("cli");
|
||||
|
||||
if (!result.is_error() && result.value())
|
||||
goto done;
|
||||
|
||||
if (result.is_error())
|
||||
{
|
||||
dwarnln("Demand paging: {}", result.error());
|
||||
Thread::current().handle_signal(SIGKILL);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & PageTable::Flags::Present)
|
||||
{
|
||||
auto* machine_code = (const uint8_t*)interrupt_stack.rip;
|
||||
dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
|
||||
machine_code[0],
|
||||
machine_code[1],
|
||||
machine_code[2],
|
||||
machine_code[3],
|
||||
machine_code[4],
|
||||
machine_code[5],
|
||||
machine_code[6],
|
||||
machine_code[7]
|
||||
);
|
||||
}
|
||||
|
||||
dwarnln(
|
||||
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
|
||||
"Register dump\r\n"
|
||||
"rax=0x{16H}, rbx=0x{16H}, rcx=0x{16H}, rdx=0x{16H}\r\n"
|
||||
"rsp=0x{16H}, rbp=0x{16H}, rdi=0x{16H}, rsi=0x{16H}\r\n"
|
||||
"rip=0x{16H}, rflags=0x{16H}\r\n"
|
||||
"cr0=0x{16H}, cr2=0x{16H}, cr3=0x{16H}, cr4=0x{16H}",
|
||||
isr_exceptions[isr], error, pid, tid,
|
||||
regs->rax, regs->rbx, regs->rcx, regs->rdx,
|
||||
regs->rsp, regs->rbp, regs->rdi, regs->rsi,
|
||||
regs->rip, regs->rflags,
|
||||
regs->cr0, regs->cr2, regs->cr3, regs->cr4
|
||||
);
|
||||
if (isr == ISR::PageFault)
|
||||
PageTable::current().debug_dump();
|
||||
Debug::dump_stack_trace();
|
||||
|
||||
if (tid && Thread::current().is_userspace())
|
||||
{
|
||||
// TODO: Confirm and fix the exception to signal mappings
|
||||
|
||||
int signal = 0;
|
||||
switch (isr)
|
||||
{
|
||||
case ISR::DeviceNotAvailable:
|
||||
case ISR::DivisionError:
|
||||
case ISR::SIMDFloatingPointException:
|
||||
case ISR::x87FloatingPointException:
|
||||
signal = SIGFPE;
|
||||
break;
|
||||
case ISR::AlignmentCheck:
|
||||
signal = SIGBUS;
|
||||
break;
|
||||
case ISR::InvalidOpcode:
|
||||
signal = SIGILL;
|
||||
break;
|
||||
case ISR::PageFault:
|
||||
signal = SIGSEGV;
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unhandled exception");
|
||||
signal = SIGABRT;
|
||||
break;
|
||||
}
|
||||
|
||||
Thread::current().handle_signal(signal);
|
||||
}
|
||||
else
|
||||
{
|
||||
panic("Unhandled exception");
|
||||
}
|
||||
|
||||
ASSERT(Thread::current().state() != Thread::State::Terminated);
|
||||
|
||||
done:
|
||||
#if __enable_sse
|
||||
if (from_userspace)
|
||||
{
|
||||
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||
Thread::current().load_sse();
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
extern "C" void cpp_irq_handler(uint64_t irq, InterruptStack& interrupt_stack)
|
||||
{
|
||||
#if __enable_sse
|
||||
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
|
||||
if (from_userspace)
|
||||
Thread::current().save_sse();
|
||||
#endif
|
||||
|
||||
if (Scheduler::current_tid())
|
||||
{
|
||||
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||
}
|
||||
|
||||
if (!InterruptController::get().is_in_service(irq))
|
||||
dprintln("spurious irq 0x{2H}", irq);
|
||||
else
|
||||
{
|
||||
InterruptController::get().eoi(irq);
|
||||
if (s_interruptables[irq])
|
||||
s_interruptables[irq]->handle_irq();
|
||||
else
|
||||
dprintln("no handler for irq 0x{2H}\n", irq);
|
||||
}
|
||||
|
||||
Scheduler::get().reschedule_if_idling();
|
||||
|
||||
ASSERT(Thread::current().state() != Thread::State::Terminated);
|
||||
|
||||
#if __enable_sse
|
||||
if (from_userspace)
|
||||
{
|
||||
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||
Thread::current().load_sse();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void flush_idt()
|
||||
{
|
||||
asm volatile("lidt %0"::"m"(s_idtr));
|
||||
}
|
||||
|
||||
static void register_interrupt_handler(uint8_t index, void(*handler)())
|
||||
{
|
||||
GateDescriptor& descriptor = s_idt[index];
|
||||
descriptor.offset1 = (uint16_t)((uint64_t)handler >> 0);
|
||||
descriptor.offset2 = (uint16_t)((uint64_t)handler >> 16);
|
||||
descriptor.offset3 = (uint32_t)((uint64_t)handler >> 32);
|
||||
|
||||
descriptor.selector = 0x08;
|
||||
descriptor.IST = 0;
|
||||
descriptor.flags = 0x8E;
|
||||
}
|
||||
|
||||
static void register_syscall_handler(uint8_t index, void(*handler)())
|
||||
{
|
||||
register_interrupt_handler(index, handler);
|
||||
s_idt[index].flags = 0xEE;
|
||||
}
|
||||
|
||||
void register_irq_handler(uint8_t irq, Interruptable* interruptable)
|
||||
{
|
||||
if (irq > s_interruptables.size())
|
||||
Kernel::panic("Trying to assign handler for irq {} while only {} are supported", irq, s_interruptables.size());
|
||||
s_interruptables[irq] = interruptable;
|
||||
}
|
||||
|
||||
#define X(num) extern "C" void isr ## num();
|
||||
ISR_LIST_X
|
||||
#undef X
|
||||
|
||||
#define X(num) extern "C" void irq ## num();
|
||||
IRQ_LIST_X
|
||||
#undef X
|
||||
|
||||
extern "C" void syscall_asm();
|
||||
|
||||
void initialize()
|
||||
{
|
||||
s_idt = (GateDescriptor*)kmalloc(0x100 * sizeof(GateDescriptor));
|
||||
ASSERT(s_idt);
|
||||
memset(s_idt, 0x00, 0x100 * sizeof(GateDescriptor));
|
||||
|
||||
s_idtr.offset = (uint64_t)s_idt;
|
||||
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
|
||||
|
||||
#define X(num) register_interrupt_handler(num, isr ## num);
|
||||
ISR_LIST_X
|
||||
#undef X
|
||||
|
||||
#define X(num) register_interrupt_handler(IRQ_VECTOR_BASE + num, irq ## num);
|
||||
IRQ_LIST_X
|
||||
#undef X
|
||||
|
||||
register_syscall_handler(0x80, syscall_asm);
|
||||
|
||||
flush_idt();
|
||||
}
|
||||
|
||||
[[noreturn]] void force_triple_fault()
|
||||
{
|
||||
// load 0 sized IDT and trigger an interrupt to force triple fault
|
||||
asm volatile("cli");
|
||||
s_idtr.size = 0;
|
||||
flush_idt();
|
||||
asm volatile("int $0x00");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,6 @@
|
|||
.section .userspace, "ax"
|
||||
.section .userspace, "aw"
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
// stack contains
|
||||
// return address
|
||||
|
@ -23,18 +25,16 @@ signal_trampoline:
|
|||
pushq %r14
|
||||
pushq %r15
|
||||
|
||||
// This is 16 byte aligned
|
||||
|
||||
movq 128(%rsp), %rdi
|
||||
movq 120(%rsp), %rax
|
||||
|
||||
// align stack to 16 bytes
|
||||
movq %rsp, %rbx
|
||||
andq $0x0F, %rbx
|
||||
subq %rbx, %rsp
|
||||
|
||||
call *%rax
|
||||
|
||||
// restore stack
|
||||
addq %rbx, %rsp
|
||||
movq $SYS_SIGNAL_DONE, %rax
|
||||
movq 128(%rsp), %rbx
|
||||
int $0x80
|
||||
|
||||
popq %r15
|
||||
popq %r14
|
||||
popq %r13
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
// arguments in RAX, RBX, RCX, RDX, RSI, RDI
|
||||
// System V ABI: RDI, RSI, RDX, RCX, R8, R9
|
||||
.global asm_syscall_handler
|
||||
asm_syscall_handler:
|
||||
pushq %rbx
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
pushq %rdi
|
||||
pushq %rsi
|
||||
pushq %rbp
|
||||
pushq %r8
|
||||
pushq %r9
|
||||
pushq %r10
|
||||
pushq %r11
|
||||
pushq %r12
|
||||
pushq %r13
|
||||
pushq %r14
|
||||
pushq %r15
|
||||
|
||||
movq %rsi, %r8
|
||||
movq %rdi, %r9
|
||||
movq %rax, %rdi
|
||||
movq %rbx, %rsi
|
||||
xchgq %rcx, %rdx
|
||||
leaq 112(%rsp), %rbx
|
||||
pushq %rbx
|
||||
call cpp_syscall_handler
|
||||
addq $8, %rsp
|
||||
|
||||
popq %r15
|
||||
popq %r14
|
||||
popq %r13
|
||||
popq %r12
|
||||
popq %r11
|
||||
popq %r10
|
||||
popq %r9
|
||||
popq %r8
|
||||
popq %rbp
|
||||
popq %rsi
|
||||
popq %rdi
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
popq %rbx
|
||||
iretq
|
||||
|
||||
|
||||
.global sys_fork_trampoline
|
||||
sys_fork_trampoline:
|
||||
pushq %rbx
|
||||
pushq %rbp
|
||||
pushq %r12
|
||||
pushq %r13
|
||||
pushq %r14
|
||||
pushq %r15
|
||||
|
||||
call read_ip
|
||||
testq %rax, %rax
|
||||
je .reload_stack
|
||||
|
||||
movq %rax, %rsi
|
||||
movq %rsp, %rdi
|
||||
call sys_fork
|
||||
|
||||
.done:
|
||||
popq %r15
|
||||
popq %r14
|
||||
popq %r13
|
||||
popq %r12
|
||||
popq %rbp
|
||||
popq %rbx
|
||||
ret
|
||||
|
||||
.reload_stack:
|
||||
call get_thread_start_sp
|
||||
movq %rax, %rsp
|
||||
xorq %rax, %rax
|
||||
jmp .done
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue