From dd168ab5fd27b5caddd9ae0a3d0113ce22bae9ea Mon Sep 17 00:00:00 2001 From: Arthur Beck Date: Sat, 25 Jan 2025 18:22:32 -0600 Subject: [PATCH] continuing to chug along, working on arch-independence --- kernel/Cargo.toml | 4 + kernel/build.rs | 4 +- kernel/config.aphro.example | 8 +- kernel/grub/boot/aphrodite.kernel | Bin 34544 -> 47808 bytes kernel/grub/boot/grub/grub.cfg | 4 +- kernel/kernel.flat | Bin 34544 -> 47808 bytes kernel/src/include/_entry.rs | 115 +++++++++++++++++++++++++ kernel/src/include/arch/x86/egatext.rs | 47 +++++++++- kernel/src/include/arch/x86/mod.rs | 17 +++- kernel/src/include/arch/x86/output.rs | 80 +++++++++++++++++ kernel/src/include/mod.rs | 2 + kernel/src/include/multiboot2.rs | 25 ++++-- kernel/src/include/util.rs | 17 ++++ kernel/src/internal/arch/x86/entry.rs | 22 +++-- 14 files changed, 313 insertions(+), 32 deletions(-) create mode 100644 kernel/src/include/_entry.rs diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8f11a11..c7c29c4 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -17,6 +17,10 @@ panic = "abort" name = "entrypoint" path = "src/internal/arch/x86/entry.rs" +[[bin]] +name = "main" +path = "src/internal/main.rs" + [lib] name = "aphrodite" path = "src/include/mod.rs" diff --git a/kernel/build.rs b/kernel/build.rs index 3d472c1..6d198a5 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -4,8 +4,8 @@ fn main() { // Begin checks println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_DISABLE_MULTIBOOT2_SUPPORT, values("true", "false", none()))"#); - println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_HALT_ON_PANIC, values("true", "false", none()))"#); - println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_SPIN_ON_PANIC, values("true", "false", none()))"#); + println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_HALT_ON_PANIC, values("true", "false", none()))"#); + println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_SPIN_ON_PANIC, values("true", "false", none()))"#); println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH, values("true", "false", none()))"#); println!(r#"cargo:rustc-check-cfg=cfg(CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH, values("true", "false", none()))"#); diff --git a/kernel/config.aphro.example b/kernel/config.aphro.example index 387c61c..adde185 100644 --- a/kernel/config.aphro.example +++ b/kernel/config.aphro.example @@ -1,5 +1,5 @@ -# config.aphro for aphrodite devel-2df6e24-out-of-tree -CFG_VERSION=devel-2df6e24-out-of-tree +# config.aphro for aphrodite devel-6a2a677-out-of-tree +CFG_VERSION=devel-6a2a677-out-of-tree CONT_WITH_DIFFERENT_VERSION=false # Begin metadata @@ -14,8 +14,8 @@ VERSION=generate CONFIG_DISABLE_MULTIBOOT2_SUPPORT=false # Panic behavior. When debugging, generally halt on panic is more useful. -CONFIG_PREUSER_HALT_ON_PANIC=true -CONFIG_PREUSER_SPIN_ON_PANIC=false +CONFIG_HALT_ON_PANIC=true +CONFIG_SPIN_ON_PANIC=false CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH=true CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH=false diff --git a/kernel/grub/boot/aphrodite.kernel b/kernel/grub/boot/aphrodite.kernel index 048f87c99f2f27c6eb981629d9f55d4e2b9230ed..88044a2e540cbc611c51b0b2157cd9053ed801c9 100755 GIT binary patch literal 47808 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

t2(bNlDS!{tp2VKRZ->Gyj) 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 HL&yIGmE2tJ literal 34544 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

t2(bNlDS!{tp2VKRZ->Gyj) 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 HL&yIGmE2tJ literal 34544 zcmeHwdwdk-x&Lgkz!Fwx!Ki7Cy6S34g|M^t#dst!7qAfmgo}8IdnE~*i@UP~TGZeq zHN&_)Ry`iir?$1WR?j(Y#oD5^?PGeP(txgV`k}=X^fD zf0z%OdFOq<@AJOT<$c~~-kEo}y{5iar_(X&QNR>3bQL!+j05MrElP=lF*Dro7}|7^b9L`E?gC%y#_7XUZGi=Yv+% zw}K}9jL4@S(Wl>hl;bq)kn0c2J)Ln{0;eT#S^}pfa9RSVC2(2-rzLP&0;eT#S^}pf za9RSVCD8YFIxW1OPFK82KYB$H?fdDKg9FIg+qjLZTYc&$=Y6X8z>7 z&K5rK6J5SCaMa(o$ozor+tqKLp=EmFn=qaqS(-#Txd`Qu9Y^Hg(FCxqD z>V`#Em^1K<3B=o5+BzelHs~@0Q&vpZ9EwIeqBCLLxDEx)@%CuQ7ijkRBSbS8n!2E_ zs_Z19$jC)-YE50GxiPegFRMg<+B)T4@Ev3rWG6_)0#QhEYFA4nV&#;Dp|9}U{SmR11}UocSEZ>Ljf=ZwS-_hp&&22fsCPQ2LgEp zMa2PMBm!Pzg-e-MbogP|F5z9j-DFsEIvWWElgBnGqYXjgNVW^=UF zhP=pf9}h{AACbL`LRD@yhRo}0D;q28Z!}*o4=bFjYpNF2;XG${+s zty8BKSyw@coYiKvTOC%X)n#>CJytJg2$iB zZl}lTby;1U%jUAX94@EJ<#M|`F0b3_=G->7-R*EY-7dG=?QwfORuAW~dF&pC$LVo- z+#Zj|>qQs6Xxgky9np}?QnTcjP=B4C4;WGa@bi8B~qVWWkiC%&0HA zdPbll8j^Ekd~`;q56&Xm4x^?7yaqXnV9UyuV94xWZT@a3Dtizz%c!q?dB{xeA{L?f z5m}i*u9RSY&X^}y(VbU#1(xLHqd0TceRlz)TVv3jXVmNaGLI7WG%QF`F_!U}DUbj9 zTuM{D!A1D9NPK_clEuP%N7jDShf5iF)7q_tQyE75$!z3YDf(Y^vAyR`pbT32PyFD4 z7t-n0Z!Qx*P5$L9hLOHcxsoZd#!@UEO-?w2VOoopA=Pju!-St1WY!n$OZ?!sRHt}Z zGC?)&C&C&02H?e`RAe5)XJ{IPqbBx_zv5v+p+r?cN%|utgl&d|uoWc5YKti$yrQJ6 z3E>?jRgn-LdRZ=Anh@T8QBF0Ar53T?;*lnxl2NMDnN(^?emVtx;=#iK z@fcm7PaQjej?smex?OLvwi@NjZNQ2LB;H~a4^r_Ih#*mKDF)+oIY~iRw4Nzn{s&P; zsnm!DQT*PdlhA)dF|vgdhOWz`o+o**pnEO77tQH~qXzblH}O0sQD+bZdd@TW1hk5x zrL7ZW+9|Y*%f{Bp^5t995t#`zW~9ct&=}ZII`(Hmms&Df8T2W%(2R-gS&ECn9cW8w zxR~v!r5r1Agf$kunZ09tA;Yj6>dx=&Cke7WcakQK==deVkpksv@EqtW+wpm7$I0>r zkKm6|1fBkX+Nm@2_Om_n@hr-_oxO7|&WSpcD3C1=K8ru>hOSH44NENShIyAt)s}`! zk(Zvhf8Za6i5myrF-_bBb=Kq8kUCSUGow%Si8>2TH-Iuu!F6?|ebtsp>*_AR$ssK< zO7l!oT``JombywH2M`U4f(2y7d#BQ0X&U|__hwPB()E+{+zr>pKA{4sKH;t}(`j7S zi^43We&a6WT`j}kRP!F9Jg1zujq4^A-t9mnr_sm|UKu6Tr7E&hkMv!m6g91@Dv_=!hB+I7s$e(JATzNWs=k4cajJZRZFX3=1%M zUIW`R`#2g9?jF&aA0TUf71~Jt<7{e)0Ud4!x4R*ED~V$bydX*JyCnIE%aQ?12iA>4334@5_06skhP=sxoz1L-JLc+dxFP0*;7mSuEIArN=mC!ooEM z^s<|T@uZplN*ke!hQ-M<;0Wb99-?(hw=fHc$KVv5)q>m|M z+_L0+x%ePmS(YWwQi^x(+=&=53289buicb9AIhAsmnx0IF&)qL?vAs){qad$|19oM zYJ6`$U&Zxv={k(nnj>uWe(|Vyu$5`mC9UT$%yr9p`r{WQq_xVNmz<9IfB;Dg5}66; z7&;_PlyuT9`qWc$qf6KJ$IvLZyZdQ*MvZ?*y4!-zi0H-L^1MCYAl*$aM@-7U}D z^G(v-G7&X@o4{t`o6ci-(&ke=ziYXw^zTU`*;z%;RHyqRonWijRlF->FzIB@ujYp zQf{iMQ>t!Ms&39!eYbm)TdsSE-S8$`_4mFTDhlRRu;PnduLuJt64gfi|4^G{Cldd5 zE_x&xEn;eMLu)~*qM|4^skPubqZQ5RQzrJQo*Jdk)zs%oswx~P6h1P737U@dg2e(^ zS0N5gC4UTi5H=gvZYJs6)O7ugvap1YJgPOkaUSH`q#@L=*F{AW);uYW?9@}PT|Y+0G^{4{Uz$n6<>`l;p< zy7;+LExPk4bw@Wpy;A9pQ5+Dr3!6Q%sOZKDVtrAS)o;E@PBLgQF!-ZqNX{5N`mK80 zS4_OAwWtY$XNquQVpkcr`|#oJXRUq3j=8B1*{8rmSgbTidW+1+e7&fHDAC5IgwTM_ zb35i>bT=d)zy!K*af7&BZV|U}6=-p5wWR>fqA|!-X{_M7CyT)5Il|F`u1jHyhdyOD zyezk8oRj*1-B52SKzz+UrMFB3Gy0p*&i8N5v{Q)tzJ_EuwM6U{53%%x=x6!*3kH_T7&f}apJn-%UlGp!bbyJrx4E*AHYo9I2nPgtj`+&4G% z`AwTLuBmD5wrkM?XvgS{8e``q?xYz?4nURJX^tVCs)bff-YQe<+{B$_Clw<&J&{gb z#y(zUC@f$%EGQoMu#kOx54(Z4lni`eU^n3QcH-f%SWzHcgprUL2PLX*cO$w3?P=^& zvkj!Tl3G2!0++e(C^ z0VQ8U{-&UdXC-DGwB_eI3g&n8~NSvaO=g-4M9(IOE28Rcy(NZGpw z*zThR?47^KfPX223%hQUfw!*_jurBho&-X@a;3tm!KY}h?I^_!B{?q$yLEiSqO9wR zUzQNYDXs>YPtY*LsG3ZgPj~9mRt2dzffCsXB!pQR8y*iEK7@%_EViW1_%nk$;MBRT zg``04<84qTdcdR%f5uy%1ep}E=;5hMlHMW~h^=~rf1s0i(n^2T!0LCfY?{5`W>iC* zgW)4ZEEZUoIe|rvcSN03V@9-xNgmPIg8Auq##JFY(2M1Lwfe2Kve|)bn$7B zhF*5;XGpWniF>EW@=8sTFDBE4Zb7=UG(8{g+i1ynGG$^>W?`8X%)z%IhL!HJJP5f+ zJruzk;UKW7#HO0(`!)Wkr+toyn=>k3C$ zaevp#!bVKH#d)UPWDr^edqaK9IE+#>r>g2FSm}^xMT$pU4rc(lRp_?U02gVcsMUA{ zW@O%p*aK6u5fhq;+<)tYhQ&*g6TZNp-As2NCPZHsf{d}UPCQbUT={IRxGi;<%x0Bd z>@)~wCUq8yY4xc@a#+Oi;$nk1S1)WXRVOKNkNASxry}$zA^nhMH3L@bjzQ-XpYeJe;>m|-yw_Vw zM6R{qCh-;A$plbP1buVbrUpy0MJVsVgg9@9fS)8doFZQJy8AHk$d;x2KUZX--3xdKy?ZVT49 z`0q)J^<^Kf_B z*#@LQGX&fFH`GR+gMJw6#Y@tQ$^G^8Ba*Zu*aZ!Y>M!I9=9F7%m+1{^sa>d0qX?Rx zKv8B%id9K2lF3c!|Iyq5UDC|96wq1OsY>V)v zKE~>vO-_|Xpo~Is-@v{C@e|>ng}Uv+`^R-#pjy4SF{uYe)LV3)h=aKEJp>jC5hCqx zLd>`s`A_#lDBi8l&Ig8OZ69+#O;S) z97s+W7#u%wVC_5dbF=4!P5RW^!d6r2Wq1!G%-m4+I?PI+&_5t|WZ%L&e zMO$>Aw|uTch$jARU?0usy8Ytw!uy46&vJ-h;Qa#bQ18e5#l5@v3#6ICM!opO;g<$d z69zsRKk@Cg|CHN1A#6FGdgBj7Q#^Rlvjh7K7d@}r)ABq~9T+s}whz7sul5!9d?jXR z^vR^C(9{5&Lm}08L2W=!1a^QR`g;}_3|mLQ&g5z6vk=n zl(HL=Fv4?%FLfA2uc)J_NPK12$5p#N6pJlVB?YQ_>6TJywYlb6{v5QSUz}b-&7a4VneD!++xr%}rE0vGAAX&mqGtMNLb(fymW@-n*$n zOl?;vsjFHv)kLx{&13J@^u~>kkbp{ zoz=|o)aqW*Kz@0fj^Z)|Vg|7wA;`WNYq!@J#3}?v`=LGAH|tspmWq{%Z=P=uD~*YJ zUqe~495|AAGnJtlLrFEyEZk^JwWtTW^WPzO>; zmG$;(E_su8%gUneih}-I$8$SU`uH(%SMLtCW*1wNge#aUCZ!HNVi^p^jaV1sp{cSh zE3ZMc^IlU|Z>dzY+7{8}tOe_FI4-K9j< zbnklOCfq?#jTC&iI4C>YWGR*xFqNg=&f4FPu)YET8Kl`XV;Iv`IVv_>zlppkDGcND zEUd@Ir*Qp3#V&@uuU~51rH2jAlI>XdD0>ImKudQ*5@ts+e{M}B?%hu-#J@iRSCLsI zz>3~E@}l2k6!Tq{zN%H8DQ>eirVt6Gb0!sFX|vFjso$$oL(XTtI$ zj5zPO=-yhV*9ps^$9cxEu-wE*vkf%gNc7hz&7!9scyc?1ZYAJG#8L~EpKrwdaReM-c>F|W*+CZVDFtyS2j3tP?|QA+ww>GDm5{6 z(O(b0ba+!Yj`%n{rB3aHxGcJ3WhaO@T3noLq;BbsAq|Gjq}xU?GcCcNqr-euEgrZW zZU=ts3f?l6{r8QAjpFBQ?-@`lDCxU#i!Tg-CEd{lh+i8Tl7GYnO|opyAjSnKH9{PX zsc%CXB@kHk)@sX*I!O5jUA%}&A;6_63d!V@_j5i{_BpVoZV$tkw2~E?@he7FvAW?aXmd(KAR*6Ro(Y`$3@ z9^$U#N;r+|NciCdIY2VfFi4Wmy>1z9H&g?Rq4Wv_CGoQt5MX}tZ9p(xVU_v5ej=N> zH9;Q`Nn!b!d(-;{Z}b>1;0v!#eFk+OL93Zr%^^Og(mfA!*Xz1h>tTaulPSC^#hF93 z#rkzMPlI&6-6(9m- zD54jChwEcK+OjaZ{8zhg^sh8J=OO(Nn<=|`p z%2J}IB1;HZK(O5KN zd>b(G99UY9z|TeSbn?S=moeRCNE$H~uDcE(Bl=?k;6oJ8N_Xkg?&N;lNy=r#C@aAm z3{bame%i4$-MCc`dK3i-TldyrX&NpRPYc0;vS8k95MLyIE(Bryz&{GbR}pEZ`PmY2 zv!A&6riPpST5jUkCTi$T%m5=ZHl*WBdJ)zP48pb}2)NKU1HVCbGWsiUs~H112o){A40?;cM$E)-umw6Tyc^&XCuV$DHh zg>8Cai$3*<=E8e8dO-JvxDEFrWbwMg-V-syx}v@_YpYV9$qWy!L?_9XWhYq*`IUgz zVlM4H%1>c8l>Q65q47$l&hrw#uk0o9HKoo@)PWQr8|HiX(TiL6;CBIje*7-NZ#{nB z#!t;}&g8#=^TnF{TQd1{lT)h6Uzy2Q>(e7oyQchyNKeDB1eSfCCja|LQ~PSYf6rXo zfz$T0ANiN!cMbBj_(wB%wf@Jro`s)!O-mc}_)fjXT@JHUbA2AJ>onJ;U%95RfXXLh znxF*)l};J*;X<<4m*5(!jLhZ0Vc{T~KL=^buR#73P5%2x6OIl9$P9{^-^g#&Deu(L zwHpmSj0D!P(^2LLZqGex$rxJYw8d6WiEbAV8V>^?Ko2& z*pQDO;<_M{R{M9?Wel?kMshZWR2$~IILhfUgB$06;2IB2nDY(t=K`27a7}ijJ?dN9 z@h0+2bA5fane&!&LmEUrVL{_#{-aF4o%chogFZuC_~FD-8Ltk_1c zciZSiY#Y7A?4$3Pee@P-AH9(6qqj->=p|+!y~G^WQQMef^k(T8yT>+2R?Q-}X z)^co7`l?H?j#S6y95N-aHhg*aDj|i$2$h@yu%#J$ozPR+xp0T8zMlWl74kI72o#7GOi2AX6$H31RY{xMM8+B@Y3q}(M zryFW+lJ}Z6H3y?ZS4=r=$yEN9H!Yj%@-$iM8!fb(t-QQL-eOr^u57iW{6)$~Y{j->hb`!Louak_)lROA9iP^K!;P(=cCXv! z*NWwwr4%FO@)5cpnb~8FVJEDBdiCcGn3_W*S?e>5_ z9B^?Vx7`);pSrR;r0m+|oV7z6!{^{b1e~=i3IR1?_hb}Q5le)F*!SvpIKAGm&7q;{ z;$%%{Wm;aI+4Ea2Z$bW=%6GZLF00QT2>3#cHtrH$vBQJ}F|vS;k+lnZ4AYOL7q@VRZFpu-!$ zZeCl!s%b={d{udQ|S@rf;7Nnn{e8K zR=W*LHsOHN=dqug}9*8WwnOvR+lrZ zRrE>LWQQF&6%)>BQ7y5~h;OxQOZjZd?RC-Ka!(jbU(Rqipt606PAlDSQar2G6LbaL zHqPM=fpeQzt*`P*hVQNIpzlXuV>4&#^aY#fTMNshz6dxCdF&z1XLC71K@P^PH9wCs z_?3A=9-D9)7@=3gZ75U7Ni&I3$VYuGe5|~ju6%q4;Bzu5Wp=^mIA}hwV^_SDvxVJ$ zH}>Q^N7rkcGJZOvp>WG85DVJ54arx=-LBDzbc@wN5~WGXp%>ZI|?PXw1q&~Z}VE+u8_|Q)APC9S~+;!Uv=#Y z?_@r6@aYc7YsVXoX2NHK%jCpOin|?-6{7Ub+NeZ z_gW#0P}mXRf>~EVqedCer;Khg<9aB*abXSJ5yJNyTsChw6bh<+A$p^amV)jeLM6M+ z>$AcTPL93Q$;`7n#0N5?DH!mDZEl|@Hv}o`a@1@z!~(q{fN&&Pjfpaw{p<7P^HD%IkLe>{yNo+BuKk zscqJ6m)o(#oqp7aJA*mGaCz{Sm&=VWq_0Vw)B~k;wB-!l5?9=|A5o~y;S6}Jc9%Dx zo%LnUrHp77zQPn}hSOKhP3^vxl{9L@4hJS2gfI>tzGQMzK$i7;9#s<@CkOKj`Di@A zbJlQ6C=zVyYT=tfhzkY4c-U^oVuHhak{kTLQSDbkRro_hC{8XM2s@pjp&4E~**Rsq z$k>E3M+W`Q5Q0M+ed{F*p4HJg+=lTnE_{GzxD9&)h)o>fup@}ixOlu;KgYT89jwll zNGRGwBEvU=Vs^+|mVM`Hl6DYZf^pd~1~?ywI7w}tl+w9+=(g%qgxWem$Yu|FJpN!9 zYd2oZSXrk)m9@#ujpVu0!DYs!&L&ir# z_%cvO`PY3Ihb5)4RO7JdFs6D~L>N=OF|x;D5oS#7;Wm9t^~NesEc}e6Jz88CQ#~x0 zjH%uj^cKunJ=n*{pM8w{ImT$8w3Iv+JvhfW{+wf! zi*t*BJHg8sqqLjdA?B#;6BZZas|nh$FW+N3526RvEDxH`aQ&wJ>sfxwSBI zz1*W{#Co>eRyK0IJp7Hcc5lmLE+a1B+dR4b($}b%d*F^((N2$na(Jc@D>}y7RVRgL z%RBh;&Infd{jnG=U!4@%Eoek&=MDKho)F!5IP7kl&s$TDF_8z@+-Trn zVKnejb_UL7G}Pf^Mgxb5jRp?G8U>sJvC+UWGDic4qZ$o-bds^MR=0;CrJhwYfzL&ec&6jYoKX>3TA--T6yD(*E6 zt_|rn;IJVvd#Jb~Bq&Y%(tFO|11|GQt&CN16&l<6$yfV)T~hBKAX@sI{byO&o0hkw~a#l57#(FZ=(07QCG4;4qB zd89Pp%pekjz+J6^a8_|%4Q?91^f5nrUs*j=TttKO0p|mb-Z57X757sO?mpn|1CHLI zRu2`oTZ4NPxJQA@WzXX0NO3P;q*^)2;Hs0>=VJ@3gCjigRmlWx$mIH&q4U ztm0ZVxcR`%&x8B12Dcix)xaSud#LnY)ZlId?l#~oS=3Owh2w|#co?{cfuk*D>Y>te zX>h*+?pMIosvw+IT#E*G5V(WD(L3|%q2eCY;AlU`MsdQ(SCr-fy<@e0=y$n zw4qvge?SFrx%BJQ;O2tfT;OO|i+ZU2Tdu)jBO0?BxLkJd9~#^pz}*2H_Sj|*mEK=9 zxCC&CJoLWI;HdBS19v}gv}Zs)h#s<;BJ2%N`K5gg4+95LXAiaAD>b;^1NVF2a>=Dr zgWCq&wmi6>YjCt5;&tG1>35F?NBbm>0GDh0PJ~dYuhgeAV6$fcRFdUFDFZ3yoD7ci zdns_20tZoL50&3J8r(GCrsct{(ctXB*@07O5K%R~UW0Q0=KwC3y}Xyf5g)VLjexSkK z58VBE=xx;C9survJoFA}a6ba>M|tRd8-qdRgM8PIfm8cLomA5|X>dOQ?k9QZ-J`)h z1l&V;=xx>Deg@pn^3Xe`!TlV#pXb4qV-Tu*JObP!z-`Y;d590c2G<7P)rK!WWtr3@ z@7LmB$ZZHV;yLI&tHIH}pnm{IdxO+NmEQ>sj`j%J3~p6&C&j%^D)>E zI%N;S(e&~5Ey_=qQHtA|SZZ4GWcaO;8NRS?c9?m`6fYX2!o?>TT* zOx|j{D`6-rr(xe%4qT@O_Y2^D0h}`j?zbA;>%hGZT(0>3um<-TaGwG9`yBMV7#M2* z{_}i>`OoRO+x?yf_jlm_4&0I)^tNblPTUwd!R)>qI4f*eZTAvPsF%#hJuh@>a8BTy z!1Ytb%%LV9*Wf-kGtB2!#5!sruxj!$j0sB9__!Jl<7yl3X^>VA6&KUs62K*Z%jK_r zrNI?oOcvM~=312)&MLhRG`M-d%>xc<$sQ`M3S&|2-wNPX00)s~4;6Qx26roPw*t2; ziyCTop9c3TaIXUQUpa8C7_(}-YvD82IvD1v9JpUK8k`*fZ17^P$fAb$eNlsJ2d*8sT=(Zw;E&aIX>aOXz_sM? z8{Hb*=Tl)Z9K@JCR2+T5Ud07~3jlYs zS_rh7{8)pd{k5BBV$CE=Z3uTGe3?p*_TZik;q6h0Xz1Oi!KJ`Js)Au6Iq2=t;Fgdt zxDub`$bp*z=cKm#7I1F?H!cV6LJjU6;NAfaLp^(_UAj$x)#su2xCYk7~D?oJI(j{&5I6VGL*f6?HIfh*2~JFLN3 zfV1Sm%`gw~;Q-E&2e({J$dLorNP|?+lVG%ddd@RUX_;8r+|N z`%@m=ziDtgf!mn}_nZdz7I1In!Hq+Vq4w`1;6BQOyHbNY4&3oPxVQ#qL`-L#mD`?w zt-+N7SDFX+z6Li9xM_KC7hqCW`KSP{A`fnf23HSUJ#YvGvWGe^(Ek;n(hC9?1kRL2 z4b4Y8G&mkO9=Kd~Ux1iOrMCvSHF@Y=p~2k+++DyKbF^Cx9JN!3-vhus0G!%)f~)DM z2KV48a1Uv451j({f(G}CQ{YZ$aF1nh#J_r|{hN+hPUT~L2B(#un!Z7Ud-@dZN*dgs zPJw$ygX=#9Zcu~UoWbSdqXhS{Dj(ZVfveNtp3Q+P$Oveq2DkGR{d+`%d+`*wmo>PT zPl3}-9pdA)Q{WmjxVKJ$`zdfFFF@pj?DXDDnod+kzsG, BI: &BootInfo) -> ! { + if ega.is_some() { + let ega = ega.unwrap(); + ega.clear_screen(WHITE_ON_BLACK); + sreset(); + + let extended_functions = crate::arch::x86::cpuid_extended_functions(); + + if extended_functions { + binfosln("This CPU supports extended functions", ega).unwrap(); + + let longmode_support = crate::arch::x86::cpuid(0x80000001).1 & (1<<29) > 1; + if longmode_support { + binfosln("This CPU supports long mode", ega).unwrap(); + } else { + binfosln("This CPU does NOT support long mode!", ega).unwrap(); + bdebugs("Long mode CPUID: ", ega).unwrap(); + bdebugbnpln(&crate::u32_as_u8_slice(crate::arch::x86::cpuid(0x80000001).1), ega).unwrap(); + } + } else { + binfosln("This CPU does NOT support extended functions or long mode!", ega).unwrap(); + } + if BI.bootloader_name.is_some() { + binfos("Kernel booted by ", ega).unwrap(); + binfosnpln(BI.bootloader_name.unwrap().into(), ega).unwrap(); + } + if BI.cmdline.is_some() { + binfos("Command line passed: \"", ega).unwrap(); + binfosnp(BI.cmdline.unwrap().into(), ega).unwrap(); + binfosnpln("\"", ega).unwrap(); + } + if BI.mem_lower.is_some() { + binfos("Amount of lower memory: ", ega).unwrap(); + binfobnpln(&crate::u32_as_u8_slice(BI.mem_lower.unwrap()), ega).unwrap(); + } + if BI.mem_upper.is_some() { + binfos("Amount of upper memory: ", ega).unwrap(); + binfobnpln(&crate::u32_as_u8_slice(BI.mem_upper.unwrap()), ega).unwrap(); + } + if BI.memory_map.is_some() { + binfos("Recieved memory map from bootloader with ", ega).unwrap(); + binfobnp(&crate::usize_as_u8_slice(BI.memory_map.unwrap().sections.len()), ega).unwrap(); + binfosnpln(" sections", ega).unwrap(); + let mut i = 0; + for ele in BI.memory_map.unwrap().sections { + binfos("Section #", ega).unwrap(); + binfobnp(&crate::usize_as_u8_slice(i), ega).unwrap(); + binfosnp(": ", ega).unwrap(); + match ele.mem_type { + 1 => { + binfosnp("Available RAM", ega).unwrap(); + }, + 2 => { + binfosnp("Reserved by hardware", ega).unwrap(); + } + 3 => { + binfosnp("ACPI information", ega).unwrap(); + }, + 4 => { + binfosnp("Reserved memory", ega).unwrap(); + }, + 5 => { + binfosnp("Defective", ega).unwrap(); + }, + _ => { + binfosnp("Reserved/unknown (type=", ega).unwrap(); + binfobnp(&crate::u32_as_u8_slice(ele.mem_type), ega).unwrap(); + binfosnp(")", ega).unwrap(); + } + } + binfosnp(", starting at ", ega).unwrap(); + binfobnp(&crate::u64_as_u8_slice(ele.base_addr), ega).unwrap(); + binfosnp(" and running for ", ega).unwrap(); + binfobnp(&crate::u64_as_u8_slice(ele.length), ega).unwrap(); + binfosnpln(" bytes", ega).unwrap(); + + i += 1; + } + } + } else { + if BI.bootloader_name.is_some() { + sinfos("Kernel booted by "); + sinfosnpln(BI.bootloader_name.unwrap().into()); + } + if BI.cmdline.is_some() { + sinfos("Command line passed: \""); + sinfosnp(BI.cmdline.unwrap().into()); + sinfosnpln("\""); + } + if BI.memory_map.is_some() { + sinfosln("Recieved memory map from bootloader"); + } + if BI.mem_lower.is_some() { + sinfos("Amount of lower memory: "); + sinfobnpln(&crate::u32_as_u8_slice(BI.mem_lower.unwrap())); + } + if BI.mem_upper.is_some() { + sinfos("Amount of upper memory: "); + sinfobnpln(&crate::u32_as_u8_slice(BI.mem_upper.unwrap())); + } + } + loop {} +} \ No newline at end of file diff --git a/kernel/src/include/arch/x86/egatext.rs b/kernel/src/include/arch/x86/egatext.rs index e97f0f2..9214fd3 100644 --- a/kernel/src/include/arch/x86/egatext.rs +++ b/kernel/src/include/arch/x86/egatext.rs @@ -14,6 +14,8 @@ pub struct FramebufferInfo { pub height: u32, /// Bits per pixel. pub bpp: u8, + /// Whether to change the cursor position after outputting text. + pub change_cursor: bool, } /// Returned when the provided position is invalid in the X direction. @@ -29,7 +31,7 @@ pub const BLACK_ON_BLACK: u8 = 0b00000000; impl FramebufferInfo { /// Writes a character to the screen. - pub fn write_char(self, pos: (u32, u32), char: u8, color: u8) -> Result<(), crate::Error<'static>> { + pub fn write_char(self, mut pos: (u32, u32), char: u8, color: u8) -> Result<(), crate::Error<'static>> { if pos.0>self.width { return Err(crate::Error::new("Invalid X position", ERR_INVALID_X)); } @@ -43,6 +45,10 @@ impl FramebufferInfo { let base_ptr = addr as *mut u16; (*base_ptr) = ((color as u16)<<8) | (char as u16); } + pos.1 += 1; + if self.change_cursor { + self.set_cursor_location(pos); + } Ok(()) } @@ -56,30 +62,52 @@ impl FramebufferInfo { } /// Writes a &str to the screen. - pub fn write_str(self, pos: (u32, u32), str: &str, color: u8) -> Result<(u32, u32), crate::Error<'static>> { + pub fn write_str(mut self, pos: (u32, u32), str: &str, color: u8) -> Result<(u32, u32), crate::Error<'static>> { let (mut x, mut y) = pos; + let change_cursor = self.change_cursor; + if change_cursor { + self.change_cursor = false; + } for char in str.as_bytes() { self.write_char((x, y), *char, color)?; + if *char == 0 { + continue + } x += 1; while x>self.width { x -= self.width; y += 1; } } + if change_cursor { + self.change_cursor = true; + self.set_cursor_location((x, y)); + } Ok((x, y)) } /// Writes a &\[u8] to the screen. - pub fn write_bytes(self, pos: (u32, u32), str: &[u8], color: u8) -> Result<(u32, u32), crate::Error<'static>> { + pub fn write_bytes(mut self, pos: (u32, u32), str: &[u8], color: u8) -> Result<(u32, u32), crate::Error<'static>> { let (mut x, mut y) = pos; + let change_cursor = self.change_cursor; + if change_cursor { + self.change_cursor = false; + } for char in str { self.write_char((x, y), *char, color)?; + if *char == 0 { + continue + } x += 1; while x>self.width { x -= self.width; y += 1; } } + if change_cursor { + self.change_cursor = true; + self.set_cursor_location((x, y)); + } Ok((x, y)) } @@ -107,4 +135,17 @@ impl FramebufferInfo { super::ports::outb(0x3D4, 0x0E); super::ports::outb(0x3D5, ((addr >> 8) & 0xFF) as u8); } + + /// Gets the cursor's location. + pub fn get_cursor_location(self) -> (u32, u32) { + let mut addr: u32 = 0; + + super::ports::outb(0x3D4, 0x0F); + addr |= super::ports::inb(0x3D5) as u32; + + super::ports::outb(0x3D4, 0x0E); + addr |= (super::ports::inb(0x3D5) as u32) << 8; + + return (addr % self.width, addr / self.width); + } } diff --git a/kernel/src/include/arch/x86/mod.rs b/kernel/src/include/arch/x86/mod.rs index 95d44b0..5079c8f 100644 --- a/kernel/src/include/arch/x86/mod.rs +++ b/kernel/src/include/arch/x86/mod.rs @@ -17,11 +17,22 @@ pub use constants::*; pub fn cpuid(id: u32) -> (u32, u32, u32) { let mut out = (0u32, 0u32, 0u32); unsafe { - // ebx is moved into eax as apparently ebx is used internally by LLVM asm!( - "cpuid", - "mov eax, ebx", in("eax") id, lateout("eax") out.0, out("edx") out.1, out("ecx") out.2 + "cpuid", in("eax") id, out("ebx") out.0, out("edx") out.1, out("ecx") out.2 ) } out +} + +/// Returns whether extended functions are available +/// (more specifically, 0x80000001 or higher) +pub fn cpuid_extended_functions() -> bool { + let out: u32; + unsafe { + asm!( + "mov eax, 0x80000000", + "cpuid", out("eax") out + ) + } + out >= 0x80000001 } \ No newline at end of file diff --git a/kernel/src/include/arch/x86/output.rs b/kernel/src/include/arch/x86/output.rs index 2d7887d..6150c5f 100644 --- a/kernel/src/include/arch/x86/output.rs +++ b/kernel/src/include/arch/x86/output.rs @@ -233,6 +233,79 @@ macro_rules! message_funcs { } Ok(()) } + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /// Outputs a $func_name message &str to the terminal. + pub fn [< b $func_name s >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name s >](s); + [< t $func_name s >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &str and a newline to the terminal. + pub fn [< b $func_name sln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name sln >](s); + [< t $func_name sln >](s, info)?; + Ok(()) + } + + /// Outputs a $func_name message &\[u8] to the terminal. + pub fn [< b $func_name b >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name b >](s); + [< t $func_name b >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &\[u8] and a newline to the terminal. + pub fn [< b $func_name bln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name bln >](s); + [< t $func_name bln >](s, info)?; + Ok(()) + } + + /// Outputs a(n) $func_name message u8 to the terminal. + pub fn [< b $func_name u >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name u >](s); + [< t $func_name u >](s, info)?; + Ok(()) + } + + /////////////////////////////////////////////////////////////// + + /// Outputs a $func_name message &str to the terminal without a prefix. + pub fn [< b $func_name snp >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name snp >](s); + [< t $func_name snp >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &str and a newline to the terminal without a prefix. + pub fn [< b $func_name snpln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name snpln >](s); + [< t $func_name snpln >](s, info)?; + Ok(()) + } + + /// Outputs a $func_name message &\[u8] to the terminal without a prefix. + pub fn [< b $func_name bnp >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name bnp >](s); + [< t $func_name bnp >](s, info)?; + Ok(()) + } + /// Outputs a $func_name message &\[u8] and a newline to the terminal without a prefix. + pub fn [< b $func_name bnpln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name bnpln >](s); + [< t $func_name bnpln >](s, info)?; + Ok(()) + } + + /// Outputs a(n) $func_name message u8 to the terminal without a prefix. + pub fn [< b $func_name unp >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> { + [< s $func_name unp >](s); + [< t $func_name unp >](s, info)?; + Ok(()) + } } } } @@ -244,3 +317,10 @@ message_funcs!(error, "[ERROR] ", CONFIG_PREUSER_OUTPUT_ERROR); message_funcs!(fatal, "[FATAL] ", CONFIG_PREUSER_OUTPUT_FATAL); message_funcs!(output, "", NONE); +/// Resets the position of output to the screen. +pub fn sreset() { + unsafe { + OUTPUT_TERM_POSITION = (0, 0); + } +} + diff --git a/kernel/src/include/mod.rs b/kernel/src/include/mod.rs index 5a45b2a..6fc216c 100644 --- a/kernel/src/include/mod.rs +++ b/kernel/src/include/mod.rs @@ -2,12 +2,14 @@ #![no_std] #![warn(missing_docs)] #![feature(ptr_metadata)] +#![feature(const_trait_impl)] mod constants; mod util; pub mod multiboot2; pub mod arch; mod errors; +pub mod _entry; #[allow(unused_imports)] // if there are no constants, then it gives a warning pub use constants::*; diff --git a/kernel/src/include/multiboot2.rs b/kernel/src/include/multiboot2.rs index 3c73ad3..550aa9c 100644 --- a/kernel/src/include/multiboot2.rs +++ b/kernel/src/include/multiboot2.rs @@ -1,7 +1,7 @@ //! Definitions of structs for multiboot2 information. Mostly used during pre-userspace. /// Used when a CString is passed. Move into separate file? -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct CString { /// The raw pointer to the string. pub ptr: *const u8, @@ -24,6 +24,15 @@ impl core::ops::Index for CString { } } +impl Into<&'static str> for CString { + fn into(self) -> &'static str { + unsafe { + let val: *const str = core::ptr::from_raw_parts(self.ptr, self.len); + return &*val; + } + } +} + /// Used for Multiboot2 tags. This shouldn't be used after a [BootInfo] struct has been initalized, but it still can be used. #[repr(C)] #[derive(Clone)] @@ -89,16 +98,14 @@ pub struct RawMemoryMap { } /// A full memory map provided by a Multiboot2 bootloader. -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct MemoryMap { /// The version of the memory map. Should be disregarded as it's 0. pub version: u32, // currently is 0, future Multiboot2 versions may increment /// Size of one entry(one [MemorySection] for Aphrodite) pub entry_size: u32, - /// A pointer to the first section. - pub sections: *const MemorySection, - /// The number of sections. - pub sections_len: usize + /// All sections. + pub sections: &'static [MemorySection], } /// A color descriptor for [ColorInfo::Palette]. @@ -190,13 +197,13 @@ pub struct BootInfo { // Due to the way modules work, it's not easily possible to make a struct that contains all the modules. // Therefore, they are loaded on the fly. - // Multiboot2 bootloaders may provide us with ELF symbols, but I'm feeling lazy and right now the kernel is a - // flat binary, so I don't care. Sorry if you are affected by this. + // Multiboot2 bootloaders may provide us with ELF symbols, but I'm feeling lazy and right now it's mostly + // unnecessary, so I don't care. Sorry if you are affected by this. /// The memory map provided by the bootloader. pub memory_map: Option, - /// The name of the bootloader(for example, "GRUB"). C-style UTF-8(null-terminated UTF-8) string. + /// The name of the bootloader(for example, "GRUB 2.12"). C-style UTF-8(null-terminated UTF-8) string. /// This should contain the original pointer provided by the bootloader. pub bootloader_name: Option, diff --git a/kernel/src/include/util.rs b/kernel/src/include/util.rs index daaade4..1cad947 100644 --- a/kernel/src/include/util.rs +++ b/kernel/src/include/util.rs @@ -72,3 +72,20 @@ pub fn usize_as_u8_slice(mut value: usize) -> [u8; 20] { } buf } + +/// Converts an u64 to an [u8; 10]. +pub fn u64_as_u8_slice(mut value: u64) -> [u8; 20] { + let mut buf = [0u8; 20]; + let mut i = 19; + if value == 0 { + buf[0] = b'0'; + } + while value > 0 { + let digit = value%10; + let char = b'0' + digit as u8; + buf[i] = char; + value = value / 10; + i -= 1; + } + buf +} diff --git a/kernel/src/internal/arch/x86/entry.rs b/kernel/src/internal/arch/x86/entry.rs index f915cb6..94a8002 100644 --- a/kernel/src/internal/arch/x86/entry.rs +++ b/kernel/src/internal/arch/x86/entry.rs @@ -8,7 +8,7 @@ #![feature(cfg_match)] use core::{arch::asm, ffi::CStr, panic::PanicInfo}; -use aphrodite::multiboot2::{BootInfo, CString, ColorInfo, FramebufferInfo, MemoryMap, PaletteColorDescriptor, RawMemoryMap, RootTag, Tag}; +use aphrodite::multiboot2::{BootInfo, CString, ColorInfo, FramebufferInfo, MemoryMap, MemorySection, PaletteColorDescriptor, RawMemoryMap, RootTag, Tag}; use aphrodite::arch::x86::output::*; use aphrodite::arch::x86::egatext as egatext; use egatext::*; @@ -142,8 +142,7 @@ extern "C" fn _start() -> ! { BI.memory_map = Some(MemoryMap { version: (*rawmemorymap).entry_version, entry_size: (*rawmemorymap).entry_size, - sections: &(*rawmemorymap).sections[0], - sections_len: (*rawmemorymap).sections.len() + sections: &*core::ptr::from_raw_parts((&(*rawmemorymap).sections[0]) as &MemorySection, (*rawmemorymap).sections.len()), }); }, 2 => { // Bootloader name @@ -268,21 +267,24 @@ extern "C" fn _start() -> ! { }, 2 => { // EGA Text sdebugsnpln("(EGA Text)"); - sdebugsln("Attempting to output to screen(will then loop for 100000000 cycles)..."); + sdebugsln("Beginning output to screen..."); let ega = egatext::FramebufferInfo { address: framebuffer_info.address, pitch: framebuffer_info.pitch, width: framebuffer_info.width, height: framebuffer_info.height, - bpp: framebuffer_info.bpp + bpp: framebuffer_info.bpp, + change_cursor: true, }; - ega.clear_screen(BLACK_ON_BLACK); + ega.clear_screen(WHITE_ON_BLACK); ega.enable_cursor(14, 15); ega.set_cursor_location((0, 0)); tdebugsln("Testing EGA Text framebuffer...", ega).unwrap(); tdebugsln("Testing EGA Text framebuffer...", ega).unwrap(); tdebugsln("Testing EGA Text framebuffer...", ega).unwrap(); + + aphrodite::_entry::_entry(Some(ega), &BI); }, _ => { unreachable!(); @@ -291,12 +293,14 @@ extern "C" fn _start() -> ! { } } - panic!("kernel unexpectedly exited"); + unsafe { + aphrodite::_entry::_entry(None, &BI); + } } #[unsafe(link_section = ".panic")] #[panic_handler] -#[cfg(not(CONFIG_PREUSER_HALT_ON_PANIC = "false"))] +#[cfg(not(CONFIG_HALT_ON_PANIC = "false"))] fn halt_on_panic(info: &PanicInfo) -> ! { let message = info.message().as_str().unwrap_or(""); if message != "" { @@ -311,7 +315,7 @@ fn halt_on_panic(info: &PanicInfo) -> ! { #[unsafe(link_section = ".panic")] #[panic_handler] -#[cfg(all(CONFIG_PREUSER_SPIN_ON_PANIC = "true", CONFIG_PREUSER_HALT_ON_PANIC = "false"))] +#[cfg(all(CONFIG_SPIN_ON_PANIC = "true", CONFIG_PREUSER_HALT_ON_PANIC = "false"))] fn spin_on_panic(info: &PanicInfo) -> ! { let message = info.message().as_str().unwrap_or(""); if message != "" {