From 8a32777319c32430641649bec9aec80c7e21ee5a Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sun, 1 Jan 2017 16:01:39 +0100 Subject: [PATCH 01/19] moved kernel sources, kprint_int, build scripts --- binary_x86/kernel | Bin 6422 -> 0 bytes bochsrc.txt | 9 + kernel.c | 163 ---------- make.sh | 30 ++ menu.lst | 5 + os.iso | Bin 0 -> 485376 bytes run.sh | 3 + src/kernel/io.asm | 11 + src/kernel/io.c | 22 ++ src/kernel/io.h | 12 + src/kernel/io.o | Bin 0 -> 1352 bytes src/kernel/ioasm.o | Bin 0 -> 416 bytes src/kernel/iso/boot/kernel | Bin 0 -> 9124 bytes src/kernel/kasm.o | Bin 0 -> 800 bytes src/kernel/kc.o | Bin 0 -> 5536 bytes src/kernel/kernel | Bin 0 -> 9124 bytes kernel.asm => src/kernel/kernel.asm | 0 src/kernel/kernel.c | 344 ++++++++++++++++++++ keyboard_map.h => src/kernel/keyboard_map.h | 0 src/kernel/lib.c | 63 ++++ src/kernel/lib.c~ | 63 ++++ src/kernel/lib.o | Bin 0 -> 1984 bytes link.ld => src/kernel/link.ld | 0 src/kernel/os.iso | Bin 0 -> 34816 bytes stage2_eltorito | Bin 0 -> 105522 bytes 25 files changed, 562 insertions(+), 163 deletions(-) delete mode 100644 binary_x86/kernel create mode 100644 bochsrc.txt delete mode 100644 kernel.c create mode 100755 make.sh create mode 100644 menu.lst create mode 100644 os.iso create mode 100755 run.sh create mode 100644 src/kernel/io.asm create mode 100644 src/kernel/io.c create mode 100644 src/kernel/io.h create mode 100644 src/kernel/io.o create mode 100644 src/kernel/ioasm.o create mode 100755 src/kernel/iso/boot/kernel create mode 100644 src/kernel/kasm.o create mode 100644 src/kernel/kc.o create mode 100755 src/kernel/kernel rename kernel.asm => src/kernel/kernel.asm (100%) create mode 100644 src/kernel/kernel.c rename keyboard_map.h => src/kernel/keyboard_map.h (100%) create mode 100644 src/kernel/lib.c create mode 100644 src/kernel/lib.c~ create mode 100644 src/kernel/lib.o rename link.ld => src/kernel/link.ld (100%) create mode 100644 src/kernel/os.iso create mode 100644 stage2_eltorito diff --git a/binary_x86/kernel b/binary_x86/kernel deleted file mode 100644 index 4dd17478fb714baf368c2dc759607bcd43378af0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6422 zcmeHMU2GIp6h5;*bg2uwMKLJE3~`mM(4~~cf{K82L=%4^bQ26T%XD|vuHBzHJEgRN zP_qzOH<-ecJ{Tc{Ct{*L^udRwP^9`~6B8dyOqwXkBy9{N4K!N1erJB#S@nS@pXQ`@ zzVq|lbLO78dpA2hExqxoAP8iOhrGnrW(rdrxO1%;iIYekic%vr7;%Ildw*zhm+zz-Ma6903!0mwB0k;GHZwEYIeYu`1 zF1~&6_iAUvH#@=Ck2@lPKMXA-Jo(pz{_qH?=D8=aKqp23g*sx+6m z5?B$Y%%r~&5TINBAP}$$@CoAA^>F6OY8aCp5nmIEP5L4tK)+C{)f~P{>^o)om;l~J z_GK+!)bw3uU&8W1U*hen_Zhqv%O^N^Pt^Mip5OA-9K5@W=KWbdgZC@@q&wv2rQf+e z9rk0%RFQPH^l(L*Jum4MUql}R@rMbcSL=9@4;+a??J}K7GZC{?Ray|HZjo39)_EG(^Wmw5 zFG57ef3jvrd9fm<+fVu019Z$6;a5WXXVA|{_d}!7{qnSMc3eAz$T5zbHX`?iPu-&v zyB$kJCqiS1d!f;bd$IoIPBI@mTp9v3wpz(Z^EO+Hsps=LF09uFTe4n4Mp{L zq0KDf9`b`?Caab-u~$(GN=_WeYCX^gyNj}#7EAqoeMMCR?=cJ(Vdx+UCj-LzpwD*> z`)dPyJf;_FwJp5g_N@+_^}Mpm?~MxIfy1$vFlJGe^`-b>juMG*9mUWQH(sFG^E=93 z&yk6Jc8+0iqPXd;z@;3$W)9AK38NkHmw`+1q2>5E+zG@h&ba5SL0AXhGzRb9?anys z|0V0UwQY~@c)oq-uGW^_ft4Q&D5^HtpDp$s>1rgol+L`@^L}sc#ZAZP=+KGO@$N!? zOKdAWXDqfZUkj|wb)JVZh998=`}e;fMi0D|XcptK_E@Xf*1D~&bw_Kv7(J+@#n)ub z^tbG2CW@5?^P1cZ)Kn9DY)w;6XcSY6X<3sgrt~B;s+?CSX4>7Q62(%*d|oN=CU{8U z!J(}G$-)NhC<+#_X%>6TM zvjX?Yw9PJZKTO+ipndV;X*%or=x!}&2Z3z!Yx51p3V0m?Jq3GO2ory1*TCl8zQz&X z340v%xN;M&dw~2M!w;v4{Ink?f8DZhzD{5q8lN+quM74`ELvi9_U5t4a1TUhqy> 16; - - /* Ports - * PIC1 PIC2 - *Command 0x20 0xA0 - *Data 0x21 0xA1 - */ - - /* ICW1 - begin initialization */ - write_port(0x20 , 0x11); - write_port(0xA0 , 0x11); - - /* ICW2 - remap offset address of IDT */ - /* - * In x86 protected mode, we have to remap the PICs beyond 0x20 because - * Intel have designated the first 32 interrupts as "reserved" for cpu exceptions - */ - write_port(0x21 , 0x20); - write_port(0xA1 , 0x28); - - /* ICW3 - setup cascading */ - write_port(0x21 , 0x00); - write_port(0xA1 , 0x00); - - /* ICW4 - environment info */ - write_port(0x21 , 0x01); - write_port(0xA1 , 0x01); - /* Initialization finished */ - - /* mask interrupts */ - write_port(0x21 , 0xff); - write_port(0xA1 , 0xff); - - /* fill the IDT descriptor */ - idt_address = (unsigned long)IDT ; - idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16); - idt_ptr[1] = idt_address >> 16 ; - - load_idt(idt_ptr); -} - -void kb_init(void) -{ - /* 0xFD is 11111101 - enables only IRQ1 (keyboard)*/ - write_port(0x21 , 0xFD); -} - -void kprint(const char *str) -{ - unsigned int i = 0; - while (str[i] != '\0') { - vidptr[current_loc++] = str[i++]; - vidptr[current_loc++] = 0x07; - } -} - -void kprint_newline(void) -{ - unsigned int line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; - current_loc = current_loc + (line_size - current_loc % (line_size)); -} - -void clear_screen(void) -{ - unsigned int i = 0; - while (i < SCREENSIZE) { - vidptr[i++] = ' '; - vidptr[i++] = 0x07; - } -} - -void keyboard_handler_main(void) -{ - unsigned char status; - char keycode; - - /* write EOI */ - write_port(0x20, 0x20); - - status = read_port(KEYBOARD_STATUS_PORT); - /* Lowest bit of status will be set if buffer is not empty */ - if (status & 0x01) { - keycode = read_port(KEYBOARD_DATA_PORT); - if(keycode < 0) - return; - - if(keycode == ENTER_KEY_CODE) { - kprint_newline(); - return; - } - - vidptr[current_loc++] = keyboard_map[(unsigned char) keycode]; - vidptr[current_loc++] = 0x07; - } -} - -void kmain(void) -{ - const char *str = "my first kernel with keyboard support"; - clear_screen(); - kprint(str); - kprint_newline(); - kprint_newline(); - - idt_init(); - kb_init(); - - while(1); -} diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..37a94da --- /dev/null +++ b/make.sh @@ -0,0 +1,30 @@ +#!/bin/bash +cd src/kernel + +nasm -f elf32 kernel.asm -o kasm.o +nasm -f elf32 io.asm -o ioasm.o +gcc -m32 -c kernel.c -o kc.o +gcc -m32 -c io.c -o io.o +gcc -m32 -c lib.c -o lib.o +ld -m elf_i386 -T link.ld -o kernel kasm.o ioasm.o kc.o io.o lib.o + +echo "kernel build end, making .iso" + +# make iso +cd ../../ +mkdir -p iso/boot/grub # create the folder structure +cp stage2_eltorito iso/boot/grub/ # copy the bootloader +cp menu.lst iso/boot/grub/ # copy menu +cp src/kernel/kernel iso/boot/ # copy the kernel + +genisoimage -R \ +-b boot/grub/stage2_eltorito \ +-no-emul-boot \ +-boot-load-size 4 \ +-A os \ +-input-charset utf8 \ +-quiet \ +-boot-info-table \ +-o os.iso \ +iso + diff --git a/menu.lst b/menu.lst new file mode 100644 index 0000000..ed1e827 --- /dev/null +++ b/menu.lst @@ -0,0 +1,5 @@ +default=0 + timeout=0 + + title os + kernel /boot/kernel diff --git a/os.iso b/os.iso new file mode 100644 index 0000000000000000000000000000000000000000..335f299193d986050c89f3cc4998cc237699c20b GIT binary patch literal 485376 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

VTk|y?4+y|JRpdD)OMo*_W5=F1-q_WasL; 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 zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr zKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{ z0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J zBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2JBtQZr oKmsH{0wh2JBtQZrKmsH{0wh2JBtQZrKmsH{0wh2J{|f~EA2f-F)&Kwi literal 0 HcmV?d00001 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..8d93d6c --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +bochs -f bochsrc.txt -q + diff --git a/src/kernel/io.asm b/src/kernel/io.asm new file mode 100644 index 0000000..45db76d --- /dev/null +++ b/src/kernel/io.asm @@ -0,0 +1,11 @@ +global outb ; make the label outb visible outside this file + + ; outb - send a byte to an I/O port + ; stack: [esp + 8] the data byte + ; [esp + 4] the I/O port + ; [esp ] return address + outb: + mov al, [esp + 8] ; move the data to be sent into the al register + mov dx, [esp + 4] ; move the address of the I/O port into the dx register + out dx, al ; send the data to the I/O port + ret ; return to the calling function diff --git a/src/kernel/io.c b/src/kernel/io.c new file mode 100644 index 0000000..caa2c4d --- /dev/null +++ b/src/kernel/io.c @@ -0,0 +1,22 @@ +#include "io.h" + + /* The I/O ports */ + #define FB_COMMAND_PORT 0x3D4 + #define FB_DATA_PORT 0x3D5 + + /* The I/O port commands */ + #define FB_HIGH_BYTE_COMMAND 14 + #define FB_LOW_BYTE_COMMAND 15 + + /** fb_move_cursor: + * Moves the cursor of the framebuffer to the given position + * + * @param pos The new position of the cursor + */ + void fb_move_cursor(unsigned short pos) + { + write_port(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); + write_port(FB_DATA_PORT, ((pos >> 8) & 0x00FF)); + write_port(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); + write_port(FB_DATA_PORT, pos & 0x00FF); + } diff --git a/src/kernel/io.h b/src/kernel/io.h new file mode 100644 index 0000000..9aa3570 --- /dev/null +++ b/src/kernel/io.h @@ -0,0 +1,12 @@ +#ifndef INCLUDE_IO_H + #define INCLUDE_IO_H + + /** outb: + * Sends the given data to the given I/O port. Defined in io.s + * + * @param port The I/O port to send the data to + * @param data The data to send to the I/O port + */ + void write_port(unsigned short port, unsigned char data); + + #endif /* INCLUDE_IO_H */ diff --git a/src/kernel/io.o b/src/kernel/io.o new file mode 100644 index 0000000000000000000000000000000000000000..f96c82675903468245d695948d2d01fc52980f3a GIT binary patch literal 1352 zcma)5&1(};5TD&m>sp%+O9i0?bEr}&eI!AHh=(T4CYNAK+uW4LCT_ZEO*ZUqY%GFE z5G?WFAK-r>*sBsfc-N!Qi_k+aLXU+wv%AkMX+Z~OfAjHs^WK{``%)_xB}oDy5-2e8 z0In;3=8gx85QC_fpG3cM@N2VoGJAYB91dR%WQ_DRwSJ(T_DCYgtz?#ESbF*HZQn>{ zQt50uo$-!5>mMJa{SA;_sM6eYB=QC?n8f&eklQ27#njhwJ~~n8DYEr-fHX*8c!hE( z5g*Qd!@Y{Y?_rpr0QF=f%2*WzCPqoL6riq-L;-c=pryd&G5^w-Fa51RcbdWduZ0*h z@+2&Ue7aw5%!h`>YEZB8ruEF=wT|srHgMj(mu1_A%iA^Xnw{1zt9F5x%4_*tnXeU# zo7xuN%H_)%hdsOD8oX`Uu6KI*3gFBDOicqUAV#8oPYE{ye<#2sqKqvo5K?QH^StTm zRYceJ(G-?#JY}xYb%8z6T^(4}ali|(ix10cR(NW2;%)6mhY;k7-bxgouQO0yzf_yOA9+#!;PIhsS8UU>zTiny41lzllm~g5MK9pdA)bkKUX3|Kiae-uY+~6skvi2@&9|ia+i~vQwM!*ZUg(LLW0}g>WgMoIqFK=-{0%Oz{ii^HbT~P~7NMYaz5yuav hP>DzDXpQIznvlRnbT=X5eDt{Q(|Ws8s*} literal 0 HcmV?d00001 diff --git a/src/kernel/ioasm.o b/src/kernel/ioasm.o new file mode 100644 index 0000000000000000000000000000000000000000..81d02fffb0371580f096e4e515c4457c7fa3bef7 GIT binary patch literal 416 zcmb<-^>JflWMqH=Mh0dE1doBi0V-hvrZpH?8JJ*7Nuoh!f-oCYmjIB%jbwrVl+6yL znUTa5fa(;H_#iVtKoE$LT@nD4Z~%(205M2Ehz6M{3FQMRFqi;T#|z^CX_$F1p)MB{ zj4BFNCE;N S*JS4FB^Kv0EtfusOru^BJ` literal 0 HcmV?d00001 diff --git a/src/kernel/iso/boot/kernel b/src/kernel/iso/boot/kernel new file mode 100755 index 0000000000000000000000000000000000000000..7ddffe186585e2d3576237a93f135bdac9ae1f38 GIT binary patch literal 9124 zcmeHMeQXow8Gp}l;7AR=(5PdDX=Y8g(1IZh0xP8=kb`!>k^#}iD0BUBAx<1S_#+93 zP|^!sToaawigjAG!Ty*usnyEXWnYz+U`S}C2`Z>mZ9;_#rkfHFn>;Wy#rr+)-Pwm* z#QxcTe&O!k=Xrkb^ZCBd+3}0=ruBj#Fmu>g86)aRCb4CBzCK@N6hb8tDt z7<7|Zm4mTM>lmAEu)cjY9-wLQAUR)FEGW^X43siZ%0MXtr3{oZP|83l1EmaE`6st?Fcz_KL_@U;4yDjE#>xjxYR4Ig^qz$&=$FmsUxvB{?MG z4-5t|exL$`XpmzE9H8>VOb~~$bX1cpt7n0$PKs@nZ7!`Tm&>WABu=&-!&2>;0>{-h zUt3%tYg&&J;7bJ#17Mbg)B#g>^Ew3pu31PO@b^5YH3yBryn8Q4hrO*c^+1#Ijg)+2 z^paGLV384xD1;?Xs*{dES!>dMsKbX+JEz_?Qfb##;ZG*zEfqZA)=#;yWLo>)XQ5SE z6`+0TRlttR86ep|UqYf%%_0&goO2Gdr^Q4xrOt6dSyt$(G^U!6A3xwiRLeXTxsjWn4hj`$#bKZYyxif zi^LgnN=xNFdTwiFJiE4XLN9cI?a_(~s=fTNMSf99Y zu|Cy&WAK|oa`TPU=FH%BA-OZ-96lscCI=2lJfRN)sPd$s_D(i-y{$sK+N;g!I!@+L zJJZTn^H#?Cxl&g#gXOhc(C9CT_k<&95`{zOWn9jZ-^MAJs7}4cYucRUL1cyV*d=_5 zFka=R1|II~&HfzDbe)G%$&#bLpoV?vf1u-;>S|Icu1?NiUnQ~M&WvH}f0oDkdNW+* z*+NCe`S#_`>3bJbZ6R(C*h$Ax5crkfHtM9L&4*{@o48|Bq;cXaspcsj)>I*Z_cGwp z6nE)^A>LuoHMPIth?Y}#=cmUpuz6~rIqMv5&W?U;Pri@Il=`uA#(M)pbPDukyvP3% z>-y4v#nz?X>q09&ecNc8j<$gI8=V-CZ#jqMTk0hNZDk`)McWJ7k4@3d1HCs{&obQS zC8lfFr*gT88lZ1Fhew>l=qYqy<;a15#!p}k3a|Bk^SfS}#h+bC&QFLS_KR)ACcPzPvRAOx!QGIp1?OD~4D z8cb~lg3%hWYT~Z>_B#Z5U;4M$jVW1d!Ml(Anbmp$_0o=KxdYDM%bAu@O+=&+Hno00 zvV*#e&`up4E1>T|?$G)9B+z!44Xfp=VRT|aN_J2$lXI91HszAd4zwU@iV_^+n-)}a zj!4HUS?oTWFRP@T#ZKx=--JI9CgrT!D+&9j;r`fubjj=(^efOpQT1lnp0Zmz#nk5P zV7&k=_&>STD<#o0`^!G0>Io)G7S&#nB`)wq4k+SoK;0r;DeidNH02TwZ6U9kn12iQ zP1+HfR>vLN3CG$h1`lRY7@$S1fukK%LY=8yvnD5HuTH1tRi~p$fVun$pW^zC5 z731d!T~7ca1(-h;>p4--GXTsT?4X_^J+?@?{Wz}9|@ko`T1!v=7{bz#XSeU9tUQF6=PdRuJF9jTlWm)E3EPu7c&=l--Y}m$PZcN z$epcUw=oC&eZXwM(K%d%q2GmGgKRTOi*3vY@!kM$wT0&bZv?y_K}R>CEH$!43b9-P z<_a+Dtr*){?yL(uJDNttr@7owtDJm2tm`054KVwFskdS_sAXL`gt&~&tzG){Wy|YU zto+83MXSXbyStTWd`}`6>D;lif_YiJkizNvuAJd(hcj@J>{xt zP<%me*j49V>aJb1e1Sf3EnQMuS6jQ3ej2>bpsF$BS474xiYAR@VqDJCILjDU^R!;| zOrAzR$5``_xgIYm4AEiIJaXpB)A%G~8D(q+!xqR7JNsA80gArD=tE?*AI(AXFPj9O zMAzWC2KZz6XdvKo`{E+GH3@`&3i3ML94vYL*(9cbm81(^K z{~gf(Mn1@IqFX?pK)$G-5gh~lI^wN2=>4Ea?GmHDN%EILKLR}UNTQE|J_`ToR1*C& z&?B%%J%#9Bfu`TdX;40X4f@ZBhk6^yS-TPqD-72+7L0I~A@#Es zpT{2w6Wh;1K_BknM2EX}S#2HjY}nMewtkbRasB$O@;1-5`n8*64+}-S0Z%Xx*H?xj zQ9emTqe?jL2}QWZm=cFplC^h6gW!ji?ocqS7|>WG>e<7(qrtf1>5N3<%;V`!%Ct<$?Aad&43ihZ#dbNeG59VjE;RR80R z{(ytsh7&>q`w{;yTH2w$Lm8V7sfnXLa1jlV&Z;>`Mq@QD6G!Lu@_lgBzX(U2o%;Ay z;AUcO4pW~O3=>EFJp%ZR zKDwx1QW{7fy(g-G8{r>bz#tyKGxj2$q;H8q6OMYgIY?$+7Pr~cz`3BD-o911$R_1_ h1}?H`j_2`W`5h@RhB>9L=T=9mtX=vjdvSUK>Bb9}xTi z3n3q%A7No3f?%cMdhYHFS+VoNopa88&Fsvc@9xzc$02JDjnPOEZJWeKf)y$t{~zO- z?0M6C0@<0&gOr= zG=GXq$6VmqDwp`4t?@lgTLENUf!mrAZeShS*0A1!UVyW)t^t=4mFgq#m5ipAR9$?G zh&t5jh_H*mpxzTI@SCC=w59TssFEV^`#q)URE1imwB1864D_EKinKr})<(;Z`l2Dt Vmc;Mr=80B;4&IW_@AA)K><4C7UcmqW literal 0 HcmV?d00001 diff --git a/src/kernel/kc.o b/src/kernel/kc.o new file mode 100644 index 0000000000000000000000000000000000000000..f393e8e3669330b5b8b6f8a5240022a928989731 GIT binary patch literal 5536 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 h1}?H`j_2`W`5h@RhB>9L=T=> 16; + + /* Ports + * PIC1 PIC2 + *Command 0x20 0xA0 + *Data 0x21 0xA1 + */ + + /* ICW1 - begin initialization */ + write_port(0x20 , 0x11); + write_port(0xA0 , 0x11); + + /* ICW2 - remap offset address of IDT */ + /* + * In x86 protected mode, we have to remap the PICs beyond 0x20 because + * Intel have designated the first 32 interrupts as "reserved" for cpu exceptions + */ + write_port(0x21 , 0x20); + write_port(0xA1 , 0x28); + + /* ICW3 - setup cascading */ + write_port(0x21 , 0x00); + write_port(0xA1 , 0x00); + + /* ICW4 - environment info */ + write_port(0x21 , 0x01); + write_port(0xA1 , 0x01); + /* Initialization finished */ + + /* mask interrupts */ + write_port(0x21 , 0xff); + write_port(0xA1 , 0xff); + + /* fill the IDT descriptor */ + idt_address = (unsigned long)IDT ; + idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16); + idt_ptr[1] = idt_address >> 16 ; + + load_idt(idt_ptr); +} + +void kb_init(void) +{ + /* 0xFD is 11111101 - enables only IRQ1 (keyboard)*/ + write_port(0x21 , 0xFD); +} + +void kprint(const char *str) +{ + unsigned int i = 0; + + while (str[i] != '\0') { + if (str[i] == '\b') + { + /*/ backspace */ + + if (fb_cursor_x > 1) + { + fb_cursor_x--; + } + else + { + if (fb_cursor_y > 0) + { + /* move cursor to previous line end */ + fb_cursor_y--; + fb_cursor_x = COLUMNS_IN_LINE - 1; + } + } + + fb_current_loc = fb_current_loc - 2; /* goto old frame buffer position and clear char */ + fb_con[fb_current_loc] = ' '; + fb_con[fb_current_loc + 1] = 0x00; + + fb_blit (); + } + + if (str[i] != '\b') + { + if (fb_cursor_x < COLUMNS_IN_LINE) + { + fb_cursor_x++; + } + else + { + fb_cursor_x = 0; + + if (fb_cursor_y < LINES - 1) + { + fb_cursor_y++; + } + else + { + fb_scroll_down (); + fb_current_loc = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); + } + } + + fb_con[fb_current_loc] = str[i]; + fb_con[fb_current_loc + 1] = fb_color; + + vidptr[fb_current_loc] = str[i]; + vidptr[fb_current_loc + 1] = fb_color; + + fb_current_loc = fb_current_loc + 2; + } + i++; + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); + } +} + +void kprint_newline(void) +{ + unsigned int line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; + if (fb_cursor_y < LINES - 1) + { + fb_current_loc = fb_current_loc + (line_size - fb_current_loc % (line_size)); + fb_cursor_y++; + fb_cursor_x = 0; + } + else + { + fb_scroll_down (); + fb_current_loc = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); + } +} + +void fb_scroll_down (void) +{ + unsigned int old_pos = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE, new_pos = 0; + unsigned int x_pos; + unsigned int y_pos; + + for (y_pos = 1; y_pos < LINES; y_pos++) + { + for (x_pos = 0; x_pos < COLUMNS_IN_LINE; x_pos++) + { + fb_con[new_pos++] = fb_con[old_pos++]; + fb_con[new_pos++] = fb_con[old_pos++]; + } + } + + new_pos = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); + /* clear last line */ + for (x_pos = 0; x_pos < COLUMNS_IN_LINE; x_pos++) + { + fb_con[new_pos++] = ' '; + fb_con[new_pos++] = 0x00; + } + + fb_blit (); +} + +void fb_blit (void) +{ + unsigned int i = 0, j = 0; + + while (i < SCREENSIZE) + { + vidptr[i++] = fb_con[j++]; + /* vidptr[i++] = fb_con[j++]; */ + } +} + +void fb_clear_screen(void) +{ + unsigned int i = 0; + while (i < SCREENSIZE) { + vidptr[i++] = ' '; + vidptr[i++] = 0x00; + } + + fb_cursor_x = 0; fb_cursor_y = 0; + fb_current_loc = 0; + + fb_clear_con (); + + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); +} + +void fb_clear_con (void) +{ + unsigned int i = 0; + while (i < SCREENSIZE) { + fb_con[i++] = ' '; + fb_con[i++] = 0x00; + } +} + +void fb_set_color (unsigned char forecolor, unsigned char backcolor) +{ + fb_color = (backcolor << 4) | (forecolor & 0x0F); +} + +void keyboard_handler_main(void) +{ + unsigned char status; + char keycode; + unsigned char ch[2]; + + /* write EOI */ + write_port(0x20, 0x20); + + status = read_port(KEYBOARD_STATUS_PORT); + /* Lowest bit of status will be set if buffer is not empty */ + if (status & 0x01) { + keycode = read_port(KEYBOARD_DATA_PORT); + if(keycode < 0) + return; + + if(keycode == ENTER_KEY_CODE) { + kprint_newline(); + return; + } + + /* + if (keycode == '\b') + { + + + } + */ + + ch[0] = keyboard_map[(unsigned char) keycode]; + ch[1] = '\0'; + + kprint (ch); + } +} + +void kmain(void) +{ + // const char *str = "red"; + fb_clear_screen(); + + fb_set_color (FB_RED, FB_BLACK); + kprint ("red"); + fb_set_color (FB_BLUE, FB_BLACK); + kprint (" cube OS"); + fb_set_color(FB_WHITE, FB_BLACK); + kprint_newline (); + + kprint_int (2017); + kprint_newline (); + + /* + unsigned int line = 1; + unsigned char ch = 'a'; + unsigned char str[2]; + + for (line = 1; line < LINES + 1; line++) + { + str[0] = ch; + str[1] = '\0'; + + kprint (str); kprint_newline(); + ch++; + } + */ + idt_init(); + kb_init(); + + while(1); +} diff --git a/keyboard_map.h b/src/kernel/keyboard_map.h similarity index 100% rename from keyboard_map.h rename to src/kernel/keyboard_map.h diff --git a/src/kernel/lib.c b/src/kernel/lib.c new file mode 100644 index 0000000..01efa94 --- /dev/null +++ b/src/kernel/lib.c @@ -0,0 +1,63 @@ +/* lib.c - some helper functions */ + +void strreverse(char* begin, char* end) { + + char aux; + + while(end>begin) + + aux=*end, *end--=*begin, *begin++=aux; + +} + +void itoa(int value, char* str, int base) { + + static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + + char* wstr=str; + + int sign; + + + + + // Validate base + + if (base<2 || base>35){ *wstr='\0'; return; } + + + + // Take care of sign + + if ((sign=value) < 0) value = -value; + + + + + // Conversion. Number is reversed. + + do *wstr++ = num[value%base]; while(value/=base); + + if(sign<0) *wstr++='-'; + + *wstr='\0'; + + + + // Reverse string + + + strreverse(str,wstr-1); + +} + + +void kprint_int (long n) +{ + char str[256]; + + itoa (n, str, 10); + kprint (str); +} + + diff --git a/src/kernel/lib.c~ b/src/kernel/lib.c~ new file mode 100644 index 0000000..7dac493 --- /dev/null +++ b/src/kernel/lib.c~ @@ -0,0 +1,63 @@ +/* lib.c - some helper functions */ + +void strreverse(char* begin, char* end) { + + char aux; + + while(end>begin) + + aux=*end, *end--=*begin, *begin++=aux; + +} + +void itoa(int value, char* str, int base) { + + static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + + char* wstr=str; + + int sign; + + + + + // Validate base + + if (base<2 || base>35){ *wstr='\0'; return; } + + + + // Take care of sign + + if ((sign=value) < 0) value = -value; + + + + + // Conversion. Number is reversed. + + do *wstr++ = num[value%base]; while(value/=base); + + if(sign<0) *wstr++='-'; + + *wstr='\0'; + + + + // Reverse string + + + strreverse(str,wstr-1); + +} + + +void kprint_int (long n) +{ + char str[256]; + + itoa (n, &str, 10); + kprint (str); +} + + diff --git a/src/kernel/lib.o b/src/kernel/lib.o new file mode 100644 index 0000000000000000000000000000000000000000..dd136372e58aaecb7ef6ae3e82aa8c1c09bcd855 GIT binary patch literal 1984 zcma)6O>7fa5T5lqVM!P4roBN`OQls1$|_ESOMebY$i^2Jq7om`cI_;&No zq9u|oD=nw~NWE5~hhB5)&81RLMJmT$t0IveDj|^rV*AbRd(J9_s$LA%Q_x;VZa%&jl zTeq~Hx&JfjJE+n%pZJ?kXhVH^S?hhgzcZtN3;dg>o9XU@)b1S>6{w>~$khBzjam=G zNjeIJ+d#STs3=DNk+Kl9^zcLcpS0fPXVI><_LBIl5RNu>b!2CI|9KFAtqHd7@96>* zNcwJAfOqj9GLfBpcj|2Jy>su|rSg( zo`;#^+IC|0*id5jJ}8s{^bwSEUqT1@IbThDe!nj{pCa`;r=bH*-dEQn3hoHe9nL|L zkqNP{g)cD?4h*(9kR<&b3<5YResNJrTucF~G)#OaS>A>!|H{j3NI^Bexd)=s$Uy*4 zrB@EcM-RpE-7-HBej}5(f1^N7#Ab{pj!-rhbC2>+*=yz(V)@a8MkU0u&z!Ht@;skb zt0kjM^;XTuPGxh{^jz0jcig5!meoF&GnO6CS}R*#rBz=s>^52X!o}H{f^~6z{*t+9 zEzZmqOpB^s!+z0(Qkz!R+-lvkpu!D%oH$OTWBHL3(FNGVt3-<;{tEUej_Lv9$B6zE z`M*T|Img(18tqHIMwHkhl##r%SUH4H)@_;g7w_UR;1@TtA^*aJu;ST z&nBbPY*GjqFK)w2bNFJT+^E$Ye6LY&c#e_3P#kZ1c6o&iUd5?cS6sX1kg@DGT5H(W z|CKL5M4JY}|EGKd(IP@S!*{~(;xLcjqvY{?=CHmA5tnrkf}{4Uo^4xy(yLxt8o5T>24#$Bq!L!@_4B=iNhr@IN`sMtf)XA7832 AYXATM literal 0 HcmV?d00001 diff --git a/link.ld b/src/kernel/link.ld similarity index 100% rename from link.ld rename to src/kernel/link.ld diff --git a/src/kernel/os.iso b/src/kernel/os.iso new file mode 100644 index 0000000000000000000000000000000000000000..730417f7de723a0560d2c08109f2089df6f5deb2 GIT binary patch literal 34816 zcmeIyF-t=+5CGtdba+lKA{`%ZalKfibZ!?xTMz{Qg@gFt{COq37Qsi?w!?Rj literal 0 HcmV?d00001 diff --git a/stage2_eltorito b/stage2_eltorito new file mode 100644 index 0000000000000000000000000000000000000000..9e1617cee78bb8f1ab6838837c9a1f36d729c9fd GIT binary patch literal 105522 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 I8%}5UKb>zEqW}N^ literal 0 HcmV?d00001 From 9d834df4cf63c5967e83fabedaadf9ffa8510784 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sun, 1 Jan 2017 16:05:09 +0100 Subject: [PATCH 02/19] updated README --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e4891c6..e5ce1e2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,27 @@ -mkeykernel +mkeykernel fork ======= +I forked mkeykernel. +I added the following things: + +- Frame buffer text scrolling, removes the top line on screen if reached last line on print +- kprint_int () function to print integer numbers. +- make.sh and run.sh scripts: + make.sh compiles the kernel and creates an .iso file. + run.sh runs bochs with the created .iso file and starts the kernel + +You should see the following start message: + +red cube OS +2017 + + +I will try to continue work on this kernel. + +Stefan Pietzonke 2017 + + + This is a kernel that can read the characters a-z and 0-9 from the keyboard and print them on screen. See the repo [mkernel](http://github.com/arjun024/mkernel) which is a minimal kernel that prints a string on the screen. mkeykernel just extends this to include keyboard support. From 0f22d26f922594f487d82012371b066511128cd9 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Tue, 3 Jan 2017 21:36:13 +0100 Subject: [PATCH 03/19] get memory map from GRUB --- src/kernel/io.o | Bin 1352 -> 0 bytes src/kernel/ioasm.o | Bin 416 -> 0 bytes src/kernel/iso/boot/kernel | Bin 9124 -> 0 bytes src/kernel/kasm.o | Bin 800 -> 0 bytes src/kernel/kc.o | Bin 5536 -> 0 bytes src/kernel/lib.o | Bin 1984 -> 0 bytes src/kernel/os.iso | Bin 34816 -> 0 bytes 7 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/kernel/io.o delete mode 100644 src/kernel/ioasm.o delete mode 100755 src/kernel/iso/boot/kernel delete mode 100644 src/kernel/kasm.o delete mode 100644 src/kernel/kc.o delete mode 100644 src/kernel/lib.o delete mode 100644 src/kernel/os.iso diff --git a/src/kernel/io.o b/src/kernel/io.o deleted file mode 100644 index f96c82675903468245d695948d2d01fc52980f3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1352 zcma)5&1(};5TD&m>sp%+O9i0?bEr}&eI!AHh=(T4CYNAK+uW4LCT_ZEO*ZUqY%GFE z5G?WFAK-r>*sBsfc-N!Qi_k+aLXU+wv%AkMX+Z~OfAjHs^WK{``%)_xB}oDy5-2e8 z0In;3=8gx85QC_fpG3cM@N2VoGJAYB91dR%WQ_DRwSJ(T_DCYgtz?#ESbF*HZQn>{ zQt50uo$-!5>mMJa{SA;_sM6eYB=QC?n8f&eklQ27#njhwJ~~n8DYEr-fHX*8c!hE( z5g*Qd!@Y{Y?_rpr0QF=f%2*WzCPqoL6riq-L;-c=pryd&G5^w-Fa51RcbdWduZ0*h z@+2&Ue7aw5%!h`>YEZB8ruEF=wT|srHgMj(mu1_A%iA^Xnw{1zt9F5x%4_*tnXeU# zo7xuN%H_)%hdsOD8oX`Uu6KI*3gFBDOicqUAV#8oPYE{ye<#2sqKqvo5K?QH^StTm zRYceJ(G-?#JY}xYb%8z6T^(4}ali|(ix10cR(NW2;%)6mhY;k7-bxgouQO0yzf_yOA9+#!;PIhsS8UU>zTiny41lzllm~g5MK9pdA)bkKUX3|Kiae-uY+~6skvi2@&9|ia+i~vQwM!*ZUg(LLW0}g>WgMoIqFK=-{0%Oz{ii^HbT~P~7NMYaz5yuav hP>DzDXpQIznvlRnbT=X5eDt{Q(|Ws8s*} diff --git a/src/kernel/ioasm.o b/src/kernel/ioasm.o deleted file mode 100644 index 81d02fffb0371580f096e4e515c4457c7fa3bef7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 416 zcmb<-^>JflWMqH=Mh0dE1doBi0V-hvrZpH?8JJ*7Nuoh!f-oCYmjIB%jbwrVl+6yL znUTa5fa(;H_#iVtKoE$LT@nD4Z~%(205M2Ehz6M{3FQMRFqi;T#|z^CX_$F1p)MB{ zj4BFNCE;N S*JS4FB^Kv0EtfusOru^BJ` diff --git a/src/kernel/iso/boot/kernel b/src/kernel/iso/boot/kernel deleted file mode 100755 index 7ddffe186585e2d3576237a93f135bdac9ae1f38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9124 zcmeHMeQXow8Gp}l;7AR=(5PdDX=Y8g(1IZh0xP8=kb`!>k^#}iD0BUBAx<1S_#+93 zP|^!sToaawigjAG!Ty*usnyEXWnYz+U`S}C2`Z>mZ9;_#rkfHFn>;Wy#rr+)-Pwm* z#QxcTe&O!k=Xrkb^ZCBd+3}0=ruBj#Fmu>g86)aRCb4CBzCK@N6hb8tDt z7<7|Zm4mTM>lmAEu)cjY9-wLQAUR)FEGW^X43siZ%0MXtr3{oZP|83l1EmaE`6st?Fcz_KL_@U;4yDjE#>xjxYR4Ig^qz$&=$FmsUxvB{?MG z4-5t|exL$`XpmzE9H8>VOb~~$bX1cpt7n0$PKs@nZ7!`Tm&>WABu=&-!&2>;0>{-h zUt3%tYg&&J;7bJ#17Mbg)B#g>^Ew3pu31PO@b^5YH3yBryn8Q4hrO*c^+1#Ijg)+2 z^paGLV384xD1;?Xs*{dES!>dMsKbX+JEz_?Qfb##;ZG*zEfqZA)=#;yWLo>)XQ5SE z6`+0TRlttR86ep|UqYf%%_0&goO2Gdr^Q4xrOt6dSyt$(G^U!6A3xwiRLeXTxsjWn4hj`$#bKZYyxif zi^LgnN=xNFdTwiFJiE4XLN9cI?a_(~s=fTNMSf99Y zu|Cy&WAK|oa`TPU=FH%BA-OZ-96lscCI=2lJfRN)sPd$s_D(i-y{$sK+N;g!I!@+L zJJZTn^H#?Cxl&g#gXOhc(C9CT_k<&95`{zOWn9jZ-^MAJs7}4cYucRUL1cyV*d=_5 zFka=R1|II~&HfzDbe)G%$&#bLpoV?vf1u-;>S|Icu1?NiUnQ~M&WvH}f0oDkdNW+* z*+NCe`S#_`>3bJbZ6R(C*h$Ax5crkfHtM9L&4*{@o48|Bq;cXaspcsj)>I*Z_cGwp z6nE)^A>LuoHMPIth?Y}#=cmUpuz6~rIqMv5&W?U;Pri@Il=`uA#(M)pbPDukyvP3% z>-y4v#nz?X>q09&ecNc8j<$gI8=V-CZ#jqMTk0hNZDk`)McWJ7k4@3d1HCs{&obQS zC8lfFr*gT88lZ1Fhew>l=qYqy<;a15#!p}k3a|Bk^SfS}#h+bC&QFLS_KR)ACcPzPvRAOx!QGIp1?OD~4D z8cb~lg3%hWYT~Z>_B#Z5U;4M$jVW1d!Ml(Anbmp$_0o=KxdYDM%bAu@O+=&+Hno00 zvV*#e&`up4E1>T|?$G)9B+z!44Xfp=VRT|aN_J2$lXI91HszAd4zwU@iV_^+n-)}a zj!4HUS?oTWFRP@T#ZKx=--JI9CgrT!D+&9j;r`fubjj=(^efOpQT1lnp0Zmz#nk5P zV7&k=_&>STD<#o0`^!G0>Io)G7S&#nB`)wq4k+SoK;0r;DeidNH02TwZ6U9kn12iQ zP1+HfR>vLN3CG$h1`lRY7@$S1fukK%LY=8yvnD5HuTH1tRi~p$fVun$pW^zC5 z731d!T~7ca1(-h;>p4--GXTsT?4X_^J+?@?{Wz}9|@ko`T1!v=7{bz#XSeU9tUQF6=PdRuJF9jTlWm)E3EPu7c&=l--Y}m$PZcN z$epcUw=oC&eZXwM(K%d%q2GmGgKRTOi*3vY@!kM$wT0&bZv?y_K}R>CEH$!43b9-P z<_a+Dtr*){?yL(uJDNttr@7owtDJm2tm`054KVwFskdS_sAXL`gt&~&tzG){Wy|YU zto+83MXSXbyStTWd`}`6>D;lif_YiJkizNvuAJd(hcj@J>{xt zP<%me*j49V>aJb1e1Sf3EnQMuS6jQ3ej2>bpsF$BS474xiYAR@VqDJCILjDU^R!;| zOrAzR$5``_xgIYm4AEiIJaXpB)A%G~8D(q+!xqR7JNsA80gArD=tE?*AI(AXFPj9O zMAzWC2KZz6XdvKo`{E+GH3@`&3i3ML94vYL*(9cbm81(^K z{~gf(Mn1@IqFX?pK)$G-5gh~lI^wN2=>4Ea?GmHDN%EILKLR}UNTQE|J_`ToR1*C& z&?B%%J%#9Bfu`TdX;40X4f@ZBhk6^yS-TPqD-72+7L0I~A@#Es zpT{2w6Wh;1K_BknM2EX}S#2HjY}nMewtkbRasB$O@;1-5`n8*64+}-S0Z%Xx*H?xj zQ9emTqe?jL2}QWZm=cFplC^h6gW!ji?ocqS7|>WG>e<7(qrtf1>5N3<%;V`!%Ct<$?Aad&43ihZ#dbNeG59VjE;RR80R z{(ytsh7&>q`w{;yTH2w$Lm8V7sfnXLa1jlV&Z;>`Mq@QD6G!Lu@_lgBzX(U2o%;Ay z;AUcO4pW~O3=>EFJp%ZR zKDwx1QW{7fy(g-G8{r>bz#tyKGxj2$q;H8q6OMYgIY?$+7Pr~cz`3BD-o911$R_1_ h1}?H`j_2`W`5h@RhB>9L=T=9mtX=vjdvSUK>Bb9}xTi z3n3q%A7No3f?%cMdhYHFS+VoNopa88&Fsvc@9xzc$02JDjnPOEZJWeKf)y$t{~zO- z?0M6C0@<0&gOr= zG=GXq$6VmqDwp`4t?@lgTLENUf!mrAZeShS*0A1!UVyW)t^t=4mFgq#m5ipAR9$?G zh&t5jh_H*mpxzTI@SCC=w59TssFEV^`#q)URE1imwB1864D_EKinKr})<(;Z`l2Dt Vmc;Mr=80B;4&IW_@AA)K><4C7UcmqW diff --git a/src/kernel/kc.o b/src/kernel/kc.o deleted file mode 100644 index f393e8e3669330b5b8b6f8a5240022a928989731..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5536 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 AYXATM diff --git a/src/kernel/os.iso b/src/kernel/os.iso deleted file mode 100644 index 730417f7de723a0560d2c08109f2089df6f5deb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34816 zcmeIyF-t=+5CGtdba+lKA{`%ZalKfibZ!?xTMz{Qg@gFt{COq37Qsi?w!?Rj From 8991abf073bb5f56db0c83664076ad9f1349852f Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Tue, 3 Jan 2017 21:39:03 +0100 Subject: [PATCH 04/19] .iso update --- os.iso | Bin 485376 -> 489472 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/os.iso b/os.iso index 335f299193d986050c89f3cc4998cc237699c20b..b60875d608da3f907c5ee690b5a93c5c2ed5e8b7 100644 GIT binary patch delta 4647 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 bvE*qB&Sv%V8o5p9ZMv;Z$HLyLJKq045JR)( From 33137b941027121de4a1418117968cd620f58eaa Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Wed, 4 Jan 2017 18:01:49 +0100 Subject: [PATCH 05/19] shift keyboard map started --- bochsrc.txt | 1 + src/kernel/kernel | Bin 9124 -> 10288 bytes src/kernel/kernel.asm | 1 + src/kernel/kernel.c | 154 ++++++++++++++++++++++++++++++-------- src/kernel/keyboard_map.h | 53 ++++++++++++- src/kernel/lib.c | 32 +++++--- src/kernel/lib.c~ | 32 +++++--- 7 files changed, 219 insertions(+), 54 deletions(-) diff --git a/bochsrc.txt b/bochsrc.txt index 9ab4a59..8453262 100644 --- a/bochsrc.txt +++ b/bochsrc.txt @@ -7,3 +7,4 @@ log: bochslog.txt clock: sync=realtime, time0=local cpu: count=1, ips=1000000 + diff --git a/src/kernel/kernel b/src/kernel/kernel index 7ddffe186585e2d3576237a93f135bdac9ae1f38..9572c54ee709842708ba8ccd5923f50cf24da973 100755 GIT binary patch literal 10288 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 GWBdoFk0%%a diff --git a/src/kernel/kernel.asm b/src/kernel/kernel.asm index 1c2fee7..ac6bda1 100644 --- a/src/kernel/kernel.asm +++ b/src/kernel/kernel.asm @@ -43,6 +43,7 @@ keyboard_handler: start: cli ;block interrupts mov esp, stack_space + push ebx call kmain hlt ;halt the CPU diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 6dda251..abe0d01 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -2,6 +2,8 @@ * Copyright (C) 2014 Arjun Sreedharan * License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html */ +#include "multiboot.h" + #include "keyboard_map.h" /* there are 25 lines each of 80 columns; each element takes 2 bytes */ @@ -18,12 +20,19 @@ #define ENTER_KEY_CODE 0x1C +/* kernel debugging mode */ +unsigned char debug_mode = 0; +unsigned char command[256]; + extern unsigned char keyboard_map[128]; +extern unsigned char keyboard_shift_map[128]; extern void keyboard_handler(void); extern char read_port(unsigned short port); extern void write_port(unsigned short port, unsigned char data); extern void load_idt(unsigned long *idt_ptr); +extern unsigned char keyboard_shift = 0; + /* fb current cursor location */ unsigned int fb_cursor_x = 0, fb_cursor_y = 0; unsigned int fb_current_loc = 0; @@ -65,6 +74,13 @@ struct IDT_entry { struct IDT_entry IDT[IDT_SIZE]; +typedef struct multiboot_memory_map { + unsigned int size; + unsigned long long int base_addr; + unsigned long long int length; + unsigned int type; +} multiboot_memory_map_t; + void idt_init(void) { @@ -144,7 +160,7 @@ void kprint(const char *str) { /* move cursor to previous line end */ fb_cursor_y--; - fb_cursor_x = COLUMNS_IN_LINE - 1; + fb_cursor_x = COLUMNS_IN_LINE; } } @@ -157,9 +173,9 @@ void kprint(const char *str) if (str[i] != '\b') { - if (fb_cursor_x < COLUMNS_IN_LINE) + if (fb_cursor_x < COLUMNS_IN_LINE - 1) { - fb_cursor_x++; + fb_cursor_x = fb_cursor_x + 2; } else { @@ -185,19 +201,35 @@ void kprint(const char *str) fb_current_loc = fb_current_loc + 2; } i++; + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); + + /* show cursor line on next position */ + vidptr[fb_current_loc] = '_'; + vidptr[fb_current_loc + 1] = fb_color; } } void kprint_newline(void) { unsigned int line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; + + /* delete cursor line */ + vidptr[fb_current_loc] = ' '; + vidptr[fb_current_loc + 1] = fb_color; + if (fb_cursor_y < LINES - 1) { fb_current_loc = fb_current_loc + (line_size - fb_current_loc % (line_size)); fb_cursor_y++; fb_cursor_x = 0; - } + + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); + + /* show cursor line on next position */ + vidptr[fb_current_loc ] = '_'; + vidptr[fb_current_loc + 1] = fb_color; + } else { fb_scroll_down (); @@ -275,8 +307,9 @@ void fb_set_color (unsigned char forecolor, unsigned char backcolor) void keyboard_handler_main(void) { unsigned char status; - char keycode; + unsigned char keycode; unsigned char ch[2]; + unsigned char pressed = 0; /* write EOI */ write_port(0x20, 0x20); @@ -301,44 +334,103 @@ void keyboard_handler_main(void) } */ - ch[0] = keyboard_map[(unsigned char) keycode]; - ch[1] = '\0'; + if (keycode & 0x80) + { + pressed = 0; + } + else + { + pressed = 1; + } + + if (keycode == KEY_SCAN_ESCAPE) + { + /* switch debug mode on/off */ + if (debug_mode == 0) + { + debug_mode = 1; + } + else + { + debug_mode = 0; + } + } + + if (debug_mode) + { + kprint_int (keycode, 16); + kprint_newline (); + } + else + { + if (keycode == KEY_SCAN_LEFT_SHIFT || keycode == KEY_SCAN_RIGHT_SHIFT) + { + /* uppercase table */ + keyboard_shift = 1; + } - kprint (ch); + if (keycode == KEY_SCAN_LEFT_SHIFT_RELEASED || keycode == KEY_SCAN_RIGHT_SHIFT_RELEASED) + { + /* lowercase table */ + keyboard_shift = 0; + } + + if (pressed == 1) + { + if (keyboard_shift == 1) + { + ch[0] = keyboard_shift_map[(unsigned char) keycode]; + } + else + { + ch[0] = keyboard_map[(unsigned char) keycode]; + } + + ch[1] = '\0'; + kprint (ch); + } + } } } -void kmain(void) + +void kmain (multiboot_info_t* mbt, unsigned int magic) { - // const char *str = "red"; + multiboot_memory_map_t* mmap = mbt->mmap_addr; + + command[0] = '\0'; + fb_clear_screen(); + fb_set_color(FB_GREEN, FB_BLACK); + kprint ("level 0: RUN"); kprint_newline (); + kprint ("level 1: memory"); kprint_newline (); + + while(mmap < mbt->mmap_addr + mbt->mmap_length) + { + kprint ("RAM at "); kprint_int ((unsigned long int) mmap->base_addr, 16); kprint (" size: "); kprint_int ((unsigned long int) mmap->length, 10); kprint (" bytes"); kprint_newline (); + + mmap = (multiboot_memory_map_t*) ( (unsigned int)mmap + mmap->size + sizeof(mmap->size) ); + } + + idt_init(); + + kprint_newline (); + kprint ("level 2: interrupts"); kprint_newline (); + + kb_init(); + kprint ("level 3: keyboard"); kprint_newline (); + kprint_newline (); + fb_set_color (FB_RED, FB_BLACK); kprint ("red"); fb_set_color (FB_BLUE, FB_BLACK); - kprint (" cube OS"); + kprint (" cube OS "); fb_set_color(FB_WHITE, FB_BLACK); - kprint_newline (); - kprint_int (2017); + kprint_int (2017, 10); kprint_newline (); - - /* - unsigned int line = 1; - unsigned char ch = 'a'; - unsigned char str[2]; - - for (line = 1; line < LINES + 1; line++) - { - str[0] = ch; - str[1] = '\0'; - - kprint (str); kprint_newline(); - ch++; - } - */ - idt_init(); - kb_init(); - + kprint_newline (); + kprint ("READY"); kprint_newline (); while(1); } diff --git a/src/kernel/keyboard_map.h b/src/kernel/keyboard_map.h index e3b2c32..d5bf724 100644 --- a/src/kernel/keyboard_map.h +++ b/src/kernel/keyboard_map.h @@ -3,6 +3,17 @@ All credits where due */ +#define KEY_SCAN_LEFT_SHIFT 0x2A +#define KEY_SCAN_RIGHT_SHIFT 0x36 + +#define KEY_SCAN_LEFT_SHIFT_RELEASED 0xAA +#define KEY_SCAN_RIGHT_SHIFT_RELEASED 0xB6 + +#define KEY_SCAN_ESCAPE 0x01 + + +unsigned char keyboard_shift; + unsigned char keyboard_map[128] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */ @@ -41,4 +52,44 @@ unsigned char keyboard_map[128] = 0, /* F11 Key */ 0, /* F12 Key */ 0, /* All other keys are undefined */ -}; \ No newline at end of file +}; + +unsigned char keyboard_shift_map[128] = +{ + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */ + '9', '0', '-', '=', '\b', /* Backspace */ + '\t', /* Tab */ + 'Q', 'W', 'E', 'R', /* 19 */ + 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', '\n', /* Enter key */ + 0, /* 29 - Control */ + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', /* 39 */ + '\'', '`', 0, /* Left shift */ + '\\', 'Z', 'X', 'C', 'V', 'B', 'N', /* 49 */ + 'M', ',', '.', '/', 0, /* Right shift */ + '*', + 0, /* Alt */ + ' ', /* Space bar */ + 0, /* Caps lock */ + 0, /* 59 - F1 key ... > */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* < ... F10 */ + 0, /* 69 - Num lock*/ + 0, /* Scroll Lock */ + 0, /* Home key */ + 0, /* Up Arrow */ + 0, /* Page Up */ + '-', + 0, /* Left Arrow */ + 0, + 0, /* Right Arrow */ + '+', + 0, /* 79 - End key*/ + 0, /* Down Arrow */ + 0, /* Page Down */ + 0, /* Insert Key */ + 0, /* Delete Key */ + 0, 0, 0, + 0, /* F11 Key */ + 0, /* F12 Key */ + 0, /* All other keys are undefined */ +}; diff --git a/src/kernel/lib.c b/src/kernel/lib.c index 01efa94..79a940f 100644 --- a/src/kernel/lib.c +++ b/src/kernel/lib.c @@ -10,7 +10,7 @@ void strreverse(char* begin, char* end) { } -void itoa(int value, char* str, int base) { +void itoa(unsigned long int value, char* str, int base) { static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; @@ -18,22 +18,16 @@ void itoa(int value, char* str, int base) { int sign; - - - // Validate base if (base<2 || base>35){ *wstr='\0'; return; } - // Take care of sign if ((sign=value) < 0) value = -value; - - // Conversion. Number is reversed. do *wstr++ = num[value%base]; while(value/=base); @@ -43,21 +37,37 @@ void itoa(int value, char* str, int base) { *wstr='\0'; - // Reverse string strreverse(str,wstr-1); - } -void kprint_int (long n) +void kprint_int (long n, int base) { char str[256]; - itoa (n, str, 10); + itoa (n, str, base); kprint (str); } +int strcmp (const char * str1, const char * str2) +{ + while (*str1 == *str2) + { + if (*str1 == '\0' || *str2 == '\0') + break; + + str1++; + str2++; + } + + + if (*str1 == '\0' && *str2 == '\0') + return 0; + else + return -1; +} + diff --git a/src/kernel/lib.c~ b/src/kernel/lib.c~ index 7dac493..ffd0c53 100644 --- a/src/kernel/lib.c~ +++ b/src/kernel/lib.c~ @@ -10,7 +10,7 @@ void strreverse(char* begin, char* end) { } -void itoa(int value, char* str, int base) { +void itoa(long long int value, char* str, int base) { static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; @@ -18,22 +18,16 @@ void itoa(int value, char* str, int base) { int sign; - - - // Validate base if (base<2 || base>35){ *wstr='\0'; return; } - // Take care of sign if ((sign=value) < 0) value = -value; - - // Conversion. Number is reversed. do *wstr++ = num[value%base]; while(value/=base); @@ -43,21 +37,37 @@ void itoa(int value, char* str, int base) { *wstr='\0'; - // Reverse string strreverse(str,wstr-1); - } -void kprint_int (long n) +void kprint_int (long n, int base) { char str[256]; - itoa (n, &str, 10); + itoa (n, str, base); kprint (str); } +int strcmp (const char * str1, const char * str2) +{ + while (*str1 == *str2) + { + if (*str1 == '\0' || *str2 == '\0') + break; + + str1++; + str2++; + } + + + if (*str1 == '\0' && *str2 == '\0') + return 0; + else + return -1; +} + From e42cb1880a93ec3d84c916039aadd8d675005ba8 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Fri, 6 Jan 2017 20:09:07 +0100 Subject: [PATCH 06/19] added vmem.c --- src/kernel/vmem.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/kernel/vmem.c diff --git a/src/kernel/vmem.c b/src/kernel/vmem.c new file mode 100644 index 0000000..7cf8d2b --- /dev/null +++ b/src/kernel/vmem.c @@ -0,0 +1,50 @@ +#include "types.h" + +uint32_t page_directory[1024] __attribute__((aligned(4096))); +uint32_t first_page_table[1024] __attribute__((aligned(4096))); + +// This should go outside any function.. +extern void loadPageDirectory(unsigned int*); +extern void enablePaging(); + +void vmem_init_page_directory (void) +{ + //set each entry to not present + uint16_t i; + + for (i = 0; i < 1024; i++) + { + // This sets the following flags to the pages: + // Supervisor: Only kernel-mode can access them + // Write Enabled: It can be both read from and written to + // Not Present: The page table is not present + page_directory[i] = 0x00000002; + } +} + +void vmem_init_page_table (void) +{ + // holds the physical address where we want to start mapping these pages to. + // in this case, we want to map these pages to the very beginning of memory. + uint16_t i; + + //we will fill all 1024 entries in the table, mapping 4 megabytes + for (i = 0; i < 1024; i++) + { + // As the address is page aligned, it will always leave 12 bits zeroed. + // Those bits are used by the attributes ;) + first_page_table[i] = (i * 0x1000) | 3; // attributes: supervisor level, read/write, present. + } + + // attributes: supervisor level, read/write, present + page_directory[0] = ((unsigned int) first_page_table) | 3; +} + +void vmem_paging (void) +{ + vmem_init_page_directory (); + vmem_init_page_table (); + + loadPageDirectory(page_directory); + enablePaging(); +} From 9c2857882611425f0c4ce92d60f7f428d00560f9 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Fri, 6 Jan 2017 20:09:59 +0100 Subject: [PATCH 07/19] kmalloc() and kfree() --- src/kernel/gdt.c | 82 ++++++++++ src/kernel/physmem.c | 349 +++++++++++++++++++++++++++++++++++++++++++ src/kernel/types.h | 35 +++++ 3 files changed, 466 insertions(+) create mode 100644 src/kernel/gdt.c create mode 100644 src/kernel/physmem.c create mode 100644 src/kernel/types.h diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c new file mode 100644 index 0000000..ba226e0 --- /dev/null +++ b/src/kernel/gdt.c @@ -0,0 +1,82 @@ +#include "types.h" + +/* taken from Brans kernel manual */ + +/* Defines a GDT entry. We say packed, because it prevents the +* compiler from doing things that it thinks is best: Prevent +* compiler "optimization" by packing */ +struct gdt_entry +{ + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle; + uint8_t access; + uint8_t granularity; + uint8_t base_high; +} __attribute__((packed)); + +/* Special pointer which includes the limit: The max bytes +* taken up by the GDT, minus 1. Again, this NEEDS to be packed */ +struct gdt_ptr +{ + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +/* Our GDT, with 3 entries, and finally our special GDT pointer */ +struct gdt_entry gdt[3]; +struct gdt_ptr gp; + +/* This will be a function in start.asm. We use this to properly +* reload the new segment registers */ +extern void gdt_flush(); + + +/* Setup a descriptor in the Global Descriptor Table */ +void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) +{ + /* Setup the descriptor base address */ + gdt[num].base_low = (base & 0xFFFF); + gdt[num].base_middle = (base >> 16) & 0xFF; + gdt[num].base_high = (base >> 24) & 0xFF; + + /* Setup the descriptor limits */ + gdt[num].limit_low = (limit & 0xFFFF); + gdt[num].granularity = ((limit >> 16) & 0x0F); + + /* Finally, set up the granularity and access flags */ + gdt[num].granularity |= (gran & 0xF0); + gdt[num].access = access; +} + +/* Should be called by main. This will setup the special GDT +* pointer, set up the first 3 entries in our GDT, and then +* finally call gdt_flush() in our assembler file in order +* to tell the processor where the new GDT is and update the +* new segment registers */ +void gdt_install() +{ + /* Setup the GDT pointer and limit */ + gp.limit = (sizeof(struct gdt_entry) * 3) - 1; + gp.base = &gdt; + + /* Our NULL descriptor */ + gdt_set_gate(0, 0, 0, 0, 0); + + /* The second entry is our Code Segment. The base address + * is 0, the limit is 4GBytes, it uses 4KByte granularity, + * uses 32-bit opcodes, and is a Code Segment descriptor. + * Please check the table above in the tutorial in order + * to see exactly what each value means */ + gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); + + /* The third entry is our Data Segment. It's EXACTLY the + * same as our code segment, but the descriptor type in + * this entry's access byte says it's a Data Segment */ + gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); + + /* Flush out the old GDT and install the new changes! */ + gdt_flush(); +} + + diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c new file mode 100644 index 0000000..1b65791 --- /dev/null +++ b/src/kernel/physmem.c @@ -0,0 +1,349 @@ +// #include + +#include "types.h" + +uint32_t physmem_pages[PAGES]; + +void set_bit (uint32_t *ptr, uint16_t bit) +{ + uint32_t ret = *ptr; + + ret |= 1 << bit; + + *ptr = ret; +} + +void clear_bit (uint32_t *ptr, uint16_t bit) +{ + uint32_t ret = *ptr; + + ret &= ~(1 << bit); + + *ptr = ret; +} + +uint16_t get_bit (uint32_t n, uint16_t bit) +{ + uint16_t bitset; + + bitset = (n >> bit) & 1; + return (bitset); +} + +void pmem_init_bitmap () +{ + uint32_t page; + + for (page = 0; page < PAGES; page++) + { + physmem_pages[page] = 0xFFFFFFFF; /* mark as reserved */ + } +} + +void pmem_set_first_page () +{ + set_bit (&physmem_pages[0], 0); +} + +uint32_t pmem_get_page_from_address (uint32_t address) +{ + uint32_t page; + page = address / MEM_BLOCK_SIZE; + + return (page); +} + +int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) +{ + /* set bits in bitmap */ + uint32_t start_p, end_p; + int16_t start_b, end_b; + + uint32_t p; int16_t b; + + uint32_t page_start = pmem_get_page_from_address (start); + uint32_t page_end = pmem_get_page_from_address (end); + + /* + kprint ("start address: "); kprint_int (start, 10); kprint_newline (); + kprint ("start page: "); kprint_int (page_start, 10); kprint_newline (); + + kprint ("end address: "); kprint_int (end, 10); kprint_newline (); + kprint ("end page: "); kprint_int (page_end, 10); kprint_newline (); + */ + + + if (page_end > MEM_BLOCKS) + { + return (MEM_ERR_RANGE); + } + + if (page_start >= PAGE_BITS) + { + start_p = page_start / PAGE_BITS; + start_b = page_start - (start_p * PAGE_BITS); + } + else + { + start_p = 0; + start_b = page_start; + } + + if (page_end >= PAGE_BITS) + { + end_p = page_end / PAGE_BITS; + end_b = page_end - (end_p * PAGE_BITS); + } + else + { + end_p = 0; + end_b = page_end; + } + + + + if (end_p > start_p) + { + for (p = start_p; p <= end_p; p++) + { + if (p == end_p) + { + for (b = 0; b <= end_b; b++) + { + if (state == ALLOCATE) + { + set_bit (&physmem_pages[p], b); + } + else + { + clear_bit (&physmem_pages[p], b); + } + } + } + else + { + if (p == start_p) + { + for (b = start_b; b <= PAGE_BITS; b++) + { + if (state == ALLOCATE) + { + set_bit (&physmem_pages[p], b); + } + else + { + clear_bit (&physmem_pages[p], b); + } + } + } + else + { + for (b = 0; b < PAGE_BITS; b++) + { + if (state == ALLOCATE) + { + set_bit (&physmem_pages[p], b); + } + else + { + clear_bit (&physmem_pages[p], b); + } + } + } + } + } + } + else + { + for (b = start_b; b <= end_b; b++) + { + if (state == ALLOCATE) + { + set_bit (&physmem_pages[p], b); + } + else + { + clear_bit (&physmem_pages[p], b); + } + + } + } + return (MEM_ERR_OK); +} + + + + +int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end) +{ + /* return pointers to free memory range */ + int8_t found_start = 0; + + uint32_t p, i; + uint16_t b; + uint32_t start_free, end_free; + size_t mempages, mempages_free = 0; + + + if (size > MEM_BLOCK_SIZE) + { + mempages = size / MEM_BLOCK_SIZE; + } + else + { + mempages = 1; + } + + for (p = 0; p < PAGES; p++) + { + for (b = 0; b < PAGE_BITS; b++) + { + if (get_bit (physmem_pages[p], b) == FREE) + { + if (found_start == 0) + { + found_start = 1; + start_free = PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE; + mempages_free = 1; + + if (mempages == 1) + { + *start = start_free; + *end = start_free; + return (MEM_ERR_OK); + } + } + else + { + mempages_free++; + } + } + else + { + if (mempages_free >= mempages) + { + end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; + + *start = start_free; + *end = end_free; + return (MEM_ERR_OK); + } + else + { + found_start = 0; mempages_free = 0; + } + } + } + if (mempages_free >= mempages) + { + end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; + + *start = start_free; + *end = end_free; + return (MEM_ERR_OK); + } + + } + return (MEM_ERR_NOMEM); +} + +uint32_t pmem_count_free_pages (void) +{ + /* for informantion only: count the free pages */ + + uint32_t pages = 0; + uint32_t p, i; + uint16_t b; + + for (p = 0; p < PAGES; p++) + { + for (b = 0; b < PAGE_BITS; b++) + { + if (get_bit (physmem_pages[p], b) == FREE) + { + pages++; + } + } + } + return (pages); +} + + +uint32_t kmalloc (size_t size) +{ + uint32_t start_free, end_free; + uint32_t *ptr; + size_t new_size; + new_size = size + sizeof (size_t); + + if (pmem_get_free (new_size, &start_free, &end_free) == MEM_ERR_OK) + { + // kprint ("free mem found"); kprint_newline (); + + if (pmem_set_bitmap (start_free, end_free, ALLOCATE) == MEM_ERR_OK) + { + ptr = start_free; + *ptr = new_size; /* store size of memory */ + return (start_free + sizeof (size_t)); + } + else + { + return (NULL); + } + } + else + { + // kprint ("kmalloc: get free failed"); kprint_newline (); + return (NULL); + } +} + +uint32_t kfree (uint32_t address) +{ + uint32_t *ptr; + uint32_t end_mem; + size_t size; + + ptr = address - sizeof (size_t); + size = *ptr; + + // kprint ("kfree: deallocate "); kprint_int ((uint32_t) size, 10); kprint_newline (); + + end_mem = address + size; + + if (pmem_set_bitmap (address, end_mem, FREE) == MEM_ERR_OK) + { + return (NULL); + } + else + { + return (MEM_ERR_BAD_MEM); + } +} + +void pmem_show_page (uint32_t page) +{ + uint16_t b; + + for (b = 0; b < PAGE_BITS; b++) + { + if (get_bit (physmem_pages[page], b) == FREE) + { + kprint ("0 "); + } + else + { + kprint ("1 "); + } + } + kprint_newline (); +} + + + +/* +void main () +{ + pmem_set_bitmap (1024 * 1024, 0x300000, ALLOCATE); +} +*/ diff --git a/src/kernel/types.h b/src/kernel/types.h new file mode 100644 index 0000000..a8545f7 --- /dev/null +++ b/src/kernel/types.h @@ -0,0 +1,35 @@ +/* 32 bit executable */ + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; + +typedef uint32_t size_t; + +#define TRUE 1 +#define FALSE 0 +#define NULL 0L + +/* memory */ +#define MEM_BLOCK_SIZE 4096 +#define MEM_BLOCKS 1048576 /* enough for 4 GB */ +#define PAGE_BITS 32 + +#define RESERVED 2 +#define FREE 0 +#define ALLOCATED 1 + +#define ALLOCATE 1 + +#define PAGES MEM_BLOCKS / PAGE_BITS + +#define MEM_ERR_OK 0 +#define MEM_ERR_BAD_MEM 1 +#define MEM_ERR_RANGE 2 +#define MEM_ERR_NOMEM 3 From 2008b7c6e0855561dd74b1d0f88258734497b014 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Fri, 6 Jan 2017 20:30:58 +0100 Subject: [PATCH 08/19] merge --- src/kernel/kernel.c | 233 ++++++++++++++++++++++++++++++-------- src/kernel/keyboard_map.h | 6 +- src/kernel/lib.c | 70 ++++++++++-- src/kernel/physmem.c | 40 ++++++- 4 files changed, 285 insertions(+), 64 deletions(-) diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index abe0d01..5de95c5 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -2,6 +2,7 @@ * Copyright (C) 2014 Arjun Sreedharan * License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html */ +#include "types.h" #include "multiboot.h" #include "keyboard_map.h" @@ -21,21 +22,27 @@ #define ENTER_KEY_CODE 0x1C /* kernel debugging mode */ -unsigned char debug_mode = 0; -unsigned char command[256]; +uint8_t debug_mode = 0; +uint8_t command[256]; -extern unsigned char keyboard_map[128]; -extern unsigned char keyboard_shift_map[128]; + +/* memory */ +uint32_t mem_start_address = 0x100000; +uint32_t mem_end_address; +uint32_t mem_use_address = 0x400000; + +extern uint8_t keyboard_map[128]; +extern uint8_t keyboard_shift_map[128]; extern void keyboard_handler(void); -extern char read_port(unsigned short port); +extern int8_t read_port(unsigned short port); extern void write_port(unsigned short port, unsigned char data); extern void load_idt(unsigned long *idt_ptr); -extern unsigned char keyboard_shift = 0; +extern uint8_t keyboard_shift; /* fb current cursor location */ -unsigned int fb_cursor_x = 0, fb_cursor_y = 0; -unsigned int fb_current_loc = 0; +uint16_t fb_cursor_x = 0, fb_cursor_y = 0; +uint32_t fb_current_loc = 0; /* fb text color */ @@ -56,40 +63,40 @@ unsigned int fb_current_loc = 0; #define FB_LIGHT_BROWN 0x14 #define FB_WHITE 0x0f -unsigned char fb_color = FB_LIGHT_GREY; /* light grey */ +uint8_t fb_color = FB_LIGHT_GREY; /* light grey */ /* video memory begins at address 0xb8000 */ -char *vidptr = (char*)0xb8000; +static int8_t *vidptr = (int8_t*)0xb8000; /* frame buffer console */ -static unsigned char fb_con[SCREENSIZE]; +uint8_t fb_con[SCREENSIZE]; struct IDT_entry { - unsigned short int offset_lowerbits; - unsigned short int selector; - unsigned char zero; - unsigned char type_attr; - unsigned short int offset_higherbits; + uint16_t offset_lowerbits; + uint16_t selector; + uint8_t zero; + uint8_t type_attr; + uint16_t offset_higherbits; }; struct IDT_entry IDT[IDT_SIZE]; typedef struct multiboot_memory_map { - unsigned int size; - unsigned long long int base_addr; - unsigned long long int length; - unsigned int type; + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; } multiboot_memory_map_t; void idt_init(void) { - unsigned long keyboard_address; - unsigned long idt_address; - unsigned long idt_ptr[2]; + uint32_t keyboard_address; + uint32_t idt_address; + uint32_t idt_ptr[2]; /* populate IDT entry of keyboard's interrupt */ - keyboard_address = (unsigned long)keyboard_handler; + keyboard_address = (uint32_t)keyboard_handler; IDT[0x21].offset_lowerbits = keyboard_address & 0xffff; IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET; IDT[0x21].zero = 0; @@ -128,7 +135,7 @@ void idt_init(void) write_port(0xA1 , 0xff); /* fill the IDT descriptor */ - idt_address = (unsigned long)IDT ; + idt_address = (uint32_t)IDT ; idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16); idt_ptr[1] = idt_address >> 16 ; @@ -143,7 +150,7 @@ void kb_init(void) void kprint(const char *str) { - unsigned int i = 0; + uint32_t i = 0; while (str[i] != '\0') { if (str[i] == '\b') @@ -170,12 +177,11 @@ void kprint(const char *str) fb_blit (); } - - if (str[i] != '\b') + else { if (fb_cursor_x < COLUMNS_IN_LINE - 1) { - fb_cursor_x = fb_cursor_x + 2; + fb_cursor_x = fb_cursor_x + 1; } else { @@ -202,7 +208,7 @@ void kprint(const char *str) } i++; - fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); + fb_move_cursor ((fb_cursor_x + 1) + fb_cursor_y * COLUMNS_IN_LINE); /* show cursor line on next position */ vidptr[fb_current_loc] = '_'; @@ -212,7 +218,7 @@ void kprint(const char *str) void kprint_newline(void) { - unsigned int line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; + uint16_t line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; /* delete cursor line */ vidptr[fb_current_loc] = ' '; @@ -220,7 +226,9 @@ void kprint_newline(void) if (fb_cursor_y < LINES - 1) { - fb_current_loc = fb_current_loc + (line_size - fb_current_loc % (line_size)); + // fb_current_loc = fb_current_loc + (line_size - (fb_current_loc % line_size)); + + fb_current_loc = fb_current_loc + (COLUMNS_IN_LINE - fb_cursor_x) * BYTES_FOR_EACH_ELEMENT; fb_cursor_y++; fb_cursor_x = 0; @@ -233,15 +241,16 @@ void kprint_newline(void) else { fb_scroll_down (); - fb_current_loc = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); + fb_current_loc = SCREENSIZE - line_size; + fb_cursor_x = 0; } } void fb_scroll_down (void) { - unsigned int old_pos = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE, new_pos = 0; - unsigned int x_pos; - unsigned int y_pos; + uint16_t old_pos = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE, new_pos = 0; + uint16_t x_pos; + uint16_t y_pos; for (y_pos = 1; y_pos < LINES; y_pos++) { @@ -265,7 +274,7 @@ void fb_scroll_down (void) void fb_blit (void) { - unsigned int i = 0, j = 0; + uint16_t i = 0, j = 0; while (i < SCREENSIZE) { @@ -276,7 +285,7 @@ void fb_blit (void) void fb_clear_screen(void) { - unsigned int i = 0; + uint16_t i = 0; while (i < SCREENSIZE) { vidptr[i++] = ' '; vidptr[i++] = 0x00; @@ -292,7 +301,7 @@ void fb_clear_screen(void) void fb_clear_con (void) { - unsigned int i = 0; + uint16_t i = 0; while (i < SCREENSIZE) { fb_con[i++] = ' '; fb_con[i++] = 0x00; @@ -306,10 +315,10 @@ void fb_set_color (unsigned char forecolor, unsigned char backcolor) void keyboard_handler_main(void) { - unsigned char status; - unsigned char keycode; - unsigned char ch[2]; - unsigned char pressed = 0; + uint8_t status; + uint8_t keycode; + uint8_t ch[2]; + uint8_t pressed = 0; /* write EOI */ write_port(0x20, 0x20); @@ -396,22 +405,118 @@ void keyboard_handler_main(void) void kmain (multiboot_info_t* mbt, unsigned int magic) { + gdt_install (); /* init memory GDT */ + // vmem_paging (); /* switch paging on */ + multiboot_memory_map_t* mmap = mbt->mmap_addr; + uint32_t mem_start, mem_end; + uint32_t mem_type; + uint8_t mem_found = 0; + int16_t mem; + uint32_t pages_free; + uint64_t ram_free; command[0] = '\0'; fb_clear_screen(); - + fb_set_color(FB_GREEN, FB_BLACK); kprint ("level 0: RUN"); kprint_newline (); kprint ("level 1: memory"); kprint_newline (); - + + pmem_init_bitmap (); + while(mmap < mbt->mmap_addr + mbt->mmap_length) { - kprint ("RAM at "); kprint_int ((unsigned long int) mmap->base_addr, 16); kprint (" size: "); kprint_int ((unsigned long int) mmap->length, 10); kprint (" bytes"); kprint_newline (); + mem_start = mmap->base_addr; + mem_end = mem_start + mmap->length; + mem_type = mmap->type; - mmap = (multiboot_memory_map_t*) ( (unsigned int)mmap + mmap->size + sizeof(mmap->size) ); + if (mem_type == 1) + { + kprint ("RAM at "); kprint_int ((uint32_t) mmap->base_addr, 16); + + if (mmap->length < 1024 * 1024) + { + kprint (" size: "); kprint_int ((uint32_t) mmap->length, 10); kprint (" bytes "); + } + else + { + kprint (" size: "); kprint_int ((uint32_t) mmap->length / (1024 * 1024), 10); kprint (" MB "); + } + kprint ("type: "); kprint_int ((uint32_t) mmap->type, 16); + + if (mem_start >= mem_start_address) + { + mem_end_address = mem_end; + mem_found = 1; + + mem = pmem_set_bitmap (mem_use_address, mem_end, FREE); + if (mem == MEM_ERR_OK) + { + kprint (" found base"); + } + else + { + kprint (" MEMORY ERROR: "); + + if (mem == MEM_ERR_RANGE) + { + kprint ("range"); + } + + if (mem == MEM_ERR_BAD_MEM || mem == MEM_ERR_DOUBLE_FREE) + { + kprint ("alloc error"); + } + } + kprint_newline (); + } + else + { + if (mem_start == 0) + { + mem_start = (uint32_t) 0x1000; /* skip first page */ + } + + mem = pmem_set_bitmap (mem_start, mem_end, FREE); + if (mem == MEM_ERR_OK) + { + kprint (" free"); + } + else + { + kprint (" MEMORY ERROR: "); + + if (mem == MEM_ERR_RANGE) + { + kprint ("range"); + } + + if (mem == MEM_ERR_BAD_MEM || mem == MEM_ERR_DOUBLE_FREE) + { + kprint ("alloc error"); + } + } + kprint_newline (); + } + } + + mmap = (multiboot_memory_map_t*) ( (uint32_t)mmap + mmap->size + sizeof(mmap->size) ); } + + pmem_set_first_page (); /* so no null pointer for free mem block can exist */ + + mem = pmem_set_bitmap ((uint32_t) 0xF00000, (uint32_t) 0xFFFFFF, ALLOCATE); + if (mem != MEM_ERR_OK) + { + kprint ("MEMORY ERROR: mark reserved"); + kprint_newline (); + } + + pages_free = pmem_count_free_pages (); + ram_free = (pages_free * 4096) / 1024 /1024; + kprint ("free pages: "); kprint_int (pages_free, 10); kprint (" = " ); kprint_int ((uint32_t) ram_free, 10); kprint (" MB"); kprint_newline (); idt_init(); @@ -431,6 +536,40 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) kprint_int (2017, 10); kprint_newline (); kprint_newline (); + + uint32_t i; + + /* + for (i = 0; i < 10; i++) + { + pmem_show_page (i); + } + */ + + + /* + uint8_t *buf; + buf = (uint8_t *) kmalloc (100000); + if (buf != NULL) + { + kprint ("mem allocated at "); kprint_int (buf, 16); kprint_newline (); + } + else + { + kprint ("mem allocate failed"); + } + kprint_newline (); + + + if (kfree (buf) != NULL) + { + kprint ("free ERROR!"); kprint_newline (); + } + */ + + kprint_newline (); + kprint ("READY"); kprint_newline (); + while(1); } diff --git a/src/kernel/keyboard_map.h b/src/kernel/keyboard_map.h index d5bf724..907bea3 100644 --- a/src/kernel/keyboard_map.h +++ b/src/kernel/keyboard_map.h @@ -12,9 +12,9 @@ #define KEY_SCAN_ESCAPE 0x01 -unsigned char keyboard_shift; +uint8_t keyboard_shift; -unsigned char keyboard_map[128] = +uint8_t keyboard_map[128] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */ '9', '0', '-', '=', '\b', /* Backspace */ @@ -54,7 +54,7 @@ unsigned char keyboard_map[128] = 0, /* All other keys are undefined */ }; -unsigned char keyboard_shift_map[128] = +uint8_t keyboard_shift_map[128] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */ '9', '0', '-', '=', '\b', /* Backspace */ diff --git a/src/kernel/lib.c b/src/kernel/lib.c index 79a940f..5c38ff4 100644 --- a/src/kernel/lib.c +++ b/src/kernel/lib.c @@ -1,8 +1,10 @@ /* lib.c - some helper functions */ -void strreverse(char* begin, char* end) { +#include "types.h" + +void strreverse(uint8_t* begin, uint8_t* end) { - char aux; + uint8_t aux; while(end>begin) @@ -10,13 +12,13 @@ void strreverse(char* begin, char* end) { } -void itoa(unsigned long int value, char* str, int base) { +void itoa(int32_t value, uint8_t* str, int32_t base) { - static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static uint8_t num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - char* wstr=str; + uint8_t* wstr=str; - int sign; + int32_t sign; // Validate base @@ -44,7 +46,7 @@ void itoa(unsigned long int value, char* str, int base) { } -void kprint_int (long n, int base) +void kprint_int (int32_t n, int32_t base) { char str[256]; @@ -52,7 +54,7 @@ void kprint_int (long n, int base) kprint (str); } -int strcmp (const char * str1, const char * str2) +int16_t strcmp (const uint8_t * str1, const uint8_t * str2) { while (*str1 == *str2) { @@ -70,4 +72,56 @@ int strcmp (const char * str1, const char * str2) return -1; } +uint32_t strlen (const uint8_t *str) +{ + uint32_t slen = 0; + + while (*str != '\0') + { + slen++; + str++; + } + + return (slen); +} + +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = src[i]; + } + + return (dest); +} + +uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = val; + } + + return (dest); +} + +uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = val; + } + + return (dest); +} + + + + diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c index 1b65791..101ebb1 100644 --- a/src/kernel/physmem.c +++ b/src/kernel/physmem.c @@ -116,7 +116,14 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) } else { - clear_bit (&physmem_pages[p], b); + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + else + { + clear_bit (&physmem_pages[p], b); + } } } } @@ -124,7 +131,7 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) { if (p == start_p) { - for (b = start_b; b <= PAGE_BITS; b++) + for (b = start_b; b < PAGE_BITS; b++) { if (state == ALLOCATE) { @@ -132,7 +139,14 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) } else { - clear_bit (&physmem_pages[p], b); + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + else + { + clear_bit (&physmem_pages[p], b); + } } } } @@ -146,7 +160,14 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) } else { - clear_bit (&physmem_pages[p], b); + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + else + { + clear_bit (&physmem_pages[p], b); + } } } } @@ -163,7 +184,14 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) } else { - clear_bit (&physmem_pages[p], b); + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + else + { + clear_bit (&physmem_pages[p], b); + } } } @@ -269,7 +297,7 @@ uint32_t pmem_count_free_pages (void) } -uint32_t kmalloc (size_t size) +void *kmalloc (size_t size) { uint32_t start_free, end_free; uint32_t *ptr; From 7bd6dd3ae6bd0f05acacc26249857906f2dfd2fa Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Fri, 6 Jan 2017 21:35:16 +0100 Subject: [PATCH 09/19] merge --- src/kernel/multiboot.h | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/kernel/multiboot.h diff --git a/src/kernel/multiboot.h b/src/kernel/multiboot.h new file mode 100644 index 0000000..6402363 --- /dev/null +++ b/src/kernel/multiboot.h @@ -0,0 +1,122 @@ +/* multiboot.h - the header for Multiboot */ + /* Copyright (C) 1999, 2001 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* Macros. */ + + /* The magic number for the Multiboot header. */ + #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + + /* The flags for the Multiboot header. */ + #ifdef __ELF__ + # define MULTIBOOT_HEADER_FLAGS 0x00000003 + #else + # define MULTIBOOT_HEADER_FLAGS 0x00010003 + #endif + + /* The magic number passed by a Multiboot-compliant boot loader. */ + #define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + + /* The size of our stack (16KB). */ + #define STACK_SIZE 0x4000 + + /* C symbol format. HAVE_ASM_USCORE is defined by configure. */ + #ifdef HAVE_ASM_USCORE + # define EXT_C(sym) _ ## sym + #else + # define EXT_C(sym) sym + #endif + + #ifndef ASM + /* Do not include here in boot.S. */ + + /* Types. */ + + /* The Multiboot header. */ + typedef struct multiboot_header + { + unsigned long magic; + unsigned long flags; + unsigned long checksum; + unsigned long header_addr; + unsigned long load_addr; + unsigned long load_end_addr; + unsigned long bss_end_addr; + unsigned long entry_addr; + } multiboot_header_t; + + /* The symbol table for a.out. */ + typedef struct aout_symbol_table + { + unsigned long tabsize; + unsigned long strsize; + unsigned long addr; + unsigned long reserved; + } aout_symbol_table_t; + + /* The section header table for ELF. */ + typedef struct elf_section_header_table + { + unsigned long num; + unsigned long size; + unsigned long addr; + unsigned long shndx; + } elf_section_header_table_t; + + /* The Multiboot information. */ + typedef struct multiboot_info + { + unsigned long flags; + unsigned long mem_lower; + unsigned long mem_upper; + unsigned long boot_device; + unsigned long cmdline; + unsigned long mods_count; + unsigned long mods_addr; + union + { + aout_symbol_table_t aout_sym; + elf_section_header_table_t elf_sec; + } u; + unsigned long mmap_length; + unsigned long mmap_addr; + } multiboot_info_t; + + /* The module structure. */ + typedef struct module + { + unsigned long mod_start; + unsigned long mod_end; + unsigned long string; + unsigned long reserved; + } module_t; + + /* The memory map. Be careful that the offset 0 is base_addr_low + but no size. */ + + /* + typedef struct memory_map + { + unsigned long size; + unsigned long base_addr_low; + unsigned long base_addr_high; + unsigned long length_low; + unsigned long length_high; + unsigned long type; + } memory_map_t; + */ + #endif /* ! ASM */ + From 255bd92b9d46c6f0e4413d871553c2b38bf3a6a0 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sat, 14 Jan 2017 11:52:14 +0100 Subject: [PATCH 10/19] threading CPU scheduler --- src/kernel/idt.c | 88 +++++++++++++++++++ src/kernel/idt.h | 105 +++++++++++++++++++++++ src/kernel/interruptStubs.asm | 153 +++++++++++++++++++++++++++++++++ src/kernel/interrupts.c | 53 ++++++++++++ src/kernel/interrupts.h | 55 ++++++++++++ src/kernel/io.c | 9 +- src/kernel/pic.c | 157 ++++++++++++++++++++++++++++++++++ src/kernel/pic.h | 37 ++++++++ src/kernel/pit.c | 26 ++++++ src/kernel/pit.h | 13 +++ src/kernel/ports.h | 33 +++++++ src/kernel/registers.h | 20 +++++ src/kernel/thread.c | 132 ++++++++++++++++++++++++++++ src/kernel/thread.h | 43 ++++++++++ src/kernel/threadS.asm | 51 +++++++++++ 15 files changed, 971 insertions(+), 4 deletions(-) create mode 100644 src/kernel/idt.c create mode 100644 src/kernel/idt.h create mode 100644 src/kernel/interruptStubs.asm create mode 100644 src/kernel/interrupts.c create mode 100644 src/kernel/interrupts.h create mode 100644 src/kernel/pic.c create mode 100644 src/kernel/pic.h create mode 100644 src/kernel/pit.c create mode 100644 src/kernel/pit.h create mode 100644 src/kernel/ports.h create mode 100644 src/kernel/registers.h create mode 100644 src/kernel/thread.c create mode 100644 src/kernel/thread.h create mode 100644 src/kernel/threadS.asm diff --git a/src/kernel/idt.c b/src/kernel/idt.c new file mode 100644 index 0000000..3db6710 --- /dev/null +++ b/src/kernel/idt.c @@ -0,0 +1,88 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + + +#include "idt.h" +#include "types.h" + +struct idt_entry idt[I86_IDT_MAX_ENTRY_COUNT]; +struct idt_ptr ip; + + +void idt_flush() +{ + asm("lidt %0" : "=m" (ip)); +} + +void idt_set_gate(uint8_t i, uint32_t base, uint16_t sel, uint8_t flags) +{ + idt[i].offset_low = base & 0xFFFF; + idt[i].offset_high = (base >> 16) & 0xFFFF; + + idt[i].selector = sel; + idt[i].zero = 0; + + idt[i].type_attr = flags; +} + +void idt_install() +{ + ip.size = sizeof(struct idt_entry) * I86_IDT_MAX_ENTRY_COUNT - 1; + ip.offset = (uint32_t)&idt; + + memset(&idt, 0, sizeof(struct idt_entry) * I86_IDT_MAX_ENTRY_COUNT); + + idt_set_gate(0, (uint32_t)isr0, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(1, (uint32_t)isr1, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(2, (uint32_t)isr2, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(3, (uint32_t)isr3, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(4, (uint32_t)isr4, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(5, (uint32_t)isr5, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(6, (uint32_t)isr6, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(7, (uint32_t)isr7, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(8, (uint32_t)isr8, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(9, (uint32_t)isr9, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(10, (uint32_t)isr10, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(11, (uint32_t)isr11, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(12, (uint32_t)isr12, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(13, (uint32_t)isr13, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(14, (uint32_t)isr14, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(15, (uint32_t)isr15, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(16, (uint32_t)isr16, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(17, (uint32_t)isr17, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(18, (uint32_t)isr18, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(19, (uint32_t)isr19, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(20, (uint32_t)isr20, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(21, (uint32_t)isr21, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(22, (uint32_t)isr22, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(23, (uint32_t)isr23, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(24, (uint32_t)isr24, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(25, (uint32_t)isr25, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(26, (uint32_t)isr26, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(27, (uint32_t)isr27, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(28, (uint32_t)isr28, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(29, (uint32_t)isr29, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(30, (uint32_t)isr30, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(31, (uint32_t)isr31, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + + idt_set_gate(32, (uint32_t)irq0, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(33, (uint32_t)irq1, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(34, (uint32_t)irq2, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(35, (uint32_t)irq3, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(36, (uint32_t)irq4, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(37, (uint32_t)irq5, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(38, (uint32_t)irq6, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(39, (uint32_t)irq7, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(40, (uint32_t)irq8, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(41, (uint32_t)irq9, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(42, (uint32_t)irq10, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(43, (uint32_t)irq11, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(44, (uint32_t)irq12, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(45, (uint32_t)irq13, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(46, (uint32_t)irq14, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + idt_set_gate(47, (uint32_t)irq15, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); + + idt_flush(); +} diff --git a/src/kernel/idt.h b/src/kernel/idt.h new file mode 100644 index 0000000..5571d6f --- /dev/null +++ b/src/kernel/idt.h @@ -0,0 +1,105 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _ARCH_IDT_H +#define _ARCH_IDT_H + +#include "types.h" + +// count of gdt entries +#define I86_IDT_MAX_ENTRY_COUNT 255 + +// idt descriptor type attribute flags +#define I86_IDT_ATTR_TASK_GATE 0x05 //00000101 +#define I86_IDT_ATTR_16BIT_INT_GATE 0x06 //00000110 +#define I86_IDT_ATTR_16BIT_TRAP_GATE 0x07 //00000111 +#define I86_IDT_ATTR_32BIT_INT_GATE 0x0E //00001110 +#define I86_IDT_ATTR_32BIT_TRAP_GATE 0x0F //00001111 + +#define I86_IDT_ATTR_SYSTEM_SEGMENT 0x10 //00010000 + +#define I86_IDT_ATTR_PRIV_KERNEL 0x00 //00000000 +#define I86_IDT_ATTR_PRIV_RING1 0x20 //00100000 +#define I86_IDT_ATTR_PRIV_RING2 0x40 //01000000 +#define I86_IDT_ATTR_PRIV_USER 0x60 //01100000 + +#define I86_IDT_ATTR_PRESENT 0x80 //10000000 + +struct idt_entry +{ + uint16_t offset_low; + uint16_t selector; + uint8_t zero; + uint8_t type_attr; + uint16_t offset_high; +} __attribute__((packed)); + +struct idt_ptr +{ + uint16_t size; + uint32_t offset; +} __attribute__((packed)); + +void idt_flush(void); +void idt_set_gate(uint8_t i, uint32_t base, uint16_t sel, uint8_t flags); +void idt_install(void); + +/** + * ISRs for Exceptions (IRQs 0 to 31) (see interrupt.S) +**/ +void isr0(void); +void isr1(void); +void isr2(void); +void isr3(void); +void isr4(void); +void isr5(void); +void isr6(void); +void isr7(void); +void isr8(void); +void isr9(void); +void isr10(void); +void isr11(void); +void isr12(void); +void isr13(void); +void isr14(void); +void isr15(void); +void isr16(void); +void isr17(void); +void isr18(void); +void isr19(void); +void isr20(void); +void isr21(void); +void isr22(void); +void isr23(void); +void isr24(void); +void isr25(void); +void isr26(void); +void isr27(void); +void isr28(void); +void isr29(void); +void isr30(void); +void isr31(void); + +/** + * ISRs for Hardware-IRQs (IRQs 32 to 47) (see interrupt.S) +**/ +void irq0(void); +void irq1(void); +void irq2(void); +void irq3(void); +void irq4(void); +void irq5(void); +void irq6(void); +void irq7(void); +void irq8(void); +void irq9(void); +void irq10(void); +void irq11(void); +void irq12(void); +void irq13(void); +void irq14(void); +void irq15(void); + +#endif diff --git a/src/kernel/interruptStubs.asm b/src/kernel/interruptStubs.asm new file mode 100644 index 0000000..9b220ab --- /dev/null +++ b/src/kernel/interruptStubs.asm @@ -0,0 +1,153 @@ +; by Andreas Galauner +; +; https://github.com/G33KatWork + + +[BITS 32] + +%macro ISR_NOERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli + push byte 0 + push byte %1 + jmp isr_common_stub +%endmacro + +%macro ISR_ERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli + push byte %1 + jmp isr_common_stub +%endmacro + +%macro IRQ 2 + [GLOBAL irq%1] + irq%1: + cli + push byte 0 + push byte %2 + jmp irq_common_stub +%endmacro + +;Define standard ISRs (Exceptions, Errors etc.) +ISR_NOERRCODE 0 +ISR_NOERRCODE 1 +ISR_NOERRCODE 2 +ISR_NOERRCODE 3 +ISR_NOERRCODE 4 +ISR_NOERRCODE 5 +ISR_NOERRCODE 6 +ISR_NOERRCODE 7 +ISR_ERRCODE 8 +ISR_NOERRCODE 9 +ISR_ERRCODE 10 +ISR_ERRCODE 11 +ISR_ERRCODE 12 +ISR_ERRCODE 13 +ISR_ERRCODE 14 +ISR_NOERRCODE 15 +ISR_NOERRCODE 16 +ISR_NOERRCODE 17 +ISR_NOERRCODE 18 +ISR_NOERRCODE 19 +ISR_NOERRCODE 20 +ISR_NOERRCODE 21 +ISR_NOERRCODE 22 +ISR_NOERRCODE 23 +ISR_NOERRCODE 24 +ISR_NOERRCODE 25 +ISR_NOERRCODE 26 +ISR_NOERRCODE 27 +ISR_NOERRCODE 28 +ISR_NOERRCODE 29 +ISR_NOERRCODE 30 +ISR_NOERRCODE 31 + +IRQ 0, 32 +IRQ 1, 33 +IRQ 2, 34 +IRQ 3, 35 +IRQ 4, 36 +IRQ 5, 37 +IRQ 6, 38 +IRQ 7, 39 +IRQ 8, 40 +IRQ 9, 41 +IRQ 10, 42 +IRQ 11, 43 +IRQ 12, 44 +IRQ 13, 45 +IRQ 14, 46 +IRQ 15, 47 + +;Our C-Handler for fault interrupts +[EXTERN interrupts_faultHandler] +isr_common_stub: + pusha ; push all registers (see regs.h for corresponding struct) + + mov ax, ds ; save old data segment decriptor + push eax + + push es + push fs + push gs + + mov ax, 0x10 ; set kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + call interrupts_faultHandler + + pop gs + pop fs + pop es + + pop eax ; reload original data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + popa + add esp, 8 ; clean up the pushed error code and pushed ISR number + sti + iret + +;Our C-Handler for common interrupts +[EXTERN interrupts_interruptHandler] +irq_common_stub: + pusha ; push all registers (see regs.h for corresponding struct) + + mov ax, ds ; save old data segment decriptor + push eax + + push es + push fs + push gs + + mov ax, 0x10 ; set kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + call interrupts_interruptHandler + + pop gs + pop fs + pop es + + pop eax ; reload original data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + popa + add esp, 8 ; clean up the pushed error code and pushed ISR number + sti + iret diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c new file mode 100644 index 0000000..0d1becd --- /dev/null +++ b/src/kernel/interrupts.c @@ -0,0 +1,53 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "interrupts.h" +#include "pic.h" + +static isrFunction isrs[I86_IDT_MAX_ENTRY_COUNT]; + +void interrupts_init() +{ + memset(isrs, 0, sizeof(isrFunction) * I86_IDT_MAX_ENTRY_COUNT); +} + +void interrupts_faultHandler(registers_t regs) +{ + if(isrs[regs.int_no] != 0) + isrs[regs.int_no](®s); + else + { + kprint ("Unhandled fault: "); + kprint_int (regs.int_no, 10); + kprint_newline (); + + interrupts_disable(); + for(;;); + } +} + +void interrupts_interruptHandler(registers_t regs) +{ + pic_notify(regs.int_no); + + if(isrs[regs.int_no] != 0) + isrs[regs.int_no](®s); + else + { + kprint ("Unhandled interrupt: "); + kprint_int (regs.int_no, 10); + kprint_newline (); + } +} + +void interrupts_registerHandler(uint8_t i, isrFunction func) +{ + isrs[i] = func; +} + +void interrupts_unregisterHandler(uint8_t i) +{ + isrs[i] = 0; +} diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h new file mode 100644 index 0000000..0b36b63 --- /dev/null +++ b/src/kernel/interrupts.h @@ -0,0 +1,55 @@ +#ifndef _INTERRUPTS_H +#define _INTERRUPTS_H +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "types.h" +#include "registers.h" +#include "idt.h" + +// The following devices use PIC 1 to generate interrupts +#define IRQ_TIMER 32 +#define IRQ_KEYBOARD 33 +#define IRQ_SERIAL2 34 +#define IRQ_SERIAL1 35 +#define IRQ_PARALLEL2 36 +#define IRQ_DISKETTE 37 +#define IRQ_PARALLEL1 38 + +// The following devices use PIC 2 to generate interrupts +#define IRQ_CMOSTIMER 39 +#define IRQ_CGARETRACE 40 +#define IRQ_AUXILIARY 41 +#define IRQ_FPU 42 +#define IRQ_HDC 43 + +// Set the following number to the maximum nested exceptions +// the kernel tries to resolve before it will panic +//#define MAX_NESTED_EXCEPTIONS 1 + +typedef void(*isrFunction)(registers_t* regs); + +//Handler called by our asm stubs +extern void interrupts_faultHandler(registers_t regs); +extern void interrupts_interruptHandler(registers_t regs); + +void interrupts_init(void); + +void interrupts_registerHandler(uint8_t i, void (*irsFunction)(registers_t* regs)); +void interrupts_unregisterHandler(uint8_t i); + +static inline void interrupts_enable(void) __attribute__((always_inline)); +static inline void interrupts_enable(void) +{ + asm volatile ("sti"); +} + +static inline void interrupts_disable(void) __attribute__((always_inline)); +static inline void interrupts_disable(void) +{ + asm volatile ("cli"); +} + +#endif diff --git a/src/kernel/io.c b/src/kernel/io.c index caa2c4d..1bf21df 100644 --- a/src/kernel/io.c +++ b/src/kernel/io.c @@ -1,4 +1,5 @@ #include "io.h" +#include "ports.h" /* The I/O ports */ #define FB_COMMAND_PORT 0x3D4 @@ -15,8 +16,8 @@ */ void fb_move_cursor(unsigned short pos) { - write_port(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); - write_port(FB_DATA_PORT, ((pos >> 8) & 0x00FF)); - write_port(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); - write_port(FB_DATA_PORT, pos & 0x00FF); + outb (FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); + outb (FB_DATA_PORT, ((pos >> 8) & 0x00FF)); + outb (FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); + outb (FB_DATA_PORT, pos & 0x00FF); } diff --git a/src/kernel/pic.c b/src/kernel/pic.c new file mode 100644 index 0000000..17c0dc5 --- /dev/null +++ b/src/kernel/pic.c @@ -0,0 +1,157 @@ +#include "pic.h" +#include "ports.h" + +//----------------------------------------------- +// Controller Registers +//----------------------------------------------- + +// PIC 1 register port addresses +#define I86_PIC1_REG_COMMAND 0x20 +#define I86_PIC1_REG_STATUS 0x20 +#define I86_PIC1_REG_DATA 0x21 +#define I86_PIC1_REG_IMR 0x21 + +// PIC 2 register port addresses +#define I86_PIC2_REG_COMMAND 0xA0 +#define I86_PIC2_REG_STATUS 0xA0 +#define I86_PIC2_REG_DATA 0xA1 +#define I86_PIC2_REG_IMR 0xA1 + +//----------------------------------------------- +// Initialization Command Bit Masks +//----------------------------------------------- + +// Initialization Control Word 1 bit masks +#define I86_PIC_ICW1_MASK_IC4 0x1 //00000001 +#define I86_PIC_ICW1_MASK_SNGL 0x2 //00000010 +#define I86_PIC_ICW1_MASK_ADI 0x4 //00000100 +#define I86_PIC_ICW1_MASK_LTIM 0x8 //00001000 +#define I86_PIC_ICW1_MASK_INIT 0x10 //00010000 + +// Initialization Control Words 2 and 3 do not require bit masks + +// Initialization Control Word 4 bit masks +#define I86_PIC_ICW4_MASK_UPM 0x1 //00000001 +#define I86_PIC_ICW4_MASK_AEOI 0x2 //00000010 +#define I86_PIC_ICW4_MASK_MS 0x4 //00000100 +#define I86_PIC_ICW4_MASK_BUF 0x8 //00001000 +#define I86_PIC_ICW4_MASK_SFNM 0x10 //00010000 + +//----------------------------------------------- +// Initialization Command 1 control bits +//----------------------------------------------- + +#define I86_PIC_ICW1_IC4_EXPECT 1 //1 +#define I86_PIC_ICW1_IC4_NO 0 //0 +#define I86_PIC_ICW1_SNGL_YES 2 //10 +#define I86_PIC_ICW1_SNGL_NO 0 //00 +#define I86_PIC_ICW1_ADI_CALLINTERVAL4 4 //100 +#define I86_PIC_ICW1_ADI_CALLINTERVAL8 0 //000 +#define I86_PIC_ICW1_LTIM_LEVELTRIGGERED 8 //1000 +#define I86_PIC_ICW1_LTIM_EDGETRIGGERED 0 //0000 +#define I86_PIC_ICW1_INIT_YES 0x10 //10000 +#define I86_PIC_ICW1_INIT_NO 0 //00000 + +//----------------------------------------------- +// Initialization Command 4 control bits +//----------------------------------------------- + +#define I86_PIC_ICW4_UPM_86MODE 1 //1 +#define I86_PIC_ICW4_UPM_MCSMODE 0 //0 +#define I86_PIC_ICW4_AEOI_AUTOEOI 2 //10 +#define I86_PIC_ICW4_AEOI_NOAUTOEOI 0 //0 +#define I86_PIC_ICW4_MS_BUFFERMASTER 4 //100 +#define I86_PIC_ICW4_MS_BUFFERSLAVE 0 //0 +#define I86_PIC_ICW4_BUF_MODEYES 8 //1000 +#define I86_PIC_ICW4_BUF_MODENO 0 //0 +#define I86_PIC_ICW4_SFNM_NESTEDMODE 0x10 //10000 +#define I86_PIC_ICW4_SFNM_NOTNESTED 0 //a binary 2 (futurama joke hehe ;) + +static unsigned char master_mask = 0xFF; +static unsigned char slave_mask = 0xFF; + +void pic_init(void) +{ + uint8_t icw = 0; + + //Initialization Control Word 1 + icw = (icw & ~I86_PIC_ICW1_MASK_INIT) | I86_PIC_ICW1_INIT_YES; + icw = (icw & ~I86_PIC_ICW1_MASK_IC4) | I86_PIC_ICW1_IC4_EXPECT; + outb (I86_PIC1_REG_COMMAND, icw); + outb (I86_PIC2_REG_COMMAND, icw); + + //Initialization Control Word 2 + outb (I86_PIC1_REG_DATA, 0x20); //First PIC starts at IRQ 0x20 + outb (I86_PIC2_REG_DATA, 0x28); //Second at 0x28 + + //Initialization Control Word 3. + //This is the connection between master and slave. + //ICW3 for master PIC is the IR that connects to secondary pic in binary format + //ICW3 for secondary PIC is the IR that connects to master pic in decimal format + outb (I86_PIC1_REG_DATA, 0x04); + outb (I86_PIC2_REG_DATA, 0x02); + + //Initialization Control Word 4 + //Enables i86 mode + icw = (icw & ~I86_PIC_ICW4_MASK_UPM) | I86_PIC_ICW4_UPM_86MODE; + outb (I86_PIC1_REG_DATA, icw); + outb (I86_PIC2_REG_DATA, icw); + + master_mask = 0xFF & ~(1<<2); + slave_mask = 0xFF; + outb(I86_PIC1_REG_IMR, master_mask); + outb(I86_PIC2_REG_IMR, slave_mask); +} +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +void pic_unmask_irq(int intNo) +{ + if (intNo > 48 || intNo < 32) + return; + + intNo -= 32; + + if(intNo >= 40) //Slave + { + slave_mask &= ~(1 << (intNo - 8)); + outb(I86_PIC2_REG_IMR, slave_mask); + } + else //Master + { + master_mask &= ~(1 << intNo); + outb(I86_PIC1_REG_IMR, master_mask); + } +} + +void pic_mask_irq(int intNo) +{ + if (intNo > 48 || intNo < 32) + return; + + intNo -= 32; + + if(intNo >= 40) //Slave + { + slave_mask |= 1 << (intNo - 8); + outb(I86_PIC2_REG_IMR, slave_mask); + } + else //Master + { + master_mask |= 1 << intNo; + outb(I86_PIC1_REG_IMR, master_mask); + } +} + +void pic_notify(int intNo) +{ + if (intNo > 48 || intNo < 32) + return; + + if (intNo >= 40) //Slave + outb(I86_PIC2_REG_COMMAND, I86_PIC_OCW2_MASK_EOI); + + outb(I86_PIC1_REG_COMMAND, I86_PIC_OCW2_MASK_EOI); +} diff --git a/src/kernel/pic.h b/src/kernel/pic.h new file mode 100644 index 0000000..9f4449f --- /dev/null +++ b/src/kernel/pic.h @@ -0,0 +1,37 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _PIC_H +#define _PIC_H + +#include "types.h" + +//----------------------------------------------- +// Command words are used to control the devices +//----------------------------------------------- + +// Command Word 2 bit masks. Use when sending commands +#define I86_PIC_OCW2_MASK_L1 1 //00000001 +#define I86_PIC_OCW2_MASK_L2 2 //00000010 +#define I86_PIC_OCW2_MASK_L3 4 //00000100 +#define I86_PIC_OCW2_MASK_EOI 0x20 //00100000 +#define I86_PIC_OCW2_MASK_SL 0x40 //01000000 +#define I86_PIC_OCW2_MASK_ROTATE 0x80 //10000000 + +// Command Word 3 bit masks. Use when sending commands +#define I86_PIC_OCW3_MASK_RIS 1 //00000001 +#define I86_PIC_OCW3_MASK_RIR 2 //00000010 +#define I86_PIC_OCW3_MASK_MODE 4 //00000100 +#define I86_PIC_OCW3_MASK_SMM 0x20 //00100000 +#define I86_PIC_OCW3_MASK_ESMM 0x40 //01000000 +#define I86_PIC_OCW3_MASK_D7 0x80 //10000000 + + +void pic_init(void); +void pic_unmask_irq(int intNo); +void pic_mask_irq(int intNo); +void pic_notify(int intNo); + +#endif diff --git a/src/kernel/pit.c b/src/kernel/pit.c new file mode 100644 index 0000000..c820f14 --- /dev/null +++ b/src/kernel/pit.c @@ -0,0 +1,26 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "pit.h" +#include "ports.h" + +void pit_init(uint32_t frequency) +{ + // The value we send to the PIT is the value to divide it's input clock + // (1193180 Hz) by, to get our required frequency. Important to note is + // that the divisor must be small enough to fit into 16-bits. + uint32_t divisor = 1193180 / frequency; + + // Send the command byte. + outb(0x43, 0x36); + + // Divisor has to be sent byte-wise, so split here into upper/lower bytes. + uint8_t l = (uint8_t)(divisor & 0xFF); + uint8_t h = (uint8_t)( (divisor>>8) & 0xFF ); + + // Send the frequency divisor. + outb(0x40, l); + outb(0x40, h); +} diff --git a/src/kernel/pit.h b/src/kernel/pit.h new file mode 100644 index 0000000..482fa1f --- /dev/null +++ b/src/kernel/pit.h @@ -0,0 +1,13 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _PIT_H_ +#define _PIT_H_ + +#include "types.h" + +void pit_init(uint32_t frequency); + +#endif diff --git a/src/kernel/ports.h b/src/kernel/ports.h new file mode 100644 index 0000000..74a402c --- /dev/null +++ b/src/kernel/ports.h @@ -0,0 +1,33 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _PORTS_H +#define _PORTS_H + +#include "types.h" + +static inline void outb(unsigned short port, unsigned char value); +static inline void outb(unsigned short port, unsigned char value) +{ + asm volatile ("outb %1, %0" : : "dN" (port), "a" (value)); +} + +static inline unsigned char inb(unsigned short port); +static inline unsigned char inb(unsigned short port) +{ + unsigned char ret; + asm volatile ("inb %1, %0" : "=a" (ret) : "dN" (port)); + return ret; +} + +static inline unsigned short inw(unsigned short port); +static inline unsigned short inw(unsigned short port) +{ + unsigned short ret; + asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port)); + return ret; +} + +#endif diff --git a/src/kernel/registers.h b/src/kernel/registers.h new file mode 100644 index 0000000..d9204bb --- /dev/null +++ b/src/kernel/registers.h @@ -0,0 +1,20 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _ARCH_REGISTERS_H +#define _ARCH_REGISTERS_H + +typedef struct registers +{ + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; // Data segment selector + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha. + uint32_t int_no, err_code; // Interrupt number and error code (if applicable) + uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically. +} registers_t; + +#endif diff --git a/src/kernel/thread.c b/src/kernel/thread.c new file mode 100644 index 0000000..3c5f75f --- /dev/null +++ b/src/kernel/thread.c @@ -0,0 +1,132 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "types.h" +#include "thread.h" + + +/* +#include +#include +#include +#include +*/ + +ThreadContext* listHead = NULL; +ThreadContext* currentContext = NULL; + +void thread_init(uint32_t baseContextAddress) +{ + kprint ("Initializing threading..."); kprint_newline (); + listHead = currentContext = (ThreadContext*) baseContextAddress; + memset(listHead, 0, sizeof(ThreadContext)); + /*print_string_static("Base thread context is at "); + print_integer_hex((uint32_t)currentContext); + print_string_static("\n");*/ +} + +void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg) +{ + ThreadContext* context = (ThreadContext*)contextAddress; + memset(context, 0, sizeof(ThreadContext)); + + context->cs = GDT_KERNEL_CODE; + context->ds = GDT_KERNEL_DATA; + context->es = GDT_KERNEL_DATA; + context->fs = GDT_KERNEL_DATA; + context->gs = GDT_KERNEL_DATA; + context->ss = GDT_KERNEL_DATA; + + context->eflags = thread_getEflags(); + + context->eip = entryPoint; + + /* We are building this stack here: + * ... + * | | + * | Stackframe of Caller | <-- Doesn't exist for now + * |------------------------| + * | Parameter | + * |------------------------| + * | Return Address | <-- Fake return address for now + * ESP --> |------------------------| + * | $foo of called function| <-- Pushed EBP, local variables etc. + * | | + * ... + */ + uint32_t* stack = (uint32_t*)(stackStart); + memset(stack, 0, stackSize); + stack[stackSize/sizeof(uint32_t) - 1] = arg; //Argument + stack[stackSize/sizeof(uint32_t) - 2] = 0; //Return Address + + context->esp = stackStart + stackSize - 2*sizeof(uint32_t); + + + ThreadContext* cur = listHead; + while(cur->next != NULL) + cur = cur->next; + + cur->next = context; +} + +static inline void interrupts_disable(void) __attribute__((always_inline)); +static inline void interrupts_disable(void) +{ + asm volatile ("cli"); +} + +void thread_schedule(registers_t* oldState) +{ + //print_string_static("Scheduling...\n"); + interrupts_disable(); + + thread_saveContext(oldState); + //print_string_static("Old context saved...\n"); + + ThreadContext* next; + if(currentContext->next == NULL) + next = listHead; + else + next = currentContext->next; + + currentContext = next; + /*print_string_static("New thread context is at "); + print_integer_hex((uint32_t)currentContext); + print_string_static("\n");*/ + + thread_switchToContext(currentContext); +} + + +void thread_saveContext(registers_t* oldState) +{ + if((oldState->cs & 0x3) == 0x3) //are we coming from usermode? + { + //ss and esp are only valid if we are coming from usermode + currentContext->ss = oldState->ss; + currentContext->esp = oldState->useresp; + } + else + { + currentContext->ss = GDT_KERNEL_DATA; + currentContext->esp = oldState->esp + 0x14; + } + + currentContext->eip = oldState->eip; + currentContext->cs = oldState->cs; + currentContext->eflags = oldState->eflags; + currentContext->eax = oldState->eax; + currentContext->ecx = oldState->ecx; + currentContext->edx = oldState->edx; + currentContext->ebx = oldState->ebx; + + currentContext->ebp = oldState->ebp; + currentContext->esi = oldState->esi; + currentContext->edi = oldState->edi; + currentContext->ds = oldState->ds; + currentContext->es = oldState->es; + currentContext->fs = oldState->fs; + currentContext->gs = oldState->gs; +} diff --git a/src/kernel/thread.h b/src/kernel/thread.h new file mode 100644 index 0000000..baedd8b --- /dev/null +++ b/src/kernel/thread.h @@ -0,0 +1,43 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _THREADINFO_H_ +#define _THREADINFO_H_ + +#include "registers.h" + +typedef struct ThreadContext_ +{ + uint32_t eip; // 0 + uint32_t cs; // 4 + uint32_t eflags; // 8 + uint32_t eax; // 12 + uint32_t ecx; // 16 + uint32_t edx; // 20 + uint32_t ebx; // 24 + uint32_t esp; // 28 + uint32_t ebp; // 32 + uint32_t esi; // 36 + uint32_t edi; // 40 + uint32_t ds; // 44 + uint32_t es; // 48 + uint32_t fs; // 52 + uint32_t gs; // 56 + uint32_t ss; // 60 + uint32_t cr3; // 64 - unused for now... + struct ThreadContext_* next; +} ThreadContext; + +uint32_t thread_getEflags(void); + +void thread_init(uint32_t baseContextAddress); +void thread_create(uint32_t infoAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg); + +void thread_schedule(registers_t* oldState); + +void thread_saveContext(registers_t* oldState); +void thread_switchToContext(ThreadContext* newContext); + +#endif diff --git a/src/kernel/threadS.asm b/src/kernel/threadS.asm new file mode 100644 index 0000000..2e60b7b --- /dev/null +++ b/src/kernel/threadS.asm @@ -0,0 +1,51 @@ +; by Andreas Galauner +; +; https://github.com/G33KatWork + + +[GLOBAL thread_getEflags] +thread_getEflags: + pushf + pop eax + ret + +[GLOBAL thread_switchToContext] +thread_switchToContext: + mov ebx, dword[esp + 4] ; Get thread info + +; mov eax, dword[ebx + 64] ; Get new pagedir pointer +; mov ecx, cr3 ; Get current pagedir pointer +; cmp eax, ecx ; Compare +; je .noPagedirChange +; mov cr3, eax ; and set new if it differs from old + +;.noPagedirChange: + + mov ecx, dword[ebx + 16] ; restore registers + mov edx, dword[ebx + 20] + mov esp, dword[ebx + 28] + mov ebp, dword[ebx + 32] + mov esi, dword[ebx + 36] + mov edi, dword[ebx + 40] + + mov eax, dword[ebx + 48] ; restore segments + mov es, eax + mov eax, dword[ebx + 44] + mov ds, eax + mov eax, dword[ebx + 52] + mov fs, eax + mov eax, dword[ebx + 56] + mov gs, eax + mov eax, dword[ebx + 60] + mov ss, eax + + mov eax, dword[ebx + 12] ; finally restore eax + + push dword[ebx + 8] ; push eflags + push dword[ebx + 4] ; push cs + push dword[ebx + 0] ; push eip + + push dword[ebx + 24] ; restore ebx + pop ebx + + iretd ; return to thread From 976853a6926261bb8c8374ebf7634ff9ad0a77d9 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sat, 14 Jan 2017 11:53:29 +0100 Subject: [PATCH 11/19] threading CPU scheduler --- make.sh | 13 +- src/kernel/gdt.c | 230 +++++++++++++++++++------- src/kernel/kernel | Bin 10288 -> 25440 bytes src/kernel/kernel.asm | 97 ++++++++--- src/kernel/kernel.c | 336 +++++++++++++++++++++++++++++--------- src/kernel/keyboard_map.h | 1 + src/kernel/physmem.c | 76 ++++++--- src/kernel/types.h | 8 + 8 files changed, 566 insertions(+), 195 deletions(-) diff --git a/make.sh b/make.sh index 37a94da..47e2c5a 100755 --- a/make.sh +++ b/make.sh @@ -2,11 +2,20 @@ cd src/kernel nasm -f elf32 kernel.asm -o kasm.o -nasm -f elf32 io.asm -o ioasm.o +nasm -f elf32 threadS.asm -o threadS.o +nasm -f elf32 interruptStubs.asm -o interruptStubs.o gcc -m32 -c kernel.c -o kc.o gcc -m32 -c io.c -o io.o gcc -m32 -c lib.c -o lib.o -ld -m elf_i386 -T link.ld -o kernel kasm.o ioasm.o kc.o io.o lib.o +gcc -m32 -c gdt.c -o gdt.o +gcc -m32 -c physmem.c -o physmem.o +gcc -m32 -c thread.c -o thread.o +gcc -m32 -c interrupts.c -o interrupts.o +gcc -m32 -c idt.c -o idt.o +gcc -m32 -c pit.c -o pit.o +gcc -m32 -c pic.c -o pic.o + +ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o echo "kernel build end, making .iso" diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c index ba226e0..dbca66f 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/gdt.c @@ -1,82 +1,190 @@ #include "types.h" -/* taken from Brans kernel manual */ +void stack_space (void); -/* Defines a GDT entry. We say packed, because it prevents the -* compiler from doing things that it thinks is best: Prevent -* compiler "optimization" by packing */ -struct gdt_entry +// gdt table +struct gdt_entry_bits { - uint16_t limit_low; - uint16_t base_low; - uint8_t base_middle; - uint8_t access; - uint8_t granularity; - uint8_t base_high; + unsigned int limit_low:16; + unsigned int base_low : 24; + //attribute byte split into bitfields + unsigned int accessed :1; + unsigned int read_write :1; //readable for code, writable for data + unsigned int conforming_expand_down :1; //conforming for code, expand down for data + unsigned int code :1; //1 for code, 0 for data + unsigned int always_1 :1; //should be 1 for everything but TSS and LDT + unsigned int DPL :2; //priviledge level + unsigned int present :1; + //and now into granularity + unsigned int limit_high :4; + unsigned int available :1; + unsigned int always_0 :1; //should always be 0 + unsigned int big :1; //32bit opcodes for code, uint32_t stack for data + unsigned int gran :1; //1 to use 4k page addressing, 0 for byte addressing + unsigned int base_high :8; } __attribute__((packed)); -/* Special pointer which includes the limit: The max bytes -* taken up by the GDT, minus 1. Again, this NEEDS to be packed */ +static struct gdt_entry_bits gdt[6]; + struct gdt_ptr { uint16_t limit; uint32_t base; } __attribute__((packed)); -/* Our GDT, with 3 entries, and finally our special GDT pointer */ -struct gdt_entry gdt[3]; struct gdt_ptr gp; -/* This will be a function in start.asm. We use this to properly -* reload the new segment registers */ -extern void gdt_flush(); +// tss structure -/* Setup a descriptor in the Global Descriptor Table */ -void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran) +// A struct describing a Task State Segment. +struct tss_entry_struct { - /* Setup the descriptor base address */ - gdt[num].base_low = (base & 0xFFFF); - gdt[num].base_middle = (base >> 16) & 0xFF; - gdt[num].base_high = (base >> 24) & 0xFF; - - /* Setup the descriptor limits */ - gdt[num].limit_low = (limit & 0xFFFF); - gdt[num].granularity = ((limit >> 16) & 0x0F); - - /* Finally, set up the granularity and access flags */ - gdt[num].granularity |= (gran & 0xF0); - gdt[num].access = access; -} - -/* Should be called by main. This will setup the special GDT -* pointer, set up the first 3 entries in our GDT, and then -* finally call gdt_flush() in our assembler file in order -* to tell the processor where the new GDT is and update the -* new segment registers */ -void gdt_install() + uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list. + uint32_t esp0; // The stack pointer to load when we change to kernel mode. + uint32_t ss0; // The stack segment to load when we change to kernel mode. + uint32_t esp1; // everything below here is unusued now.. + uint32_t ss1; + uint32_t esp2; + uint32_t ss2; + 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; + uint32_t es; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t fs; + uint32_t gs; + uint32_t ldt; + uint16_t trap; + uint16_t iomap_base; +} __packed; + +typedef struct tss_entry_struct tss_entry_t; +tss_entry_t tss_entry; + +void set_gdt (void) { - /* Setup the GDT pointer and limit */ - gp.limit = (sizeof(struct gdt_entry) * 3) - 1; + gp.limit = (sizeof(struct gdt_entry_bits) * 6) - 1; gp.base = &gdt; - - /* Our NULL descriptor */ - gdt_set_gate(0, 0, 0, 0, 0); - - /* The second entry is our Code Segment. The base address - * is 0, the limit is 4GBytes, it uses 4KByte granularity, - * uses 32-bit opcodes, and is a Code Segment descriptor. - * Please check the table above in the tutorial in order - * to see exactly what each value means */ - gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); - - /* The third entry is our Data Segment. It's EXACTLY the - * same as our code segment, but the descriptor type in - * this entry's access byte says it's a Data Segment */ - gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); - - /* Flush out the old GDT and install the new changes! */ - gdt_flush(); + + //....insert your null_seg 0 segments here or whatever + struct gdt_entry_bits *null_seg; + null_seg = (void *) &gdt[0]; + null_seg->limit_low = 0; + null_seg->base_low = 0; + null_seg->accessed = 0; + null_seg->read_write = 0; + null_seg->conforming_expand_down = 0; + null_seg->code = 0; + null_seg->always_1 = 1; + null_seg->DPL = 0; + null_seg->present = 0; + null_seg->limit_high = 0; + null_seg->available = 0; + null_seg->big = 0; + null_seg->gran = 0; + null_seg->base_high = 0; + + // ring 0 + struct gdt_entry_bits *null_code; + struct gdt_entry_bits *null_data; + null_code = (void *) &gdt[1]; + null_data = (void *) &gdt[2]; + null_code->limit_low = 0xFFFF; // 0 + null_code->base_low = 0; + null_code->accessed = 0; + null_code->read_write = 1; + null_code->conforming_expand_down = 0; + null_code->code = 1; + null_code->always_1 = 1; + null_code->DPL = 0; + null_code->present = 1; + null_code->limit_high = 0xF; + null_code->available = 1; + null_code->big = 1; + null_code->gran = 1; + null_code->base_high = 0; + *null_data = *null_code; + null_data->code = 0; + + // ring 3 + struct gdt_entry_bits *code; + struct gdt_entry_bits *data; + //I assume your ring 0 segments are in gdt[1] and gdt[2] (0 is null segment) + code=(void*)&gdt[3]; //gdt is a static array of gdt_entry_bits or equivalent + data=(void*)&gdt[4]; + code->limit_low=0xFFFF; + code->base_low=0; + code->accessed=0; + code->read_write=1; //make it readable for code segments + code->conforming_expand_down=0; //don't worry about this.. + code->code=1; //this is to signal its a code segment + code->always_1=1; + code->DPL=3; //set it to ring 3 + code->present=1; + code->limit_high=0xF; + code->available=1; + code->always_0=0; + code->big=1; //signal it's 32 bits + code->gran=1; //use 4k page addressing + code->base_high=0; + *data=*code; //copy it all over, cause most of it is the same + data->code=0; //signal it's not code; so it's data. + + write_tss(&gdt[5]); //we'll implement this function later... + + //...go on to install GDT segments and such + gdt_flush (); + //after those are installed we'll tell the CPU where our TSS is: + tss_flush(); //implement this later } - +void write_tss(struct gdt_entry_bits *g) +{ + // Firstly, let's compute the base and limit of our entry into the GDT. + uint32_t base = (uint32_t) &tss_entry; + uint32_t limit = sizeof(tss_entry); + + // Now, add our TSS descriptor's address to the GDT. + g->limit_low=limit&0xFFFF; + g->base_low=base&0xFFFFFF; //isolate bottom 24 bits + g->accessed=1; //This indicates it's a TSS and not a LDT. This is a changed meaning + g->read_write=0; //This indicates if the TSS is busy or not. 0 for not busy + g->conforming_expand_down=0; //always 0 for TSS + g->code=1; //For TSS this is 1 for 32bit usage, or 0 for 16bit. + g->always_1=0; //indicate it is a TSS + g->DPL=3; //same meaning + g->present=1; //same meaning + g->limit_high=(limit&0xF0000)>>16; //isolate top nibble + g->available=0; + g->always_0=0; //same thing + g->big=0; //should leave zero according to manuals. No effect + g->gran=0; //so that our computed GDT limit is in bytes, not pages + g->base_high=(base&0xFF000000)>>24; //isolate top byte. + + // Ensure the TSS is initially zero'd. + memset(&tss_entry, 0, sizeof(tss_entry)); + + // tss_entry.ss0 = stack_space; // Set the kernel stack segment. + // tss_entry.esp0 = stack_space; // Set the kernel stack pointer. + + tss_entry.ss0 = GDT_KERNEL_CODE; // Set the kernel stack segment. + tss_entry.esp0 = GDT_KERNEL_DATA; // Set the kernel stack pointer. + + //note that CS is loaded from the IDT entry and should be the regular kernel code segment +} + +void set_kernel_stack(uint32_t stack) //this will update the ESP0 stack used when an interrupt occurs +{ + tss_entry.esp0 = stack; +} diff --git a/src/kernel/kernel b/src/kernel/kernel index 9572c54ee709842708ba8ccd5923f50cf24da973..20a4b16d5eb5dbda57c202af0823dbb977d5301e 100755 GIT binary patch literal 25440 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 literal 10288 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 16; - - /* Ports - * PIC1 PIC2 - *Command 0x20 0xA0 - *Data 0x21 0xA1 - */ - - /* ICW1 - begin initialization */ - write_port(0x20 , 0x11); - write_port(0xA0 , 0x11); - - /* ICW2 - remap offset address of IDT */ - /* - * In x86 protected mode, we have to remap the PICs beyond 0x20 because - * Intel have designated the first 32 interrupts as "reserved" for cpu exceptions - */ - write_port(0x21 , 0x20); - write_port(0xA1 , 0x28); - - /* ICW3 - setup cascading */ - write_port(0x21 , 0x00); - write_port(0xA1 , 0x00); - - /* ICW4 - environment info */ - write_port(0x21 , 0x01); - write_port(0xA1 , 0x01); - /* Initialization finished */ - - /* mask interrupts */ - write_port(0x21 , 0xff); - write_port(0xA1 , 0xff); - - /* fill the IDT descriptor */ - idt_address = (uint32_t)IDT ; - idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16); - idt_ptr[1] = idt_address >> 16 ; - - load_idt(idt_ptr); -} +// threading +uint8_t threading = 0; +uint8_t threads_request = 0; void kb_init(void) { /* 0xFD is 11111101 - enables only IRQ1 (keyboard)*/ - write_port(0x21 , 0xFD); + outb(0x21 , 0xFC); } + + void kprint(const char *str) { uint32_t i = 0; @@ -234,6 +190,8 @@ void kprint_newline(void) fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); + fb_blit (); + /* show cursor line on next position */ vidptr[fb_current_loc ] = '_'; vidptr[fb_current_loc + 1] = fb_color; @@ -313,35 +271,90 @@ void fb_set_color (unsigned char forecolor, unsigned char backcolor) fb_color = (backcolor << 4) | (forecolor & 0x0F); } -void keyboard_handler_main(void) +void div_by_zero_handler_main (void) +{ + fb_set_color (FB_RED, FB_BLACK); + kprint ("KERNEL PANIC! division by ZERO"); kprint_newline (); + fb_set_color (FB_WHITE, FB_BLACK); + + outb (0x20, 0x20); +} + +void div_by_zero () +{ + uint8_t i; + + i = 23 / 0; +} + +void keyboard_handler(registers_t* regs) { uint8_t status; uint8_t keycode; uint8_t ch[2]; uint8_t pressed = 0; - /* write EOI */ - write_port(0x20, 0x20); - - status = read_port(KEYBOARD_STATUS_PORT); + /* debug */ + uint32_t i; + + + status = inb(KEYBOARD_STATUS_PORT); /* Lowest bit of status will be set if buffer is not empty */ if (status & 0x01) { - keycode = read_port(KEYBOARD_DATA_PORT); + keycode = inb(KEYBOARD_DATA_PORT); if(keycode < 0) return; if(keycode == ENTER_KEY_CODE) { kprint_newline(); + + if (command_mode) + { + command[com_ind] = '\0'; + + kprint (command); kprint ("> "); + if (strcmp (command, "div") == 0) + { + div_by_zero (); + } + + if (strcmp (command, "page") == 0) + { + command[0] = '\0'; com_ind = 0; + + for (i = 0; i < LINES -1; i++) + { + pmem_show_page (i); + } + } + + if (strcmp (command, "tasks") == 0) + { + command[0] = '\0'; com_ind = 0; + + threads_request = 1; + } + + command[0] = '\0'; + com_ind = 0; + } + return; } - /* if (keycode == '\b') { - + // backspace + if (command_mode == 1) + { + if (com_ind >= 2) + { + com_ind = com_ind - 2; + command[com_ind] = '\0'; + } + } } - */ if (keycode & 0x80) { @@ -365,6 +378,32 @@ void keyboard_handler_main(void) } } + if (keycode == KEY_SCAN_F1) + { + /* switch command console on */ + + if (command_mode == 0) + { + command_mode = 1; + + fb_set_color (FB_RED, FB_BLACK); + kprint ("COMMAND MODE"); kprint_newline (); + kprint ("press F1 to EXIT"); kprint_newline (); + kprint ("commands:"); kprint_newline(); + kprint ("div [division by zero interrupt check]"); kprint_newline (); + kprint ("page [show usage of pages]"); kprint_newline (); + kprint ("tasks [multitasking demo]"); kprint_newline (); + kprint ("press ENTER for prompt!"); kprint_newline (); + } + else + { + command_mode = 0; + command[0] = '\0'; com_ind = 0; + fb_set_color (FB_WHITE, FB_BLACK); + } + } + + if (debug_mode) { kprint_int (keycode, 16); @@ -389,10 +428,22 @@ void keyboard_handler_main(void) if (keyboard_shift == 1) { ch[0] = keyboard_shift_map[(unsigned char) keycode]; + + if (command_mode) + { + command[com_ind] = ch[0]; + com_ind++; + } } else { ch[0] = keyboard_map[(unsigned char) keycode]; + + if (command_mode) + { + command[com_ind] = ch[0]; + com_ind++; + } } ch[1] = '\0'; @@ -402,10 +453,116 @@ void keyboard_handler_main(void) } } +uint32_t clock (void) +{ + return (clock_ticks); +} + +void kdelay (uint32_t ticks) +{ + uint32_t end_ticks = clock () + ticks; + + while (clock () < end_ticks) + { + } +} + +loop_mark () +{ + /* simple loop "benchmark" */ + + uint32_t start_time, end_time, loop_time, i, j, k; + uint32_t loop_max = 20000; + uint32_t loops_per_sec; + + kprint ("loop mark: "); + + start_time = clock (); + + for (i = 1; i <= loop_max; i++) + { + for (j = 1; j <= loop_max; j++) + { + + } + } + + end_time = clock (); + loop_time = end_time - start_time; + + loops_per_sec = (loop_max * loop_max) / (loop_time / TIMER_FREQ); + + kprint_int (loop_time, 10); kprint_newline (); + kprint ("loops per sec: "); kprint_int (loops_per_sec, 10); kprint_newline (); kprint_newline (); +} + + +void timerHandler(registers_t* regs) +{ + clock_ticks++; + + if (threading) + { + thread_schedule(regs); + } + // kprint ("clock: "); kprint_int (clock_ticks, 10); kprint_newline (); +} + +void thread_a(uint32_t argument) +{ + uint32_t ticks; + for(;;) + { + ticks = clock (); + kprint ("thread A: "); kprint_int (ticks, 10); kprint_newline (); + for(uint32_t i = 0; i < 200000; i++); + } +} + +void thread_b(uint32_t argument) +{ + uint32_t ticks; + for(;;) + { + ticks = clock (); + kprint ("thread B: "); kprint_int (ticks, 10); kprint_newline (); + for(uint32_t i = 0; i < 200000; i++); + } +} + +void run_threads (void) +{ + uint8_t *thread_base = (uint8_t *) kmalloc (20000); + uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); + + uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); + + thread_init(thread_base); + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0x61); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 0x61); + + threading = 1; +} + + void kmain (multiboot_info_t* mbt, unsigned int magic) { - gdt_install (); /* init memory GDT */ + set_gdt (); /* init memory GDT */ + + idt_install(); //set up IDT + interrupts_init(); //set up callback table + pic_init(); //set up PIC + interrupts_enable(); + interrupts_registerHandler(IRQ_TIMER, timerHandler); + interrupts_registerHandler(IRQ_KEYBOARD, keyboard_handler); + pit_init(100); + + + + // vmem_paging (); /* switch paging on */ multiboot_memory_map_t* mmap = mbt->mmap_addr; @@ -518,15 +675,19 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) ram_free = (pages_free * 4096) / 1024 /1024; kprint ("free pages: "); kprint_int (pages_free, 10); kprint (" = " ); kprint_int ((uint32_t) ram_free, 10); kprint (" MB"); kprint_newline (); - idt_init(); - kprint_newline (); kprint ("level 2: interrupts"); kprint_newline (); - kb_init(); + // kb_init(); + pic_unmask_irq(IRQ_KEYBOARD); + + + kprint ("level 3: keyboard"); kprint_newline (); kprint_newline (); + // loop_mark (); + fb_set_color (FB_RED, FB_BLACK); kprint ("red"); fb_set_color (FB_BLUE, FB_BLACK); @@ -567,9 +728,24 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) } */ + kprint ("F1 = command console"); kprint_newline(); kprint_newline (); kprint ("READY"); kprint_newline (); - while(1); + pic_unmask_irq(IRQ_TIMER); + + + // jump_usermode (); + + while(1) + { + kdelay (200); + + if (threads_request == 1) + { + run_threads(); + threads_request = 0; + } + } } diff --git a/src/kernel/keyboard_map.h b/src/kernel/keyboard_map.h index 907bea3..667d6f3 100644 --- a/src/kernel/keyboard_map.h +++ b/src/kernel/keyboard_map.h @@ -10,6 +10,7 @@ #define KEY_SCAN_RIGHT_SHIFT_RELEASED 0xB6 #define KEY_SCAN_ESCAPE 0x01 +#define KEY_SCAN_F1 0x3B uint8_t keyboard_shift; diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c index 101ebb1..af30623 100644 --- a/src/kernel/physmem.c +++ b/src/kernel/physmem.c @@ -49,11 +49,15 @@ uint32_t pmem_get_page_from_address (uint32_t address) { uint32_t page; page = address / MEM_BLOCK_SIZE; + if (address % MEM_BLOCK_SIZE != 0) + { + page++; + } return (page); } -int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) +int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t state) { /* set bits in bitmap */ uint32_t start_p, end_p; @@ -62,16 +66,23 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) uint32_t p; int16_t b; uint32_t page_start = pmem_get_page_from_address (start); - uint32_t page_end = pmem_get_page_from_address (end); + uint32_t page_end; + - /* - kprint ("start address: "); kprint_int (start, 10); kprint_newline (); - kprint ("start page: "); kprint_int (page_start, 10); kprint_newline (); + if (pages != 0) + { + page_end = page_start + pages - 1; + } + else + { + page_end = pmem_get_page_from_address (end); + } - kprint ("end address: "); kprint_int (end, 10); kprint_newline (); - kprint ("end page: "); kprint_int (page_end, 10); kprint_newline (); - */ + // kprint ("start address: "); kprint_int (start, 10); kprint_newline (); + // kprint ("start page: "); kprint_int (page_start, 10); kprint_newline (); + // kprint ("end address: "); kprint_int (end, 10); kprint_newline (); + // kprint ("end page: "); kprint_int (page_end, 10); kprint_newline (); if (page_end > MEM_BLOCKS) { @@ -100,8 +111,6 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) end_b = page_end; } - - if (end_p > start_p) { for (p = start_p; p <= end_p; p++) @@ -180,17 +189,17 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) { if (state == ALLOCATE) { - set_bit (&physmem_pages[p], b); + set_bit (&physmem_pages[start_p], b); } else { - if (get_bit (physmem_pages[p], b) == FREE) + if (get_bit (physmem_pages[start_p], b) == FREE) { return (MEM_ERR_DOUBLE_FREE); } else { - clear_bit (&physmem_pages[p], b); + clear_bit (&physmem_pages[start_p], b); } } @@ -199,10 +208,7 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint8_t state) return (MEM_ERR_OK); } - - - -int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end) +int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pages) { /* return pointers to free memory range */ int8_t found_start = 0; @@ -216,11 +222,19 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end) if (size > MEM_BLOCK_SIZE) { mempages = size / MEM_BLOCK_SIZE; + + if (size % MEM_BLOCK_SIZE != 0) + { + mempages++; + } } else { mempages = 1; } + *pages = mempages; + + // kprint ("pmem_get_free: mempages = "); kprint_int (mempages, 10); kprint_newline (); for (p = 0; p < PAGES; p++) { @@ -244,6 +258,14 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end) else { mempages_free++; + if (mempages_free >= mempages) + { + end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; + + *start = start_free; + *end = end_free; + return (MEM_ERR_OK); + } } } else @@ -296,22 +318,24 @@ uint32_t pmem_count_free_pages (void) return (pages); } - void *kmalloc (size_t size) { uint32_t start_free, end_free; uint32_t *ptr; size_t new_size; + uint32_t pages; new_size = size + sizeof (size_t); - if (pmem_get_free (new_size, &start_free, &end_free) == MEM_ERR_OK) + // kprint ("kmalloc: allocate "); kprint_int ((uint32_t) new_size, 10); kprint_newline (); + + if (pmem_get_free (new_size, &start_free, &end_free, &pages) == MEM_ERR_OK) { // kprint ("free mem found"); kprint_newline (); - if (pmem_set_bitmap (start_free, end_free, ALLOCATE) == MEM_ERR_OK) + if (pmem_set_bitmap (start_free, end_free, pages, ALLOCATE) == MEM_ERR_OK) { ptr = start_free; - *ptr = new_size; /* store size of memory */ + *ptr = pages; /* store size of memoryin pages */ return (start_free + sizeof (size_t)); } else @@ -330,16 +354,16 @@ uint32_t kfree (uint32_t address) { uint32_t *ptr; uint32_t end_mem; - size_t size; + size_t pages; ptr = address - sizeof (size_t); - size = *ptr; + pages = *ptr; - // kprint ("kfree: deallocate "); kprint_int ((uint32_t) size, 10); kprint_newline (); + // kprint ("kfree: deallocate "); kprint_int ((uint32_t) pages, 10); kprint_newline (); - end_mem = address + size; + end_mem = (address - sizeof (size_t)) + pages * MEM_BLOCK_SIZE; - if (pmem_set_bitmap (address, end_mem, FREE) == MEM_ERR_OK) + if (pmem_set_bitmap (address - sizeof (size_t), end_mem, pages, FREE) == MEM_ERR_OK) { return (NULL); } diff --git a/src/kernel/types.h b/src/kernel/types.h index a8545f7..51c7bf2 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -33,3 +33,11 @@ typedef uint32_t size_t; #define MEM_ERR_BAD_MEM 1 #define MEM_ERR_RANGE 2 #define MEM_ERR_NOMEM 3 +#define MEM_ERR_DOUBLE_FREE 4 + +/* PIT timer Hz */ +#define TIMER_FREQ 100 + +//gdt descriptor offsets +#define GDT_KERNEL_CODE 0x08 +#define GDT_KERNEL_DATA 0x10 From 28d3b59c95d3b3eb43a723a1b192c4288a8d0d8c Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sat, 14 Jan 2017 11:57:52 +0100 Subject: [PATCH 12/19] removed lib.c~ --- src/kernel/lib.c~ | 73 ----------------------------------------------- 1 file changed, 73 deletions(-) delete mode 100644 src/kernel/lib.c~ diff --git a/src/kernel/lib.c~ b/src/kernel/lib.c~ deleted file mode 100644 index ffd0c53..0000000 --- a/src/kernel/lib.c~ +++ /dev/null @@ -1,73 +0,0 @@ -/* lib.c - some helper functions */ - -void strreverse(char* begin, char* end) { - - char aux; - - while(end>begin) - - aux=*end, *end--=*begin, *begin++=aux; - -} - -void itoa(long long int value, char* str, int base) { - - static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - - char* wstr=str; - - int sign; - - // Validate base - - if (base<2 || base>35){ *wstr='\0'; return; } - - - // Take care of sign - - if ((sign=value) < 0) value = -value; - - - // Conversion. Number is reversed. - - do *wstr++ = num[value%base]; while(value/=base); - - if(sign<0) *wstr++='-'; - - *wstr='\0'; - - - // Reverse string - - - strreverse(str,wstr-1); -} - - -void kprint_int (long n, int base) -{ - char str[256]; - - itoa (n, str, base); - kprint (str); -} - -int strcmp (const char * str1, const char * str2) -{ - while (*str1 == *str2) - { - if (*str1 == '\0' || *str2 == '\0') - break; - - str1++; - str2++; - } - - - if (*str1 == '\0' && *str2 == '\0') - return 0; - else - return -1; -} - - From 00ddb6c726ae5d1714557ecee57b5958a667fc36 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sat, 14 Jan 2017 13:10:38 +0100 Subject: [PATCH 13/19] threads output delay, ctrl key for command mode --- os.iso | Bin 489472 -> 501760 bytes src/kernel/kernel | Bin 25440 -> 25440 bytes src/kernel/kernel.c | 8 ++++---- src/kernel/keyboard_map.h | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/os.iso b/os.iso index b60875d608da3f907c5ee690b5a93c5c2ed5e8b7..7769e4c3070fac5d4f1b08856372027920b75047 100644 GIT binary patch delta 20941 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 CK36gT delta 5956 zcmb7IdvH@#8b9|YEuj>0p#=&SxwM64K}!pY6)M{Tp}PVSfhsJ6A#GEdzEabK$4X60 z3N{j){8R#PqDT9Ua9fG$7q^-BCeKeCkxH=EkX8 zn<;M-f|{A1@T6OVTsT!U_ZH6XU=OOvdoX7<4QA)4p0qu5dUedRDPtoJ9u@9Se5yO* zS@)upTj*>X>Oy#wD)_bb767)3J*--NxI^tb+=7^LXNDdWEm~61+Tx|z%T^RE&Sh)Y zCJ4R|3Tk8jN=W_V4h0*U~8&qu@mZ<#_EtU)256+FIHx^4Xo2KHX z&3~S(K0jZ>Z~0)utz&;HVfWegvqIXDq`6FDcVYJIS%HieF=JG!#MrrCvNu<@h#f|c ze|?s|Cq&QRF3&Q2#YaYA>USaKwM53w+;~UMk=Q!ry-|$ymG4*o5we`7d=)D8U0mnw zNmZO<7;6e0H8R!}oC$+IQ=i||=R4jNJXavGa^HRf+}4P$#v~91PI@HP*_aGc6LvNt zd+c|Z+o4Et)5n49?J-o2uap(@(9n?ggv7~;Lzt>8iE-r08OppEIlJNo0!)i>7y#p< zq^FXph{5lpN(CUkj3ZURzoVRrA*clA^l**}dqZWaq54d{ep7G9IVl0*A}0z_h|ARD z?MZGmDQ4x)Csq7@|K`EdS~m4cW)fow<>PqB6*svxUr71&_kpk97E?Tfqbb*mA?`Bu zLC7w>itPA{4al0%ZER=x2Dj7iO_wo$K-e~38!Cd2^TL^auMF9qplgac@{)*HT^wMB zO20RYrYoY44^*50f=%P*6EY@EF|)4~)$xxEj@q2ewDN2tp(A!gs620D>_x z?j4v+C_W5o4uy@%=K}df-Z1PgSoUD(Uv2Og_X+PB+b6UfB&RfoTg`&8-Fpnn+tQrE z*=_HDQ2iB-(=_Dh6mrxe`n_D##sfVrQ0ChBOeqqc7Q7cz{B7iyue;-%=n?SL`;Mz= z6}qvMX2piXf<5VvH`m?CEI(l+wqDIaV*F{bgU|B zP~55x4}2PGO~Muw6m0K9gfsQxiB7>*U}4Y3E>jON!Ylm6y{-2PzSX_{)qSlS1>fdA zWBWb>t$WKpi7)^E0(eb5g14b(&9*nZQ15L}(zd-o_Nb6)R%S)5bT*Sx+W!%?DhKgL z)O@VKdA8qsB6ZVd?Kn%y1K1o12XLmlOatV&c5K60{%8666%X(z2855Wsre9b9OMDC zq%<`Q{0ZJ{`v^)UQtHu4?oH&qDU{FE@-h&f?e}wiraqhlAI`@q+9N;Yv8IMTuJcT6 zmAm-C2y~~ zX5~3GS4s+0hbv)cjw=z*g>FnsgC;2M|1)Q`M_8<};hNxH0(}I?7u@4XpV8BfT1;im z^ljM4Iba+^4FRJQw*tRkzBMbIYS3}FORDlVyl4tt$42#=4E}-~+|{_Qeu0}9OMdVs z(g1H+rKnV@cN6LIp8RfT$gFHv)hX*dAUXd-&flW(^Ff7oa>3_ZP^8(%>wp=y^jabg z;+26D*cca)wtUF7aRv0cY8N3Y!=03$0Btt1LUmobbQ?-C6-cig$?qp>;=6g>V zT5SWH9#Jc7Q;!}lDo^92HibS$9e_z7Tb~}l_FZdM7VsI^P|2-(h-Fqv)Eq@4SBfz= z;aN-#r(-hS4w{3mjDX{?xZL2^XNCOfS)su3cf|#Pt$!02Cag6TF5eh1z9&whR}q$u z-<5BNhE~E0RyfYEM_f8KVYyeQTPBFrUHuYv`fJ6E!AYQ8mfnAI9$p&chk>@y5i?P~l0

BvaVdqig!!WC27`jyU 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 diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index c399cd6..f657ddc 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -378,7 +378,7 @@ void keyboard_handler(registers_t* regs) } } - if (keycode == KEY_SCAN_F1) + if (keycode == KEY_SCAN_F1 || keycode == KEY_SCAN_CTRL_LEFT) { /* switch command console on */ @@ -516,7 +516,7 @@ void thread_a(uint32_t argument) { ticks = clock (); kprint ("thread A: "); kprint_int (ticks, 10); kprint_newline (); - for(uint32_t i = 0; i < 200000; i++); + kdelay (50); } } @@ -527,7 +527,7 @@ void thread_b(uint32_t argument) { ticks = clock (); kprint ("thread B: "); kprint_int (ticks, 10); kprint_newline (); - for(uint32_t i = 0; i < 200000; i++); + kdelay (50); } } @@ -728,7 +728,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) } */ - kprint ("F1 = command console"); kprint_newline(); + kprint ("F1 or CTRL-LEFT = command console"); kprint_newline(); kprint_newline (); kprint ("READY"); kprint_newline (); diff --git a/src/kernel/keyboard_map.h b/src/kernel/keyboard_map.h index 667d6f3..c357352 100644 --- a/src/kernel/keyboard_map.h +++ b/src/kernel/keyboard_map.h @@ -11,6 +11,7 @@ #define KEY_SCAN_ESCAPE 0x01 #define KEY_SCAN_F1 0x3B +#define KEY_SCAN_CTRL_LEFT 0x1D uint8_t keyboard_shift; From a505915e7df865a14f133ef96d76d402dbaad250 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sun, 15 Jan 2017 18:28:39 +0100 Subject: [PATCH 14/19] kernel shell, thread priority --- os.iso | Bin 501760 -> 503808 bytes src/kernel/gdt.c | 4 +- src/kernel/kernel | Bin 25440 -> 27276 bytes src/kernel/kernel.asm | 31 +++- src/kernel/kernel.c | 357 ++++++++++++++++++++++++------------------ src/kernel/lib.c | 55 ++++++- src/kernel/physmem.c | 16 ++ src/kernel/thread.c | 116 ++++++++++++-- src/kernel/thread.h | 2 + src/kernel/types.h | 6 + 10 files changed, 414 insertions(+), 173 deletions(-) diff --git a/os.iso b/os.iso index 7769e4c3070fac5d4f1b08856372027920b75047..f45af798ae247a624ae69866d6e8741ebb85c7a6 100644 GIT binary patch delta 13232 zcmaJ|3tW^{-ac~}m=Tc~FA*W?XlkXPXlh!fn2wrhJ6>o_^MVQig8BiIX4ya^1x>h? z=CRV&c3ZcvtTipLv>Jr~O_QuPx7$))ItnE!DVa6j^FQzV4sTZ9{C>@x=l?wCfBxrw zk&5cO7d3R>?kTcgVCr+>35#pzilROI>Y;;%4IVb=rj!)x;1v7}8l)N4-QiD+(vmy# z{O3nn9Wgg|T(>B=DwYyfuO&7nHrmSa*IDX%tc%?{UgKx(O|&|k2_4t1*qhdw5)hhy zUbDEOo~*lD6VItz@Ed3KJ=QhVjqhzPV+EV4#XR-#97^OouK8Sd*ZwJOWyIfX6!n^A zQq+_6Mj7=B`k0-u2iZK$+q_ZQ?n5^ zOCK8b@X?B+miX<_YnYP_LGIOAqE{*J6;(KnvDU9ECzbLO4jw!z25sa+#>o!Rj2*s| zx$9cC=IEN>nuawp=h_*Ya=MmB29Vz)ziS8HUv0g#r>NS=rpxbQ8EZ)^JK=A#%el8P z7P)V%DBj3>*@hB$daQV9BkyMGNWeQ*e6W!xc0MrV3sWNTCBec3J+0BoTB}qZ7Vx`NK3=908m!&N_B&aP zmAf=t?y=McHItLc=OYloPmHm-x?4wOeOdk925!NZwQQdri=#4Vska0*zq$><-nl#+ zY%m<`@JuZp#&nkuUM;pf&AW&%pXPnKQrV?3T#sg=#Zj>?7z%|bDADa1?un(!qrh@d zsJ3|MS$?%`W=AwgwNpI*Ebo6qAk7|dw^-^O6>N3TQ4;z&6j~dYb9sLix?;M&sf#=` zni@HIjQH*uKCF{!BgEi%bTcYS(Tkza^4@*O?pJY}T2T|I2nGfSI-np6gu)AR_uKLF);oZz9Bx!M=p(%0PwiI&C#M=Rap<9tQz-4R#S5P zmrw}F3zWpjW2)5Qs?>9uOX)C`S^_yDQL0%=bx>1jVYQQsOoC-f@Ujy4RHQ72%dsYf zS1BGdyRgb$Cc$zg$TkZc(xM4W6_6QicDlIs8{OKdOQW z7#~XUf?4snp>UM^gdkJ}x2X#L`+};VJWsttgSAPS-_^}uzS&CL&WDeizcPN>6 z5i4X{i=Uc+b~o0!lHoL`PK;2kn8jQet+TFCl_+z!pnxpwT1-wbOwGzfsv(A{38s*@ zMIO}ZUhcoIN`;n_?(r#yA#W44jjb!Wb%iI3T4rsk_n`RGFG%18CuNZoup~7EsV5yJCZ*~#p;ORnD(Xa^NkL}unR2+ zLk;#{>IC9lqi3{QB-+ z4x@g!6{1>qlj}~^9p!%ed0L68TEzIjbMNAYDUEzfA3J-2Ng{beZ1U4X&^YbhT8K1-RFppLYcRmo2h#Gn^=XWu~xi>s2L zlux0?k6@a;e+5k=w@?%tCXOn{8T^cT(S|Dlh)MA~SwnWN&$o zDsd0$ja-;MH2DTyrF@y^V|T-(%|}wTH%v4iwT!J@^o%lWI$@ZOhfPNeQ$xAGu1*U# z-9sLH4D*(z_RctR>_6Ee5Lwj@vXdu0k8?^CBbK=n+*$w&t+$4rO~%+0rx{iMf~dy>kEG6fv@muoT*e zx9a?hici#dIpQp~sJ|cx|Egvtu6vPR<#YXh5U;CzpM0+>`l}%JYPR17@nReCyFrv{ zy!;1Rn%W6Nro|$5zsP&oZfuXix7R5W{+D<2y`_ror>RvR(8^kftB%o&wMt>v6aja= z-ceIclJ}M5#CO_2<0w-8OpQQoc35%yb?&5Q#<2cJPbz{AZV(+)qJQY3eGtjFm68Uv zRo)2N2v1Z99={!Yf z;hU*VmiE&Lq<>k{tBajCS_#zIpw@kv#I^2L%BtU-;h47iWbdD0#{>!aF&r8v1=4&i zpw#uyQ4#dWT%xM2tAk#f^4K^kkak(Wf#6QNH;`u5Tk8B0tI%a~W@3>0p8|;vMJ)2EB&zri2tMSEDKBZoPUjhbjaa6uT@p)(VU>BORgZ_4Qh68hM!uCS(zo%u zZ5R3j%t{t}xA7ZnH3a%4i?iGKaNDB<8k0oIEBq$g?F3#)5=&m;xA?lqc7VlI*>Kd@ z0Y&9R%LZ-O)CTE^LN#RJ$uPiCUQ5?#=Xletxwm|~ANlGAU$w#DQ76l+Wdm9Hy5U8J zq0Ga}{RFW{W9m67zj%dz#rqyPj=^>sU*VeFh2x=9Zoq+~xaz|7l)d3-Wy5wp%5rxD z9DC%emsC@b-N|ZG?bt+=h=3IvA~T(OWaqx9@^U!S~9VP8FJTVg;YqCVhjhn~+X62T)zX z2dMxm_z3%OEKJ2!8+(c2m3$7Ym7(1jQ-Zx(HwrpTQi7U)97MtDmAWRc!*P6VZkl1{hZoT+uVIlWHKJIEF0y!VDJlCQE(U z_=%0aSz%*I@cz!ad?Dm`Ss=m9y2+z?F!EiiXInN=4g)iyW_d@=qIWm~#hn$`(6H#X)V4sm{!`O# zL8pb8$Q`*SUYvP{yKI99JQZIV_b$JI`z*K{MP}d{LMuWi=CH(vUln8Iaom3*CxQ&j zu?MC&G3!d_w9<=eX1wTt+h7DJ`)G`@@r<#L$SqG?;OLNqU`;adTsCS~?%H5%l2}H3 zyqUMk-3H$)No*lL$;?N|fWZ$-67|HVnt8jNZSWJ5HdKM0W~QTMe}kTdQwZ@3%zTVg z&s^0AKPO4dCqCcI$IAD$m8LehGD)l@ezlp$L$txKNfJTgH=6lQQonxJ2LF~Mnu*_L z=DWySjQkZz!c`4EXy)T&dxJlaBs|1_Z06(T=i0JY8~iFs6cI1Ye1d%0;LjzA?ZjU; z^NI2SgO5rU4a7T>P2JaB-eK?w$-?;__?~9oCA%7Y|74L${7^IBOE#(v9_>3iSu7*| zb~E2w?lyQ&ve-iWpUr$<88G;{$)cY43^RYdoNe&A$s1}w7ntdOvcEyEOcp7`7n}Jc z`IB*)D@_*jiT{h4A0Xd1_zlToZ4K{hdkp)3Cylpn#k|W=Z6-{+~qis(NfGbu6_VXD&_UDY; zg-i1?T!l+riwm-nT!mQ~uEiPoOEPj@1z8!nxdRx!m$`0rU> z`7D(o&nOowOfOhmz?P-oo52bS)AI{mlyoh~UAl1bXcx;}x^!7ydj4YN33HhlUT$_x zRl?cIcV0%`(){~X)`B@nE+dy~c0qRDvRt^!Sh6sS{LInHbIs24<`!lbX60w3FUnqW zuWJz;%n^S%z;Co`M%P&J?g73+On;wG68qlgcUqWm9OOOuyS1YGVV)2*62a8fin$;0 zgh}@xeYiFh!l{k1F|qa%ep`pg+3&?zt&F)~dJzA80KN|{k70hCKO&+Vf3FHE(rm$h zhxdz$gS;27Iv_qf$oukF-WU83@VD=azVw@OP)s_+yJv1b7z*LhvCUV3Ru=YbR%l`D zb?~_6wedM^v8)Hb5By{^k68FP6^e?NZjYxAipNk^)E7wiI3!*@#N&L}$k=7zUWY=V zr_FVl;E!;Di-+y6heM%kT;RNGOzlg$EaKD!47+{-`?SeGxPZKf-p5;b885x`h>itBzufDEg3X zJ#4SH+1fLz@{2J3>r-v))gis>Q!$Fhp+x$ZPsLnH+u^16QSlJcKFX5>ya5M^6r{ri z=eCWN$>6twryE9?=dUV(3#MG~!~YWs-Dc*|*UR;$qcW&ccR@49tl@Y2loJwchhQB9 ze-uPL_`ic6ZFcZLTaeA*_kkZ}=2y1y&Pc`@z>hWaz9E{x1!D>X5ug44BYp-n{XY|# z^%(IhkWT(mJXX*9`d&b~!nAkw0|kdsZ&ym0cj z6PV{{1yQg;XwE_NM?KvD{(JCCpbeM9bF^`OJsItcjr=wgN-ziFek8(gjc{11#?*|_ zIz>(9Bxw8LpqgyfM%-W_i3_eKgMZ{yC^VS-8XF2)Mvs?~W*Ic=Pl>pX&@1baK6*;r zOzB-n-*8%OOi@RXPB|?e!X_2fiuBkf@f@Y&@lRkQo5VZN_Zx}y^SHMh!8|jU6>-3- zT8A2BJ~V$is}`TWMM%%Y3&cIJhxJ^W6Qtb&e!bGhMZ185z?;vC%#RVxde5gkeCelhOX)2}RHh zKil5P==4FjC-7@gPDV#E!UKRk{j?UKuto!Sk8!dubpCeW?N>Y5SRGFVX4kd(p96du z80Gks!#o%k>w}IQJ_t+~bsEnebNN924FKOcxGA zzXO==ck0E69MFM~-m8rQcLT39IoJzKHyXo1Eim0s415Tf{cDLxERg4EXS?A|8W89h&%h-N9Vo1XG4Zz{Y~qiCfu9 z-~~1(yIa?<18%}J-k{@+z!jzdp97}XSXwoxOSb{9OmMi&+aJ@dtsle03BVf80kiS;oZArd1hrht^L$s59 zs|y^M{~Iw4X&;~r@xb&bK_BYr?!XDALE9hrJi5pzU=VOGlYSVm#4Mtd3i%rcoZ;*0 zWUKXv?}TBxX$aiNNA892adL2 zsn+>M;MO5d_M)Eu67VIAG5Sydp$LrsTvGsapG?KBXpC|PFukJ~8Lk4}f*RsS)wmB5 zvrK${V3#SNTY-&TvlEYG(}6F=I2m1)r~>n?7=NXv05Tz44 zstv%j4HqE;wZPwj^DzcS0aF1l0jFYCWa{`$;0abI>!f#ioeu_j$fFN6$syo>b)Y}J z&>Z|5crFIbbR8c9rk5T1kX{1QztU;g&RT#M#W~q+Fi-#?;C;QFjEv`9L&bam zJUZUV`sxAy8<-yB4E}TA=h3Ct!9WFG06v8Z^we=QHkmcp{pg_GiT3{Y;jVru{Y9~kCi zZ8jE;mB7XskYq)`J1|D*8cj7SA>LGA0GM8z=|lRpz<)F04Zx2&oa!n~`8NUogciGo z_CGRgg@N8X4F}tRBQT9pU?6@c@E7f!jNVHKzX?3m)B?4@8%+5R0iVa-(LtYPp95F- zbgFHh@}B}8rUJw~qlmwU;VV zWE9R@=*=%!nlFwV=R@M}%_v-$#j*?XhY|~9u+b;DM=UtObH&jUe1^E`B!92{o!-2R z{OpBev(od$k0<%Jo%Ikh)3fvE>2Zj!zU32pEY7%p!P4~nMf2c%-n?b$3m0cB(jGin zdg0P+F+}p>0r}n~^YAK)r&E?qvFCf26&B3PUSg^n;#roTy)-|&@P1J(`5xgr#joPN zVPeB6-n|`NnTp0UcC&c<6z>sDH>N!kag>S{CqYNi0mV99M4!h0oZYjf1D{{#9`HMIZ$ delta 11446 zcmaJ{3tW^{+COs~m=Tegl)NI2S3t|~PKt&rpZ{~7=Q+>0 zA1pZ?Us4yp%~N9kfvL|A&stpXK3cMycOEu)$nfDqMA&oK zc;xRs-~PF!%eJUo=48WAd-^=lyPWrq%!13$>#HY~^KTt|(+M$n9Um5V^xE$151s4* zIjlWn&C@Er4K&(iekx-T2gisNhq%ihE5{Lhc?@HfZhLNpyU|hYZmihbSnduvw!1?g z)u5K_K{_j-lXn9y^Lt4V3WWmu?5h?1K%g;J-W<(XY0V7i)ViCgRcH61NtM&=mF{Ls zont#owL8jw2f3p<5Ld!jZs4kAdBh$N6xQMjIaV#to8I#>k?4G?uN@XkEdDgM2l-_tGW zW+=3}a`ugIy4sAQbj=+t?s=T>LmrJ(zMJxV1?FRnqJG3|uGPMDN zLiG}rx1jscXrz3fvnBLEqf;Im0Y`&V#FCA?n>nhRT2wcUs5WinU2IR1 z@;^q3zis4q_%SH0auOt^HLoMEe3oyOZ)>AA&=_46c=Y>F$Rm4RqjBt%9d6*a(GD$# zELU1ig06g*toli*+!EG>;qWvCW_NT(V?Ll}J^$lRm@+LB0u$>9luBy`Kx@DIN0z@( z7AQ;Ba`#2dtc!uuma?E@V>b!uv$r4!6)OopIDuaJA8%%03*!AZI_Xqlw zU*T71cR9%vPLvX2e|l4B8}_#D;#)@ee@FRTnc7D;{4V8you zHZ8vHu-OGQRt9}-_p6en!KM|+>1b~hzI!;zsI}p)D5EaluR6w}4I8VR_8&D))ZlvS zdq0h?{XZ&^{D3-v2HkemCtG##D~7mMiP!1kN<(~7iA!~{*AQP<;(T2^+YonZCYKNB zV*dm~o~-1ly8JGP8xpn9+0T=$HO3*IqWQ4T*^(S@9OaK#r$_dVQhhD|bu|<^`vo$A z6b_Nt$ z_vRS`-myKSQ5*DFg^dnZ3p90jLz-b>2bNk`@P|#%hN9-o|8HWy`#jC1DtDttIfuX3M%nqA9gC+Z=hvlJcug9?FC9u@M{%R#!@MT92L;cInO z&efT7R2*up`#Kqcj6;-_t0}<1UQ2~4IJU3u3%v5U7fTxEs=cB{?!~gDFSY*gp7bg# zl-4kqkWV3t74Bvg&q5W?3VfrA9#6A;$)<&!O{r>(&L1KdqYgA0_98RtNRXxMZ9@*^ zD3iTHW$#Yio?mNVvz)3MRJm6MyWeWCue2(I*)Rx?d6ZB2P^=0=i!~2tA|@voq9)Q( zqM?SUu|?$4MSeF{DqXF;q4o_#Az!ncbi1-%6=?ogYvnwTG8pnT(xO+UXjfGr{YWeN zn~+b+T#eozp!|o25^^(4j=GC-y*8Qonfy0eF1yF!t6dj-J==@1>&SCe;9Yq2U<+!~ zRfVBs2M>^}m%Ip7)gRH=S-x;7fU#GZmW*8NcV7%oZaGnnEZW?-9DzBjRjUd-P92dT z-?*$qay|734Zz>44qB~?13>cqXfgavJ~mz`YK%tVWLN2dM=K2qtP9h{vv2Z#{+E=_ zwM)vX(s5R%MXQ11k+(+}VOIs-P>RPi#fGuUw^sSDtt#)4v3mVatl|1wRsCl*T~+%% zNcdH<%s$Mw9*A&-7dTr09MIUKZt>`GB-STDs9ZOCH+E z@)&Dka<#A9{I9Hsg9u)ss{VCF|@iU z4Zo&Q>8{gzZ<9$SPN~e&?N259lPIHZJ?1gcK}tu1R5%Qm8X4IOJGE0ZVY+*EC7L#;J__%U6(Bxq*^P zkxW;$*J!onD&k5F?s3IEqI3D07;}184JWx#8!spsvO9F@(DBjz`)9mvBN^3qGAZ~2o6_Xw) zZL}hI`B5TC`=M6I7hP3$6-%rKdx9KK<9;7zsUehqY*g6_?|@ICYQ(uD@y^@)A=>}~ zPbG=lw(=y~wFH0}Nn+eqo?_cYpnH-i*~&-SRuDLqC_dcENB8P0#{ibrJde7y!A+QW zxwY&rR<$MtEha&A?l#`U`!D?t30#G*7T*tI&2-96eW`X)KT-~!sD9#IKGyP35N15` zns$ySxs%nV+3jj>Yru+4fthwoxu!RQ`o525hGxIL!ed>n@9448OZ3S;V(RaG&8iLDy!(?*+*&p!1 zDu568-S!pSwdX=)! zQ?npDoEE{q@?Ik*=A#$y(zd^r(WwqQ-(Zg}tw}?5?7|gpoIR}h(57QTNOb;?k55eE zjM4e9sREm(yQy+oY=yJZ9gQa!=!SewQf`!gyd>6s$TR%u*v4t|OiM>Y&r@j{DyHA+ zv;l{J(wbi*ZPySq&Ets}gjn?|14~K}iK^VLGLQ!VdkxVoa?N^$rl!7Oz$3?5RQ1)B z4*ju0mcd2Dw_U4y?coC~!n2o8vPI$a7?Iyayu6op>Y1Rezvj5Cvc40=m4d1wqlWhz zH&CT?lS--hQgu|NJ4N3VcAQrN`B;4@bgkj0>6yeTy~>bkP<7^kuXxCsN`t zSUKBXnc+lFmCwFPk09mKnsAA@S~)Gc;sh=usF+(3cTgAYIp+*ZXB~_rv8Lpxp52^V zG3y|25yi0m=?AU2QBJ#xOV~;A^nTuTW*fN0n-KhJL1A_L(MEI!j2lf<;o)dl-c_5n zst|=$r9maIHkh2?WO>hE1mxR2`8ZOl)y7#H9F?%PmUrrwZl<|xz}kj3z_nE4oa$l!M-2oLf5%)C>+VDLv0L<#YqoB0lMvB9575Zj1vH1n}? zw839V5T}V}iKg^+lI;vWDp5E;0v~JUyU4H93XA;4CyF%U6U}@#`DcS4k|>rDpK9jg z#LMK;tS0_7GoL7D8vK?-5hVUyGe1BMH29r~qKWuDX8sO&-PqC( zBnsCd@JG%3AbH5(KTQ;#L%hFjPz+}6GY&c#^|nP2$Z?3*5AkWXxA6KM(N;0>!gZKO z*aMK1*8DR@+L@njoJtfPX>B=oEaxE(=ELi64B?^!7q2hmz za$olhcgEDrhg|N=%&D29Tm|!r7Q0XcYv?yD15V5|3! zb)gVmPs6r~{|+mRxghewN6azt_;DT|xen#=$Hd0typx}}9l#Tgg+hn$UV2L$HEmQJ zIA5@O=$t)fx|C;kTbGvMzu^NTc|autWo{37^zGw)s{icj#{?o0>yr%ytm zab^;Ucucux4ErL>gH8}S9`Viz-qF7ju$f@1DlTBUH6`M8>0m34@73&)xqZ+%0-ZLv zUz^)ZXW!Fg8*-#heMr^}**9TXJ36W{I}%p)6?JU4$V8OaekGndiSk60>%S74sho#0 zuNMbVj&h;S)4<+(G3XO?{x+1Ks}F@{L*5bue@JDG?AL)mS}!h;ohFq3iSn1c(BXW2LBHD|0^zrBpwBiN2##=f|mMz=7Pd$DBNvU@U@KbArOJO7HpmXopP$;*x&KymLg53?B%Cq9a=NMh}D4#he zV!q(<0~iiVP3J;M$p$N>68W#A-! zCz!CQzH<0a2#mhAVCwt;Y}fT~Lk4dG=b?qvCAXoA+QabWC?}(hvI9KC0?&V8@aDU2b&J=-~1yQ>v zU`IfSdi@c=Gfeuaz{dK~fm>N7@LKfwL%RNA;3qpenV&{GwO|WL4oj(z;rz|^#23g zjrqfZC{&zpQg8r&)WykYFp`0;z-vsiBpI0g6~-oq7T`$W42&6?|D;bxvW2ED%L1n7 z&OGpx(K*2L{U(K62&|Ykq(BCq1g0AueJBqB;Ke3f4jc`};MW6ZVGvh9K=t1MwvIK{ zfk;*jd>o z&}HJ$Vq+n~IArTh_U%@hf2Ih#LBZJNI`9ZaCrbL)QAPlxfv;m>8m9+55%_?~;S^wc zqA&u;0$$qP$>?B4EiMFp5BBh*y5LDGjX%@0s|3>$FX}EE8WY$KeTbO z4tff=0n?KXeJITLfuD}XJF;H?L*Qsky9ac92$(MU^dbE^KLpf5O<-sBz_D03egy#q z@Eve}Ors}sd92iL zOclBT&&KXVM;xlq2lzFNY9k|e0>6lq(pi@HF~B(p=nY*z1NdLir*i@E(}3w!iar$3 z4B&!rm-<;g1RG5)C^1j z_)qB4{(8XMfaw`Vef$q1MqS!KyCkj1%h~!fj5C; z(FJ$w20j2jj-?m>?m-#+2$g0|kgBBVz-h#zB3t6#uUXfR%UF4;XmY1hu7+mBP7ccVN zVoMW`P5PU<%Btq;-N4jvrF9b98l#&<5R0TooNeMg2hqjCnT}~;*Q(Gr)(!O;if^ih z45|ZzoGj*C;+@3gOFTM~*5XDbz!;_5Muu2*3ICFnPM;H}h__LWqFYAR6viIiD82;d zSyRN#OT2&kCm<`q+Z_I5iDK|&-aYaLP9G;CMdoGRJK|_IPE=yvCEihlp5PJhw12_6 U>MCD#wau!lC`7z|HFC%Q0@ojz?f?J) diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c index dbca66f..9bb883e 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/gdt.c @@ -109,7 +109,7 @@ void set_gdt (void) null_code->always_1 = 1; null_code->DPL = 0; null_code->present = 1; - null_code->limit_high = 0xF; + null_code->limit_high = 0xFFFF; null_code->available = 1; null_code->big = 1; null_code->gran = 1; @@ -132,7 +132,7 @@ void set_gdt (void) code->always_1=1; code->DPL=3; //set it to ring 3 code->present=1; - code->limit_high=0xF; + code->limit_high=0xFFFF; code->available=1; code->always_0=0; code->big=1; //signal it's 32 bits diff --git a/src/kernel/kernel b/src/kernel/kernel index 9d8002a7155169bc4911b8ea61a0fdfc09558504..33d66f0d947b351c2bcfb6462d42f85bc1c3f88b 100755 GIT binary patch literal 27276 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≻&g@eq3`EaLb3IJ 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 DxA1W} diff --git a/src/kernel/kernel.asm b/src/kernel/kernel.asm index 52066f8..6fe6806 100644 --- a/src/kernel/kernel.asm +++ b/src/kernel/kernel.asm @@ -13,8 +13,10 @@ global start global gdt_fush global stack_space global jump_usermode +global enter_usermode extern kmain ;this is defined in the c file +extern run_kshell global loadPageDirectory loadPageDirectory: @@ -68,7 +70,7 @@ tss_flush: global jump_usermode ;you may need to remove this _ to work right.. jump_usermode: -; extern print_a +extern run_kshell mov ax,0x23 mov ds,ax mov es,ax @@ -80,10 +82,33 @@ jump_usermode: push eax ;push our current stack just for the heck of it pushf push 0x1B; ;user code segment with bottom 2 bits set for ring 3 - ; call print_a ;may need to remove the _ for this to work right + call run_kshell ;may need to remove the _ for this to work right iret - + +global enter_usermode +enter_usermode: + cli + mov ax, 0x23 ; user mode data selector is 0x20 (GDT entry 3). Also sets RPL to 3 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax +; Now we can perform the switch to user mode. This is done by building the stack frame for IRET and issuing the IRET: + push 0x23 ; SS, notice it uses same selector as above + push esp ; ESP + pushfd ; EFLAGS + push 0x1b ; CS, user mode code selector is 0x18. With RPL 3 this is 0x1b + lea eax, [a] ; EIP first + push eax + + call run_kshell + iretd +a: + add esp, 4 ; fix stack + rts + + start: cli ;block interrupts mov esp, stack_space diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index f657ddc..4f28c71 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -12,11 +12,6 @@ void jump_usermode (void); -/* there are 25 lines each of 80 columns; each element takes 2 bytes */ -#define LINES 25 -#define COLUMNS_IN_LINE 80 -#define BYTES_FOR_EACH_ELEMENT 2 -#define SCREENSIZE BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE * LINES #define KEYBOARD_DATA_PORT 0x60 #define KEYBOARD_STATUS_PORT 0x64 @@ -26,12 +21,6 @@ void jump_usermode (void); #define ENTER_KEY_CODE 0x1C -/* kernel debugging mode */ -uint8_t debug_mode = 0; -uint8_t command_mode = 0; -uint8_t command[256]; -int16_t com_ind = 0; - /* memory */ uint32_t mem_start_address = 0x100000; @@ -42,7 +31,11 @@ uint32_t mem_use_address = 0x400000; extern uint8_t keyboard_map[128]; extern uint8_t keyboard_shift_map[128]; +// keyboard buffer +uint8_t keyboard_ch = NULL; + extern void load_idt(unsigned long *idt_ptr); +extern void enter_usermode (void); void timerHandler(registers_t* regs); void thread(uint32_t argument); @@ -93,7 +86,6 @@ typedef struct multiboot_memory_map { // threading -uint8_t threading = 0; uint8_t threads_request = 0; void kb_init(void) @@ -135,6 +127,10 @@ void kprint(const char *str) } else { + // new + + fb_current_loc = (fb_cursor_y * BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE) + (fb_cursor_x * BYTES_FOR_EACH_ELEMENT); + if (fb_cursor_x < COLUMNS_IN_LINE - 1) { fb_cursor_x = fb_cursor_x + 1; @@ -271,6 +267,20 @@ void fb_set_color (unsigned char forecolor, unsigned char backcolor) fb_color = (backcolor << 4) | (forecolor & 0x0F); } +void fb_get_cursor (uint32_t *x, uint32_t *y) +{ + *x = fb_cursor_x; + *y = fb_cursor_y; +} + +void fb_set_cursor (uint32_t x, uint32_t y) +{ + fb_move_cursor (y * COLUMNS_IN_LINE + x); + + fb_cursor_x = x; + fb_cursor_y = y; +} + void div_by_zero_handler_main (void) { fb_set_color (FB_RED, FB_BLACK); @@ -287,6 +297,124 @@ void div_by_zero () i = 23 / 0; } +void kshell (uint32_t argument) +{ + uint8_t ch; + uint8_t command[256]; + uint8_t command_ind = 0; + + command[0] = '\0'; + + uint8_t input[256]; + uint8_t input_ind = 0; + + input[0] = '\0'; + + uint32_t i, page_start, page_end; + + kprint ("Welcome to ksh, the kernel shell."); kprint_newline(); + + while (1) + { + fb_set_color (FB_WHITE, FB_BLACK); + kprint ("help = list commands "); + fb_set_color (FB_LIGHT_BLUE, FB_BLACK); + kprint ("user"); + fb_set_color (FB_GREEN, FB_BLACK); + kprint ("@"); + fb_set_color (FB_RED, FB_BLACK); + kprint ("ksh> "); + fb_set_color (FB_WHITE, FB_BLACK); + command_ind = 0; + command[0] = '\0'; + + kshell_loop: + ch = getch (); // blocking call + if (ch != '\r' && ch != '\b') + { + command[command_ind] = ch; + if (command_ind < 256) + { + command_ind++; + goto kshell_loop; + } + } + else + { + if (ch == '\r') + { + // return char, check if command + command[command_ind] = '\0'; + + if (strcmp (command, "tasks") == 0) + { + run_threads (); + } + + if (strcmp (command, "page") == 0) + { + kprint ("start page block? "); + command_ind = 0; + + input_ind = 0; input[0] = '\0'; + + page_loop: + ch = getch (); // blocking call + if (ch != '\r' && ch != '\b') + { + input[input_ind] = ch; + if (input_ind < 256) + { + input_ind++; + goto page_loop; + } + } + input[input_ind] = '\0'; + page_start = atoi (input); + page_end = page_start + 23; + + for (i = page_start; i <= page_end; i++) + { + pmem_show_page (i); + } + } + + if (strcmp (command, "loopmark") == 0) + { + loop_mark (); + } + + if (strcmp (command, "help") == 0) + { + kprint ("commands: page [list memory pages], loopmark [simple benchmark]"); kprint_newline (); + kprint ("tasks [multithreading demo]"); kprint_newline (); + } + } + else + { + // backspace + + if (command_ind > 0) + { + command_ind = command_ind - 1; + command[command_ind] = '\0'; + goto kshell_loop; + } + } + } + } +} + +void run_kshell (void) +{ + uint8_t *ksh_base = (uint8_t *) kmalloc (4096 * 2); + uint8_t *ksh_context = (uint8_t *) kmalloc (4096); + uint8_t *ksh_stack = (uint8_t *) kmalloc (4096 * 2); + + thread_init(ksh_base); + thread_create(ksh_context, ksh_stack, 4096 * 2, (uint32_t)kshell, 0x61); +} + void keyboard_handler(registers_t* regs) { uint8_t status; @@ -297,7 +425,6 @@ void keyboard_handler(registers_t* regs) /* debug */ uint32_t i; - status = inb(KEYBOARD_STATUS_PORT); /* Lowest bit of status will be set if buffer is not empty */ if (status & 0x01) { @@ -307,53 +434,13 @@ void keyboard_handler(registers_t* regs) if(keycode == ENTER_KEY_CODE) { kprint_newline(); - - if (command_mode) - { - command[com_ind] = '\0'; - - kprint (command); kprint ("> "); - if (strcmp (command, "div") == 0) - { - div_by_zero (); - } - - if (strcmp (command, "page") == 0) - { - command[0] = '\0'; com_ind = 0; - - for (i = 0; i < LINES -1; i++) - { - pmem_show_page (i); - } - } - - if (strcmp (command, "tasks") == 0) - { - command[0] = '\0'; com_ind = 0; - - threads_request = 1; - } - - command[0] = '\0'; - com_ind = 0; - } - - return; + keyboard_ch = '\r'; + return; } if (keycode == '\b') { // backspace - - if (command_mode == 1) - { - if (com_ind >= 2) - { - com_ind = com_ind - 2; - command[com_ind] = '\0'; - } - } } if (keycode & 0x80) @@ -364,91 +451,35 @@ void keyboard_handler(registers_t* regs) { pressed = 1; } - - if (keycode == KEY_SCAN_ESCAPE) - { - /* switch debug mode on/off */ - if (debug_mode == 0) - { - debug_mode = 1; - } - else - { - debug_mode = 0; - } - } - if (keycode == KEY_SCAN_F1 || keycode == KEY_SCAN_CTRL_LEFT) + if (keycode == KEY_SCAN_LEFT_SHIFT || keycode == KEY_SCAN_RIGHT_SHIFT) { - /* switch command console on */ - - if (command_mode == 0) - { - command_mode = 1; - - fb_set_color (FB_RED, FB_BLACK); - kprint ("COMMAND MODE"); kprint_newline (); - kprint ("press F1 to EXIT"); kprint_newline (); - kprint ("commands:"); kprint_newline(); - kprint ("div [division by zero interrupt check]"); kprint_newline (); - kprint ("page [show usage of pages]"); kprint_newline (); - kprint ("tasks [multitasking demo]"); kprint_newline (); - kprint ("press ENTER for prompt!"); kprint_newline (); - } - else - { - command_mode = 0; - command[0] = '\0'; com_ind = 0; - fb_set_color (FB_WHITE, FB_BLACK); - } + /* uppercase table */ + keyboard_shift = 1; } - - if (debug_mode) + if (keycode == KEY_SCAN_LEFT_SHIFT_RELEASED || keycode == KEY_SCAN_RIGHT_SHIFT_RELEASED) { - kprint_int (keycode, 16); - kprint_newline (); + /* lowercase table */ + keyboard_shift = 0; } - else - { - if (keycode == KEY_SCAN_LEFT_SHIFT || keycode == KEY_SCAN_RIGHT_SHIFT) - { - /* uppercase table */ - keyboard_shift = 1; - } - - if (keycode == KEY_SCAN_LEFT_SHIFT_RELEASED || keycode == KEY_SCAN_RIGHT_SHIFT_RELEASED) - { - /* lowercase table */ - keyboard_shift = 0; - } - if (pressed == 1) + if (pressed == 1) + { + if (keyboard_shift == 1) { - if (keyboard_shift == 1) - { - ch[0] = keyboard_shift_map[(unsigned char) keycode]; + ch[0] = keyboard_shift_map[(unsigned char) keycode]; - if (command_mode) - { - command[com_ind] = ch[0]; - com_ind++; - } - } - else - { - ch[0] = keyboard_map[(unsigned char) keycode]; - - if (command_mode) - { - command[com_ind] = ch[0]; - com_ind++; - } - } - - ch[1] = '\0'; - kprint (ch); } + else + { + ch[0] = keyboard_map[(unsigned char) keycode]; + } + + ch[1] = '\0'; + kprint (ch); + + keyboard_ch = ch[0]; } } } @@ -502,49 +533,73 @@ void timerHandler(registers_t* regs) { clock_ticks++; - if (threading) - { - thread_schedule(regs); - } + thread_schedule (regs); + // kprint ("clock: "); kprint_int (clock_ticks, 10); kprint_newline (); } void thread_a(uint32_t argument) { uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + thread_set_priority (5); // increase thread priority for(;;) { ticks = clock (); kprint ("thread A: "); kprint_int (ticks, 10); kprint_newline (); - kdelay (50); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); } } void thread_b(uint32_t argument) { uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + thread_set_priority (-10); // decrease thread priority for(;;) { ticks = clock (); kprint ("thread B: "); kprint_int (ticks, 10); kprint_newline (); - kdelay (50); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } +} + +void thread_c(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + // normal priority = 0 + for(;;) + { + ticks = clock (); + kprint ("thread C: "); kprint_int (ticks, 10); kprint_newline (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); } } void run_threads (void) { - uint8_t *thread_base = (uint8_t *) kmalloc (20000); uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); - thread_init(thread_base); + uint8_t *thread_c_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_c_stack = (uint8_t *) kmalloc (4096); + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0x61); thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 0x61); - - threading = 1; + thread_create(thread_c_context, thread_c_stack, 4096, (uint32_t)thread_c, 0x61); } @@ -573,7 +628,6 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) uint32_t pages_free; uint64_t ram_free; - command[0] = '\0'; fb_clear_screen(); @@ -635,7 +689,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) { mem_start = (uint32_t) 0x1000; /* skip first page */ } - + mem = pmem_set_bitmap (mem_start, mem_end, FREE); if (mem == MEM_ERR_OK) { @@ -655,6 +709,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) kprint ("alloc error"); } } + kprint_newline (); } } @@ -664,12 +719,14 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) pmem_set_first_page (); /* so no null pointer for free mem block can exist */ + /* mem = pmem_set_bitmap ((uint32_t) 0xF00000, (uint32_t) 0xFFFFFF, ALLOCATE); if (mem != MEM_ERR_OK) { kprint ("MEMORY ERROR: mark reserved"); kprint_newline (); } + */ pages_free = pmem_count_free_pages (); ram_free = (pages_free * 4096) / 1024 /1024; @@ -728,24 +785,16 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) } */ - kprint ("F1 or CTRL-LEFT = command console"); kprint_newline(); - kprint_newline (); - kprint ("READY"); kprint_newline (); pic_unmask_irq(IRQ_TIMER); - // jump_usermode (); + // enter_usermode (); + run_kshell(); - while(1) + while (1) { - kdelay (200); - - if (threads_request == 1) - { - run_threads(); - threads_request = 0; - } + kdelay (100); } } diff --git a/src/kernel/lib.c b/src/kernel/lib.c index 5c38ff4..c4bbd36 100644 --- a/src/kernel/lib.c +++ b/src/kernel/lib.c @@ -2,6 +2,8 @@ #include "types.h" +extern uint8_t keyboard_ch; + void strreverse(uint8_t* begin, uint8_t* end) { uint8_t aux; @@ -45,7 +47,44 @@ void itoa(int32_t value, uint8_t* str, int32_t base) { strreverse(str,wstr-1); } - +// A utility function to check whether x is numeric +uint8_t isNumericChar(uint8_t x) +{ + return (x >= '0' && x <= '9')? TRUE: FALSE; +} + +// A simple atoi() function. If the given string contains +// any invalid character, then this function returns 0 +int32_t atoi(uint8_t *str) +{ + if (*str == NULL) + return 0; + + int32_t res = 0; // Initialize result + int32_t sign = 1; // Initialize sign as positive + int32_t i = 0; // Initialize index of first digit + + // If number is negative, then update sign + if (str[0] == '-') + { + sign = -1; + i++; // Also update index of first digit + } + + // Iterate through all digits of input string and update result + for (; str[i] != '\0'; ++i) + { + if (isNumericChar(str[i]) == FALSE) + return 0; // You may add some lines to write error message + // to error stream + res = res*10 + str[i] - '0'; + } + + // Return result with sign + return sign*res; +} + + void kprint_int (int32_t n, int32_t base) { char str[256]; @@ -121,7 +160,19 @@ uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count) return (dest); } - +uint8_t getch (void) +{ + uint8_t ch; + + while (keyboard_ch == NULL) + { + kdelay (10); + } + + ch = keyboard_ch; + keyboard_ch = NULL; // reset buffer, kind of hack I know ;) + return (ch); +} diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c index af30623..4283cc1 100644 --- a/src/kernel/physmem.c +++ b/src/kernel/physmem.c @@ -376,6 +376,22 @@ uint32_t kfree (uint32_t address) void pmem_show_page (uint32_t page) { uint16_t b; + uint32_t cursor_x, cursor_y; + + if (page >= PAGES) + { + // ERROR out of range, return + return; + } + + kprint_int (page * PAGE_BITS * MEM_BLOCK_SIZE, 16); + fb_get_cursor (&cursor_x, &cursor_y); + + if (cursor_x < 15) + { + cursor_x = 15; + fb_set_cursor (cursor_x, cursor_y); + } for (b = 0; b < PAGE_BITS; b++) { diff --git a/src/kernel/thread.c b/src/kernel/thread.c index 3c5f75f..1dd39b9 100644 --- a/src/kernel/thread.c +++ b/src/kernel/thread.c @@ -17,6 +17,8 @@ ThreadContext* listHead = NULL; ThreadContext* currentContext = NULL; + + void thread_init(uint32_t baseContextAddress) { kprint ("Initializing threading..."); kprint_newline (); @@ -43,6 +45,9 @@ void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackS context->eip = entryPoint; + context->priority = 0; + context->next_switch = 0; + /* We are building this stack here: * ... * | | @@ -71,32 +76,119 @@ void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackS cur->next = context; } +void thread_exit (uint32_t ret_code) +{ + /* exit thread, remove thread from list */ + // ThreadContext* currentContext; + ThreadContext* search = listHead; + + while (search->next != currentContext) + { + search = search->next; + } + + /* skip current thread in previous thread->next */ + search->next = currentContext->next; + + kfree (currentContext->esp); + kfree (currentContext); +} + +void thread_set_priority (int32_t priority) +{ + currentContext->priority = priority; +} + + + static inline void interrupts_disable(void) __attribute__((always_inline)); static inline void interrupts_disable(void) { asm volatile ("cli"); } +uint32_t thread_fair_schedule (void) +{ + /* get number of current threads */ + uint32_t threads = 0; + uint32_t ticks_per_sec = 100; + uint32_t fair_schedule = 0; + ThreadContext* search = listHead; + + while (search->next != NULL) + { + threads++; + search = search->next; + } + + if (threads > 0) + { + fair_schedule = ticks_per_sec / threads; + fair_schedule = ticks_per_sec / fair_schedule; + } + + return (fair_schedule); +} + + void thread_schedule(registers_t* oldState) { + uint8_t do_schedule = 0; + uint32_t fair_schedule = thread_fair_schedule (); + //print_string_static("Scheduling...\n"); interrupts_disable(); - thread_saveContext(oldState); - //print_string_static("Old context saved...\n"); + if (fair_schedule > 0) + { + // number of tasks greater as zero -> fair_schedule is not zero + if (currentContext->next_switch == 0) + { + if (currentContext->priority >= 0) + { + currentContext->next_switch = clock () + (fair_schedule * currentContext->priority); + } + else + { + if (currentContext->priority < 0) + { + currentContext->next_switch = clock () + 1; + } + else + { + currentContext->next_switch = clock () + fair_schedule; + } + } + } + else + { + if (currentContext->next_switch <= clock ()) + { + /* time to switch to next task */ + currentContext->next_switch = 0; + do_schedule = 1; + } + } + } + + if (do_schedule == 1) + { + thread_saveContext(oldState); + //print_string_static("Old context saved...\n"); - ThreadContext* next; - if(currentContext->next == NULL) - next = listHead; - else - next = currentContext->next; + ThreadContext* next; + if(currentContext->next == NULL) + next = listHead; + else + next = currentContext->next; - currentContext = next; - /*print_string_static("New thread context is at "); - print_integer_hex((uint32_t)currentContext); - print_string_static("\n");*/ + currentContext = next; + /*print_string_static("New thread context is at "); + print_integer_hex((uint32_t)currentContext); + print_string_static("\n");*/ - thread_switchToContext(currentContext); + thread_switchToContext(currentContext); + } } diff --git a/src/kernel/thread.h b/src/kernel/thread.h index baedd8b..88c88de 100644 --- a/src/kernel/thread.h +++ b/src/kernel/thread.h @@ -27,6 +27,8 @@ typedef struct ThreadContext_ uint32_t gs; // 56 uint32_t ss; // 60 uint32_t cr3; // 64 - unused for now... + int32_t priority; // task priority + uint32_t next_switch; // next thread switch at this clock struct ThreadContext_* next; } ThreadContext; diff --git a/src/kernel/types.h b/src/kernel/types.h index 51c7bf2..270ddd7 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -41,3 +41,9 @@ typedef uint32_t size_t; //gdt descriptor offsets #define GDT_KERNEL_CODE 0x08 #define GDT_KERNEL_DATA 0x10 + +/* there are 25 lines each of 80 columns; each element takes 2 bytes */ +#define LINES 25 +#define COLUMNS_IN_LINE 80 +#define BYTES_FOR_EACH_ELEMENT 2 +#define SCREENSIZE BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE * LINES From 05319886df8129dac283344bf4c294bd22fbd34f Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Sun, 15 Jan 2017 18:32:36 +0100 Subject: [PATCH 15/19] README update --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e5ce1e2..bb2264b 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,14 @@ I added the following things: - Frame buffer text scrolling, removes the top line on screen if reached last line on print - kprint_int () function to print integer numbers. +- library with a few new functions +- ksh a kernel shell +- multithreading, code taken from a workshop and modified to fit. +- thread priority settings, see multithread demo. - make.sh and run.sh scripts: make.sh compiles the kernel and creates an .iso file. run.sh runs bochs with the created .iso file and starts the kernel -You should see the following start message: - -red cube OS -2017 - I will try to continue work on this kernel. From 47e2aad70ba0dc297a840b163a33db3a18e02998 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Thu, 19 Jan 2017 22:16:22 +0100 Subject: [PATCH 16/19] threads: names, priorities, messages --- bochsrc.txt | 2 +- make.sh | 4 +- os.iso | Bin 503808 -> 505856 bytes src/kernel/kernel | Bin 27276 -> 30484 bytes src/kernel/kernel.c | 118 +++++++++++++++++++++++++++++++++++++++++--- src/kernel/lib.c | 38 +++++++++++++- src/kernel/thread.c | 94 ++++++++++++++++++++++++++++++++++- src/kernel/thread.h | 26 +++++++++- 8 files changed, 267 insertions(+), 15 deletions(-) diff --git a/bochsrc.txt b/bochsrc.txt index 8453262..caffba6 100644 --- a/bochsrc.txt +++ b/bochsrc.txt @@ -1,4 +1,4 @@ - megs: 32 + megs: 512 display_library: sdl romimage: file=/usr/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest diff --git a/make.sh b/make.sh index 47e2c5a..6a1d4ac 100755 --- a/make.sh +++ b/make.sh @@ -1,6 +1,7 @@ #!/bin/bash cd src/kernel +rm *.o nasm -f elf32 kernel.asm -o kasm.o nasm -f elf32 threadS.asm -o threadS.o nasm -f elf32 interruptStubs.asm -o interruptStubs.o @@ -14,8 +15,9 @@ gcc -m32 -c interrupts.c -o interrupts.o gcc -m32 -c idt.c -o idt.o gcc -m32 -c pit.c -o pit.o gcc -m32 -c pic.c -o pic.o +gcc -m32 -c message.c -o message.o -ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o +ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o message.o echo "kernel build end, making .iso" diff --git a/os.iso b/os.iso index f45af798ae247a624ae69866d6e8741ebb85c7a6..ecd62a3ec31e4e7128574a3e67c0278944bd7c70 100644 GIT binary patch delta 14292 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 Qu=0|9<)w)Amm+umFJrhFl>h($ delta 10933 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 diff --git a/src/kernel/kernel b/src/kernel/kernel index 33d66f0d947b351c2bcfb6462d42f85bc1c3f88b..019bbabaf41ee3e398d1bfe44be592c55c38373f 100755 GIT binary patch literal 30484 zcmeHwdtlVXwf}6gfdvD*Ku`pfFAob83~vmA5+19FJeEK#3K;U(kPUgUj{s^kbctqN zgZ!$!TC|mFf4ADI^|lqM+8PKY6m5-CTkjQXidbjaSR+P=kjwr)XCB}ECIY{Ee~B!g?#zicf+?RP6a7x~SHZa0$L-flC&+WPwW-xMYD#7Pw@AOBT3fflC&+ zWPwW-xMYD#7Wn^afs~hC7)A}8Tb^?~H}Xkz;TVT=)ccXPbxnYrMq@wFYMrAR3FEX4 zZvEKBiubzMj`w#o?r7b?ceI>Xgm-!eYbxG5fjQaxc#bxyk=D}Do+nF(@zIwt_VyVo zsdV%z5E{~=sO{mYL=PXZpLu=v+b*_}j~d8W2NVdmIrw*%GM0918Dr60N7S8O9Lepf zcXv8p$?bCPad-Z6=O0MZa9akyofv;ZjBZD9#NFn6#oZR!-&XHVfH5K1_zdFtK6pyH z(6Q2y{INtL5kBCMQdW|f%fYg($q$}pcL$j!_D^hxh{ii?EzUh`j>B2^UErOsgooBL zRvhlMRS!}?nA?gY0v-PrP{WB{*BBhC%91Rw&4SCrqG1C}VV)8Z1qn9qH~#Em2kj|_ zgd04z=0=HfG5+IJD0Nn9K(d^#v_x9=wWiqcWsmGvsyJV1w6)k8rC5c7uFE!?x;C1+ zW)(|yM5ly$;m#`rBLQK-NTs-w4@`k!gsO12(muM>5zXze9d_;+7QZ09tp-`O3S^O%&=8X96gaIZ;AE)?$h%?d*XV7um5x|`iMuJl+IQL`>s2^0k zt>(;A&gw&u?lPrka37>^Yx+H`4)LcTcxp5%1-nlnX}|)oXrt}WL1tWtz##e*L?1L= ziUP%&InGhyQ}Jbvw~IxZF+fC`85U%NH*SUvMo1;kQSZ^ubF!c3%zhrU3savDPb!`h zy;sTJ!OKYGTj;&WJ?ZsJyXtd0oqKXS_Z_oG-a(-&e9YPBaQ#loJTklUEUNQ3>NsE7 zDTmBqr1c1*tCsqU)PeaW;@YX5XXkxUV_Q=(HItie&9RtbY%LTu9Jf6Ey*P{?Zb;K432i{&(Ez4DSUy2p`T!Q z(8^%vqbv+g!QitpIJr$SSoG&Zk=N?opV!uW-Y+;3-;UrgO!lYY|Ih0mO`|>%hzYFT zeLDUQu{rBD!yJS?xLq)y=Z=Q(U}~^ISjEHlOP|&?hSK}MPgxdKlR|}3XQgYPYKHGh~`X8cu*ISj}C&dT@ z4903ha4fKLJZ0f{8gqo;xYf#0qdA;)yR-p3!T{epbzTE3L+1FuHo&0%$^fmRzpZ@a zNP$3Ct`I)BO{n0MD)5OEVdPz}RrUQ+NFApBt}bH07%da^{`!?rop+EA3H`i4H!Adp z8lCY4jezGA`XJD;UqVuC&1vS`{w$TsPh(w_48qtHcoGX)vATFk$dt3#j=Tzr^DwVPZHjcNlb5$fQ_uM*W7!nf^}9erdhG|MT?_Xx3xP#eX>FcA}m znO1}n{e=>xP=X@0w1|JdgZgf-cOxbdh^;o*XZuTVZRvWxRnxwzt@&zrXaGL~10M_z zsb#ER>>===%2(SPxAr1gaXY?kVElHl7rtkP`ZPB(TJJJK{!AeEB#{hOvxOE*9$J6f zLIX&gbI}M(o9q2jU>s9WX(I`%4cWEp&5%e$)EM@TH+2pQl@He~L47@l{ z%i&BB0{{A@444KIV;PIMDRN8G_-dJe_}!o&F$wJ!3+?VYDQu;*Yv*70m7REi z94P9pMICfYB(4sBKSDCTPX;|zV-4=39{#&Yq8^6)E`h{sOCwNEU#mr9{d8jp64W^d zBpGa&XbTBJ<}xDvTBN^@m2}`0DY7{q7Q#9(hiMm>&D9_7^mub||hVbuyCmhxA$iwJcPANmWg9a+y zO}vuyFdD*dzM;4t6I?dUbvzN`yqD%$j3p$z(eTJqf-6OH9i@TRIz-5di&!k;uhEhT zVe}(BXS+N6F|7Pb(-4<3p7`?KD4%v;tU%VLgbfECt0l-)oGettIUBx{n zY>N9o2nAj7ziN-K6>|2%3;b%UoB^hs;pdaHt*4v}A*Tv*Oxylqu+ZD;%_lWVPCw+} zh(;y7S*=mLA>6S~8S zY&$$F%C2a3I!!U{G{s=q))CFlz_Kk|bKK^v8;%-6D%rVQ+nND+eZeZkn)CVCaW(Ng(+i8J-y$1_L; z)L&|;-zw@qFL_1nPXuOHvUawp2h|5F)x%hyBxbqWo!_O!Vsm37HX5V3JmP}=ZP741 z1=HF~F@(8?`}&FgVw3L}-sX?75TwEEZK9RwO7B|*u2bL)i6iEe5Uqq8!Vb99!+R$Q zwqS3f+ZDaPpnn>duC@OYE4YWD88#1Cs5fSB$|qVQT0^~;eqNo13NgE-n7CD^ha~Qs zIt7x1^Xjxx=~M+zk|!`7Vyi0Lh=}UIN+s%0;+L3>ou{!OAW{#@52o47^rR4JYYs$VY{>3!JF&(RQ5+N=?*Zplij zuu>cU(87=>7{*u`ocwjofUqt3dvBQKdo;$7vnfdUFsW5TxKvdBG^YG7(MTYLV|*Qd0YolmVieI3@C= z4??~dQfP*#>0nz^Pp@-Cmv%PHvw;`*ADpgnMX;N@J@tJkSdV3{M7bT|8V3vgSyW1g zAfX1tT`Ps+8=K~yL@1q4ls(g{ciK}ahEiz<(Lso-cX!p+bWN)6hwoXNd+!zN?6kcU z-fGv##b*-#zes*qVtZq#uV4_bD9!FJVs3VKQdPD255e@xac$^3z(auqtB-H*X`ioV*+G*a_W``<# z`*bpWD|Ck$8tOW+2!=r%JYTGa(`jr;B+vR(gsV9{ayP1`{ZG`450^dJ-WVJu!vLir zVe}#FPA6|T_mJ1o*R1`)KWpC50L`oWl}33wVZJGSG7a+GkgtpvcZA(t5b2O2$-@7P zs_sw}y-fUrE&NxCLDEIX0Pud7ItCyi*l{BSowz{C`-%_y)#Pl`qeUd}0D7qv7OfqDkFk2ZetT?G@E|gP^rQ$N z1*KF`N=3;oN?7I+Lt6OQxq45U3H+xOIBNw?TY$qZ3vlcyD{!9)^a>wqulMwlq5*tv z1>Ud%s6%WAvwLF+pDjAo-Vk0S3T;@kQSS~TVM1RU6{vSWUc7h2VD?S|hf{=diXwZb zC=Lrn^-i%pWu?@bK(Fv&r%;>N0Kg*Xg95Ux0O}BhdKXQw+fJ5C&28w0%X@E zaO@+|TSbv}P=K@pmv*4OS?zEtJ zX;iS|E*cD2G?t(i%q(`4=-g}17tTG7@ZVXVFX|n|`%YlW2TqI=99@W!at*RAll*&5 zI`=F}k=%rIfQ^ksS206?}W>_ha}KIA_;IKcwq6vHeoOc9Fs#y0n*}%OJ-{V-UJv36=1aYel5;8K9}fdbyJvfU?uj(vQH zkXP^NO{?pYQe{66fANe;S!B-UYzKDT6|Fhjj>R~$>|ODt*CK1a03S%}Bh^Mcc6qQv zXZ)E&-T|^os!$ej@9v^R`YL7T$i5@TA}xK~l=NvPcj@(120u9wb#wl=bx*g9JYVf! zQ|*joZEBIU+jrzdqy>AbNbxulbDuLOxG+`t82ai7E~9xr`vfmXOS_uw57J~zGsm`? zuEc$eRevn?lNFC!ewzT@%!v~1g2@tEaIvKHSIo8JR3QCM>(t)yG5(p>8yJi5R4+iZ=?&BFy0aaWoeHG@j08<5lTmgX>DdfTGVR}4cGszs!{lac; zi(})0!?%e246(VQ21?8gBwTh9!TJ@*bhDvZMRkYl(hX8aIKf$WKUivO&a%(~v<_Qh z@C~4ANA8o@SD;-iEK-kCGvOMJJ*>w?gLu_!i{KRlIRa17rC2NRfE9yywwwF?-w-A8 z2vUlra;vUP<5pG7{O?qZms@N`O3N`PZi-p^q0|0A)N)7#v(nhwgt&ih|P7TAjf z$1Hwx@mIe^e!CprQ%OxZAU;o8Kc0CW)9)Ae0z^VVR_?@2r|vpeziDZ?dJod4Zj%nD z7NE(rXRxV>U=VvmT@CIwPlLN1e;s&@?BYKaf3{xF8X=ve#TIg=t-vkGIaSE?WE8&Y%dKfq`3?$PuYy&_t z=_j`xtHe&xZ~rRL;+(x991ytYBn}Rvb9-d$Q^=F2MpXYR|MkBo67=G!?v&AVylV`M zb_QKj6MeL{q?TTyI^!-m7k5T$*J4yjE6Or-_}DT@+xhJfvaRW5ND*t*YN5s`sRo~m zMq%wpxD0}$;yV(iMxx>y>#dUYXaJ`re4cziG!+zf?;1_QS3@{SA*1OEv;o>*R)I0G z9;3yXC{3i5nJCdk8CjZcma@#!94hfUd!c`r&f6o{BFD^~n?aEVfvZ4ODn~sF1;ZlV zgfWIUaRCvoIeUp~DZ zaBEkC2eI3R?FsjgNQy=wjN5E2%_JD{lP;D#4IW^j_i$-U1a=&R4(&69xhlhFpAwMU z9@$C$>~7=7N0E&?VCjp<5%r`4XYCLDU6!H}i&1JJN3(|`5Inq4%t*LINv;=|6%y0E z^`WBmYP3%0^jd>EP-Uzh9dywlco-2b3U?+Y z!6xUaf)nr1KVVed2W1+A%Xn z)0M$zg1)WkdtjiR&`v%+IJl>W){~AN8^Sxq=sA8wr48JtK?K2=?@El|*(G=yO&(Z@ zG7wHgX496j+Nyp|aP6{itx{ZrEnIs9SJ1+>NO66xw)|_}gG`(hkyS%9LSVSyBzj?0 zUci*E$}~~W6mwZSau2*k9*vCwW~WfRNi^15cLgfbAiiHUl!1oK(4g$pg)_3WwJ@E^ zPOw{%uv@3Fo61!kI#-=P?mrIKbg|%Y=q;}-gt|mB(-4;CG(m_&FW;gcM627H{$a>? zp6eFWzbW-;=AGqHD+4qqn-ED>I^n!=oFGXkbkA?s3rtlXo;m7fjsoessa~ z9j(y?Svz=i!JHi}(FN&=fFlvGC#EF=L)N@4PuuPM#BexZF}y&tN*9)TIOX5D1u>U{ zYcV!?=wuEdM1!5bjvCvVhM-E+ofX|4CT1kdAxVfvX09VA(E?}3Cx~(Sff%QGZ>uDf zr82^Ya;Kkfd?%4O6F;XH5~7_|qMetn677iiIVG>KBpbR&v*SdwpNVGqpL#S~teWj; z>6Rbt{3tGiq|Kowa~<{c(~R6M?C3`x5*E>$CwBfau?_CV*1TQtV3;uQoxv#{i#>yQ zr;;u9164|iJ*-Npu{u?<$L?08wAdz9>J{6dO1)$2M9C4$RhT}pTScjFY=L-Q7Mm@e z>9J|z*)KLhJp0E+izhDN#;#N)SIntO17c~aG*J8qgY+B}`vU4yDI@l&Dh-Z(s7jZ| zj;PWVu|KHNkk}ilbY<*iRT>)mxhf5d{aBTT#~xFqt6~kRbhWrjPpyrJ-$M_>WFKj= zk22Xuo9tsG`!(?p)%sSvf*xbz8|X1EzMdZAONe*~#3-FL;>9wfeqAU1q@X?*yWLCiu96-(g5MCJVsT_R z_C*B>o1{tX+QYpFEt#CJtm`O_tUZiF35`0q@UR_cAi16R@d~xSXK8!=l9n{w!cRAE z;oJGgLug>nHPtL`W~Y108j<}{bDWA$p6Ki?x8z8ehkx*i7+%edJEyu>$qu-;)ZxL+ zo?)2hkO7(-cUkc^o~rRbvEoyB91CH=|DqM2%8zLLUMt?tU(@*gR(u+NM&sYI;(PI^ z#{b2N@67`m-)hA>_!^DpR(v0Q1-!ZOq!r(nPtg2ltoX~+RvPS(GRUgvwc&Jj zK?a`*-UNs)*b^$rx>$P`m5ZdRdddbbJmHZ=IEK8ef>iJ!z z=e_rsI?g$dj!HxNk=A)N{0gm-hS&Af5EV%ws`yR)-cYrY`tfX1Z#Qg7X1s4aneg7x z!gS73LqY-?HzA<=MfJMF@?pOGbc(SC+{j7zy0{C>Icj|e8s z5W4KF*usom1s_RdG}na_$>^_07HVSiiQlTK_EH>pH!v$6M*|uy_p0!2>ESCs6ceEq znI7?^%ksf&+`th!G-uP!8jC$i5@;94MO#ySXa`CZWdTinCOhzZdE*UCbR{d~kebJf z#g-N6thD{1;=`+Phu98|lD?Y#I%20cBXmq?`rJ{eDQ$cSHR)0X-4g0h3Z504Io*DK z0Bd50HtiqiKy;@tdM=EP?dcp>1h=NBkUl#%J#;4(@p_7H-0ifl$U%#E2b(FIl!D{8 z&=oAne+?ChzoQ#}uWtOfm;`>=5Ebg)4gFTyLcD9;>dsl_USfFWExTpGNTbMC?F;xS zD~y7z#vSgQ<&1sDTUJPO&L?c+@H8y(v72YxI_Ktz9n#tr04ed&?{RTLmVtZla;38fyZ+ z@~Sd4=dCC#p?20uwTw08p|YS)w&1JSU=%?BDoSmQH3j*F8#nkXLls5nyHHGXXmCxS zq;j)S5h^e6`i;tB!yl@sAVG@0PISarL&_^0P{Mek&ju-Tz3hL$sDg}uw-B(bZp=dw zpCq)QGH9$^l)Gx-^6xA&mbzE2oOiQ3$v!_>T!3Q!wN-v!MUWM!XQ6uLvjSG;t%esT zUvK2(E>pmi>kVl?mNRduksmae5%AsRg&;&k&Mvpfg<{`?BKIlm0fHN1X*rJotaey^9&mvjyZy+H_6jZeMafT~~)RRseIH|=^v z+tz~0e18%1dyAM+7%K1@%U2o(%W=x9g3u zY^*ZAfsjJm6Af6V((emy6+mH$udE2(0}!kX=4(#{v^RmzttdfkBLI!!e1uFljHx)V z4D+}6*zU?lX-^s!cY zVUoTU_yp+KFiX~7Bk2ZE_u%gw=+~rJ=nKQCsaK`^z6q@m&u;uB-bf^d&`hg!ARd=Z z@QeZa0RFygOe6;O7GOoa{A%5DE=PA*g zu*r1r>;lh9D^JRD(GuBh73hBi9rt#V?F5o`34(qcblgZu(o2(ec@*@Mpo^0gOP^B~ zN*xSTqq%KpQzCJ@RR-*mvLdPHQSdAW4~`a+?c~XJ@b=8;Mn(?kzraSCX^)%3sRls0 zxbX>oc~^(BNhA3+Ngd{Z=ceXFf_7iJ@qCojVLf>I97-gxTut(UW!l=J(;CTl;z0hx~ zzC?4t2Cw}gK_{Evpp60T>cfeIzX#0#oDEtKw5%R7Qzy&#B-usasedz(Sk$9F(P}~a z^?7IpXitLH3ffCO>ZLp@J4N*y!Bh4YUL`Olt$m^e$WhQ<$L0r-=tH=6;GVbOC|+qi z@5Scgg~s_9(D#8}OhR-#rW6P#QT>IW_r{*+P2K481>FFy2=r?~{}(HrVrfS*PU^w) z6nG|EdCHP;@+|1T0R5L%ddiDt8;3xj_iiFF-%3X;Z&v;%J=?&O2cC6So|N0oex#?t z=Ad6lhl!v+5BiF3^_QCUmw^5u=xe*tSDW-Q(67Yd&&}QF3rzY$pl5@Aqm_P_Ad9^7 zJm`0VzQ9VqPtpzG-U59a=oiZy?cjM1Jl{ebn7)KBQm;-GmKq0i22O;3j$h$!x5l&> zbE#XAQRScmAK5e;y!7(|Dx30Tmj!)2=n0&K&#}_CB>Pkg`Y_bLP@i{$J`40I*81hi z`i-D327Q*59!k<%K`#V-q?NudNoN=fMWA0O7aO4aL4T;Hy|znx8IZdWJpb_l?w(lf zMK*m&<^bX;0Z+w;i9`@((@r!7%93N?A@KaJCr?V1P?qdV<96WRddy!bcL=%x`dgrT zK>sVs-P(9N*+x5fQa?&07Fu~yvP~H-2V-MF?>0tA&s-_PK)vbUSr49~p8Z{$?C)yu z>~2jY?zOfCy=LF3_**smmm(3)$^BcwRWx z!}iLq<3P9fVa)b%B4Jo%$ao*zGu|hI_bc#TYvoNHZzBVeJ|56>u*EzTWvdTLWtSN7 z1i`Z&JS9DNQr0JJ^Eh~Z0-jJ$p7Nw^UMF3^GuFyuS$`b`y$f{eqp3@gaJ3klU7$O8 zB7rUaBt2Ks4WNeO1j_+>#7ZY0)+X0W+2AP$&%4O2W*gv1J!}_>lKqRoGYmhR8*i0k zSx0UMeGKSpt@M;+-uwyZ>n~8>K%E1i`#}G|TA%bhlC&oWkAWW)K5ONf2cBn=Jn8ts z-cP`@yC=^RNt;gwkAYh(bFDmgOPi6MJ)qA8eX}(_WGt0gW61#TJ>Y$?J&}0YDvR{q zCEJVI$23KUk7*)ux6iOCQ>RUzG4r~tS(C55!O>^aX0JcEHRP+TTC=V%%MTP4Zzw6< zST=j?dbakiErrzu73C8%C$Vpf;Oi!cR$UXiVg5@%kM>u5$DOn4_S{>Rd#L?+D;M5$ z^PReuXhJb86<4YiEp?1!L;uDKn=`nL6Fd-@QyLE96y1 zPG>A@u7{lO`y!E!;v!)$!r9mwh@^l2Pkw9+8;Ih+JD}k!6onpC-@x+;@YC2$LH%p+JOG$- z4#9+f3BPBep4ga!&A@XB^pkKeHV3f&Xn9MnNXnb1iFgwIW<1xUKDA?Gy_ly5{ch;r zi1sE6x`W-;gTB58?CSxGe5!EN-gl|o;$n1}fb@T$2mda>H(&=}er)V#D3n=XvHh;7 zB(G7G>7o1Uygu3AgXf_?YN5XcdL8;tkwpD}AJ6+O@W(ysTR_p?XF%w9v0;RyLQu(2 z7o#=nKuGKhc#)Df0P+U`eirs5e+@wYM*)7<>0n0A64`_0Ph;+Vha>p0JwgFi_yA(>Q?~%ex{2VcoKXso+ogD zk>+!P=?20^6|W@!M}X;6nnnic`(wawO>r^uEAjspFrB>8hxqpcrV};|9|BA#m>T{Q z;ClwRFh*qkzX7I`A5A|Fm`(;Yd;&221W&jB8DKi$)buX_(@B$tzXnVvLmEy1rhj*b z9;APNq=*}4xERf!r2kOBpBpYQZU`O)m|OZY7VxB07o+_UqR#|OCt+ItY`}wIZyHBL zpAVQ$Fg1M<;AP`n>`p~r2DlOVrWP>8Lq1?SVWbafxdHG`5FdC5eFG?T9ZAN^J%CG~ zAI*K#-VXrNi6ece{!@T;z8jcgXDiJ0!;P4223ZXnw|jMJl@4~E=|ob) z^e=C{JPGlx>^qL=mhmwK@a52--mysDT)=eFtn0f04@`HlPZj+(z*!jIw6-DsJfd6p zy?}?JC%V2L@Q3i9mcIos-5AjH8o(UmkKR{EUKH^15iUmWY6O2D@RyeH_&i|xM-}N` zk|g{63h)ubCDwn$e*o~)7XP*Y9&v+<(fWnx9|EQyf0f`N><_pIujj0DgS7i#@C0HGt^`2YpDN^?>O_Si@ext+o<~D1(;6eHN6_}Z;&sw zzwQN0|1Jg|qW?Qkpc@mK|53nU#MeR~NZt#8vn~3+0a(xP15?u2dw{R#?_#u1Mf}GA z(}}W{#{tvL3l0Ah@BvGHItRF8WYYgvV#&~9iO+F>Y5hBf$(9|Qbbj8A%ZAo@wb6JTF$ zuP(r&M!6Wh{}VkOiT@So`%}d~9Iy}kv_C=gF@WhN4}D1A@qnL)Jz!p;&n&=nqe0Ve z1YA58zvBaf>aPO46!AfOlmxE>9EYS4itYux(B)#Z-$?XLfazw8mbV%3_~9-_`#VIx z4=~;I(Ddzq=Rv=*KoI>Yz(=6}NCp22@W{-hK8FBjW4!EE^!EVY0)6O1z4{37YZ$+y z6+I4kqJ{nyV7hsv`OgCW0P%N|;_n4N{fDLh{Q*<_XnPF>Tx-ccqXE-@Cris45BO8W z3+)S1e=-5n4I)jS4w!CkX?PZ3x-q2Txqx3E=wh^gO7-1sJFF^KBQSy%UnZ9q^0SxWs-w!QTT+H@-Cg zV}R*Kk%oT=co+ON3kc$W5pct0E>@@D-vFM9{7m}~ME?WeKVg0xuHX*=)6E?Ekd==C zrW-sOX7a!^Ge1zyipxTQlBvv(6E1}kf&i;Zwgzxmhmt%SGz%+5L*|K800dvaA3zK0aBXEUR1lC% z0e>EyqE=Sq;Ux}A_&0TjWlKKMp05y+%JTz|l847e7AVWF_R3JcqieMi4ta^}F$Tk*v%kl+Z zz#CM&vU_UEjOK7u4lIVTrdWROU5d9Mb0bDvB!^ z8buH2fV#Z0$jf|HNy`Mlp^UUhob|y00snORntC05Pno9W<>$$hVY1njse)rF+W=W$ znjw&r#rJghSsq~K1xkFyLFOw8q7@u~<8+%kSpc0;X%^ckj%xENyqn8>70}DSDT_Kt zcBA&Ev5n#cwYxajL9{t_ma=J}(x11Ll?OKDmEaWJ+O<%H-@5^)^j^QEZ_}o*yu2-0 zGc#e{ysE;yU`eQAV`lyqhN}R11zYp(^7f&8 z!bfG^d`f8``8Q3aYbHpjfvWsMuO43T5)&%Xtql>mRIi{+1k$n)I^`=|P?9ghPK^91 z!j^^Q7-vO6hT9$5R;q_|34%14CVU`5vQVf&BU?5lhdFhN;!6#eBB0K#l^7UcwPsB8D+lkQAh%WN`M(0;Y<^ zK5XSllPK}U`941kt}-dbo*8)*_hK@$W(t~`6qdqlG6kU@*G&AlA+oVt+y+SwWQ@=} zv{twg_c}zxvHW1APbe>Ptw^dwPUC*%?O}JHAg$As?JVMq*l=wGI!x|~DwLF+#fM?JzpC!5^ z@>{0AvM4{8&oaFwaAkhEmt_(@^E?9#3HX9E48BY$2UmeItIC2bvjEv5lO}RxBC+iF zFMSOI7p)1ge8dOq75tz6)l>bED;TU@fgnD-E{c8FEZhrQiM6o)V3{XAv~Q{T9-5E! zeGc{wEL+K%52iBPrG1QX*f`NN5=1iSgJw)Vdf%IP0ltljk5r=fv~2L*tc3t);Ez6= z@JDFkqy4=4JbI5#enozW)VK7gPQK delta 10149 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 F{{@jG-fRE> diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 4f28c71..d09b00e 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -9,6 +9,7 @@ #include "ports.h" #include "interrupts.h" #include "keyboard_map.h" +#include "message.h" void jump_usermode (void); @@ -25,7 +26,7 @@ void jump_usermode (void); /* memory */ uint32_t mem_start_address = 0x100000; uint32_t mem_end_address; -uint32_t mem_use_address = 0x400000; +uint32_t mem_use_address = 0x800000; extern uint8_t keyboard_map[128]; @@ -310,7 +311,7 @@ void kshell (uint32_t argument) input[0] = '\0'; - uint32_t i, page_start, page_end; + uint32_t i, page_start, page_end, pid; kprint ("Welcome to ksh, the kernel shell."); kprint_newline(); @@ -351,6 +352,11 @@ void kshell (uint32_t argument) run_threads (); } + if (strcmp (command, "btasks") == 0) + { + run_threads_background (); + } + if (strcmp (command, "page") == 0) { kprint ("start page block? "); @@ -384,10 +390,43 @@ void kshell (uint32_t argument) loop_mark (); } + if (strcmp (command, "threads") == 0) + { + thread_show_info (); + } + + if (strcmp (command, "kill") == 0) + { + kprint ("pid? "); + command_ind = 0; + + input_ind = 0; input[0] = '\0'; + + kill_loop: + ch = getch (); // blocking call + if (ch != '\r' && ch != '\b') + { + input[input_ind] = ch; + if (input_ind < 256) + { + input_ind++; + goto kill_loop; + } + } + input[input_ind] = '\0'; + pid = atoi (input); + + if (thread_kill (pid) == 0) + { + kprint ("thread killed!"); kprint_newline (); + } + } + if (strcmp (command, "help") == 0) { kprint ("commands: page [list memory pages], loopmark [simple benchmark]"); kprint_newline (); - kprint ("tasks [multithreading demo]"); kprint_newline (); + kprint ("tasks [multithreading demo], btasks [background threads demo], threads [show number of running threads]"); kprint_newline (); + kprint ("kill [kill thread]"); kprint_newline (); } } else @@ -412,7 +451,7 @@ void run_kshell (void) uint8_t *ksh_stack = (uint8_t *) kmalloc (4096 * 2); thread_init(ksh_base); - thread_create(ksh_context, ksh_stack, 4096 * 2, (uint32_t)kshell, 0x61); + thread_create(ksh_context, ksh_stack, 4096 * 2, (uint32_t)kshell, 0x61, "kshell"); } void keyboard_handler(registers_t* regs) @@ -543,6 +582,8 @@ void thread_a(uint32_t argument) uint32_t ticks; uint32_t ticks_max = clock () + 10000; + uint8_t data[20]; + thread_set_priority (5); // increase thread priority for(;;) { @@ -551,6 +592,19 @@ void thread_a(uint32_t argument) kdelay (5); if (ticks >= ticks_max) thread_exit (0); + + if (message_read (&data, 0) == 0) + { + // end signal -> EXIT + fb_set_color (FB_RED, FB_BLACK); + kprint ("thread A: got SHUTDOWN MESSAGE: "); kprint (data); kprint_newline(); + + if (strcmp (data, "kill") == 0) + { + fb_set_color (FB_WHITE, FB_BLACK); + thread_exit (0); + } + } } } @@ -588,6 +642,8 @@ void thread_c(uint32_t argument) void run_threads (void) { + uint32_t shutdown_thread = clock () + 5000; + uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); @@ -597,11 +653,57 @@ void run_threads (void) uint8_t *thread_c_context = (uint8_t *) kmalloc (4096); uint8_t *thread_c_stack = (uint8_t *) kmalloc (4096); - thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0x61); - thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 0x61); - thread_create(thread_c_context, thread_c_stack, 4096, (uint32_t)thread_c, 0x61); + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0, "a-print"); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 1, "b-print"); + thread_create(thread_c_context, thread_c_stack, 4096, (uint32_t)thread_c, 2, "c-print"); + + while (clock () < shutdown_thread) + { + kdelay (100); + } + message_send ("kill", 0, 5); +} + + +void thread_a_backgr(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + for(;;) + { + ticks = clock (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } } + +void thread_b_backgr(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + for(;;) + { + ticks = clock (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } +} + +void run_threads_background (void) +{ + uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); + + uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); + + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a_backgr, 0, "a"); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b_backgr, 1, "b"); +} void kmain (multiboot_info_t* mbt, unsigned int magic) { @@ -793,6 +895,8 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) // enter_usermode (); run_kshell(); + //kshell (0); + while (1) { kdelay (100); diff --git a/src/kernel/lib.c b/src/kernel/lib.c index c4bbd36..5adf82c 100644 --- a/src/kernel/lib.c +++ b/src/kernel/lib.c @@ -124,13 +124,47 @@ uint32_t strlen (const uint8_t *str) return (slen); } +uint8_t *strcpy (uint8_t *dest, uint8_t *src) +{ + uint32_t i = 0; + + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + } + + dest[i] = '\0'; + + return (dest); +} + +uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) +{ + uint32_t i = 0; + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + if (i == len) break; + } + + dest[i] = '\0'; + + return (dest); +} + uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) { uint32_t i; - for (i = 1; i <= count; i++) + uint8_t *src_ptr = src; + uint8_t *dest_ptr = dest; + + + while (count-- > 0) { - dest[i] = src[i]; + *dest_ptr++ = *src_ptr++; } return (dest); diff --git a/src/kernel/thread.c b/src/kernel/thread.c index 1dd39b9..09691ba 100644 --- a/src/kernel/thread.c +++ b/src/kernel/thread.c @@ -17,7 +17,20 @@ ThreadContext* listHead = NULL; ThreadContext* currentContext = NULL; +uint32_t thread_number_of_threads (void) +{ + /* get number of current threads */ + uint32_t threads = 0; + ThreadContext* search = listHead; + while (search->next != NULL) + { + threads++; + search = search->next; + } + + return (threads); +} void thread_init(uint32_t baseContextAddress) { @@ -29,7 +42,7 @@ void thread_init(uint32_t baseContextAddress) print_string_static("\n");*/ } -void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg) +void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) { ThreadContext* context = (ThreadContext*)contextAddress; memset(context, 0, sizeof(ThreadContext)); @@ -45,9 +58,15 @@ void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackS context->eip = entryPoint; - context->priority = 0; + context->priority = THREAD_PRIORITY_NORMAL; + context->child_of = currentContext->pid; + context->pid = (uint32_t) (thread_number_of_threads () + 1); + context->request = THREAD_NO_REQUEST; + context->signal = THREAD_BREAK_ALLOWED; context->next_switch = 0; + strncpy (context->name, name, THREAD_NAME_LEN); + /* We are building this stack here: * ... * | | @@ -93,6 +112,54 @@ void thread_exit (uint32_t ret_code) kfree (currentContext->esp); kfree (currentContext); } + +uint32_t thread_kill (uint32_t pid) +{ + /* kill thread, remove thread from list */ + uint8_t found_pid = 0; + + ThreadContext* search = listHead; + ThreadContext* kill; + + while (found_pid == 0) + { + if (search->pid == pid) + { + found_pid = 1; + kill = search; + } + else + { + search = search->next; + if (search->next == NULL) + { + // end of threads list, no given pid found + break; + } + } + } + + if (found_pid) + { + search = listHead; + while (search->next != kill) + { + search = search->next; + } + + search->next = kill->next; + + kfree (kill->esp); + kfree (kill); + + return (0); + } + else + { + return (1); + } +} + void thread_set_priority (int32_t priority) { @@ -107,6 +174,29 @@ static inline void interrupts_disable(void) asm volatile ("cli"); } + + +void thread_show_info (void) +{ + /* show thread infos */ + uint8_t run = 1; + uint32_t threads = 0, mthreads = 0; + ThreadContext* search = listHead; + + do + { + kprint ("thread: '"); kprint (search->name); kprint ("' thread pid: "); kprint_int (search->pid, 10); kprint (", priority: "); kprint_int (search->priority, 10); + kprint (", child of: "); kprint_int (search->child_of, 10); kprint_newline (); + threads++; + + if (run == 0 && threads == mthreads) break; + search = search->next; + if (search->next == NULL) mthreads = threads + 1, run = 0; + + } while (1); + kprint ("total threads running: "); kprint_int (threads, 10); kprint_newline (); +} + uint32_t thread_fair_schedule (void) { /* get number of current threads */ diff --git a/src/kernel/thread.h b/src/kernel/thread.h index 88c88de..2554426 100644 --- a/src/kernel/thread.h +++ b/src/kernel/thread.h @@ -8,6 +8,23 @@ #include "registers.h" +#define THREAD_NAME_LEN 256 + +#define THREAD_MASTER_ID 0 + +// signals +#define THREAD_BREAK_PROTECT 1 +#define THREAD_BREAK_ALLOWED 0 + +// requests +#define THREAD_SHUTDOWN 1 +#define THREAD_NO_REQUEST 0 + +// priority +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_HIGH 10 +#define THREAD_PRIORITY_LOW -5 + typedef struct ThreadContext_ { uint32_t eip; // 0 @@ -27,15 +44,20 @@ typedef struct ThreadContext_ uint32_t gs; // 56 uint32_t ss; // 60 uint32_t cr3; // 64 - unused for now... - int32_t priority; // task priority + uint32_t pid; // process id + uint32_t child_of; // pid of "master" thread + int32_t request; // OS request for shutdown thread + uint32_t signal; // maybe CTRL-C OR CTRL-D allowed or not? + int32_t priority; // task priority uint32_t next_switch; // next thread switch at this clock + uint8_t name[THREAD_NAME_LEN]; // name of thread, stored if set at thread create struct ThreadContext_* next; } ThreadContext; uint32_t thread_getEflags(void); void thread_init(uint32_t baseContextAddress); -void thread_create(uint32_t infoAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg); +void thread_create(uint32_t infoAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name); void thread_schedule(registers_t* oldState); From 744f7a578cdd37ebb643af531ccff4597b50ce4f Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Mon, 23 Nov 2020 20:22:58 +0100 Subject: [PATCH 17/19] Big code cleanup! --- ChangeLog | 7 + make.sh | 26 +-- menu.lst | 2 +- os.iso | Bin 505856 -> 509952 bytes src/kernel/gdt.c | 106 ++++----- src/kernel/idt.c | 4 +- src/kernel/idt.h | 2 + src/kernel/interruptStubs.asm | 2 + src/kernel/interrupts.c | 5 +- src/kernel/interrupts.h | 2 + src/kernel/kernel | Bin 30484 -> 33888 bytes src/kernel/kernel.asm | 12 +- src/kernel/kernel.c | 406 +++++++++++++++++++--------------- src/kernel/lib.c | 94 ++++---- src/kernel/physmem.c | 108 +++++---- src/kernel/protos.h | 88 ++++++++ src/kernel/thread.c | 145 +++++++----- src/kernel/thread.h | 2 + 18 files changed, 603 insertions(+), 408 deletions(-) create mode 100644 ChangeLog create mode 100644 src/kernel/protos.h diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9ca98b7 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,7 @@ +RedCube OS (0.1) + +After some long break, I am back!! +Did some code cleanup and wrote a "protos.h" file which was really needed. +Now some compiler warnings are still there, but this is a new start! + +-- Stefan Pietzonke Mon Nov 23 2020 20:20 +0100 diff --git a/make.sh b/make.sh index 6a1d4ac..9f6f54e 100755 --- a/make.sh +++ b/make.sh @@ -5,19 +5,20 @@ rm *.o nasm -f elf32 kernel.asm -o kasm.o nasm -f elf32 threadS.asm -o threadS.o nasm -f elf32 interruptStubs.asm -o interruptStubs.o -gcc -m32 -c kernel.c -o kc.o -gcc -m32 -c io.c -o io.o -gcc -m32 -c lib.c -o lib.o -gcc -m32 -c gdt.c -o gdt.o -gcc -m32 -c physmem.c -o physmem.o -gcc -m32 -c thread.c -o thread.o -gcc -m32 -c interrupts.c -o interrupts.o -gcc -m32 -c idt.c -o idt.o -gcc -m32 -c pit.c -o pit.o -gcc -m32 -c pic.c -o pic.o -gcc -m32 -c message.c -o message.o +clang -m32 -c kernel.c -o kc.o +clang -m32 -c io.c -o io.o +clang -m32 -c lib.c -o lib.o +clang -m32 -c gdt.c -o gdt.o +clang -m32 -c physmem.c -o physmem.o +clang -m32 -c thread.c -o thread.o +clang -m32 -c interrupts.c -o interrupts.o +clang -m32 -c idt.c -o idt.o +clang -m32 -c pit.c -o pit.o +clang -m32 -c pic.c -o pic.o +clang -m32 -c message.c -o message.o +clang -m32 -c syscall.c -o syscall.o -ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o message.o +ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o message.o syscall.o echo "kernel build end, making .iso" @@ -38,4 +39,3 @@ genisoimage -R \ -boot-info-table \ -o os.iso \ iso - diff --git a/menu.lst b/menu.lst index ed1e827..068ccd3 100644 --- a/menu.lst +++ b/menu.lst @@ -1,5 +1,5 @@ default=0 - timeout=0 + timeout=5 title os kernel /boot/kernel diff --git a/os.iso b/os.iso index ecd62a3ec31e4e7128574a3e67c0278944bd7c70..e09ffe897030ba1b6250c7aa57fd1e51c6b262c5 100644 GIT binary patch delta 27303 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 ZH>&FVysGm9Rp$q*&hPfC^Sj$S{}06z%)I~r delta 23844 zcmbV!3tZGy+W(mwjEc@r#U!O1yrgc1Xl9tEc=?r*=4jE|MMF&q@sg{6wnaZ^q?jg* z&5G7;ZuRcgcHJ)Mb{)jPtlcncx7~Cj#X93qkr9%z&j0(IbAB^FNPFM+KObSv^E}`4 zoaa2}IroF5RYOW!&wbff>ORiI=lE}J-mRNTC-y0_X#>-SIPHwt@bA@M`|q@EoqFf@ z)5cw#F>c(XahVyD#!bk`aE{OD#>nXQVO+c#n$*HKV zt3&A~XnSbz)=NU?q@NbZ*0Zyv;b${9CEQHuPN>x{IBk|vmMU|k#&h)V*Q3wYO|@6S z8cM

(`%c-h})2^fL`g*^_w&WAa#vRGku6OZ7w&p!&sKX|Hi<%qf zqa93lS}fYk&XLG-#y?6S_UL$(W68d2&Qyns67Kx0<}+K$%j35&jZHxQq#~(g0`oFE z8^wCFBnnstvHGR42j(-D^39Iy3i}a{z3FiExQFdtBX8$0w&k-ep)E~Yc%Z3ZnD5bo z!F*UR#y&b>6%`D-8iGn!5DIlk&UuWv4qeGu zFvlI#lkx&NofUeAXGcz_r$+Diw(9*l0ZiuC5zzPxA$50NKyUHv&|3n#S}OD?kWqo- z7ZK<|VDdXrkAgH_9gRl)yWLXFRsNP_eik5Qb?bp?(A%iC;croMQF@!L(Nn{wxIN`} zg74YkA5_X%p1;FZ++PufpWE^Rf*&~#yi@{|hYAO&v=j?&gFyLM(C-Dv&+P$`kYvLy z?L@)btDN zlh#)x*_H9LZJwH~A$VHWp(5EG+`1d26x((Mky~%G?SXSEkVlBN3a{+4k;_Hi3QfZO zA>}DQvczjzi9&Yxjjry1H*9}{f3Q)`K zQXXoIwPDxRur`{=ZtU2jRL5f??u2{#3n%bA3TSH!{IVw^X?&pHheM+F@thWh~FJ> zK}PUliV^5IAsul7Rk!U2-63fVb;<@$O>*Cg{&{63C!X*W?}2tLrahUjg!Zz!_Y625 z`rDy-d@v|A>+Kjl-~%`{WZOewc?JQ4?sn*|k2Nb26mYTrjuan{U$Ji2J=zQ##v!xM zdHafRGh+k8xyIkU99|eAtvp5bM@>)3nx2Z)wB9MaeL6nbcuLe?4g7AtnpA#>`ePU| zf;mYQ^ExYXIy^Nw9kqv?fqh7H`VV<}?5(JxAxMt&Jd-j#j7**#RkGvl#SkArh?P=} z(R(o21hgu%{hYifa+KBOQ8_tDYPc1nEv6e=qZni|G((OuGy#d>UCda*hcPc87@Yi_ z%ShQEC@WwEPCfyKVHSdtO8?8>MWa4`+fT&N_`@gh-Qoty!}|!7Mx?jE%flT45>rg> zHI_lKYZpx2IS}pi7yPi_9sdb)pqk;zT83lz=F>#^j{sM%A7}ODO{bz!B-<&8fsP4F z8iggtWHH=hv7{VuSSL`&1nNVHGNi3N_;L%v-AXljP9h}0&SzQxy#?SNE5ONzS^ypa zSZM`#xJ?2q72tymjBpkDccmrY^)5`qZ+GEfO#a6a1K(9V;G$X*iRr9DKOWgfFi-hL zIE1+Mn*{)McUSrcP>GeoFFw8ts~%LhJXGZ3LlTiD#9%98yFwTQdC#pNL#>cIOeLQ3 zN6{+6cOyl;wjLMW6cvd#0iTam(9i4AZq^0O1gqOcUD_iEbG^NOv*C(0pJ+%QPsV@^ z+XFfe>f9e17|@%M2tt%e4FV4pK!tb?Wc9Yl^8+(PcAUvgP*yee;-l7fZil( z+{Dw;1Ot^BD4O9KVS`87z{gOSQ46oI+P+IFDaTTvvy%Yms;fo5fBYzH=WS?4#6S4Y z3w}uP4^s0IJNbL+2hS>YJ=oz_psKXwIP+|NhEi*eW2F?2#PB#YvYbDX`PLaUWPCT1 z9GjHSyy(U0*m8-sZ4)d6n862|yw(`ZZL(UxiWar;W^56VuhM_bPFdBhBVpRYf~7v8 zH(Jy}aWPoLg*P_7h?%{Qu%ZA~P`DN>=MT0~?VS}m0#k1{{7p8vX!FZx-hw6kR#W(6 zX~~bNMSb~x__*GmQp#BG@V&qw&ySl!wv>@bO{7I1Pv& zDuhYq>W+_`jzRQp)HGN+3Z{xVfJRpj@oxd2{~h|VI-`+4pm$dBdeD>g&hrMQijx>$@( z)?13d;5UdI=*7+FQ%7zJVyp8KwmP*zy~%iENO@$z@k4>X*M92??0Ew!+41&(egFYo z&@)=p#NWYQ3vJKmQW%msmk4uGq&W(9fLANn2*9`(zp5%%blh-bd^&WYZmtz@NU(~0 zghf>V37Y>X#@vBAzQ8L=vc}JFzJD6IWGwRW2hk)hZ6sp>o#MB^#jlkk5x)%*j8MY7 zY+>GSjtOrm^IG{&J!LKKrZyCL7a@;-x1ot4tZjX7;xb+^g|gnA8Ks4_%+A` zYn30~l74t|u)sxLFw4JEl>aBR2G=a*bBCqK&7w$qw<60_ksei~i#OAFC`&Z-w)LvW zIbDk!&JSElRnDX$A4d*X1?Fnm^!aqGzzeWqff`w1$@(voMI)DF%`#a$#u{g`n&E#1 zJJ;kj#bCWm)`3d@d+(6H+?8AJL+ut8FbsS!K@EIka8%>p?XC2G@UB8t3zW@7HAjm$ zPcTv0SXK&MPkfL4-@*dB2|Gajttmw)i->?+&fla}6k_Rdd{2h6!dML!xDcE&sQB~e z3ny4RO)`Ia3VDv9$#@>}nZd%Z0|9rT=PVa$ok4>p@V51sz$yCQ7ba?vf0(VlNGRHg zhT!K~74?lNN>z%w=qc;2Ct2uO2R$+GzBGUgH_&{s$%sEHcwJgFV6x&ZT5Pg-rN6CK z`SOol+ohGJt!mMLXqPFw6zbWo?ZTr*Q49aIgjroUfiIs-E)2nLehmVcuNaD7IT&yF z=NN%bQv3PuLs0mzu%0(Ww1ME=^A_>a_#~5;ggI{*vWS3p2eUFz;d=c$#w%&ikp17z zZ!?Ee46x6^NUR~hkq!R^mdkKjrGKYjKd0Dxz~<>#V}x(CV@%cCf<7pBqIE@D3uYzJ zeA7zv4OVh(!K`Gg%u>^YKQPvKqQcpZ^5gI0wJh zCtixBB-?nY(9tN&8emZ&urz((6h*2Kz|RD*rA1ULGF4_a1oJb=j9FgGlgS1sm}gPY zB(Tp*tjK;uaJI#BXNi2UeSosvj|EF~lHTgMlNOK-q0nIs+c6%{&;+)i|1H!X-gOqB zD{~)T*;~{Yhk=K%+dssLpbnmI5yebUmR})w9fIeTJObN`XnkDiccWQ-JTWe?6(*A0 zspNfx{OgEpUBme;R(1EmHk>Z7qD0K*G_aT^(X#5(q|+KTNa)!v^+c>j-79%NH)=y% zU>ap~vFa*i)jG5$H3lXH9BBDN2&*=%U4p(0`kgh#)`&sFb9WG9Kj)bEc`?@ASTqFn z1GP;_XuW{m*r0!cTFE%`3R{0XD(Wm+Hd=Im7h+prpj!yHT_7O)B_yafs`g(%TL%Rh zCm=6N2>&ZQ%=eI+$?F#h*#k=UXURr4)JDYf&k>k)3R6k>D-408?Xr`fqMoM8x^uGc59LEF(ktdzHcIyQR6b{Pyxq{XFT3^@@EL>CZIUo?kwn&{|TJQ zU!#cM?Z#nuXI?Eg+@6|{FaJ}LJR&6p9tpfR4vb?qN~T=2(h_$D74zi9Sb&a(IdmYa zBXp7~ebGQp5^TizE)HUKH;$A7MCC+@Oq#5_H+}>yhMZ|M9=(Xh+Z&(=(tzIKFVSpw zB;fb3J5bM#dK9n%YG}47X=ArKjxTlx=XF$0w*d?O?~a#f0i679c660wDG-4SFYm8}ts!DlZER(oCC?3yS^=0$32G zwRsn!79QjdEN^xVvR&{#a26vP4d?*)j--&V&(UE~@CK;+|W*m10 z=HdEE^dwDmEjnAmaBJF_%xiJX>PnMpD)bguD&n)BX(@KCEfscAX<=gVS0Xp{Kf1*7 zQ})9!1y;~qn3?}9)B;x_z}j$2q_^Y+tU3;v7Kp9>Oz0@9djd-2;4c99zlsq!QELbpB!RnO-f^?zLD_JAd>kpe3d$NnO_;Rv8rWD?xiz!YWCVQnN z_k>v2EP)Svyg!r60^WpG1pCr*u}J4e8gERe&N3#0`UY#(s7qiWnU7rXv^aL6+p?PP zJvDCsm#oM474E#+qgVp$rTC}rg3AukVMbu!Wims}F;C5GyM)iS2lmy5-9p1`hvGSE zk7}M8s1^N*G@vDcfz$Wegv%tMI+P-0hf*+ig+U!piO@S@#9rxig_BNit>v1($s^1Q z?DKz#vpVWItyDCEi?FdirU=;#ig?!wGX&9Q)bbReu)>!}i|+ga}2ALP0CUe)k#~ag5m-{u!ogB|iqd1vYyZ>mQ(Qh>2 zrccLBntm!a-T5iUVOVTVcgYRA*b(fT=5;nW@1ePwrjfFe&Zxmy@nLBwpGUb~KrZ1y zzm_tnC7yQW_p8C+;b-u;w=0t+D?=- z4JkesXP>p)6){fj%jd*r~FB%g!mZhcJzN~apEsqyiApEF}Vr_{*uYZ z#JQo>V(f8`4)JVRN9H~P6NOLzw=TUI>Wp9T=`LBE#lTkSg)=tN!e27bueTt&Mf=~? zfy##;gEk}P)H^|S3xkfIhr#ROxXm4zEBzD;gAN%6THNGB5eAXzF^8T;+fyeE5mkiw zSqzX<#a6?zF7xvBqo_;%Z8Y8IoWux$?ga++SQrtD2b=vGCr+_$-P^BoTLdQ?9L>%z*lnac5(aGZ2KEK0;av?YMO2gV9~i#mmy2%|R}(9t|u=j+%1OqUo?Ft%87 zH<`Jq|Ei4X|CfxBH5RXt)rNGtcy2Ppa~Ffp|G)H}MLWH+k~SKWJ$Jo`(O@TrI%a7) zjlXdoHQiZgKsCCd4FJb-eY$LbNcw5Kze_wU5F-l)=ng!Z!gxsUz%Ur#@yd|m4=hC& z*I*zu3#A*anJCls6plX;5aJxEvr=#IRqCzyX~P!N=;VJC1A#`}ER)uTIW%|{*cM<^ zV{!^65jG-iwAIhS;<1AovkmGZNun_$-FD$0;n2r5Ru#%IRbW!>r1=(OJ%)L4FiKA_ zAj0~aEaTz?umgw2K7Ox!NkN6Gt+q?-^~L-sHadmSUsl&h?ksT&kKg@@2naW#kq(P+ z0Qokqbg}gTWmcH~bWpIcB=dLl**FL*G!Mpq9OTe?7(i6m^=|<|bF`ybu0nIPo1MXp z&LBY-;+O!c$v}BNxlSA>z5Nft7T5HZ{tbfntmL81=-wY~){Zu7A5#1W{@OpIQS3^M zcw=dN+i>{p1S+Q{{$Oc-DebR1BAT3=-KT;L}hl{2lPGhT{Lsf)a4S!l%p4W9+0sxW023X}=cQNe#Kp z%s@w_ePkB66H7E&uZh$cTC9mwMl3Z!rRFwdWyY9OD8+9~fK8a*TLU;z$4s7+OyLHN zS3@d{#&6#&0hrC}(ABU}7gY7p_w6GLGKt>4MvGE?{7rcxDE9Wa!Ei>4G&*Z;;kt|u z3`+i{&PpF5v;_wgdP=}&OEcCpvXs!#Mq==MPJI4;-WKJ#KEjoG`5f5Bxh7SyrfKm zwLr{BTG!$SiQe1at?7+6`mB64f-uErCay|k}^jb_%Sx5@`Y*W zEw7*^!BP>;wB*0KYO$dn@Fnw!0F>3;4+PbPPWJJ^#XfzRb*4+nN`IB;B+dKP_<(0O zi-hw&mK=fECNQBG27aQU26rTr>G)WkTR(dYGrEs&v%s!aumKiWjZju-LCsdE@6;7Q z$)`|?>mzb(5%mMOu5c{vR#ih_idSixmnVves}+Ncffl1y4G#yaN|;_JN=q~=1EZ<4 z-la-PMoF>opj|@e5IA-boTr8%cy77yT!-+S8kE}1L8(jh2TKi#b{duZC7rA=6_(3K z4Mm+|*fJ?B$!RtagSy;A--BYy>b~;IpyagtK~+xpOIq;ShL+>117uwb?JG>;>hjR0 zwBV~00ek>P?|^|fxFR01uBKNOSd(0KZRGkGuRL^Xe+B=V=7W%pnM(~xy7`GVp~ylTPNNRKGE4=YM>(zN@hFv+?i3%5 zRiCAWKLX0Szg>l!PuB&^@SkuLd4pA=s(6k)XLcZa3X$l4^gMgctUz{Z zWQYVb%n(DX1)b`zO$NZi$qe+gP*VcgP79e8$aY)EF@fwP3ppx~t;I+qIA?euJ2^%N z=Xe9zDHbv%kZrXaW(>DTwLtbL3z-zi9%CWhf$R(m=?r8~i1q$pzkn~ZYH{_xM*9MF zt>NTHQ{lI4IFDw9PAtiAb6>Ry!Lkq!UTpIAC>H!^MJIm?Im+r%kR_;R1~>Z&jG-x3=N4RZ0tc zkTS@szG0VQ_7i`6LA?Iq?@<9tC5OLOsR7}GDs@(Pze)`Zzpqj$;dfQ)?C|R_G<9%xkTf+vvi3^`zK)btgZZ zLfv9o%ud_~98Z)v0=uN?xDlXU){!XBph%gIe||*7W<#iIyvFjkz%K=EAD+4l#_*2K zKkg>BSp_!kfI#q{w({*ff`y*ozi8z<_@9H%GT%J0d~H}NM-zCo_&rOuAv3HyZ>gOd2k|EL=G zwDNoN!zRC!^-c^uQK^_ zt^8y@+2mhi+R{lW#gIvTmgbJzrw`7VdW3u7n=N9E8p<) z!KUB?t00X#O#Y`<{!ku~3*v^*7gqi-zTf1Bt^DEU^RYCLFrgn)%UbM$I}0Wj%;YbMfeTA@{JrBz{I2DAvTye4%8Cq9iCwa_VWwh@PzXV~nFe za}XrGn}FJLBm_(nil;0>zbk6c_~wo;zxdslH(g(&dU!OF;koY~n1K`ym`f?O#_a_) z@(#SbaQlzC-?V`l*th?1;9r4#$MN?RRir-+e>Nn`DeYQZHAp&ao`l>gt6|AY_ETQ^ z_SBThFJXVK%7`Zf=3f)?FYw;VH7#c7oyyQVZjTu`Wo*b$^0(4j1CBp2`K7yQk>d_&p|UuW-v#?rCjh; z(uJHjeAM_ADsO^<(9Y4!*^ClQwmiDQ7R$%4I!I2!83*a11&}XErt7otBt>u=vgjm< zg&!pibflrtNlgzrj}b|kD97WMZ)6qT!&5Nm1d1Li6}hGDkvwsNLskoWHU5~wVT;%% z4nc!tunE7I;Iz}gDU&eu*#pv4ItLPV(gOo}H`698#Qbwo9Bp)Ystrh89n-^BH(i#;yui>Q(?PtAz7gM6UG^La^Mb8G$3VP{{-eMcs@5pb{)8w`)O-R! z+W@*sfE30)Ulf%J-qn!*6>>b>ix;#Z?wLZ!n<2*qbX;B#_sj#3AA@`@Erz;SI!hYi z1)F;Ept@-Ey6%_-aYGLPGancnD#rEX%7UrL z4k9KbPn5)sm;%sc4bdoV+qz)Bh#Rp4m>zqgQOwbC%!0T#N+CZ59k|hs%U24y!Ki(= z1JDZ!4p;&DT@Js~zOQb2y=Y=DWP5;n7dSjMj4Oh{Yo#rWYTN?MK4211bF0B5bHfX7 zzK<;s(o$w1;-P?q!y!C(Z#23-CX_NS=q$(zAu(T8}g;5~2QQP5Ji z-if0LYj-!S{e3t9wE*Oi7PBkbmx+cV1+ySe#F@%vUF0)_+zZ}v$S;8WA*-C?X-hm- zDu8(cm<)+AMD#`#17v6mFuA}iwqoqp z#VV5Ig4ZF>!1xodsl|vf06Y)Cf-VZ?#T3ki{2=6uy2!7M$yY;uHcoyo?;@WWliv$@ z7UWaok%(aXjgX50=y?Ec1Yo9Bfg$By@IHjR4Dz{FIl0<0ezXGfEHLLG7Gli_jN@E~ z@YN`=jbz;WJcqy2+iZ<&(e)g+VsMpx4?uEj7O?cvn9?zYveH7n1o9~Eyrx*?o8px! zg?upbPmRlsF16kcz$5_1#R0utYvKw*kY__a$to|3%bOs-3Gy_nd~saP&?A;Z9v_ye z|69goF97QSxVO8@HcOXzp?DTB|N0zP%~p?*TVIxA0b%liS$i-VEkruzDQce8@t$xm zFz*=MLH2dRTyin>-+uq@Hk;Y67jiG;A42Yf{2xenDdeMgA+5kTzKBL=Suyskm<~g8 zGd2={E?tEz&5=sH$T$I*CBQ82UgJga8ea>{_NHj`4r@WAr#P zw|F~1kAHdkAR}W!kZ*(h;_k+bmBx_gnt^%YP&fB0&yIrJ*#rOO&*5m)YqR!$8TA9Y zM|}psKLUJ#wLr&c8~Kn7@+lAuvmT$?t}-8%yHuc^IIl0V?Va zS`+uqTVxC{Bdr+AlI#HFosd&4W5z5O%`LiUC*&R;jpBq;$PKaP%aH&tc&RuIcLNYm z03oL)Dvd9pvVd6w%*PmJV}$_Y*y|LQQWGo(W-$JsX0%n0Wo@|`^5NfLGqlJGu*Zkc zrvX@U1_fT^*$w$h$UnE{CqwtgJ<5Uc;!lm9v0|nJ^OT7}`zPT~d7cJndw0+yakpmx zw>An z>qs8^?0D=4dw(0YC|y3)FSXf6i>cIPUp{l@CEii9RxDe&bgg${`uOy57hE)2CcOC1 z&?b%>H-5rzZG#>%>`v6(L09(pWKN?+1JPJL;dAc{lcs?XW?pI1)Z~nfiP4y2@X3nN z7=iJrm~Y}S{@}AcM!N+qei78cdQ{*2qdm>I0Tlf=SMpLQd z0x!q+5-6a8Y%GEKy2)=t1wtq=L&)9inr`wX-RPBFX#7Q>==O?91>Q;NMvc+^8P(wK zE*ZX$ZUcQOPTS(GMznf-zeu?27{WgMJ1s zr8ese4-5tUu}8yyyN8Jpc)E}3(_3S-uONCn=szcDjCN&2PXpa5Ucb(ms?jECZwBe2vj0M)X?H52kC(i*KUu!1qx+tf9GwXu5S{x=Ewq8jwUcyQ@O z8l$0}3>XCZJFh0X579$Gb4!irlwqtxW3-zm`9#okc5N0o8T0_SoO%<MCqA}Xk5WOEX-K@}u6#Nr3JxZow zhdlN*Xq>W>SjfKtoq(9x1Df*x1e)$UOnDS^!)RQJ(F%+Jz0lw3(#r&Bpy?jLr0M^a zd3`LRUb%P_$t}HP9O$!90oodnzGr`Z>+Slp&V@mJBOE z4?rm~YywT!eWttwG)Fh2RW|7hf}TG_W3;0qEuEc0`jr3Ysol=tE6L z8)3TnG3g#o#9xx7hJyik4Q&_zf)u=x$k@N(QaVK;dKBcd=9@v5z%oJiv|(;QTatVl z=!d3i>`9e>F6f6RYwQ_C-vF8}VCh2!Eup!Xu5wJq3ILk$dSn_Hq;MT*x>Ymf#i0L! zq0?-#J3!MPdE-lXU<(p-;c4O@0PP>Gu~}e{z864eSt{_Z0fsrL_p`g$r=SP+(HNby zP=-UG=_<%n$U)P^sY!nedbeeuIt98dEgl1BW1Y}u3DQxZY0*#$1=OvLi@;cfpqdH> z6*vWS5vJojMPC8>a{K`b9RZU3deC&aO&@Y$F6gT~av&jjKIj_}gmi*ObP;H})id$G z0lggtN!o3&yTJI}*&3Sx4rTZ)=;tvI(e8@q?Vzu7#RsNmK<~h~kfG!?py{HNK4j2a zpy}$@q<4Y-9Qw^}{yu2Btf!0nzRYm5zk-p}Ph&452Nl>1nr_`q1!2%{qnpx#m*mGl zpAQ$C9_s`>bg0H??@IC{OaeP#@E;XE74%BrX>(2T;h;Y>_0#-68jR=R5_nhy(In7x zA4wnLP6eGe5`TRHniO0OdLCkgPTGiG3_1ej5G7v$dX}a!Iy58suR+t@HhoCnM$n^E zHAd$XV*WS4psQLFuo?7pSU3_4Qt$-m{is2jqF)7_mL4}~59loPn(a#dDd;&Uiau1} z7ogunHy)iXKSvN=rZOa;t^Q^4Kp)T)M5f0EfiATSM8iOv zi&#R92K_Z+hR(LALg}FC6+mqMp8y8Ez@QIhm;{=x{Y`or=(qZ5jE(_Gfe!i+bW7UK z5j`I?U5=ah9MG8GSCai12q3pf$PAaD_r^zT?~5ouQWzme=1-z z=od$5;%u7e`$5yo4HI7tnw|xi^kbm6p~)tJLHHLzSN76axuX99dOU{eblN==;C(Ru zhUqd@(FZ}(?K*wPphKX)xUhPv-Eq#8^@SVQsukG9)m!b3#|PwIK6n0%>2q`EUv}A5 z`qjBtPoFVY&#lgOI6kZ%=X6}xXVtp(E7un0u3fQl^~$v?ssm2P)arjb9b*RM=5ETI zn11t$!rXN?fCTpVp=`%a(0o>C2ZEE@kOA-%^;qZgpXGsHbDDJ#$)h zpI(kZ2BtExEzF#cffYT}{2=nghc;Lg_k^SE;=K#8uEEyU{Ft~4!5|#ElGsL$!phE+ z5td~1K`}8LW+H=)IRkK&0+LNMg=7JDxv2#_FMjCrYy1$KaJ22H$i?`IwD}<;=;t^1 zapQ+@v`>8ixJ{4ILRNje>YMrqDa=qb0Y_AS+RHI4{VgP$pq%P|K7L3q#cU7!SbWDG wU5x=CvHH9u$559a<7h?oyd+2TA7~b*!0EI8f$p8QV diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c index 9bb883e..356f16c 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/gdt.c @@ -1,4 +1,5 @@ #include "types.h" +#include "protos.h" void stack_space (void); @@ -43,7 +44,7 @@ struct tss_entry_struct uint32_t prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list. uint32_t esp0; // The stack pointer to load when we change to kernel mode. uint32_t ss0; // The stack segment to load when we change to kernel mode. - uint32_t esp1; // everything below here is unusued now.. + uint32_t esp1; // everything below here is unusued now.. uint32_t ss1; uint32_t esp2; uint32_t ss2; @@ -58,25 +59,61 @@ struct tss_entry_struct uint32_t ebp; uint32_t esi; uint32_t edi; - uint32_t es; - uint32_t cs; - uint32_t ss; - uint32_t ds; - uint32_t fs; - uint32_t gs; - uint32_t ldt; + uint32_t es; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t fs; + uint32_t gs; + uint32_t ldt; uint16_t trap; uint16_t iomap_base; } __packed; - + typedef struct tss_entry_struct tss_entry_t; tss_entry_t tss_entry; +void write_tss(struct gdt_entry_bits *g) +{ + // Firstly, let's compute the base and limit of our entry into the GDT. + uint32_t base = (uint32_t) &tss_entry; + uint32_t limit = sizeof(tss_entry); + + // Now, add our TSS descriptor's address to the GDT. + g->limit_low=limit&0xFFFF; + g->base_low=base&0xFFFFFF; //isolate bottom 24 bits + g->accessed=1; //This indicates it's a TSS and not a LDT. This is a changed meaning + g->read_write=0; //This indicates if the TSS is busy or not. 0 for not busy + g->conforming_expand_down=0; //always 0 for TSS + g->code=1; //For TSS this is 1 for 32bit usage, or 0 for 16bit. + g->always_1=0; //indicate it is a TSS + g->DPL=3; //same meaning + g->present=1; //same meaning + g->limit_high=(limit&0xF0000)>>16; //isolate top nibble + g->available=0; + g->always_0=0; //same thing + g->big=0; //should leave zero according to manuals. No effect + g->gran=0; //so that our computed GDT limit is in bytes, not pages + g->base_high=(base&0xFF000000)>>24; //isolate top byte. + + // Ensure the TSS is initially zero'd. + memset((uint8_t *) &tss_entry, 0, sizeof(tss_entry)); + + // tss_entry.ss0 = stack_space; // Set the kernel stack segment. + // tss_entry.esp0 = stack_space; // Set the kernel stack pointer. + + tss_entry.ss0 = GDT_KERNEL_CODE; // Set the kernel stack segment. + tss_entry.esp0 = GDT_KERNEL_DATA; // Set the kernel stack pointer. + + //note that CS is loaded from the IDT entry and should be the regular kernel code segment +} + + void set_gdt (void) { gp.limit = (sizeof(struct gdt_entry_bits) * 6) - 1; - gp.base = &gdt; - + gp.base = (uint32_t) &gdt; + //....insert your null_seg 0 segments here or whatever struct gdt_entry_bits *null_seg; null_seg = (void *) &gdt[0]; @@ -94,7 +131,7 @@ void set_gdt (void) null_seg->big = 0; null_seg->gran = 0; null_seg->base_high = 0; - + // ring 0 struct gdt_entry_bits *null_code; struct gdt_entry_bits *null_data; @@ -116,7 +153,7 @@ void set_gdt (void) null_code->base_high = 0; *null_data = *null_code; null_data->code = 0; - + // ring 3 struct gdt_entry_bits *code; struct gdt_entry_bits *data; @@ -127,7 +164,7 @@ void set_gdt (void) code->base_low=0; code->accessed=0; code->read_write=1; //make it readable for code segments - code->conforming_expand_down=0; //don't worry about this.. + code->conforming_expand_down=0; //don't worry about this.. code->code=1; //this is to signal its a code segment code->always_1=1; code->DPL=3; //set it to ring 3 @@ -140,50 +177,15 @@ void set_gdt (void) code->base_high=0; *data=*code; //copy it all over, cause most of it is the same data->code=0; //signal it's not code; so it's data. - - write_tss(&gdt[5]); //we'll implement this function later... - + + write_tss(&gdt[5]); //we'll implement this function later... + //...go on to install GDT segments and such gdt_flush (); //after those are installed we'll tell the CPU where our TSS is: tss_flush(); //implement this later } -void write_tss(struct gdt_entry_bits *g) -{ - // Firstly, let's compute the base and limit of our entry into the GDT. - uint32_t base = (uint32_t) &tss_entry; - uint32_t limit = sizeof(tss_entry); - - // Now, add our TSS descriptor's address to the GDT. - g->limit_low=limit&0xFFFF; - g->base_low=base&0xFFFFFF; //isolate bottom 24 bits - g->accessed=1; //This indicates it's a TSS and not a LDT. This is a changed meaning - g->read_write=0; //This indicates if the TSS is busy or not. 0 for not busy - g->conforming_expand_down=0; //always 0 for TSS - g->code=1; //For TSS this is 1 for 32bit usage, or 0 for 16bit. - g->always_1=0; //indicate it is a TSS - g->DPL=3; //same meaning - g->present=1; //same meaning - g->limit_high=(limit&0xF0000)>>16; //isolate top nibble - g->available=0; - g->always_0=0; //same thing - g->big=0; //should leave zero according to manuals. No effect - g->gran=0; //so that our computed GDT limit is in bytes, not pages - g->base_high=(base&0xFF000000)>>24; //isolate top byte. - - // Ensure the TSS is initially zero'd. - memset(&tss_entry, 0, sizeof(tss_entry)); - - // tss_entry.ss0 = stack_space; // Set the kernel stack segment. - // tss_entry.esp0 = stack_space; // Set the kernel stack pointer. - - tss_entry.ss0 = GDT_KERNEL_CODE; // Set the kernel stack segment. - tss_entry.esp0 = GDT_KERNEL_DATA; // Set the kernel stack pointer. - - //note that CS is loaded from the IDT entry and should be the regular kernel code segment -} - void set_kernel_stack(uint32_t stack) //this will update the ESP0 stack used when an interrupt occurs { tss_entry.esp0 = stack; diff --git a/src/kernel/idt.c b/src/kernel/idt.c index 3db6710..945e050 100644 --- a/src/kernel/idt.c +++ b/src/kernel/idt.c @@ -3,7 +3,7 @@ * https://github.com/G33KatWork */ - +#include "interrupts.h" #include "idt.h" #include "types.h" @@ -83,6 +83,6 @@ void idt_install() idt_set_gate(45, (uint32_t)irq13, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); idt_set_gate(46, (uint32_t)irq14, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); idt_set_gate(47, (uint32_t)irq15, 0x08, I86_IDT_ATTR_PRESENT | I86_IDT_ATTR_32BIT_INT_GATE | I86_IDT_ATTR_PRIV_KERNEL); - + idt_flush(); } diff --git a/src/kernel/idt.h b/src/kernel/idt.h index 5571d6f..652dbf3 100644 --- a/src/kernel/idt.h +++ b/src/kernel/idt.h @@ -102,4 +102,6 @@ void irq13(void); void irq14(void); void irq15(void); +void isr128(void); // syscall interruot 80 + #endif diff --git a/src/kernel/interruptStubs.asm b/src/kernel/interruptStubs.asm index 9b220ab..de4e8fd 100644 --- a/src/kernel/interruptStubs.asm +++ b/src/kernel/interruptStubs.asm @@ -64,6 +64,7 @@ ISR_NOERRCODE 28 ISR_NOERRCODE 29 ISR_NOERRCODE 30 ISR_NOERRCODE 31 +ISR_NOERRCODE 128 IRQ 0, 32 IRQ 1, 33 @@ -82,6 +83,7 @@ IRQ 13, 45 IRQ 14, 46 IRQ 15, 47 + ;Our C-Handler for fault interrupts [EXTERN interrupts_faultHandler] isr_common_stub: diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 0d1becd..159eb80 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -1,10 +1,11 @@ /* by Andreas Galauner - * + * * https://github.com/G33KatWork */ #include "interrupts.h" #include "pic.h" +#include "protos.h" static isrFunction isrs[I86_IDT_MAX_ENTRY_COUNT]; @@ -31,7 +32,7 @@ void interrupts_faultHandler(registers_t regs) void interrupts_interruptHandler(registers_t regs) { pic_notify(regs.int_no); - + if(isrs[regs.int_no] != 0) isrs[regs.int_no](®s); else diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index 0b36b63..8fb5561 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -25,6 +25,8 @@ #define IRQ_FPU 42 #define IRQ_HDC 43 +#define IRQ_SYSCALL 38 + // Set the following number to the maximum nested exceptions // the kernel tries to resolve before it will panic //#define MAX_NESTED_EXCEPTIONS 1 diff --git a/src/kernel/kernel b/src/kernel/kernel index 019bbabaf41ee3e398d1bfe44be592c55c38373f..9fbe7e86f54da34e9bf0c053f8151dae023265e1 100755 GIT binary patch literal 33888 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 literal 30484 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 diff --git a/src/kernel/kernel.asm b/src/kernel/kernel.asm index 6fe6806..7178987 100644 --- a/src/kernel/kernel.asm +++ b/src/kernel/kernel.asm @@ -71,6 +71,8 @@ tss_flush: global jump_usermode ;you may need to remove this _ to work right.. jump_usermode: extern run_kshell +extern user_print + cli mov ax,0x23 mov ds,ax mov es,ax @@ -82,8 +84,10 @@ extern run_kshell push eax ;push our current stack just for the heck of it pushf push 0x1B; ;user code segment with bottom 2 bits set for ring 3 - call run_kshell ;may need to remove the _ for this to work right iret +;end + + global enter_usermode @@ -102,12 +106,10 @@ enter_usermode: lea eax, [a] ; EIP first push eax - call run_kshell - iretd + iretd ; itetd a: add esp, 4 ; fix stack - rts - + ret start: cli ;block interrupts diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index d09b00e..4a0e6e3 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -10,6 +10,9 @@ #include "interrupts.h" #include "keyboard_map.h" #include "message.h" +#include "syscall.h" + +#include "protos.h" void jump_usermode (void); @@ -80,13 +83,13 @@ uint8_t fb_con[SCREENSIZE]; typedef struct multiboot_memory_map { uint32_t size; - uint64_t base_addr; + uint64_t base_addr; uint64_t length; uint32_t type; } multiboot_memory_map_t; -// threading +// threading uint8_t threads_request = 0; void kb_init(void) @@ -100,12 +103,12 @@ void kb_init(void) void kprint(const char *str) { uint32_t i = 0; - + while (str[i] != '\0') { if (str[i] == '\b') { /*/ backspace */ - + if (fb_cursor_x > 1) { fb_cursor_x--; @@ -119,19 +122,19 @@ void kprint(const char *str) fb_cursor_x = COLUMNS_IN_LINE; } } - + fb_current_loc = fb_current_loc - 2; /* goto old frame buffer position and clear char */ fb_con[fb_current_loc] = ' '; fb_con[fb_current_loc + 1] = 0x00; - + fb_blit (); } else { // new - - fb_current_loc = (fb_cursor_y * BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE) + (fb_cursor_x * BYTES_FOR_EACH_ELEMENT); - + + fb_current_loc = (fb_cursor_y * BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE) + (fb_cursor_x * BYTES_FOR_EACH_ELEMENT); + if (fb_cursor_x < COLUMNS_IN_LINE - 1) { fb_cursor_x = fb_cursor_x + 1; @@ -139,7 +142,7 @@ void kprint(const char *str) else { fb_cursor_x = 0; - + if (fb_cursor_y < LINES - 1) { fb_cursor_y++; @@ -150,19 +153,19 @@ void kprint(const char *str) fb_current_loc = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); } } - + fb_con[fb_current_loc] = str[i]; fb_con[fb_current_loc + 1] = fb_color; - + vidptr[fb_current_loc] = str[i]; vidptr[fb_current_loc + 1] = fb_color; - + fb_current_loc = fb_current_loc + 2; } i++; fb_move_cursor ((fb_cursor_x + 1) + fb_cursor_y * COLUMNS_IN_LINE); - + /* show cursor line on next position */ vidptr[fb_current_loc] = '_'; vidptr[fb_current_loc + 1] = fb_color; @@ -172,27 +175,27 @@ void kprint(const char *str) void kprint_newline(void) { uint16_t line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; - + /* delete cursor line */ vidptr[fb_current_loc] = ' '; vidptr[fb_current_loc + 1] = fb_color; - + if (fb_cursor_y < LINES - 1) { // fb_current_loc = fb_current_loc + (line_size - (fb_current_loc % line_size)); - + fb_current_loc = fb_current_loc + (COLUMNS_IN_LINE - fb_cursor_x) * BYTES_FOR_EACH_ELEMENT; fb_cursor_y++; fb_cursor_x = 0; - + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); - + fb_blit (); - + /* show cursor line on next position */ vidptr[fb_current_loc ] = '_'; vidptr[fb_current_loc + 1] = fb_color; - } + } else { fb_scroll_down (); @@ -206,7 +209,7 @@ void fb_scroll_down (void) uint16_t old_pos = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE, new_pos = 0; uint16_t x_pos; uint16_t y_pos; - + for (y_pos = 1; y_pos < LINES; y_pos++) { for (x_pos = 0; x_pos < COLUMNS_IN_LINE; x_pos++) @@ -215,7 +218,7 @@ void fb_scroll_down (void) fb_con[new_pos++] = fb_con[old_pos++]; } } - + new_pos = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); /* clear last line */ for (x_pos = 0; x_pos < COLUMNS_IN_LINE; x_pos++) @@ -223,20 +226,20 @@ void fb_scroll_down (void) fb_con[new_pos++] = ' '; fb_con[new_pos++] = 0x00; } - + fb_blit (); } - + void fb_blit (void) { uint16_t i = 0, j = 0; - + while (i < SCREENSIZE) { vidptr[i++] = fb_con[j++]; /* vidptr[i++] = fb_con[j++]; */ } -} +} void fb_clear_screen(void) { @@ -245,12 +248,12 @@ void fb_clear_screen(void) vidptr[i++] = ' '; vidptr[i++] = 0x00; } - + fb_cursor_x = 0; fb_cursor_y = 0; fb_current_loc = 0; - + fb_clear_con (); - + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); } @@ -277,7 +280,7 @@ void fb_get_cursor (uint32_t *x, uint32_t *y) void fb_set_cursor (uint32_t x, uint32_t y) { fb_move_cursor (y * COLUMNS_IN_LINE + x); - + fb_cursor_x = x; fb_cursor_y = y; } @@ -287,14 +290,14 @@ void div_by_zero_handler_main (void) fb_set_color (FB_RED, FB_BLACK); kprint ("KERNEL PANIC! division by ZERO"); kprint_newline (); fb_set_color (FB_WHITE, FB_BLACK); - + outb (0x20, 0x20); } void div_by_zero () { uint8_t i; - + i = 23 / 0; } @@ -303,18 +306,19 @@ void kshell (uint32_t argument) uint8_t ch; uint8_t command[256]; uint8_t command_ind = 0; - + command[0] = '\0'; - + uint8_t input[256]; uint8_t input_ind = 0; - + input[0] = '\0'; - + uint32_t i, page_start, page_end, pid; - + uint32_t free_mem_kbytes; + kprint ("Welcome to ksh, the kernel shell."); kprint_newline(); - + while (1) { fb_set_color (FB_WHITE, FB_BLACK); @@ -328,13 +332,13 @@ void kshell (uint32_t argument) fb_set_color (FB_WHITE, FB_BLACK); command_ind = 0; command[0] = '\0'; - + kshell_loop: ch = getch (); // blocking call if (ch != '\r' && ch != '\b') { command[command_ind] = ch; - if (command_ind < 256) + if (command_ind < 255) { command_ind++; goto kshell_loop; @@ -346,30 +350,30 @@ void kshell (uint32_t argument) { // return char, check if command command[command_ind] = '\0'; - - if (strcmp (command, "tasks") == 0) + + if (strcmp (command, (const uint8_t *) "tasks") == 0) { run_threads (); } - - if (strcmp (command, "btasks") == 0) + + if (strcmp (command, (const uint8_t *) "btasks") == 0) { run_threads_background (); } - - if (strcmp (command, "page") == 0) + + if (strcmp (command, (const uint8_t *) "page") == 0) { kprint ("start page block? "); command_ind = 0; - + input_ind = 0; input[0] = '\0'; - + page_loop: ch = getch (); // blocking call if (ch != '\r' && ch != '\b') { input[input_ind] = ch; - if (input_ind < 256) + if (input_ind < 255) { input_ind++; goto page_loop; @@ -378,36 +382,45 @@ void kshell (uint32_t argument) input[input_ind] = '\0'; page_start = atoi (input); page_end = page_start + 23; - + for (i = page_start; i <= page_end; i++) { pmem_show_page (i); } - } - - if (strcmp (command, "loopmark") == 0) + } + + if (strcmp (command, (const uint8_t *) "mem") == 0) + { + free_mem_kbytes = pmem_get_free_ram_info (); + kprint ("free memory: "); + kprint_int (free_mem_kbytes, 10); + kprint (" KB"); + kprint_newline (); + } + + if (strcmp (command, (const uint8_t *) "loopmark") == 0) { loop_mark (); } - - if (strcmp (command, "threads") == 0) + + if (strcmp (command, (const uint8_t *) "threads") == 0) { thread_show_info (); } - - if (strcmp (command, "kill") == 0) + + if (strcmp (command, (const uint8_t *) "kill") == 0) { kprint ("pid? "); command_ind = 0; - + input_ind = 0; input[0] = '\0'; - + kill_loop: ch = getch (); // blocking call if (ch != '\r' && ch != '\b') { input[input_ind] = ch; - if (input_ind < 256) + if (input_ind < 255) { input_ind++; goto kill_loop; @@ -415,16 +428,16 @@ void kshell (uint32_t argument) } input[input_ind] = '\0'; pid = atoi (input); - + if (thread_kill (pid) == 0) { kprint ("thread killed!"); kprint_newline (); } } - - if (strcmp (command, "help") == 0) + + if (strcmp (command, (const uint8_t *) "help") == 0) { - kprint ("commands: page [list memory pages], loopmark [simple benchmark]"); kprint_newline (); + kprint ("commands: page [list memory pages], mem [list free memory], loopmark [simple benchmark]"); kprint_newline (); kprint ("tasks [multithreading demo], btasks [background threads demo], threads [show number of running threads]"); kprint_newline (); kprint ("kill [kill thread]"); kprint_newline (); } @@ -432,7 +445,7 @@ void kshell (uint32_t argument) else { // backspace - + if (command_ind > 0) { command_ind = command_ind - 1; @@ -443,17 +456,25 @@ void kshell (uint32_t argument) } } } - + void run_kshell (void) { uint8_t *ksh_base = (uint8_t *) kmalloc (4096 * 2); uint8_t *ksh_context = (uint8_t *) kmalloc (4096); uint8_t *ksh_stack = (uint8_t *) kmalloc (4096 * 2); - - thread_init(ksh_base); - thread_create(ksh_context, ksh_stack, 4096 * 2, (uint32_t)kshell, 0x61, "kshell"); + + thread_init ((uint32_t) ksh_base); + thread_create ((uint32_t) ksh_context, (uint32_t) ksh_stack, 4096 * 2, (uint32_t) kshell, 0x61, (uint8_t *) "kshell"); } +void user_print (void) +{ + syscall_kprint ("Hello from userland!"); + syscall_kprint_newline (); +} + + + void keyboard_handler(registers_t* regs) { uint8_t status; @@ -463,7 +484,7 @@ void keyboard_handler(registers_t* regs) /* debug */ uint32_t i; - + status = inb(KEYBOARD_STATUS_PORT); /* Lowest bit of status will be set if buffer is not empty */ if (status & 0x01) { @@ -480,8 +501,8 @@ void keyboard_handler(registers_t* regs) if (keycode == '\b') { // backspace - } - + } + if (keycode & 0x80) { pressed = 0; @@ -490,34 +511,34 @@ void keyboard_handler(registers_t* regs) { pressed = 1; } - + if (keycode == KEY_SCAN_LEFT_SHIFT || keycode == KEY_SCAN_RIGHT_SHIFT) { /* uppercase table */ keyboard_shift = 1; } - + if (keycode == KEY_SCAN_LEFT_SHIFT_RELEASED || keycode == KEY_SCAN_RIGHT_SHIFT_RELEASED) { /* lowercase table */ keyboard_shift = 0; } - + if (pressed == 1) { if (keyboard_shift == 1) { ch[0] = keyboard_shift_map[(unsigned char) keycode]; - + } else { ch[0] = keyboard_map[(unsigned char) keycode]; - } - + } + ch[1] = '\0'; - kprint (ch); - + kprint ((const char *) ch); + keyboard_ch = ch[0]; } } @@ -531,75 +552,75 @@ uint32_t clock (void) void kdelay (uint32_t ticks) { uint32_t end_ticks = clock () + ticks; - + while (clock () < end_ticks) { } } -loop_mark () +void loop_mark () { /* simple loop "benchmark" */ - + uint32_t start_time, end_time, loop_time, i, j, k; uint32_t loop_max = 20000; uint32_t loops_per_sec; - + kprint ("loop mark: "); - + start_time = clock (); - + for (i = 1; i <= loop_max; i++) { for (j = 1; j <= loop_max; j++) { - + } } - + end_time = clock (); loop_time = end_time - start_time; - - loops_per_sec = (loop_max * loop_max) / (loop_time / TIMER_FREQ); - + + loops_per_sec = (loop_max * loop_max) / (loop_time / TIMER_FREQ); + kprint_int (loop_time, 10); kprint_newline (); kprint ("loops per sec: "); kprint_int (loops_per_sec, 10); kprint_newline (); kprint_newline (); } - - + + void timerHandler(registers_t* regs) { clock_ticks++; - + thread_schedule (regs); // kprint ("clock: "); kprint_int (clock_ticks, 10); kprint_newline (); } - + void thread_a(uint32_t argument) { uint32_t ticks; uint32_t ticks_max = clock () + 10000; - + uint8_t data[20]; - + thread_set_priority (5); // increase thread priority for(;;) { ticks = clock (); kprint ("thread A: "); kprint_int (ticks, 10); kprint_newline (); kdelay (5); - + if (ticks >= ticks_max) thread_exit (0); - - if (message_read (&data, 0) == 0) + + if (message_read ((uint8_t *) &data, 0) == 0) { // end signal -> EXIT fb_set_color (FB_RED, FB_BLACK); - kprint ("thread A: got SHUTDOWN MESSAGE: "); kprint (data); kprint_newline(); - - if (strcmp (data, "kill") == 0) + kprint ("thread A: got SHUTDOWN MESSAGE: "); kprint ((const char *) data); kprint_newline(); + + if (strcmp (data, (const uint8_t *) "kill") == 0) { fb_set_color (FB_WHITE, FB_BLACK); thread_exit (0); @@ -607,61 +628,61 @@ void thread_a(uint32_t argument) } } } - + void thread_b(uint32_t argument) { uint32_t ticks; uint32_t ticks_max = clock () + 10000; - + thread_set_priority (-10); // decrease thread priority for(;;) { ticks = clock (); kprint ("thread B: "); kprint_int (ticks, 10); kprint_newline (); kdelay (5); - + if (ticks >= ticks_max) thread_exit (0); } } - + void thread_c(uint32_t argument) { uint32_t ticks; uint32_t ticks_max = clock () + 10000; - + // normal priority = 0 for(;;) { ticks = clock (); kprint ("thread C: "); kprint_int (ticks, 10); kprint_newline (); kdelay (5); - + if (ticks >= ticks_max) thread_exit (0); } } - + void run_threads (void) { uint32_t shutdown_thread = clock () + 5000; - + uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); - + uint8_t *thread_c_context = (uint8_t *) kmalloc (4096); uint8_t *thread_c_stack = (uint8_t *) kmalloc (4096); - - thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0, "a-print"); - thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 1, "b-print"); - thread_create(thread_c_context, thread_c_stack, 4096, (uint32_t)thread_c, 2, "c-print"); - + + thread_create((uint32_t) thread_a_context, (uint32_t) thread_a_stack, 4096, (uint32_t)thread_a, 0, (uint8_t *) "a-print"); + thread_create((uint32_t) thread_b_context, (uint32_t) thread_b_stack, 4096, (uint32_t)thread_b, 1, (uint8_t *) "b-print"); + thread_create((uint32_t) thread_c_context, (uint32_t) thread_c_stack, 4096, (uint32_t)thread_c, 2, (uint8_t *) "c-print"); + while (clock () < shutdown_thread) { kdelay (100); } - message_send ("kill", 0, 5); + message_send ((uint8_t *) "kill", 0, 5); } @@ -669,26 +690,26 @@ void thread_a_backgr(uint32_t argument) { uint32_t ticks; uint32_t ticks_max = clock () + 10000; - + for(;;) { ticks = clock (); kdelay (5); - + if (ticks >= ticks_max) thread_exit (0); } } - + void thread_b_backgr(uint32_t argument) { uint32_t ticks; uint32_t ticks_max = clock () + 10000; - + for(;;) { ticks = clock (); kdelay (5); - + if (ticks >= ticks_max) thread_exit (0); } } @@ -700,15 +721,15 @@ void run_threads_background (void) uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); - - thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a_backgr, 0, "a"); - thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b_backgr, 1, "b"); + + thread_create((uint32_t) thread_a_context, (uint32_t) thread_a_stack, 4096, (uint32_t)thread_a_backgr, 0, (uint8_t *) "a"); + thread_create((uint32_t) thread_b_context, (uint32_t) thread_b_stack, 4096, (uint32_t)thread_b_backgr, 1, (uint8_t *) "b"); } - + void kmain (multiboot_info_t* mbt, unsigned int magic) { set_gdt (); /* init memory GDT */ - + idt_install(); //set up IDT interrupts_init(); //set up callback table pic_init(); //set up PIC @@ -716,39 +737,39 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) interrupts_registerHandler(IRQ_TIMER, timerHandler); interrupts_registerHandler(IRQ_KEYBOARD, keyboard_handler); pit_init(100); - - - - + + + + // vmem_paging (); /* switch paging on */ - - multiboot_memory_map_t* mmap = mbt->mmap_addr; + + multiboot_memory_map_t* mmap = (multiboot_memory_map_t *) mbt->mmap_addr; uint32_t mem_start, mem_end; uint32_t mem_type; uint8_t mem_found = 0; int16_t mem; - + uint32_t pages_free; uint64_t ram_free; - + fb_clear_screen(); - + fb_set_color(FB_GREEN, FB_BLACK); kprint ("level 0: RUN"); kprint_newline (); kprint ("level 1: memory"); kprint_newline (); pmem_init_bitmap (); - - while(mmap < mbt->mmap_addr + mbt->mmap_length) + + while (mmap < (mbt->mmap_addr + mbt->mmap_length)) { mem_start = mmap->base_addr; mem_end = mem_start + mmap->length; mem_type = mmap->type; - + if (mem_type == 1) { - kprint ("RAM at "); kprint_int ((uint32_t) mmap->base_addr, 16); - + kprint ("RAM at "); kprint_int ((uint32_t) mmap->base_addr, 16); + if (mmap->length < 1024 * 1024) { kprint (" size: "); kprint_int ((uint32_t) mmap->length, 10); kprint (" bytes "); @@ -758,13 +779,13 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) kprint (" size: "); kprint_int ((uint32_t) mmap->length / (1024 * 1024), 10); kprint (" MB "); } kprint ("type: "); kprint_int ((uint32_t) mmap->type, 16); - + if (mem_start >= mem_start_address) { mem_end_address = mem_end; mem_found = 1; - - mem = pmem_set_bitmap (mem_use_address, mem_end, FREE); + + mem = pmem_set_bitmap (mem_use_address, mem_end, 0, FREE); if (mem == MEM_ERR_OK) { kprint (" found base"); @@ -772,27 +793,27 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) else { kprint (" MEMORY ERROR: "); - + if (mem == MEM_ERR_RANGE) { kprint ("range"); } - + if (mem == MEM_ERR_BAD_MEM || mem == MEM_ERR_DOUBLE_FREE) { kprint ("alloc error"); } } - kprint_newline (); + kprint_newline (); } else - { + { if (mem_start == 0) { mem_start = (uint32_t) 0x1000; /* skip first page */ } - - mem = pmem_set_bitmap (mem_start, mem_end, FREE); + + mem = pmem_set_bitmap (mem_start, mem_end, 0, FREE); if (mem == MEM_ERR_OK) { kprint (" free"); @@ -800,27 +821,27 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) else { kprint (" MEMORY ERROR: "); - + if (mem == MEM_ERR_RANGE) { kprint ("range"); } - + if (mem == MEM_ERR_BAD_MEM || mem == MEM_ERR_DOUBLE_FREE) { kprint ("alloc error"); - } + } } - + kprint_newline (); } } - - mmap = (multiboot_memory_map_t*) ( (uint32_t)mmap + mmap->size + sizeof(mmap->size) ); + + mmap = (multiboot_memory_map_t*) ( (uint32_t) mmap + mmap->size + sizeof(mmap->size) ); } - + pmem_set_first_page (); /* so no null pointer for free mem block can exist */ - + /* mem = pmem_set_bitmap ((uint32_t) 0xF00000, (uint32_t) 0xFFFFFF, ALLOCATE); if (mem != MEM_ERR_OK) @@ -829,44 +850,44 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) kprint_newline (); } */ - + pages_free = pmem_count_free_pages (); ram_free = (pages_free * 4096) / 1024 /1024; kprint ("free pages: "); kprint_int (pages_free, 10); kprint (" = " ); kprint_int ((uint32_t) ram_free, 10); kprint (" MB"); kprint_newline (); - + kprint_newline (); kprint ("level 2: interrupts"); kprint_newline (); - + // kb_init(); pic_unmask_irq(IRQ_KEYBOARD); - - - + + + kprint ("level 3: keyboard"); kprint_newline (); kprint_newline (); - + // loop_mark (); - + fb_set_color (FB_RED, FB_BLACK); kprint ("red"); fb_set_color (FB_BLUE, FB_BLACK); kprint (" cube OS "); fb_set_color(FB_WHITE, FB_BLACK); - + kprint_int (2017, 10); kprint_newline (); kprint_newline (); - + uint32_t i; - + /* for (i = 0; i < 10; i++) { pmem_show_page (i); } */ - - + + /* uint8_t *buf; buf = (uint8_t *) kmalloc (100000); @@ -879,24 +900,45 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) kprint ("mem allocate failed"); } kprint_newline (); - - + + if (kfree (buf) != NULL) { kprint ("free ERROR!"); kprint_newline (); } */ - + kprint ("READY"); kprint_newline (); - + pic_unmask_irq(IRQ_TIMER); - - - // enter_usermode (); + pic_unmask_irq(IRQ_CMOSTIMER); + + uint8_t *thread_base = (uint8_t *) kmalloc (4096 * 2); + uint8_t *thread_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_stack = (uint8_t *) kmalloc (4096 * 2); + + + + initialise_syscalls (); + // kdelay (500); + pic_unmask_irq (IRQ_SYSCALL); + + // thread_init(thread_base); + + + // switch_to_user_mode (); + // enter_usermode (); // unhandled fault 13 + // jump_usermode (); run_kshell(); - + + // user_print (); + // thread_create(thread_context, thread_stack, 4096 * 2, (uint32_t)user_print, 0x61, "userprint"); + + + // user_print (); + //kshell (0); - + while (1) { kdelay (100); diff --git a/src/kernel/lib.c b/src/kernel/lib.c index 5adf82c..fe0f3c3 100644 --- a/src/kernel/lib.c +++ b/src/kernel/lib.c @@ -1,76 +1,77 @@ /* lib.c - some helper functions */ #include "types.h" +#include "protos.h" extern uint8_t keyboard_ch; void strreverse(uint8_t* begin, uint8_t* end) { - + uint8_t aux; - + while(end>begin) - + aux=*end, *end--=*begin, *begin++=aux; - + } - + void itoa(int32_t value, uint8_t* str, int32_t base) { - + static uint8_t num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - + uint8_t* wstr=str; - + int32_t sign; - + // Validate base - + if (base<2 || base>35){ *wstr='\0'; return; } - + // Take care of sign - + if ((sign=value) < 0) value = -value; - + // Conversion. Number is reversed. - + do *wstr++ = num[value%base]; while(value/=base); - + if(sign<0) *wstr++='-'; - + *wstr='\0'; - + // Reverse string - + strreverse(str,wstr-1); } - + // A utility function to check whether x is numeric uint8_t isNumericChar(uint8_t x) { return (x >= '0' && x <= '9')? TRUE: FALSE; } - + // A simple atoi() function. If the given string contains // any invalid character, then this function returns 0 int32_t atoi(uint8_t *str) { if (*str == NULL) return 0; - + int32_t res = 0; // Initialize result int32_t sign = 1; // Initialize sign as positive int32_t i = 0; // Initialize index of first digit - + // If number is negative, then update sign if (str[0] == '-') { sign = -1; i++; // Also update index of first digit } - + // Iterate through all digits of input string and update result for (; str[i] != '\0'; ++i) { @@ -79,17 +80,17 @@ int32_t atoi(uint8_t *str) // to error stream res = res*10 + str[i] - '0'; } - + // Return result with sign return sign*res; } - - + + void kprint_int (int32_t n, int32_t base) { char str[256]; - - itoa (n, str, base); + + itoa (n, (uint8_t *) str, base); kprint (str); } @@ -114,28 +115,28 @@ int16_t strcmp (const uint8_t * str1, const uint8_t * str2) uint32_t strlen (const uint8_t *str) { uint32_t slen = 0; - + while (*str != '\0') { slen++; str++; } - + return (slen); } uint8_t *strcpy (uint8_t *dest, uint8_t *src) { uint32_t i = 0; - + while (src[i] != '\0') { dest[i] = src[i]; i++; } - + dest[i] = '\0'; - + return (dest); } @@ -148,65 +149,62 @@ uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) i++; if (i == len) break; } - + dest[i] = '\0'; - + return (dest); } -uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) +uint8_t *memcpy (uint8_t *dest, uint8_t *src, uint32_t count) { uint32_t i; - + uint8_t *src_ptr = src; uint8_t *dest_ptr = dest; - + while (count-- > 0) { *dest_ptr++ = *src_ptr++; } - + return (dest); } uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) { uint32_t i; - + for (i = 1; i <= count; i++) { dest[i] = val; } - + return (dest); } uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count) { uint32_t i; - + for (i = 1; i <= count; i++) { dest[i] = val; } - + return (dest); } uint8_t getch (void) { uint8_t ch; - + while (keyboard_ch == NULL) { kdelay (10); } - + ch = keyboard_ch; keyboard_ch = NULL; // reset buffer, kind of hack I know ;) return (ch); } - - - diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c index 4283cc1..b4b47c2 100644 --- a/src/kernel/physmem.c +++ b/src/kernel/physmem.c @@ -1,31 +1,32 @@ // #include #include "types.h" +#include "protos.h" uint32_t physmem_pages[PAGES]; void set_bit (uint32_t *ptr, uint16_t bit) { uint32_t ret = *ptr; - + ret |= 1 << bit; - + *ptr = ret; } void clear_bit (uint32_t *ptr, uint16_t bit) { uint32_t ret = *ptr; - + ret &= ~(1 << bit); - + *ptr = ret; } uint16_t get_bit (uint32_t n, uint16_t bit) { uint16_t bitset; - + bitset = (n >> bit) & 1; return (bitset); } @@ -33,7 +34,7 @@ uint16_t get_bit (uint32_t n, uint16_t bit) void pmem_init_bitmap () { uint32_t page; - + for (page = 0; page < PAGES; page++) { physmem_pages[page] = 0xFFFFFFFF; /* mark as reserved */ @@ -56,19 +57,19 @@ uint32_t pmem_get_page_from_address (uint32_t address) return (page); } - + int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t state) { /* set bits in bitmap */ uint32_t start_p, end_p; int16_t start_b, end_b; - + uint32_t p; int16_t b; - + uint32_t page_start = pmem_get_page_from_address (start); uint32_t page_end; - - + + if (pages != 0) { page_end = page_start + pages - 1; @@ -77,18 +78,18 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s { page_end = pmem_get_page_from_address (end); } - + // kprint ("start address: "); kprint_int (start, 10); kprint_newline (); // kprint ("start page: "); kprint_int (page_start, 10); kprint_newline (); - + // kprint ("end address: "); kprint_int (end, 10); kprint_newline (); // kprint ("end page: "); kprint_int (page_end, 10); kprint_newline (); - + if (page_end > MEM_BLOCKS) { return (MEM_ERR_RANGE); } - + if (page_start >= PAGE_BITS) { start_p = page_start / PAGE_BITS; @@ -99,7 +100,7 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s start_p = 0; start_b = page_start; } - + if (page_end >= PAGE_BITS) { end_p = page_end / PAGE_BITS; @@ -110,7 +111,7 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s end_p = 0; end_b = page_end; } - + if (end_p > start_p) { for (p = start_p; p <= end_p; p++) @@ -202,27 +203,27 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s clear_bit (&physmem_pages[start_p], b); } } - + } } return (MEM_ERR_OK); } - + int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pages) { /* return pointers to free memory range */ int8_t found_start = 0; - + uint32_t p, i; uint16_t b; uint32_t start_free, end_free; size_t mempages, mempages_free = 0; - - + + if (size > MEM_BLOCK_SIZE) { mempages = size / MEM_BLOCK_SIZE; - + if (size % MEM_BLOCK_SIZE != 0) { mempages++; @@ -233,9 +234,9 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa mempages = 1; } *pages = mempages; - + // kprint ("pmem_get_free: mempages = "); kprint_int (mempages, 10); kprint_newline (); - + for (p = 0; p < PAGES; p++) { for (b = 0; b < PAGE_BITS; b++) @@ -247,7 +248,7 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa found_start = 1; start_free = PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE; mempages_free = 1; - + if (mempages == 1) { *start = start_free; @@ -261,7 +262,7 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa if (mempages_free >= mempages) { end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; - + *start = start_free; *end = end_free; return (MEM_ERR_OK); @@ -273,7 +274,7 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa if (mempages_free >= mempages) { end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; - + *start = start_free; *end = end_free; return (MEM_ERR_OK); @@ -287,12 +288,12 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa if (mempages_free >= mempages) { end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; - + *start = start_free; *end = end_free; return (MEM_ERR_OK); } - + } return (MEM_ERR_NOMEM); } @@ -300,11 +301,11 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa uint32_t pmem_count_free_pages (void) { /* for informantion only: count the free pages */ - + uint32_t pages = 0; uint32_t p, i; uint16_t b; - + for (p = 0; p < PAGES; p++) { for (b = 0; b < PAGE_BITS; b++) @@ -317,7 +318,16 @@ uint32_t pmem_count_free_pages (void) } return (pages); } - + +uint32_t pmem_get_free_ram_info (void) +{ + uint32_t free_mem_kbytes; + + free_mem_kbytes = pmem_count_free_pages () * (MEM_BLOCK_SIZE / 1024); + + return (free_mem_kbytes); +} + void *kmalloc (size_t size) { uint32_t start_free, end_free; @@ -325,18 +335,18 @@ void *kmalloc (size_t size) size_t new_size; uint32_t pages; new_size = size + sizeof (size_t); - + // kprint ("kmalloc: allocate "); kprint_int ((uint32_t) new_size, 10); kprint_newline (); - + if (pmem_get_free (new_size, &start_free, &end_free, &pages) == MEM_ERR_OK) { // kprint ("free mem found"); kprint_newline (); - + if (pmem_set_bitmap (start_free, end_free, pages, ALLOCATE) == MEM_ERR_OK) { - ptr = start_free; + ptr = &start_free; *ptr = pages; /* store size of memoryin pages */ - return (start_free + sizeof (size_t)); + return ((void *) start_free + sizeof (size_t)); } else { @@ -355,14 +365,14 @@ uint32_t kfree (uint32_t address) uint32_t *ptr; uint32_t end_mem; size_t pages; - - ptr = address - sizeof (size_t); + + ptr = (uint32_t *) address - sizeof (size_t); pages = *ptr; - + // kprint ("kfree: deallocate "); kprint_int ((uint32_t) pages, 10); kprint_newline (); - + end_mem = (address - sizeof (size_t)) + pages * MEM_BLOCK_SIZE; - + if (pmem_set_bitmap (address - sizeof (size_t), end_mem, pages, FREE) == MEM_ERR_OK) { return (NULL); @@ -377,22 +387,22 @@ void pmem_show_page (uint32_t page) { uint16_t b; uint32_t cursor_x, cursor_y; - + if (page >= PAGES) { // ERROR out of range, return return; } - + kprint_int (page * PAGE_BITS * MEM_BLOCK_SIZE, 16); fb_get_cursor (&cursor_x, &cursor_y); - + if (cursor_x < 15) { cursor_x = 15; fb_set_cursor (cursor_x, cursor_y); } - + for (b = 0; b < PAGE_BITS; b++) { if (get_bit (physmem_pages[page], b) == FREE) @@ -407,8 +417,8 @@ void pmem_show_page (uint32_t page) kprint_newline (); } - - + + /* void main () { diff --git a/src/kernel/protos.h b/src/kernel/protos.h new file mode 100644 index 0000000..fabb083 --- /dev/null +++ b/src/kernel/protos.h @@ -0,0 +1,88 @@ +// protos.h - function prototypes +// +// + +// for registers_t define +#include "registers.h" + +// kernel.c +void fb_blit (void); +void fb_scroll_down (void); +void fb_move_cursor (unsigned short pos); +void fb_clear_con (void); +void loop_mark (); +void kprint (const char *str); +void kprint_newline (void); +void kdelay (uint32_t ticks); +void gdt_flush (void); +void tss_flush (void); +void fb_get_cursor (uint32_t *x, uint32_t *y); +void fb_set_cursor (uint32_t x, uint32_t y); +uint32_t clock (void); + +// lib.c +void strreverse(uint8_t* begin, uint8_t* end); +void itoa(int32_t value, uint8_t* str, int32_t base); +uint8_t isNumericChar(uint8_t x); +int32_t atoi(uint8_t *str); +void kprint_int (int32_t n, int32_t base); +int16_t strcmp (const uint8_t * str1, const uint8_t * str2); +uint32_t strlen (const uint8_t *str); +uint8_t *strcpy (uint8_t *dest, uint8_t *src); +uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len); +uint8_t *memcpy (uint8_t *dest, uint8_t *src, uint32_t count); +uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count); +uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count); +uint8_t getch (void); +void run_threads (void); +void run_threads_background (void); + +// physmem.c +void pmem_init_bitmap (); +void pmem_set_first_page (); +uint32_t pmem_get_page_from_address (uint32_t address); +int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t state); +int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pages); +uint32_t pmem_count_free_pages (void); +uint32_t pmem_get_free_ram_info (void); +void *kmalloc (size_t size); +uint32_t kfree (uint32_t address); +void pmem_show_page (uint32_t page); + +// thread.c +void thread_message_read (uint8_t *message); +uint32_t thread_number_of_threads (void); +void thread_init(uint32_t baseContextAddress); +void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name); +void thread_exit (uint32_t ret_code); +uint32_t thread_get_own_pid (void); +uint32_t thread_kill (uint32_t pid); +void thread_set_priority (int32_t priority); +void thread_show_info (void); +uint32_t thread_fair_schedule (void); +void thread_schedule(registers_t* oldState); +void thread_saveContext(registers_t* oldState); +void switch_to_user_mode (); +void init_elf (void* image); +void init_multitasking(struct multiboot_info* mb_info); +void get_thread_input_stream (uint8_t *ch); + +// message.c +uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len); +uint8_t message_read (uint8_t *message, uint32_t thread); + +// gdt.c +void set_gdt (void); +void set_kernel_stack(uint32_t stack); + +// pic.c +void pic_init(void); +void pic_unmask_irq(int intNo); +void pic_mask_irq(int intNo); +void pic_notify(int intNo); + +// pit.c +void pit_init(uint32_t frequency); + +// physmem.c +void *kmalloc (size_t size); diff --git a/src/kernel/thread.c b/src/kernel/thread.c index 09691ba..940361d 100644 --- a/src/kernel/thread.c +++ b/src/kernel/thread.c @@ -1,11 +1,12 @@ /* by Andreas Galauner - * + * * https://github.com/G33KatWork */ #include "types.h" #include "thread.h" - +#include "interrupts.h" +#include "protos.h" /* #include @@ -23,12 +24,16 @@ uint32_t thread_number_of_threads (void) uint32_t threads = 0; ThreadContext* search = listHead; + interrupts_disable (); + while (search->next != NULL) { threads++; search = search->next; } - + + interrupts_enable (); + return (threads); } @@ -36,7 +41,7 @@ void thread_init(uint32_t baseContextAddress) { kprint ("Initializing threading..."); kprint_newline (); listHead = currentContext = (ThreadContext*) baseContextAddress; - memset(listHead, 0, sizeof(ThreadContext)); + memset((uint8_t *) listHead, 0, sizeof(ThreadContext)); /*print_string_static("Base thread context is at "); print_integer_hex((uint32_t)currentContext); print_string_static("\n");*/ @@ -44,29 +49,31 @@ void thread_init(uint32_t baseContextAddress) void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) { + // interrupts_disable (); + ThreadContext* context = (ThreadContext*)contextAddress; - memset(context, 0, sizeof(ThreadContext)); - + memset((uint8_t *) context, 0, sizeof(ThreadContext)); + context->cs = GDT_KERNEL_CODE; context->ds = GDT_KERNEL_DATA; context->es = GDT_KERNEL_DATA; context->fs = GDT_KERNEL_DATA; context->gs = GDT_KERNEL_DATA; context->ss = GDT_KERNEL_DATA; - + context->eflags = thread_getEflags(); - + context->eip = entryPoint; - + context->priority = THREAD_PRIORITY_NORMAL; context->child_of = currentContext->pid; context->pid = (uint32_t) (thread_number_of_threads () + 1); context->request = THREAD_NO_REQUEST; context->signal = THREAD_BREAK_ALLOWED; context->next_switch = 0; - + strncpy (context->name, name, THREAD_NAME_LEN); - + /* We are building this stack here: * ... * | | @@ -81,18 +88,20 @@ void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackS * ... */ uint32_t* stack = (uint32_t*)(stackStart); - memset(stack, 0, stackSize); + memset((uint8_t *) stack, 0, stackSize); stack[stackSize/sizeof(uint32_t) - 1] = arg; //Argument stack[stackSize/sizeof(uint32_t) - 2] = 0; //Return Address - + context->esp = stackStart + stackSize - 2*sizeof(uint32_t); - - + context->stack_size = stackSize; + context->stack_start = stackStart; + ThreadContext* cur = listHead; while(cur->next != NULL) cur = cur->next; - + cur->next = context; + // interrupts_enable (); } void thread_exit (uint32_t ret_code) @@ -100,27 +109,29 @@ void thread_exit (uint32_t ret_code) /* exit thread, remove thread from list */ // ThreadContext* currentContext; ThreadContext* search = listHead; - + while (search->next != currentContext) { search = search->next; } - + /* skip current thread in previous thread->next */ search->next = currentContext->next; - - kfree (currentContext->esp); - kfree (currentContext); + + kfree ((uint32_t) currentContext->esp); + kfree ((uint32_t) currentContext); } uint32_t thread_kill (uint32_t pid) { /* kill thread, remove thread from list */ uint8_t found_pid = 0; - + ThreadContext* search = listHead; ThreadContext* kill; - + + interrupts_disable (); + while (found_pid == 0) { if (search->pid == pid) @@ -138,7 +149,7 @@ uint32_t thread_kill (uint32_t pid) } } } - + if (found_pid) { search = listHead; @@ -146,36 +157,29 @@ uint32_t thread_kill (uint32_t pid) { search = search->next; } - + search->next = kill->next; - + kfree (kill->esp); kfree (kill); - + + interrupts_enable (); return (0); } else { + interrupts_enable (); return (1); } } - - + + void thread_set_priority (int32_t priority) { currentContext->priority = priority; } - - - -static inline void interrupts_disable(void) __attribute__((always_inline)); -static inline void interrupts_disable(void) -{ - asm volatile ("cli"); -} - void thread_show_info (void) { /* show thread infos */ @@ -183,20 +187,24 @@ void thread_show_info (void) uint32_t threads = 0, mthreads = 0; ThreadContext* search = listHead; + interrupts_disable (); + do { - kprint ("thread: '"); kprint (search->name); kprint ("' thread pid: "); kprint_int (search->pid, 10); kprint (", priority: "); kprint_int (search->priority, 10); + kprint ("thread: '"); kprint (search->name); kprint ("' pid: "); kprint_int (search->pid, 10); kprint (", priority: "); kprint_int (search->priority, 10); kprint (", child of: "); kprint_int (search->child_of, 10); kprint_newline (); threads++; - + if (run == 0 && threads == mthreads) break; search = search->next; if (search->next == NULL) mthreads = threads + 1, run = 0; - + } while (1); kprint ("total threads running: "); kprint_int (threads, 10); kprint_newline (); + + interrupts_enable (); } - + uint32_t thread_fair_schedule (void) { /* get number of current threads */ @@ -204,31 +212,31 @@ uint32_t thread_fair_schedule (void) uint32_t ticks_per_sec = 100; uint32_t fair_schedule = 0; ThreadContext* search = listHead; - + while (search->next != NULL) { threads++; search = search->next; } - + if (threads > 0) { fair_schedule = ticks_per_sec / threads; fair_schedule = ticks_per_sec / fair_schedule; } - + return (fair_schedule); } - + void thread_schedule(registers_t* oldState) { uint8_t do_schedule = 0; uint32_t fair_schedule = thread_fair_schedule (); - + //print_string_static("Scheduling...\n"); interrupts_disable(); - + if (fair_schedule > 0) { // number of tasks greater as zero -> fair_schedule is not zero @@ -260,23 +268,23 @@ void thread_schedule(registers_t* oldState) } } } - + if (do_schedule == 1) { thread_saveContext(oldState); //print_string_static("Old context saved...\n"); - + ThreadContext* next; if(currentContext->next == NULL) next = listHead; else next = currentContext->next; - + currentContext = next; /*print_string_static("New thread context is at "); print_integer_hex((uint32_t)currentContext); print_string_static("\n");*/ - + thread_switchToContext(currentContext); } } @@ -295,7 +303,7 @@ void thread_saveContext(registers_t* oldState) currentContext->ss = GDT_KERNEL_DATA; currentContext->esp = oldState->esp + 0x14; } - + currentContext->eip = oldState->eip; currentContext->cs = oldState->cs; currentContext->eflags = oldState->eflags; @@ -303,7 +311,7 @@ void thread_saveContext(registers_t* oldState) currentContext->ecx = oldState->ecx; currentContext->edx = oldState->edx; currentContext->ebx = oldState->ebx; - + currentContext->ebp = oldState->ebp; currentContext->esi = oldState->esi; currentContext->edi = oldState->edi; @@ -312,3 +320,32 @@ void thread_saveContext(registers_t* oldState) currentContext->fs = oldState->fs; currentContext->gs = oldState->gs; } + + +void switch_to_user_mode () +{ + // Set up our kernel stack. + set_kernel_stack(GDT_KERNEL_DATA); + + // Set up a stack structure for switching to user mode. + asm volatile(" \ + cli; \ + mov $0x23, %ax; \ + mov %ax, %ds; \ + mov %ax, %es; \ + mov %ax, %fs; \ + mov %ax, %gs; \ + \ + \ + mov %esp, %eax; \ + pushl $0x23; \ + pushl %esp; \ + pushf; \ + pushl $0x1B; \ + push $1f; \ + iret; \ + 1: \ + "); + + +} diff --git a/src/kernel/thread.h b/src/kernel/thread.h index 2554426..647adbc 100644 --- a/src/kernel/thread.h +++ b/src/kernel/thread.h @@ -51,6 +51,8 @@ typedef struct ThreadContext_ int32_t priority; // task priority uint32_t next_switch; // next thread switch at this clock uint8_t name[THREAD_NAME_LEN]; // name of thread, stored if set at thread create + uint32_t stack_size; + uint32_t stack_start; struct ThreadContext_* next; } ThreadContext; From 6856e7828bbd3ab88a66f807e01064ea33867b23 Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Wed, 25 Nov 2020 15:43:02 +0100 Subject: [PATCH 18/19] code cleanup --- bochsrc.txt | 3 +- make-clang.sh | 42 ++ make.sh | 27 +- menu.lst | 1 + os.iso | Bin 509952 -> 0 bytes out.txt | 314 ++++++++++++ src/kernel/colors.h | 17 + src/kernel/elf.h | 36 ++ src/kernel/gdt.c | 15 +- src/kernel/idt.h | 5 +- src/kernel/interruptStubs.asm | 9 +- src/kernel/interrupts.c | 5 +- src/kernel/interrupts.h | 4 +- src/kernel/kernel | Bin 33888 -> 0 bytes src/kernel/kernel.c | 104 +++- src/kernel/kernel.c~ | 904 ++++++++++++++++++++++++++++++++++ src/kernel/lib.c | 94 ++-- src/kernel/lib.c~ | 212 ++++++++ src/kernel/message.c | 126 +++++ src/kernel/message.c~ | 112 +++++ src/kernel/message.h | 16 + src/kernel/message.h~ | 11 + src/kernel/physmem.c | 101 ++-- src/kernel/protos.h | 37 +- src/kernel/syscall.c | 97 ++++ src/kernel/syscall.h | 79 +++ src/kernel/thread.c | 107 +++- src/kernel/thread.c~ | 314 ++++++++++++ src/kernel/thread.h~ | 67 +++ src/user/Makefile | 20 + src/user/lib.c | 190 +++++++ src/user/test.c | 246 +++++++++ src/user/test.ld | 28 ++ 33 files changed, 3143 insertions(+), 200 deletions(-) create mode 100755 make-clang.sh delete mode 100644 os.iso create mode 100644 out.txt create mode 100644 src/kernel/colors.h create mode 100644 src/kernel/elf.h delete mode 100755 src/kernel/kernel create mode 100644 src/kernel/kernel.c~ create mode 100644 src/kernel/lib.c~ create mode 100644 src/kernel/message.c create mode 100644 src/kernel/message.c~ create mode 100644 src/kernel/message.h create mode 100644 src/kernel/message.h~ create mode 100644 src/kernel/syscall.c create mode 100644 src/kernel/syscall.h create mode 100644 src/kernel/thread.c~ create mode 100644 src/kernel/thread.h~ create mode 100644 src/user/Makefile create mode 100644 src/user/lib.c create mode 100644 src/user/test.c create mode 100644 src/user/test.ld diff --git a/bochsrc.txt b/bochsrc.txt index caffba6..20047c5 100644 --- a/bochsrc.txt +++ b/bochsrc.txt @@ -1,5 +1,5 @@ megs: 512 - display_library: sdl + display_library: sdl2 romimage: file=/usr/share/bochs/BIOS-bochs-latest vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest ata0-master: type=cdrom, path=os.iso, status=inserted @@ -7,4 +7,3 @@ log: bochslog.txt clock: sync=realtime, time0=local cpu: count=1, ips=1000000 - diff --git a/make-clang.sh b/make-clang.sh new file mode 100755 index 0000000..64a0fde --- /dev/null +++ b/make-clang.sh @@ -0,0 +1,42 @@ +#!/bin/bash +cd src/kernel + +rm *.o +nasm -f elf32 kernel.asm -o kasm.o +nasm -f elf32 threadS.asm -o threadS.o +nasm -f elf32 interruptStubs.asm -o interruptStubs.o +clang -m32 -c kernel.c -o kc.o +clang -m32 -c io.c -o io.o +clang -m32 -c lib.c -o lib.o +clang -m32 -c gdt.c -o gdt.o +clang -m32 -c physmem.c -o physmem.o +clang -m32 -c thread.c -o thread.o +clang -m32 -c interrupts.c -o interrupts.o +clang -m32 -c idt.c -o idt.o +clang -m32 -c pit.c -o pit.o +clang -m32 -c pic.c -o pic.o +clang -m32 -c message.c -o message.o +clang -m32 -c syscall.c -o syscall.o + +ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o message.o syscall.o + +echo "kernel build end, making .iso" + +# make iso +cd ../../ +rm os.iso +mkdir -p iso/boot/grub # create the folder structure +cp stage2_eltorito iso/boot/grub/ # copy the bootloader +cp menu.lst iso/boot/grub/ # copy menu +cp src/kernel/kernel iso/boot/ # copy the kernel +cp src/user/test.bin iso/ +genisoimage -R \ +-b boot/grub/stage2_eltorito \ +-no-emul-boot \ +-boot-load-size 4 \ +-A os \ +-input-charset utf8 \ +-quiet \ +-boot-info-table \ +-o os.iso \ +iso diff --git a/make.sh b/make.sh index 9f6f54e..f4b4278 100755 --- a/make.sh +++ b/make.sh @@ -5,18 +5,18 @@ rm *.o nasm -f elf32 kernel.asm -o kasm.o nasm -f elf32 threadS.asm -o threadS.o nasm -f elf32 interruptStubs.asm -o interruptStubs.o -clang -m32 -c kernel.c -o kc.o -clang -m32 -c io.c -o io.o -clang -m32 -c lib.c -o lib.o -clang -m32 -c gdt.c -o gdt.o -clang -m32 -c physmem.c -o physmem.o -clang -m32 -c thread.c -o thread.o -clang -m32 -c interrupts.c -o interrupts.o -clang -m32 -c idt.c -o idt.o -clang -m32 -c pit.c -o pit.o -clang -m32 -c pic.c -o pic.o -clang -m32 -c message.c -o message.o -clang -m32 -c syscall.c -o syscall.o +gcc -m32 -c kernel.c -o kc.o +gcc -m32 -c io.c -o io.o +gcc -m32 -c lib.c -o lib.o +gcc -m32 -c gdt.c -o gdt.o +gcc -m32 -c physmem.c -o physmem.o +gcc -m32 -c thread.c -o thread.o +gcc -m32 -c interrupts.c -o interrupts.o +gcc -m32 -c idt.c -o idt.o +gcc -m32 -c pit.c -o pit.o +gcc -m32 -c pic.c -o pic.o +gcc -m32 -c message.c -o message.o +gcc -m32 -c syscall.c -o syscall.o ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o io.o lib.o gdt.o physmem.o thread.o threadS.o interrupts.o interruptStubs.o idt.o pit.o pic.o message.o syscall.o @@ -24,11 +24,12 @@ echo "kernel build end, making .iso" # make iso cd ../../ +rm os.iso mkdir -p iso/boot/grub # create the folder structure cp stage2_eltorito iso/boot/grub/ # copy the bootloader cp menu.lst iso/boot/grub/ # copy menu cp src/kernel/kernel iso/boot/ # copy the kernel - +cp src/user/test.bin iso/ genisoimage -R \ -b boot/grub/stage2_eltorito \ -no-emul-boot \ diff --git a/menu.lst b/menu.lst index 068ccd3..e0cc883 100644 --- a/menu.lst +++ b/menu.lst @@ -3,3 +3,4 @@ default=0 title os kernel /boot/kernel + module /test.bin diff --git a/os.iso b/os.iso deleted file mode 100644 index e09ffe897030ba1b6250c7aa57fd1e51c6b262c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 509952 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

T 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 zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** J5cq!}@XrN$6eR!v diff --git a/out.txt b/out.txt new file mode 100644 index 0000000..9c0ba28 --- /dev/null +++ b/out.txt @@ -0,0 +1,314 @@ +In Datei, eingebunden von kernel.c:15: +protos.h:20:9: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcmp«; erwartet wurde »int(const char *, const char *)« [-Wbuiltin-declaration-mismatch] + 20 | int16_t strcmp (const uint8_t * str1, const uint8_t * str2); + | ^~~~~~ +protos.h:1:1: Anmerkung: »strcmp« wird in Header »« deklariert + +++ |+#include + 1 | // protos.h - function prototypes +protos.h:21:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strlen«; erwartet wurde »unsigned int(const char *)« [-Wbuiltin-declaration-mismatch] + 21 | uint32_t strlen (const uint8_t *str); + | ^~~~~~ +protos.h:21:10: Anmerkung: »strlen« wird in Header »« deklariert +protos.h:22:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcpy«; erwartet wurde »char *(char *, const char *)« [-Wbuiltin-declaration-mismatch] + 22 | uint8_t *strcpy (uint8_t *dest, uint8_t *src); + | ^~~~~~ +protos.h:22:10: Anmerkung: »strcpy« wird in Header »« deklariert +protos.h:23:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strncpy«; erwartet wurde »char *(char *, const char *, unsigned int)« [-Wbuiltin-declaration-mismatch] + 23 | uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len); + | ^~~~~~~ +protos.h:23:10: Anmerkung: »strncpy« wird in Header »« deklariert +protos.h:24:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memcpy«; erwartet wurde »void *(void *, const void *, unsigned int)« [-Wbuiltin-declaration-mismatch] + 24 | uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); + | ^~~~~~ +protos.h:24:10: Anmerkung: »memcpy« wird in Header »« deklariert +protos.h:25:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memset«; erwartet wurde »void *(void *, int, unsigned int)« [-Wbuiltin-declaration-mismatch] + 25 | uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count); + | ^~~~~~ +protos.h:25:10: Anmerkung: »memset« wird in Header »« deklariert +kernel.c: In Funktion »div_by_zero«: +kernel.c:301:12: Warnung: Division durch Null [-Wdiv-by-zero] + 301 | i = 23 / 0; + | ^ +kernel.c: In Funktion »event_handler«: +kernel.c:482:7: Warnung: Implizite Deklaration der Funktion »message_read«; meinten Sie »thread_message_read«? [-Wimplicit-function-declaration] + 482 | if (message_read (&msg, &sender) == 0) + | ^~~~~~~~~~~~ + | thread_message_read +kernel.c:488:5: Warnung: Implizite Deklaration der Funktion »message_send« [-Wimplicit-function-declaration] + 488 | message_send (keyboard_ch, sender, 1); + | ^~~~~~~~~~~~ +kernel.c: In Funktion »run_event_handler«: +kernel.c:503:17: Warnung: Übergabe des Arguments 1 von »thread_init« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 503 | thread_init(base); + | ^~~~ + | | + | uint8_t * {alias unsigned char *} +In Datei, eingebunden von kernel.c:15: +protos.h:44:27: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} + 44 | void thread_init(uint32_t baseContextAddress); + | ~~~~~~~~~^~~~~~~~~~~~~~~~~~ +kernel.c:504:16: Warnung: Übergabe des Arguments 1 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 504 | thread_create(event_context, event_stack, 4096 * 2, (uint32_t)event_handler, 0, "event_handler"); + | ^~~~~~~~~~~~~ + | | + | uint8_t * {alias unsigned char *} +In Datei, eingebunden von kernel.c:15: +protos.h:45:29: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} + 45 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name); + | ~~~~~~~~~^~~~~~~~~~~~~~ +kernel.c:504:31: Warnung: Übergabe des Arguments 2 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 504 | thread_create(event_context, event_stack, 4096 * 2, (uint32_t)event_handler, 0, "event_handler"); + | ^~~~~~~~~~~ + | | + | uint8_t * {alias unsigned char *} +In Datei, eingebunden von kernel.c:15: +protos.h:45:54: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} + 45 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name); + | ~~~~~~~~~^~~~~~~~~~ +kernel.c: In Funktion »kmain«: +kernel.c:770:5: Warnung: Implizite Deklaration der Funktion »set_gdt« [-Wimplicit-function-declaration] + 770 | set_gdt (); /* init memory GDT */ + | ^~~~~~~ +kernel.c:774:2: Warnung: Implizite Deklaration der Funktion »pic_init« [-Wimplicit-function-declaration] + 774 | pic_init(); //set up PIC + | ^~~~~~~~ +kernel.c:778:2: Warnung: Implizite Deklaration der Funktion »pit_init« [-Wimplicit-function-declaration] + 778 | pit_init(100); + | ^~~~~~~~ +kernel.c:785:36: Warnung: Initialisierung von »multiboot_memory_map_t *« {alias »struct multiboot_memory_map *«} von »long unsigned int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 785 | multiboot_memory_map_t* mmap = mbt->mmap_addr; + | ^~~ +kernel.c:802:13: Warnung: Vergleich zwischen Zeiger und Ganzzahl + 802 | while(mmap < mbt->mmap_addr + mbt->mmap_length) + | ^ +kernel.c:907:5: Warnung: Implizite Deklaration der Funktion »pic_unmask_irq« [-Wimplicit-function-declaration] + 907 | pic_unmask_irq(IRQ_KEYBOARD); + | ^~~~~~~~~~~~~~ +lib.c: In Funktion »kprint_int«: +lib.c:93:5: Warnung: Implizite Deklaration der Funktion »kprint«; meinten Sie »kprint_int«? [-Wimplicit-function-declaration] + 93 | kprint (str); + | ^~~~~~ + | kprint_int +lib.c: Auf höchster Ebene: +lib.c:96:9: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcmp«; erwartet wurde »int(const char *, const char *)« [-Wbuiltin-declaration-mismatch] + 96 | int16_t strcmp (const uint8_t * str1, const uint8_t * str2) + | ^~~~~~ +lib.c:4:1: Anmerkung: »strcmp« wird in Header »« deklariert + 3 | #include "types.h" + +++ |+#include + 4 | +lib.c:114:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strlen«; erwartet wurde »unsigned int(const char *)« [-Wbuiltin-declaration-mismatch] + 114 | uint32_t strlen (const uint8_t *str) + | ^~~~~~ +lib.c:114:10: Anmerkung: »strlen« wird in Header »« deklariert +lib.c:127:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcpy«; erwartet wurde »char *(char *, const char *)« [-Wbuiltin-declaration-mismatch] + 127 | uint8_t *strcpy (uint8_t *dest, uint8_t *src) + | ^~~~~~ +lib.c:127:10: Anmerkung: »strcpy« wird in Header »« deklariert +lib.c:142:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strncpy«; erwartet wurde »char *(char *, const char *, unsigned int)« [-Wbuiltin-declaration-mismatch] + 142 | uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) + | ^~~~~~~ +lib.c:142:10: Anmerkung: »strncpy« wird in Header »« deklariert +lib.c:157:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memcpy«; erwartet wurde »void *(void *, const void *, unsigned int)« [-Wbuiltin-declaration-mismatch] + 157 | uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) + | ^~~~~~ +lib.c:157:10: Anmerkung: »memcpy« wird in Header »« deklariert +lib.c: In Funktion »memcpy«: +lib.c:161:24: Warnung: Initialisierung streicht Qualifizierer »const« von Zeiger-Zieltyp [-Wdiscarded-qualifiers] + 161 | uint8_t *src_ptr = src; + | ^~~ +lib.c: Auf höchster Ebene: +lib.c:173:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memset«; erwartet wurde »void *(void *, int, unsigned int)« [-Wbuiltin-declaration-mismatch] + 173 | uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) + | ^~~~~~ +lib.c:173:10: Anmerkung: »memset« wird in Header »« deklariert +lib.c: In Funktion »getch«: +lib.c:203:9: Warnung: Implizite Deklaration der Funktion »kdelay« [-Wimplicit-function-declaration] + 203 | kdelay (10); + | ^~~~~~ +gdt.c: In Funktion »write_tss«: +gdt.c:99:4: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] + 99 | memset(&tss_entry, 0, sizeof(tss_entry)); + | ^~~~~~ +gdt.c:99:4: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« +gdt.c:2:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit + 1 | #include "types.h" + +++ |+#include + 2 | +gdt.c: In Funktion »set_gdt«: +gdt.c:119:13: Warnung: Zuweisung an »uint32_t« {alias »unsigned int«} aus »struct gdt_entry_bits (*)[6]« wandelt einen Zeiger in eine Ganzzahl um, ohne explizite Typkonvertierung [-Wint-conversion] + 119 | gp.base = &gdt; + | ^ +gdt.c:153:29: Warnung: Vorzeichenlose Umwandlung von »int« nach »unsigned char:4« ändert den Wert von »65535« nach »15« [-Woverflow] + 153 | null_code->limit_high = 0xFFFF; + | ^~~~~~ +gdt.c:176:22: Warnung: Vorzeichenlose Umwandlung von »int« nach »unsigned char:4« ändert den Wert von »65535« nach »15« [-Woverflow] + 176 | code->limit_high=0xFFFF; + | ^~~~~~ +gdt.c:188:5: Warnung: Implizite Deklaration der Funktion »gdt_flush« [-Wimplicit-function-declaration] + 188 | gdt_flush (); + | ^~~~~~~~~ +gdt.c:190:5: Warnung: Implizite Deklaration der Funktion »tss_flush« [-Wimplicit-function-declaration] + 190 | tss_flush(); //implement this later + | ^~~~~~~~~ +physmem.c: In Funktion »kmalloc«: +physmem.c:346:17: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 346 | ptr = start_free; + | ^ +physmem.c:348:32: Warnung: Rückgabe von »uint32_t« {alias »unsigned int«} aus einer Funktion mit Rückgabetyp »void *« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 348 | return (start_free + sizeof (size_t)); + | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ +physmem.c: In Funktion »kfree«: +physmem.c:368:9: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 368 | ptr = address - sizeof (size_t); + | ^ +physmem.c: In Funktion »pmem_show_page«: +physmem.c:396:5: Warnung: Implizite Deklaration der Funktion »kprint_int« [-Wimplicit-function-declaration] + 396 | kprint_int (page * PAGE_BITS * MEM_BLOCK_SIZE, 16); + | ^~~~~~~~~~ +physmem.c:397:5: Warnung: Implizite Deklaration der Funktion »fb_get_cursor« [-Wimplicit-function-declaration] + 397 | fb_get_cursor (&cursor_x, &cursor_y); + | ^~~~~~~~~~~~~ +physmem.c:402:9: Warnung: Implizite Deklaration der Funktion »fb_set_cursor« [-Wimplicit-function-declaration] + 402 | fb_set_cursor (cursor_x, cursor_y); + | ^~~~~~~~~~~~~ +physmem.c:409:13: Warnung: Implizite Deklaration der Funktion »kprint« [-Wimplicit-function-declaration] + 409 | kprint ("0 "); + | ^~~~~~ +physmem.c:416:5: Warnung: Implizite Deklaration der Funktion »kprint_newline« [-Wimplicit-function-declaration] + 416 | kprint_newline (); + | ^~~~~~~~~~~~~~ +thread.c: In Funktion »thread_init«: +thread.c:62:2: Warnung: Implizite Deklaration der Funktion »kprint« [-Wimplicit-function-declaration] + 62 | kprint ("Initializing threading..."); kprint_newline (); + | ^~~~~~ +thread.c:62:40: Warnung: Implizite Deklaration der Funktion »kprint_newline« [-Wimplicit-function-declaration] + 62 | kprint ("Initializing threading..."); kprint_newline (); + | ^~~~~~~~~~~~~~ +thread.c:64:2: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] + 64 | memset(listHead, 0, sizeof(ThreadContext)); + | ^~~~~~ +thread.c:64:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« +thread.c:12:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit + 11 | #include "elf.h" + +++ |+#include + 12 | +thread.c: In Funktion »thread_create«: +thread.c:75:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« + 75 | memset(context, 0, sizeof(ThreadContext)); + | ^~~~~~ +thread.c:75:2: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit +thread.c:95:5: Warnung: Implizite Deklaration der Funktion »strncpy« [-Wimplicit-function-declaration] + 95 | strncpy (context->name, name, THREAD_NAME_LEN); + | ^~~~~~~ +thread.c:95:5: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »strncpy« +thread.c:95:5: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »strncpy« bereit +thread.c: In Funktion »thread_exit«: +thread.c:141:5: Warnung: Implizite Deklaration der Funktion »kfree« [-Wimplicit-function-declaration] + 141 | kfree (currentContext->esp); + | ^~~~~ +thread.c: In Funktion »thread_show_info«: +thread.c:221:74: Warnung: Implizite Deklaration der Funktion »kprint_int« [-Wimplicit-function-declaration] + 221 | kprint ("thread: '"); kprint (search->name); kprint ("' pid: "); kprint_int (search->pid, 10); kprint (", priority: "); kprint_int (search->priority, 10); + | ^~~~~~~~~~ +thread.c: In Funktion »thread_schedule«: +thread.c:274:47: Warnung: Implizite Deklaration der Funktion »clock« [-Wimplicit-function-declaration] + 274 | currentContext->next_switch = clock () + (fair_schedule * currentContext->priority); + | ^~~~~ +thread.c: In Funktion »switch_to_user_mode«: +thread.c:355:5: Warnung: Implizite Deklaration der Funktion »set_kernel_stack« [-Wimplicit-function-declaration] + 355 | set_kernel_stack(GDT_KERNEL_DATA); + | ^~~~~~~~~~~~~~~~ +thread.c: In Funktion »init_elf«: +thread.c:403:9: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« + 403 | memset(dest, 0, ph->mem_size); + | ^~~~~~ +thread.c:403:9: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit +thread.c:404:9: Warnung: Implizite Deklaration der Funktion »memcpy« [-Wimplicit-function-declaration] + 404 | memcpy(dest, src, ph->file_size); + | ^~~~~~ +thread.c:404:9: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memcpy« +thread.c:404:9: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memcpy« bereit +thread.c:409:29: Warnung: Implizite Deklaration der Funktion »kmalloc« [-Wimplicit-function-declaration] + 409 | task_base = (uint8_t *) kmalloc (8192 * 2); + | ^~~~~~~ +thread.c:417:19: Warnung: Übergabe des Arguments 1 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 417 | thread_create(task_context, task_stack, 4096 * 2, (uint32_t) header->entry, 0, "task"); + | ^~~~~~~~~~~~ + | | + | uint8_t * {alias unsigned char *} +thread.c:70:29: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} + 70 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) + | ~~~~~~~~~^~~~~~~~~~~~~~ +thread.c:417:33: Warnung: Übergabe des Arguments 2 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 417 | thread_create(task_context, task_stack, 4096 * 2, (uint32_t) header->entry, 0, "task"); + | ^~~~~~~~~~ + | | + | uint8_t * {alias unsigned char *} +thread.c:70:54: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} + 70 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) + | ~~~~~~~~~^~~~~~~~~~ +thread.c: In Funktion »init_multitasking«: +thread.c:425:3: Warnung: Implizite Deklaration der Funktion »run_kshell« [-Wimplicit-function-declaration] + 425 | run_kshell (); + | ^~~~~~~~~~ +thread.c:428:34: Warnung: Initialisierung von »struct module *« von »long unsigned int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 428 | struct module* modules = mb_info->mods_addr; + | ^~~~~~~ +thread.c: In Funktion »get_thread_input_stream«: +thread.c:444:7: Warnung: Zuweisung an »uint8_t *« {alias »unsigned char *«} von »uint8_t« {alias »unsigned char«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 444 | ch = keyboard_ch; + | ^ +interrupts.c: In Funktion »interrupts_init«: +interrupts.c:13:5: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] + 13 | memset(isrs, 0, sizeof(isrFunction) * I86_IDT_MAX_ENTRY_COUNT); + | ^~~~~~ +interrupts.c:13:5: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« +interrupts.c:8:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit + 7 | #include "pic.h" + +++ |+#include + 8 | +interrupts.c: In Funktion »interrupts_faultHandler«: +interrupts.c:22:9: Warnung: Implizite Deklaration der Funktion »kprint« [-Wimplicit-function-declaration] + 22 | kprint ("Unhandled fault: "); + | ^~~~~~ +interrupts.c:23:9: Warnung: Implizite Deklaration der Funktion »kprint_int« [-Wimplicit-function-declaration] + 23 | kprint_int (regs.int_no, 10); + | ^~~~~~~~~~ +interrupts.c:24:9: Warnung: Implizite Deklaration der Funktion »kprint_newline« [-Wimplicit-function-declaration] + 24 | kprint_newline (); + | ^~~~~~~~~~~~~~ +idt.c: In Funktion »idt_install«: +idt.c:35:2: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] + 35 | memset(&idt, 0, sizeof(struct idt_entry) * I86_IDT_MAX_ENTRY_COUNT); + | ^~~~~~ +idt.c:35:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« +idt.c:9:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit + 8 | #include "types.h" + +++ |+#include + 9 | +message.c:8:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memcpy«; erwartet wurde »void *(void *, const void *, unsigned int)« [-Wbuiltin-declaration-mismatch] + 8 | uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); + | ^~~~~~ +message.c:4:1: Anmerkung: »memcpy« wird in Header »« deklariert + 3 | #include "interrupts.h" + +++ |+#include + 4 | +message.c: In Funktion »message_send«: +message.c:26:39: Warnung: Implizite Deklaration der Funktion »kmalloc« [-Wimplicit-function-declaration] + 26 | msg_head = (struct message *) kmalloc (sizeof (struct message)); + | ^~~~~~~ +message.c:35:24: Warnung: Zuweisung an »uint8_t *« {alias »unsigned char *«} von »int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 35 | msg_head->data = kmalloc (len); + | ^ +message.c:67:20: Warnung: Zuweisung an »uint8_t *« {alias »unsigned char *«} von »int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 67 | next->data = kmalloc (len); + | ^ +message.c: In Funktion »message_read«: +message.c:103:27: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 103 | thread_sender_pid = current_msg->thread_sender_num; + | ^ +message.c:115:35: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] + 115 | thread_sender_pid = current_msg->thread_sender_num; + | ^ +kernel build end, making .iso diff --git a/src/kernel/colors.h b/src/kernel/colors.h new file mode 100644 index 0000000..9c7e5dd --- /dev/null +++ b/src/kernel/colors.h @@ -0,0 +1,17 @@ +/* fb text color */ +#define FB_BLACK 0x00 +#define FB_BLUE 0x01 +#define FB_GREEN 0x02 +#define FB_CYAN 0x03 +#define FB_RED 0x04 +#define FB_MAGENTA 0x05 +#define FB_BROWN 0x06 +#define FB_LIGHT_GREY 0x07 +#define FB_DARK_GREY 0x08 +#define FB_LIGHT_BLUE 0x09 +#define FB_LIGHT_GREEN 0x10 +#define FB_LIGHT_CYAN 0x11 +#define FB_LIGHT_RED 0x12 +#define FB_LIGHT_MAGENTA 0x13 +#define FB_LIGHT_BROWN 0x14 +#define FB_WHITE 0x0f diff --git a/src/kernel/elf.h b/src/kernel/elf.h new file mode 100644 index 0000000..8355a92 --- /dev/null +++ b/src/kernel/elf.h @@ -0,0 +1,36 @@ +#ifndef ELF_H +#define ELF_H + +#include "types.h" + +#define ELF_MAGIC 0x464C457F + +struct elf_header { + uint32_t magic; + uint32_t version; + uint64_t reserved; + uint64_t version2; + uint32_t entry; + uint32_t ph_offset; + uint32_t sh_offset; + uint32_t flags; + uint16_t header_size; + uint16_t ph_entry_size; + uint16_t ph_entry_count; + uint16_t sh_entry_size; + uint16_t sh_entry_count; + uint16_t sh_str_table_index; +} __attribute__((packed)); + +struct elf_program_header { + uint32_t type; + uint32_t offset; + uint32_t virt_addr; + uint32_t phys_addr; + uint32_t file_size; + uint32_t mem_size; + uint32_t flags; + uint32_t alignment; +} __attribute__((packed)); + +#endif diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c index 356f16c..0951e70 100644 --- a/src/kernel/gdt.c +++ b/src/kernel/gdt.c @@ -1,5 +1,4 @@ #include "types.h" -#include "protos.h" void stack_space (void); @@ -97,7 +96,7 @@ void write_tss(struct gdt_entry_bits *g) g->base_high=(base&0xFF000000)>>24; //isolate top byte. // Ensure the TSS is initially zero'd. - memset((uint8_t *) &tss_entry, 0, sizeof(tss_entry)); + memset(&tss_entry, 0, sizeof(tss_entry)); // tss_entry.ss0 = stack_space; // Set the kernel stack segment. // tss_entry.esp0 = stack_space; // Set the kernel stack pointer. @@ -108,11 +107,16 @@ void write_tss(struct gdt_entry_bits *g) //note that CS is loaded from the IDT entry and should be the regular kernel code segment } +void set_kernel_stack(uint32_t stack) //this will update the ESP0 stack used when an interrupt occurs +{ + tss_entry.esp0 = stack; +} + void set_gdt (void) { gp.limit = (sizeof(struct gdt_entry_bits) * 6) - 1; - gp.base = (uint32_t) &gdt; + gp.base = &gdt; //....insert your null_seg 0 segments here or whatever struct gdt_entry_bits *null_seg; @@ -185,8 +189,3 @@ void set_gdt (void) //after those are installed we'll tell the CPU where our TSS is: tss_flush(); //implement this later } - -void set_kernel_stack(uint32_t stack) //this will update the ESP0 stack used when an interrupt occurs -{ - tss_entry.esp0 = stack; -} diff --git a/src/kernel/idt.h b/src/kernel/idt.h index 652dbf3..1343669 100644 --- a/src/kernel/idt.h +++ b/src/kernel/idt.h @@ -1,5 +1,5 @@ /* by Andreas Galauner - * + * * https://github.com/G33KatWork */ @@ -59,6 +59,7 @@ void isr6(void); void isr7(void); void isr8(void); void isr9(void); + void isr10(void); void isr11(void); void isr12(void); @@ -102,6 +103,6 @@ void irq13(void); void irq14(void); void irq15(void); -void isr128(void); // syscall interruot 80 +// void isr128(void); // syscall interruot 80 #endif diff --git a/src/kernel/interruptStubs.asm b/src/kernel/interruptStubs.asm index de4e8fd..d513766 100644 --- a/src/kernel/interruptStubs.asm +++ b/src/kernel/interruptStubs.asm @@ -64,7 +64,6 @@ ISR_NOERRCODE 28 ISR_NOERRCODE 29 ISR_NOERRCODE 30 ISR_NOERRCODE 31 -ISR_NOERRCODE 128 IRQ 0, 32 IRQ 1, 33 @@ -91,7 +90,7 @@ isr_common_stub: mov ax, ds ; save old data segment decriptor push eax - + push es push fs push gs @@ -103,7 +102,7 @@ isr_common_stub: mov gs, ax call interrupts_faultHandler - + pop gs pop fs pop es @@ -126,7 +125,7 @@ irq_common_stub: mov ax, ds ; save old data segment decriptor push eax - + push es push fs push gs @@ -138,7 +137,7 @@ irq_common_stub: mov gs, ax call interrupts_interruptHandler - + pop gs pop fs pop es diff --git a/src/kernel/interrupts.c b/src/kernel/interrupts.c index 159eb80..0d1becd 100644 --- a/src/kernel/interrupts.c +++ b/src/kernel/interrupts.c @@ -1,11 +1,10 @@ /* by Andreas Galauner - * + * * https://github.com/G33KatWork */ #include "interrupts.h" #include "pic.h" -#include "protos.h" static isrFunction isrs[I86_IDT_MAX_ENTRY_COUNT]; @@ -32,7 +31,7 @@ void interrupts_faultHandler(registers_t regs) void interrupts_interruptHandler(registers_t regs) { pic_notify(regs.int_no); - + if(isrs[regs.int_no] != 0) isrs[regs.int_no](®s); else diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index 8fb5561..8cfe2ed 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -1,7 +1,7 @@ #ifndef _INTERRUPTS_H #define _INTERRUPTS_H /* by Andreas Galauner - * + * * https://github.com/G33KatWork */ @@ -25,7 +25,7 @@ #define IRQ_FPU 42 #define IRQ_HDC 43 -#define IRQ_SYSCALL 38 +#define IRQ_SYSCALL 37 // Set the following number to the maximum nested exceptions // the kernel tries to resolve before it will panic diff --git a/src/kernel/kernel b/src/kernel/kernel deleted file mode 100755 index 9fbe7e86f54da34e9bf0c053f8151dae023265e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33888 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 diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 4a0e6e3..6565b7a 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -301,6 +301,7 @@ void div_by_zero () i = 23 / 0; } + void kshell (uint32_t argument) { uint8_t ch; @@ -353,12 +354,12 @@ void kshell (uint32_t argument) if (strcmp (command, (const uint8_t *) "tasks") == 0) { - run_threads (); + // run_threads (); } if (strcmp (command, (const uint8_t *) "btasks") == 0) { - run_threads_background (); + // run_threads_background (); } if (strcmp (command, (const uint8_t *) "page") == 0) @@ -463,8 +464,44 @@ void run_kshell (void) uint8_t *ksh_context = (uint8_t *) kmalloc (4096); uint8_t *ksh_stack = (uint8_t *) kmalloc (4096 * 2); - thread_init ((uint32_t) ksh_base); - thread_create ((uint32_t) ksh_context, (uint32_t) ksh_stack, 4096 * 2, (uint32_t) kshell, 0x61, (uint8_t *) "kshell"); + thread_init((uint32_t) ksh_base); + thread_create((uint32_t) ksh_context, (uint32_t) ksh_stack, 4096 * 2, (uint32_t)kshell, 0x61, (uint8_t *) "kshell"); +} + + + +void event_handler (void) +{ + // run this as task/thread 1!!! + + uint8_t msg; + uint32_t sender; + + for (;;) + { + if (message_read (&msg, &sender) == 0) + { + if (msg == MSG_EVENT_KEY) + { + // kprint ("event_handler: got msg"); kprint_newline (); + + message_send (keyboard_ch, sender, 1); + } + } + } +} + + +void run_event_handler (void) +{ + // must be started as thread 0!!! + + uint8_t *base = (uint8_t *) kmalloc (4096 * 2); + uint8_t *event_context = (uint8_t *) kmalloc (4096); + uint8_t *event_stack = (uint8_t *) kmalloc (4096 * 2); + + thread_init(base); + thread_create(event_context, event_stack, 4096 * 2, (uint32_t)event_handler, 0, "event_handler"); } void user_print (void) @@ -537,7 +574,7 @@ void keyboard_handler(registers_t* regs) } ch[1] = '\0'; - kprint ((const char *) ch); + kprint (ch); keyboard_ch = ch[0]; } @@ -598,6 +635,7 @@ void timerHandler(registers_t* regs) // kprint ("clock: "); kprint_int (clock_ticks, 10); kprint_newline (); } +/* void thread_a(uint32_t argument) { uint32_t ticks; @@ -614,13 +652,13 @@ void thread_a(uint32_t argument) if (ticks >= ticks_max) thread_exit (0); - if (message_read ((uint8_t *) &data, 0) == 0) + if (message_read (&data, 0) == 0) { // end signal -> EXIT fb_set_color (FB_RED, FB_BLACK); - kprint ("thread A: got SHUTDOWN MESSAGE: "); kprint ((const char *) data); kprint_newline(); + kprint ("thread A: got SHUTDOWN MESSAGE: "); kprint (data); kprint_newline(); - if (strcmp (data, (const uint8_t *) "kill") == 0) + if (strcmp (data, "kill") == 0) { fb_set_color (FB_WHITE, FB_BLACK); thread_exit (0); @@ -647,13 +685,13 @@ void thread_b(uint32_t argument) void thread_c(uint32_t argument) { - uint32_t ticks; + uint32_t ticks;thread_sender_pid = current_msg->thread_sender_num; uint32_t ticks_max = clock () + 10000; // normal priority = 0 for(;;) { - ticks = clock (); + ticks = clock ();thread_sender_pid = current_msg->thread_sender_num; kprint ("thread C: "); kprint_int (ticks, 10); kprint_newline (); kdelay (5); @@ -668,21 +706,21 @@ void run_threads (void) uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); - uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_b_cothread_sender_pid = current_msg->thread_sender_num;ntext = (uint8_t *) kmalloc (4096); uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); uint8_t *thread_c_context = (uint8_t *) kmalloc (4096); uint8_t *thread_c_stack = (uint8_t *) kmalloc (4096); - thread_create((uint32_t) thread_a_context, (uint32_t) thread_a_stack, 4096, (uint32_t)thread_a, 0, (uint8_t *) "a-print"); - thread_create((uint32_t) thread_b_context, (uint32_t) thread_b_stack, 4096, (uint32_t)thread_b, 1, (uint8_t *) "b-print"); - thread_create((uint32_t) thread_c_context, (uint32_t) thread_c_stack, 4096, (uint32_t)thread_c, 2, (uint8_t *) "c-print"); + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0, "a-print"); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 1, "b-print"); + thread_create(thread_c_context, thread_c_stack, 4096, (uint32_t)thread_c, 2, "c-print"); while (clock () < shutdown_thread) { kdelay (100); } - message_send ((uint8_t *) "kill", 0, 5); + message_send ("kill", 0, 5); } @@ -702,10 +740,10 @@ void thread_a_backgr(uint32_t argument) void thread_b_backgr(uint32_t argument) { - uint32_t ticks; + uint32_t ticks;thread_sender_pid = current_msg->thread_sender_num; uint32_t ticks_max = clock () + 10000; - for(;;) + for(;;)thread_sender_pid = current_msg->thread_sender_num; { ticks = clock (); kdelay (5); @@ -722,9 +760,10 @@ void run_threads_background (void) uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); - thread_create((uint32_t) thread_a_context, (uint32_t) thread_a_stack, 4096, (uint32_t)thread_a_backgr, 0, (uint8_t *) "a"); - thread_create((uint32_t) thread_b_context, (uint32_t) thread_b_stack, 4096, (uint32_t)thread_b_backgr, 1, (uint8_t *) "b"); + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a_backgr, 0, "a"); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b_backgr, 1, "b"); } +*/ void kmain (multiboot_info_t* mbt, unsigned int magic) { @@ -743,7 +782,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) // vmem_paging (); /* switch paging on */ - multiboot_memory_map_t* mmap = (multiboot_memory_map_t *) mbt->mmap_addr; + multiboot_memory_map_t* mmap = mbt->mmap_addr; uint32_t mem_start, mem_end; uint32_t mem_type; uint8_t mem_found = 0; @@ -760,7 +799,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) pmem_init_bitmap (); - while (mmap < (mbt->mmap_addr + mbt->mmap_length)) + while(mmap < mbt->mmap_addr + mbt->mmap_length) { mem_start = mmap->base_addr; mem_end = mem_start + mmap->length; @@ -837,7 +876,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) } } - mmap = (multiboot_memory_map_t*) ( (uint32_t) mmap + mmap->size + sizeof(mmap->size) ); + mmap = (multiboot_memory_map_t*) ( (uint32_t)mmap + mmap->size + sizeof(mmap->size) ); } pmem_set_first_page (); /* so no null pointer for free mem block can exist */ @@ -851,6 +890,12 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) } */ + if (pmem_set_bitmap (0x000000, 0x2FFFFFF, 0, ALLOCATE) != MEM_ERR_OK) + { + kprint ("MEMORY ERROR: mark module load reserved"); + kprint_newline (); + } + pages_free = pmem_count_free_pages (); ram_free = (pages_free * 4096) / 1024 /1024; kprint ("free pages: "); kprint_int (pages_free, 10); kprint (" = " ); kprint_int ((uint32_t) ram_free, 10); kprint (" MB"); kprint_newline (); @@ -910,6 +955,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) kprint ("READY"); kprint_newline (); + pic_unmask_irq(IRQ_TIMER); pic_unmask_irq(IRQ_CMOSTIMER); @@ -918,10 +964,10 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) uint8_t *thread_stack = (uint8_t *) kmalloc (4096 * 2); + initialise_syscalls (); + kdelay (500); + pic_unmask_irq (IRQ_SYSCALL); - initialise_syscalls (); - // kdelay (500); - pic_unmask_irq (IRQ_SYSCALL); // thread_init(thread_base); @@ -929,7 +975,11 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) // switch_to_user_mode (); // enter_usermode (); // unhandled fault 13 // jump_usermode (); - run_kshell(); + // run_kshell(); + + run_event_handler (); + kdelay (1000); + // init_multitasking (mbt); // user_print (); // thread_create(thread_context, thread_stack, 4096 * 2, (uint32_t)user_print, 0x61, "userprint"); @@ -937,7 +987,7 @@ void kmain (multiboot_info_t* mbt, unsigned int magic) // user_print (); - //kshell (0); + kshell (0); while (1) { diff --git a/src/kernel/kernel.c~ b/src/kernel/kernel.c~ new file mode 100644 index 0000000..d09b00e --- /dev/null +++ b/src/kernel/kernel.c~ @@ -0,0 +1,904 @@ +/* +* Copyright (C) 2014 Arjun Sreedharan +* License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html +*/ +#include "types.h" +#include "registers.h" +#include "multiboot.h" +#include "idt.h" +#include "ports.h" +#include "interrupts.h" +#include "keyboard_map.h" +#include "message.h" + +void jump_usermode (void); + + +#define KEYBOARD_DATA_PORT 0x60 +#define KEYBOARD_STATUS_PORT 0x64 +#define IDT_SIZE 256 +#define INTERRUPT_GATE 0x8e +#define KERNEL_CODE_SEGMENT_OFFSET 0x08 + +#define ENTER_KEY_CODE 0x1C + + +/* memory */ +uint32_t mem_start_address = 0x100000; +uint32_t mem_end_address; +uint32_t mem_use_address = 0x800000; + + +extern uint8_t keyboard_map[128]; +extern uint8_t keyboard_shift_map[128]; + +// keyboard buffer +uint8_t keyboard_ch = NULL; + +extern void load_idt(unsigned long *idt_ptr); +extern void enter_usermode (void); + +void timerHandler(registers_t* regs); +void thread(uint32_t argument); + +extern uint8_t keyboard_shift; + +/* fb current cursor location */ +uint16_t fb_cursor_x = 0, fb_cursor_y = 0; +uint32_t fb_current_loc = 0; + +/* timer */ +static uint32_t clock_ticks = 0; + + +/* fb text color */ +#define FB_BLACK 0x00 +#define FB_BLUE 0x01 +#define FB_GREEN 0x02 +#define FB_CYAN 0x03 +#define FB_RED 0x04 +#define FB_MAGENTA 0x05 +#define FB_BROWN 0x06 +#define FB_LIGHT_GREY 0x07 +#define FB_DARK_GREY 0x08 +#define FB_LIGHT_BLUE 0x09 +#define FB_LIGHT_GREEN 0x10 +#define FB_LIGHT_CYAN 0x11 +#define FB_LIGHT_RED 0x12 +#define FB_LIGHT_MAGENTA 0x13 +#define FB_LIGHT_BROWN 0x14 +#define FB_WHITE 0x0f + +uint8_t fb_color = FB_LIGHT_GREY; /* light grey */ + +/* video memory begins at address 0xb8000 */ +static int8_t *vidptr = (int8_t*)0xb8000; + +/* frame buffer console */ +uint8_t fb_con[SCREENSIZE]; + + +typedef struct multiboot_memory_map { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} multiboot_memory_map_t; + + +// threading +uint8_t threads_request = 0; + +void kb_init(void) +{ + /* 0xFD is 11111101 - enables only IRQ1 (keyboard)*/ + outb(0x21 , 0xFC); +} + + + +void kprint(const char *str) +{ + uint32_t i = 0; + + while (str[i] != '\0') { + if (str[i] == '\b') + { + /*/ backspace */ + + if (fb_cursor_x > 1) + { + fb_cursor_x--; + } + else + { + if (fb_cursor_y > 0) + { + /* move cursor to previous line end */ + fb_cursor_y--; + fb_cursor_x = COLUMNS_IN_LINE; + } + } + + fb_current_loc = fb_current_loc - 2; /* goto old frame buffer position and clear char */ + fb_con[fb_current_loc] = ' '; + fb_con[fb_current_loc + 1] = 0x00; + + fb_blit (); + } + else + { + // new + + fb_current_loc = (fb_cursor_y * BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE) + (fb_cursor_x * BYTES_FOR_EACH_ELEMENT); + + if (fb_cursor_x < COLUMNS_IN_LINE - 1) + { + fb_cursor_x = fb_cursor_x + 1; + } + else + { + fb_cursor_x = 0; + + if (fb_cursor_y < LINES - 1) + { + fb_cursor_y++; + } + else + { + fb_scroll_down (); + fb_current_loc = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); + } + } + + fb_con[fb_current_loc] = str[i]; + fb_con[fb_current_loc + 1] = fb_color; + + vidptr[fb_current_loc] = str[i]; + vidptr[fb_current_loc + 1] = fb_color; + + fb_current_loc = fb_current_loc + 2; + } + i++; + + fb_move_cursor ((fb_cursor_x + 1) + fb_cursor_y * COLUMNS_IN_LINE); + + /* show cursor line on next position */ + vidptr[fb_current_loc] = '_'; + vidptr[fb_current_loc + 1] = fb_color; + } +} + +void kprint_newline(void) +{ + uint16_t line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE; + + /* delete cursor line */ + vidptr[fb_current_loc] = ' '; + vidptr[fb_current_loc + 1] = fb_color; + + if (fb_cursor_y < LINES - 1) + { + // fb_current_loc = fb_current_loc + (line_size - (fb_current_loc % line_size)); + + fb_current_loc = fb_current_loc + (COLUMNS_IN_LINE - fb_cursor_x) * BYTES_FOR_EACH_ELEMENT; + fb_cursor_y++; + fb_cursor_x = 0; + + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); + + fb_blit (); + + /* show cursor line on next position */ + vidptr[fb_current_loc ] = '_'; + vidptr[fb_current_loc + 1] = fb_color; + } + else + { + fb_scroll_down (); + fb_current_loc = SCREENSIZE - line_size; + fb_cursor_x = 0; + } +} + +void fb_scroll_down (void) +{ + uint16_t old_pos = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE, new_pos = 0; + uint16_t x_pos; + uint16_t y_pos; + + for (y_pos = 1; y_pos < LINES; y_pos++) + { + for (x_pos = 0; x_pos < COLUMNS_IN_LINE; x_pos++) + { + fb_con[new_pos++] = fb_con[old_pos++]; + fb_con[new_pos++] = fb_con[old_pos++]; + } + } + + new_pos = SCREENSIZE - (BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE); + /* clear last line */ + for (x_pos = 0; x_pos < COLUMNS_IN_LINE; x_pos++) + { + fb_con[new_pos++] = ' '; + fb_con[new_pos++] = 0x00; + } + + fb_blit (); +} + +void fb_blit (void) +{ + uint16_t i = 0, j = 0; + + while (i < SCREENSIZE) + { + vidptr[i++] = fb_con[j++]; + /* vidptr[i++] = fb_con[j++]; */ + } +} + +void fb_clear_screen(void) +{ + uint16_t i = 0; + while (i < SCREENSIZE) { + vidptr[i++] = ' '; + vidptr[i++] = 0x00; + } + + fb_cursor_x = 0; fb_cursor_y = 0; + fb_current_loc = 0; + + fb_clear_con (); + + fb_move_cursor (fb_cursor_x + fb_cursor_y * COLUMNS_IN_LINE); +} + +void fb_clear_con (void) +{ + uint16_t i = 0; + while (i < SCREENSIZE) { + fb_con[i++] = ' '; + fb_con[i++] = 0x00; + } +} + +void fb_set_color (unsigned char forecolor, unsigned char backcolor) +{ + fb_color = (backcolor << 4) | (forecolor & 0x0F); +} + +void fb_get_cursor (uint32_t *x, uint32_t *y) +{ + *x = fb_cursor_x; + *y = fb_cursor_y; +} + +void fb_set_cursor (uint32_t x, uint32_t y) +{ + fb_move_cursor (y * COLUMNS_IN_LINE + x); + + fb_cursor_x = x; + fb_cursor_y = y; +} + +void div_by_zero_handler_main (void) +{ + fb_set_color (FB_RED, FB_BLACK); + kprint ("KERNEL PANIC! division by ZERO"); kprint_newline (); + fb_set_color (FB_WHITE, FB_BLACK); + + outb (0x20, 0x20); +} + +void div_by_zero () +{ + uint8_t i; + + i = 23 / 0; +} + +void kshell (uint32_t argument) +{ + uint8_t ch; + uint8_t command[256]; + uint8_t command_ind = 0; + + command[0] = '\0'; + + uint8_t input[256]; + uint8_t input_ind = 0; + + input[0] = '\0'; + + uint32_t i, page_start, page_end, pid; + + kprint ("Welcome to ksh, the kernel shell."); kprint_newline(); + + while (1) + { + fb_set_color (FB_WHITE, FB_BLACK); + kprint ("help = list commands "); + fb_set_color (FB_LIGHT_BLUE, FB_BLACK); + kprint ("user"); + fb_set_color (FB_GREEN, FB_BLACK); + kprint ("@"); + fb_set_color (FB_RED, FB_BLACK); + kprint ("ksh> "); + fb_set_color (FB_WHITE, FB_BLACK); + command_ind = 0; + command[0] = '\0'; + + kshell_loop: + ch = getch (); // blocking call + if (ch != '\r' && ch != '\b') + { + command[command_ind] = ch; + if (command_ind < 256) + { + command_ind++; + goto kshell_loop; + } + } + else + { + if (ch == '\r') + { + // return char, check if command + command[command_ind] = '\0'; + + if (strcmp (command, "tasks") == 0) + { + run_threads (); + } + + if (strcmp (command, "btasks") == 0) + { + run_threads_background (); + } + + if (strcmp (command, "page") == 0) + { + kprint ("start page block? "); + command_ind = 0; + + input_ind = 0; input[0] = '\0'; + + page_loop: + ch = getch (); // blocking call + if (ch != '\r' && ch != '\b') + { + input[input_ind] = ch; + if (input_ind < 256) + { + input_ind++; + goto page_loop; + } + } + input[input_ind] = '\0'; + page_start = atoi (input); + page_end = page_start + 23; + + for (i = page_start; i <= page_end; i++) + { + pmem_show_page (i); + } + } + + if (strcmp (command, "loopmark") == 0) + { + loop_mark (); + } + + if (strcmp (command, "threads") == 0) + { + thread_show_info (); + } + + if (strcmp (command, "kill") == 0) + { + kprint ("pid? "); + command_ind = 0; + + input_ind = 0; input[0] = '\0'; + + kill_loop: + ch = getch (); // blocking call + if (ch != '\r' && ch != '\b') + { + input[input_ind] = ch; + if (input_ind < 256) + { + input_ind++; + goto kill_loop; + } + } + input[input_ind] = '\0'; + pid = atoi (input); + + if (thread_kill (pid) == 0) + { + kprint ("thread killed!"); kprint_newline (); + } + } + + if (strcmp (command, "help") == 0) + { + kprint ("commands: page [list memory pages], loopmark [simple benchmark]"); kprint_newline (); + kprint ("tasks [multithreading demo], btasks [background threads demo], threads [show number of running threads]"); kprint_newline (); + kprint ("kill [kill thread]"); kprint_newline (); + } + } + else + { + // backspace + + if (command_ind > 0) + { + command_ind = command_ind - 1; + command[command_ind] = '\0'; + goto kshell_loop; + } + } + } + } +} + +void run_kshell (void) +{ + uint8_t *ksh_base = (uint8_t *) kmalloc (4096 * 2); + uint8_t *ksh_context = (uint8_t *) kmalloc (4096); + uint8_t *ksh_stack = (uint8_t *) kmalloc (4096 * 2); + + thread_init(ksh_base); + thread_create(ksh_context, ksh_stack, 4096 * 2, (uint32_t)kshell, 0x61, "kshell"); +} + +void keyboard_handler(registers_t* regs) +{ + uint8_t status; + uint8_t keycode; + uint8_t ch[2]; + uint8_t pressed = 0; + + /* debug */ + uint32_t i; + + status = inb(KEYBOARD_STATUS_PORT); + /* Lowest bit of status will be set if buffer is not empty */ + if (status & 0x01) { + keycode = inb(KEYBOARD_DATA_PORT); + if(keycode < 0) + return; + + if(keycode == ENTER_KEY_CODE) { + kprint_newline(); + keyboard_ch = '\r'; + return; + } + + if (keycode == '\b') + { + // backspace + } + + if (keycode & 0x80) + { + pressed = 0; + } + else + { + pressed = 1; + } + + if (keycode == KEY_SCAN_LEFT_SHIFT || keycode == KEY_SCAN_RIGHT_SHIFT) + { + /* uppercase table */ + keyboard_shift = 1; + } + + if (keycode == KEY_SCAN_LEFT_SHIFT_RELEASED || keycode == KEY_SCAN_RIGHT_SHIFT_RELEASED) + { + /* lowercase table */ + keyboard_shift = 0; + } + + if (pressed == 1) + { + if (keyboard_shift == 1) + { + ch[0] = keyboard_shift_map[(unsigned char) keycode]; + + } + else + { + ch[0] = keyboard_map[(unsigned char) keycode]; + } + + ch[1] = '\0'; + kprint (ch); + + keyboard_ch = ch[0]; + } + } +} + +uint32_t clock (void) +{ + return (clock_ticks); +} + +void kdelay (uint32_t ticks) +{ + uint32_t end_ticks = clock () + ticks; + + while (clock () < end_ticks) + { + } +} + + +loop_mark () +{ + /* simple loop "benchmark" */ + + uint32_t start_time, end_time, loop_time, i, j, k; + uint32_t loop_max = 20000; + uint32_t loops_per_sec; + + kprint ("loop mark: "); + + start_time = clock (); + + for (i = 1; i <= loop_max; i++) + { + for (j = 1; j <= loop_max; j++) + { + + } + } + + end_time = clock (); + loop_time = end_time - start_time; + + loops_per_sec = (loop_max * loop_max) / (loop_time / TIMER_FREQ); + + kprint_int (loop_time, 10); kprint_newline (); + kprint ("loops per sec: "); kprint_int (loops_per_sec, 10); kprint_newline (); kprint_newline (); +} + + +void timerHandler(registers_t* regs) +{ + clock_ticks++; + + thread_schedule (regs); + + // kprint ("clock: "); kprint_int (clock_ticks, 10); kprint_newline (); +} + +void thread_a(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + uint8_t data[20]; + + thread_set_priority (5); // increase thread priority + for(;;) + { + ticks = clock (); + kprint ("thread A: "); kprint_int (ticks, 10); kprint_newline (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + + if (message_read (&data, 0) == 0) + { + // end signal -> EXIT + fb_set_color (FB_RED, FB_BLACK); + kprint ("thread A: got SHUTDOWN MESSAGE: "); kprint (data); kprint_newline(); + + if (strcmp (data, "kill") == 0) + { + fb_set_color (FB_WHITE, FB_BLACK); + thread_exit (0); + } + } + } +} + +void thread_b(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + thread_set_priority (-10); // decrease thread priority + for(;;) + { + ticks = clock (); + kprint ("thread B: "); kprint_int (ticks, 10); kprint_newline (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } +} + +void thread_c(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + // normal priority = 0 + for(;;) + { + ticks = clock (); + kprint ("thread C: "); kprint_int (ticks, 10); kprint_newline (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } +} + +void run_threads (void) +{ + uint32_t shutdown_thread = clock () + 5000; + + uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); + + uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); + + uint8_t *thread_c_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_c_stack = (uint8_t *) kmalloc (4096); + + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a, 0, "a-print"); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b, 1, "b-print"); + thread_create(thread_c_context, thread_c_stack, 4096, (uint32_t)thread_c, 2, "c-print"); + + while (clock () < shutdown_thread) + { + kdelay (100); + } + message_send ("kill", 0, 5); +} + + +void thread_a_backgr(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + for(;;) + { + ticks = clock (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } +} + +void thread_b_backgr(uint32_t argument) +{ + uint32_t ticks; + uint32_t ticks_max = clock () + 10000; + + for(;;) + { + ticks = clock (); + kdelay (5); + + if (ticks >= ticks_max) thread_exit (0); + } +} + +void run_threads_background (void) +{ + uint8_t *thread_a_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_a_stack = (uint8_t *) kmalloc (4096); + + uint8_t *thread_b_context = (uint8_t *) kmalloc (4096); + uint8_t *thread_b_stack = (uint8_t *) kmalloc (4096); + + thread_create(thread_a_context, thread_a_stack, 4096, (uint32_t)thread_a_backgr, 0, "a"); + thread_create(thread_b_context, thread_b_stack, 4096, (uint32_t)thread_b_backgr, 1, "b"); +} + +void kmain (multiboot_info_t* mbt, unsigned int magic) +{ + set_gdt (); /* init memory GDT */ + + idt_install(); //set up IDT + interrupts_init(); //set up callback table + pic_init(); //set up PIC + interrupts_enable(); + interrupts_registerHandler(IRQ_TIMER, timerHandler); + interrupts_registerHandler(IRQ_KEYBOARD, keyboard_handler); + pit_init(100); + + + + + // vmem_paging (); /* switch paging on */ + + multiboot_memory_map_t* mmap = mbt->mmap_addr; + uint32_t mem_start, mem_end; + uint32_t mem_type; + uint8_t mem_found = 0; + int16_t mem; + + uint32_t pages_free; + uint64_t ram_free; + + fb_clear_screen(); + + fb_set_color(FB_GREEN, FB_BLACK); + kprint ("level 0: RUN"); kprint_newline (); + kprint ("level 1: memory"); kprint_newline (); + + pmem_init_bitmap (); + + while(mmap < mbt->mmap_addr + mbt->mmap_length) + { + mem_start = mmap->base_addr; + mem_end = mem_start + mmap->length; + mem_type = mmap->type; + + if (mem_type == 1) + { + kprint ("RAM at "); kprint_int ((uint32_t) mmap->base_addr, 16); + + if (mmap->length < 1024 * 1024) + { + kprint (" size: "); kprint_int ((uint32_t) mmap->length, 10); kprint (" bytes "); + } + else + { + kprint (" size: "); kprint_int ((uint32_t) mmap->length / (1024 * 1024), 10); kprint (" MB "); + } + kprint ("type: "); kprint_int ((uint32_t) mmap->type, 16); + + if (mem_start >= mem_start_address) + { + mem_end_address = mem_end; + mem_found = 1; + + mem = pmem_set_bitmap (mem_use_address, mem_end, FREE); + if (mem == MEM_ERR_OK) + { + kprint (" found base"); + } + else + { + kprint (" MEMORY ERROR: "); + + if (mem == MEM_ERR_RANGE) + { + kprint ("range"); + } + + if (mem == MEM_ERR_BAD_MEM || mem == MEM_ERR_DOUBLE_FREE) + { + kprint ("alloc error"); + } + } + kprint_newline (); + } + else + { + if (mem_start == 0) + { + mem_start = (uint32_t) 0x1000; /* skip first page */ + } + + mem = pmem_set_bitmap (mem_start, mem_end, FREE); + if (mem == MEM_ERR_OK) + { + kprint (" free"); + } + else + { + kprint (" MEMORY ERROR: "); + + if (mem == MEM_ERR_RANGE) + { + kprint ("range"); + } + + if (mem == MEM_ERR_BAD_MEM || mem == MEM_ERR_DOUBLE_FREE) + { + kprint ("alloc error"); + } + } + + kprint_newline (); + } + } + + mmap = (multiboot_memory_map_t*) ( (uint32_t)mmap + mmap->size + sizeof(mmap->size) ); + } + + pmem_set_first_page (); /* so no null pointer for free mem block can exist */ + + /* + mem = pmem_set_bitmap ((uint32_t) 0xF00000, (uint32_t) 0xFFFFFF, ALLOCATE); + if (mem != MEM_ERR_OK) + { + kprint ("MEMORY ERROR: mark reserved"); + kprint_newline (); + } + */ + + pages_free = pmem_count_free_pages (); + ram_free = (pages_free * 4096) / 1024 /1024; + kprint ("free pages: "); kprint_int (pages_free, 10); kprint (" = " ); kprint_int ((uint32_t) ram_free, 10); kprint (" MB"); kprint_newline (); + + kprint_newline (); + kprint ("level 2: interrupts"); kprint_newline (); + + // kb_init(); + pic_unmask_irq(IRQ_KEYBOARD); + + + + kprint ("level 3: keyboard"); kprint_newline (); + kprint_newline (); + + // loop_mark (); + + fb_set_color (FB_RED, FB_BLACK); + kprint ("red"); + fb_set_color (FB_BLUE, FB_BLACK); + kprint (" cube OS "); + fb_set_color(FB_WHITE, FB_BLACK); + + kprint_int (2017, 10); + kprint_newline (); + kprint_newline (); + + uint32_t i; + + /* + for (i = 0; i < 10; i++) + { + pmem_show_page (i); + } + */ + + + /* + uint8_t *buf; + buf = (uint8_t *) kmalloc (100000); + if (buf != NULL) + { + kprint ("mem allocated at "); kprint_int (buf, 16); kprint_newline (); + } + else + { + kprint ("mem allocate failed"); + } + kprint_newline (); + + + if (kfree (buf) != NULL) + { + kprint ("free ERROR!"); kprint_newline (); + } + */ + + kprint ("READY"); kprint_newline (); + + pic_unmask_irq(IRQ_TIMER); + + + // enter_usermode (); + run_kshell(); + + //kshell (0); + + while (1) + { + kdelay (100); + } +} diff --git a/src/kernel/lib.c b/src/kernel/lib.c index fe0f3c3..5adf82c 100644 --- a/src/kernel/lib.c +++ b/src/kernel/lib.c @@ -1,77 +1,76 @@ /* lib.c - some helper functions */ #include "types.h" -#include "protos.h" extern uint8_t keyboard_ch; void strreverse(uint8_t* begin, uint8_t* end) { - + uint8_t aux; - + while(end>begin) - + aux=*end, *end--=*begin, *begin++=aux; - + } - + void itoa(int32_t value, uint8_t* str, int32_t base) { - + static uint8_t num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - + uint8_t* wstr=str; - + int32_t sign; - + // Validate base - + if (base<2 || base>35){ *wstr='\0'; return; } - + // Take care of sign - + if ((sign=value) < 0) value = -value; - + // Conversion. Number is reversed. - + do *wstr++ = num[value%base]; while(value/=base); - + if(sign<0) *wstr++='-'; - + *wstr='\0'; - + // Reverse string - + strreverse(str,wstr-1); } - + // A utility function to check whether x is numeric uint8_t isNumericChar(uint8_t x) { return (x >= '0' && x <= '9')? TRUE: FALSE; } - + // A simple atoi() function. If the given string contains // any invalid character, then this function returns 0 int32_t atoi(uint8_t *str) { if (*str == NULL) return 0; - + int32_t res = 0; // Initialize result int32_t sign = 1; // Initialize sign as positive int32_t i = 0; // Initialize index of first digit - + // If number is negative, then update sign if (str[0] == '-') { sign = -1; i++; // Also update index of first digit } - + // Iterate through all digits of input string and update result for (; str[i] != '\0'; ++i) { @@ -80,17 +79,17 @@ int32_t atoi(uint8_t *str) // to error stream res = res*10 + str[i] - '0'; } - + // Return result with sign return sign*res; } - - + + void kprint_int (int32_t n, int32_t base) { char str[256]; - - itoa (n, (uint8_t *) str, base); + + itoa (n, str, base); kprint (str); } @@ -115,28 +114,28 @@ int16_t strcmp (const uint8_t * str1, const uint8_t * str2) uint32_t strlen (const uint8_t *str) { uint32_t slen = 0; - + while (*str != '\0') { slen++; str++; } - + return (slen); } uint8_t *strcpy (uint8_t *dest, uint8_t *src) { uint32_t i = 0; - + while (src[i] != '\0') { dest[i] = src[i]; i++; } - + dest[i] = '\0'; - + return (dest); } @@ -149,62 +148,65 @@ uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) i++; if (i == len) break; } - + dest[i] = '\0'; - + return (dest); } -uint8_t *memcpy (uint8_t *dest, uint8_t *src, uint32_t count) +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) { uint32_t i; - + uint8_t *src_ptr = src; uint8_t *dest_ptr = dest; - + while (count-- > 0) { *dest_ptr++ = *src_ptr++; } - + return (dest); } uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) { uint32_t i; - + for (i = 1; i <= count; i++) { dest[i] = val; } - + return (dest); } uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count) { uint32_t i; - + for (i = 1; i <= count; i++) { dest[i] = val; } - + return (dest); } uint8_t getch (void) { uint8_t ch; - + while (keyboard_ch == NULL) { kdelay (10); } - + ch = keyboard_ch; keyboard_ch = NULL; // reset buffer, kind of hack I know ;) return (ch); } + + + diff --git a/src/kernel/lib.c~ b/src/kernel/lib.c~ new file mode 100644 index 0000000..a132222 --- /dev/null +++ b/src/kernel/lib.c~ @@ -0,0 +1,212 @@ +/* lib.c - some helper functions */ + +#include "types.h" + +extern uint8_t keyboard_ch; + +void strreverse(uint8_t* begin, uint8_t* end) { + + uint8_t aux; + + while(end>begin) + + aux=*end, *end--=*begin, *begin++=aux; + +} + +void itoa(int32_t value, uint8_t* str, int32_t base) { + + static uint8_t num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + + uint8_t* wstr=str; + + int32_t sign; + + // Validate base + + if (base<2 || base>35){ *wstr='\0'; return; } + + + // Take care of sign + + if ((sign=value) < 0) value = -value; + + + // Conversion. Number is reversed. + + do *wstr++ = num[value%base]; while(value/=base); + + if(sign<0) *wstr++='-'; + + *wstr='\0'; + + + // Reverse string + + + strreverse(str,wstr-1); +} + +// A utility function to check whether x is numeric +uint8_t isNumericChar(uint8_t x) +{ + return (x >= '0' && x <= '9')? TRUE: FALSE; +} + +// A simple atoi() function. If the given string contains +// any invalid character, then this function returns 0 +int32_t atoi(uint8_t *str) +{ + if (*str == NULL) + return 0; + + int32_t res = 0; // Initialize result + int32_t sign = 1; // Initialize sign as positive + int32_t i = 0; // Initialize index of first digit + + // If number is negative, then update sign + if (str[0] == '-') + { + sign = -1; + i++; // Also update index of first digit + } + + // Iterate through all digits of input string and update result + for (; str[i] != '\0'; ++i) + { + if (isNumericChar(str[i]) == FALSE) + return 0; // You may add some lines to write error message + // to error stream + res = res*10 + str[i] - '0'; + } + + // Return result with sign + return sign*res; +} + + +void kprint_int (int32_t n, int32_t base) +{ + char str[256]; + + itoa (n, str, base); + kprint (str); +} + +int16_t strcmp (const uint8_t * str1, const uint8_t * str2) +{ + while (*str1 == *str2) + { + if (*str1 == '\0' || *str2 == '\0') + break; + + str1++; + str2++; + } + + + if (*str1 == '\0' && *str2 == '\0') + return 0; + else + return -1; +} + +uint32_t strlen (const uint8_t *str) +{ + uint32_t slen = 0; + + while (*str != '\0') + { + slen++; + str++; + } + + return (slen); +} + +uint8_t *strcpy (uint8_t *dest, uint8_t *src) +{ + uint32_t i = 0; + + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + } + + dest[i] = '\0'; + + return (dest); +} + +uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) +{ + uint32_t i = 0; + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + if (i = len) break; + } + + dest[i] = '\0'; + + return (dest); +} + +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) +{ + uint32_t i; + + uint8_t *src_ptr = src; + uint8_t *dest_ptr = dest; + + + while (count-- > 0) + { + *dest_ptr++ = *src_ptr++; + } + + return (dest); +} + +uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = val; + } + + return (dest); +} + +uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = val; + } + + return (dest); +} + +uint8_t getch (void) +{ + uint8_t ch; + + while (keyboard_ch == NULL) + { + kdelay (10); + } + + ch = keyboard_ch; + keyboard_ch = NULL; // reset buffer, kind of hack I know ;) + return (ch); +} + + + diff --git a/src/kernel/message.c b/src/kernel/message.c new file mode 100644 index 0000000..eca71c8 --- /dev/null +++ b/src/kernel/message.c @@ -0,0 +1,126 @@ +#include "types.h" +#include "message.h" +#include "interrupts.h" + +struct message *msg_head = NULL; + + +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); +uint32_t thread_get_own_pid (void); + +uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len) +{ + struct message *current_msg = msg_head; + struct message *next; + + uint32_t thread_sender_num; + + interrupts_disable (); + + thread_sender_num = thread_get_own_pid (); + + if (msg_head == NULL) + { + // no message stored, allocate msg_head + + msg_head = (struct message *) kmalloc (sizeof (struct message)); + if (msg_head == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + msg_head->data_len = len; + msg_head->data = kmalloc (len); + if (msg_head->data == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + memcpy (msg_head->data, message, len); + msg_head->thread_num = thread; + msg_head->data_ready = 1; + msg_head->next = NULL; + msg_head->thread_sender_num = thread_sender_num; + } + else + { + // search next empty message in list + next = current_msg->next; + while (next->next != NULL) + { + next = next->next; + } + + next = (struct message *) kmalloc (sizeof (struct message)); + if (next == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + next->data_len = len; + next->data = kmalloc (len); + if (next->data == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + memcpy (next->data, message, len); + next->thread_num = thread; + next->data_ready = 1; + next->next = NULL; + msg_head->thread_sender_num = thread_sender_num; + } + interrupts_enable (); + return (0); +} + +uint8_t message_read (uint8_t *message, uint32_t *thread_sender_pid) +{ + struct message *current_msg = msg_head; + struct message *next; + + uint32_t thread_pid; + + interrupts_disable (); + + thread_pid = thread_get_own_pid (); + + next = current_msg->next; + + if (current_msg->thread_num == thread_pid) + { + // thread number matches, copy data + + memcpy (message, current_msg->data, current_msg->data_len); + thread_sender_pid = current_msg->thread_sender_num; + interrupts_enable (); + return (0); + } + else + { + while (next->next != NULL) + { + if (next->thread_num == thread_pid) + { + // thread number matches, copy data + memcpy (message, next->data, next->data_len); + thread_sender_pid = current_msg->thread_sender_num; + interrupts_enable (); + return (0); + } + next = next->next; + } + } + + // message matching thread number not found + interrupts_enable (); + return (1); +} diff --git a/src/kernel/message.c~ b/src/kernel/message.c~ new file mode 100644 index 0000000..1895913 --- /dev/null +++ b/src/kernel/message.c~ @@ -0,0 +1,112 @@ +#include "types.h" +#include "message.h" +#include "interrupts.h" + +struct message *msg_head = NULL; + + +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); + +uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len) +{ + struct message *current_msg = msg_head; + struct message *next; + + interrupts_disable (); + + if (msg_head == NULL) + { + // no message stored, allocate msg_head + + msg_head = (struct message *) kmalloc (sizeof (struct message)); + if (msg_head == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + msg_head->data_len = len; + msg_head->data = kmalloc (len); + if (msg_head->data == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + memcpy (msg_head->data, message, len); + msg_head->thread_num = thread; + msg_head->data_ready = 1; + msg_head->next = NULL; + } + else + { + // search next empty message in list + next = current_msg->next; + while (next->next != NULL) + { + next = next->next; + } + + next = (struct message *) kmalloc (sizeof (struct message)); + if (next == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + next->data_len = len; + next->data = kmalloc (len); + if (next->data == NULL) + { + // error nomem + interrupts_enable (); + return (1); + } + + memcpy (next->data, message, len); + next->thread_num = thread; + next->data_ready = 1; + next->next = NULL; + } + interrupts_enable (); + return (0); +} + +uint8_t message_read (uint8_t *message, uint32_t thread) +{ + struct message *current_msg = msg_head; + struct message *next; + + interrupts_disable (); + next = current_msg->next; + + if (current_msg->thread_num == thread) + { + // tread number matches, copy data + + memcpy (message, current_msg->data, current_msg->data_len); + interrupts_enable (); + return (0); + } + else + { + while (next->next != NULL) + { + if (next->thread_num == thread) + { + // tread number matches, copy data + memcpy (message, next->data, next->data_len); + interrupts_enable (); + return (0); + } + next = next->next; + } + } + + // message matching thread number not found + interrupts_enable (); + return (1); +} diff --git a/src/kernel/message.h b/src/kernel/message.h new file mode 100644 index 0000000..249d6d9 --- /dev/null +++ b/src/kernel/message.h @@ -0,0 +1,16 @@ +// message definitions + +#define MSG_EVENT_KEY 0 +#define MSG_EVENT_KILL_THREAD 1 +#define MSG_ACK 2 +#define MSG_ERROR 3 + +struct message +{ + uint8_t *data; // buffer for message + uint32_t data_len; // message length + uint8_t data_ready; // 1 = data not written yet, 0 = data ready, message is in buffer + uint32_t thread_num; + uint32_t thread_sender_num; + struct message *next; +}; diff --git a/src/kernel/message.h~ b/src/kernel/message.h~ new file mode 100644 index 0000000..5b9e747 --- /dev/null +++ b/src/kernel/message.h~ @@ -0,0 +1,11 @@ +// message definitions + +struct message +{ + uint8_t *data; // buffer for message + uint32_t data_len; // message length + uint8_t data_ready; // 1 = data not written yet, 0 = data ready, message is in buffer + uint32_t thread_num; + struct message *next; +}; + diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c index b4b47c2..3b88d0e 100644 --- a/src/kernel/physmem.c +++ b/src/kernel/physmem.c @@ -1,32 +1,31 @@ // #include #include "types.h" -#include "protos.h" uint32_t physmem_pages[PAGES]; void set_bit (uint32_t *ptr, uint16_t bit) { uint32_t ret = *ptr; - + ret |= 1 << bit; - + *ptr = ret; } void clear_bit (uint32_t *ptr, uint16_t bit) { uint32_t ret = *ptr; - + ret &= ~(1 << bit); - + *ptr = ret; } uint16_t get_bit (uint32_t n, uint16_t bit) { uint16_t bitset; - + bitset = (n >> bit) & 1; return (bitset); } @@ -34,7 +33,7 @@ uint16_t get_bit (uint32_t n, uint16_t bit) void pmem_init_bitmap () { uint32_t page; - + for (page = 0; page < PAGES; page++) { physmem_pages[page] = 0xFFFFFFFF; /* mark as reserved */ @@ -57,19 +56,19 @@ uint32_t pmem_get_page_from_address (uint32_t address) return (page); } - + int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t state) { /* set bits in bitmap */ uint32_t start_p, end_p; int16_t start_b, end_b; - + uint32_t p; int16_t b; - + uint32_t page_start = pmem_get_page_from_address (start); uint32_t page_end; - - + + if (pages != 0) { page_end = page_start + pages - 1; @@ -78,18 +77,18 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s { page_end = pmem_get_page_from_address (end); } - + // kprint ("start address: "); kprint_int (start, 10); kprint_newline (); // kprint ("start page: "); kprint_int (page_start, 10); kprint_newline (); - + // kprint ("end address: "); kprint_int (end, 10); kprint_newline (); // kprint ("end page: "); kprint_int (page_end, 10); kprint_newline (); - + if (page_end > MEM_BLOCKS) { return (MEM_ERR_RANGE); } - + if (page_start >= PAGE_BITS) { start_p = page_start / PAGE_BITS; @@ -100,7 +99,7 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s start_p = 0; start_b = page_start; } - + if (page_end >= PAGE_BITS) { end_p = page_end / PAGE_BITS; @@ -111,7 +110,7 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s end_p = 0; end_b = page_end; } - + if (end_p > start_p) { for (p = start_p; p <= end_p; p++) @@ -203,27 +202,27 @@ int16_t pmem_set_bitmap (uint32_t start, uint32_t end, uint32_t pages, uint8_t s clear_bit (&physmem_pages[start_p], b); } } - + } } return (MEM_ERR_OK); } - + int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pages) { /* return pointers to free memory range */ int8_t found_start = 0; - + uint32_t p, i; uint16_t b; uint32_t start_free, end_free; size_t mempages, mempages_free = 0; - - + + if (size > MEM_BLOCK_SIZE) { mempages = size / MEM_BLOCK_SIZE; - + if (size % MEM_BLOCK_SIZE != 0) { mempages++; @@ -234,9 +233,9 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa mempages = 1; } *pages = mempages; - + // kprint ("pmem_get_free: mempages = "); kprint_int (mempages, 10); kprint_newline (); - + for (p = 0; p < PAGES; p++) { for (b = 0; b < PAGE_BITS; b++) @@ -248,7 +247,7 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa found_start = 1; start_free = PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE; mempages_free = 1; - + if (mempages == 1) { *start = start_free; @@ -262,7 +261,7 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa if (mempages_free >= mempages) { end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; - + *start = start_free; *end = end_free; return (MEM_ERR_OK); @@ -274,7 +273,7 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa if (mempages_free >= mempages) { end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; - + *start = start_free; *end = end_free; return (MEM_ERR_OK); @@ -288,12 +287,12 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa if (mempages_free >= mempages) { end_free = (PAGE_BITS * MEM_BLOCK_SIZE * p + b * MEM_BLOCK_SIZE) - 1; - + *start = start_free; *end = end_free; return (MEM_ERR_OK); } - + } return (MEM_ERR_NOMEM); } @@ -301,11 +300,11 @@ int16_t pmem_get_free (size_t size, uint32_t *start, uint32_t *end, uint32_t *pa uint32_t pmem_count_free_pages (void) { /* for informantion only: count the free pages */ - + uint32_t pages = 0; uint32_t p, i; uint16_t b; - + for (p = 0; p < PAGES; p++) { for (b = 0; b < PAGE_BITS; b++) @@ -322,9 +321,9 @@ uint32_t pmem_count_free_pages (void) uint32_t pmem_get_free_ram_info (void) { uint32_t free_mem_kbytes; - + free_mem_kbytes = pmem_count_free_pages () * (MEM_BLOCK_SIZE / 1024); - + return (free_mem_kbytes); } @@ -335,18 +334,18 @@ void *kmalloc (size_t size) size_t new_size; uint32_t pages; new_size = size + sizeof (size_t); - + // kprint ("kmalloc: allocate "); kprint_int ((uint32_t) new_size, 10); kprint_newline (); - + if (pmem_get_free (new_size, &start_free, &end_free, &pages) == MEM_ERR_OK) { // kprint ("free mem found"); kprint_newline (); - + if (pmem_set_bitmap (start_free, end_free, pages, ALLOCATE) == MEM_ERR_OK) { - ptr = &start_free; + ptr = start_free; *ptr = pages; /* store size of memoryin pages */ - return ((void *) start_free + sizeof (size_t)); + return (start_free + sizeof (size_t)); } else { @@ -365,14 +364,14 @@ uint32_t kfree (uint32_t address) uint32_t *ptr; uint32_t end_mem; size_t pages; - - ptr = (uint32_t *) address - sizeof (size_t); + + ptr = address - sizeof (size_t); pages = *ptr; - + // kprint ("kfree: deallocate "); kprint_int ((uint32_t) pages, 10); kprint_newline (); - + end_mem = (address - sizeof (size_t)) + pages * MEM_BLOCK_SIZE; - + if (pmem_set_bitmap (address - sizeof (size_t), end_mem, pages, FREE) == MEM_ERR_OK) { return (NULL); @@ -387,22 +386,22 @@ void pmem_show_page (uint32_t page) { uint16_t b; uint32_t cursor_x, cursor_y; - + if (page >= PAGES) { // ERROR out of range, return return; } - + kprint_int (page * PAGE_BITS * MEM_BLOCK_SIZE, 16); fb_get_cursor (&cursor_x, &cursor_y); - + if (cursor_x < 15) { cursor_x = 15; fb_set_cursor (cursor_x, cursor_y); } - + for (b = 0; b < PAGE_BITS; b++) { if (get_bit (physmem_pages[page], b) == FREE) @@ -417,8 +416,8 @@ void pmem_show_page (uint32_t page) kprint_newline (); } - - + + /* void main () { diff --git a/src/kernel/protos.h b/src/kernel/protos.h index fabb083..7ceb00f 100644 --- a/src/kernel/protos.h +++ b/src/kernel/protos.h @@ -2,23 +2,12 @@ // // -// for registers_t define -#include "registers.h" - -// kernel.c void fb_blit (void); void fb_scroll_down (void); void fb_move_cursor (unsigned short pos); void fb_clear_con (void); + void loop_mark (); -void kprint (const char *str); -void kprint_newline (void); -void kdelay (uint32_t ticks); -void gdt_flush (void); -void tss_flush (void); -void fb_get_cursor (uint32_t *x, uint32_t *y); -void fb_set_cursor (uint32_t x, uint32_t y); -uint32_t clock (void); // lib.c void strreverse(uint8_t* begin, uint8_t* end); @@ -30,12 +19,10 @@ int16_t strcmp (const uint8_t * str1, const uint8_t * str2); uint32_t strlen (const uint8_t *str); uint8_t *strcpy (uint8_t *dest, uint8_t *src); uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len); -uint8_t *memcpy (uint8_t *dest, uint8_t *src, uint32_t count); +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count); uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count); uint8_t getch (void); -void run_threads (void); -void run_threads_background (void); // physmem.c void pmem_init_bitmap (); @@ -66,23 +53,3 @@ void switch_to_user_mode (); void init_elf (void* image); void init_multitasking(struct multiboot_info* mb_info); void get_thread_input_stream (uint8_t *ch); - -// message.c -uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len); -uint8_t message_read (uint8_t *message, uint32_t thread); - -// gdt.c -void set_gdt (void); -void set_kernel_stack(uint32_t stack); - -// pic.c -void pic_init(void); -void pic_unmask_irq(int intNo); -void pic_mask_irq(int intNo); -void pic_notify(int intNo); - -// pit.c -void pit_init(uint32_t frequency); - -// physmem.c -void *kmalloc (size_t size); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c new file mode 100644 index 0000000..f97bd25 --- /dev/null +++ b/src/kernel/syscall.c @@ -0,0 +1,97 @@ +// syscall.c -- Defines the implementation of a system call system. +// Written for JamesM's kernel development tutorials. + +#include "interrupts.h" +#include "pic.h" +#include "syscall.h" + +#include "types.h" + +void kprint_int (int32_t n, int32_t base); +void kprint (const char *str); +void kprint_newline (void); +void fb_set_color (unsigned char forecolor, unsigned char backcolor); +uint8_t getch (void); +uint32_t thread_kill (uint32_t pid); +void pmem_show_page (uint32_t page); +uint32_t pmem_get_free_ram_info (void); +void kdelay (uint32_t ticks); +void get_thread_input_stream (uint8_t ch); +uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len); +uint8_t message_read (uint8_t *message, uint32_t *thread_sender_pid); +void thread_exit (uint32_t ret_code); + +static void syscall_handler(registers_t *regs); + +DEFN_SYSCALL1(kprint, 0, const char*); +DEFN_SYSCALL0(kprint_newline, 1); +DEFN_SYSCALL2(kprint_int, 2, int, int); +DEFN_SYSCALL2(fb_set_color, 3, char, char); +DEFN_SYSCALL0(getch, 4); +DEFN_SYSCALL1(thread_kill, 5, uint32_t); +DEFN_SYSCALL1(pmem_show_page, 6, uint32_t); +DEFN_SYSCALL0(pmem_get_free_ram_info, 7); +DEFN_SYSCALL1(kdelay, 8, uint32_t); +DEFN_SYSCALL1(get_thread_input_stream, 9, uint8_t); +DEFN_SYSCALL3(message_send, 10, uint8_t*, uint32_t, uint32_t); +DEFN_SYSCALL2(message_read, 11, uint8_t*, uint32_t); +DEFN_SYSCALL1(thread_exit, 12, uint32_t); + +static void *syscalls[13] = +{ + &kprint, + &kprint_newline, + &kprint_int, + &fb_set_color, + &getch, + &thread_kill, + &pmem_show_page, + &pmem_get_free_ram_info, + &kdelay, + &get_thread_input_stream, + &message_send, + &message_read, + &thread_exit +}; +uint32_t num_syscalls = 13; + +void initialise_syscalls() +{ + // Register our syscall handler. + interrupts_registerHandler (IRQ_SYSCALL, syscall_handler); +} + +void syscall_handler(registers_t *regs) +{ + // Firstly, check if the requested syscall number is valid. + // The syscall number is found in EAX. + + // kprint ("syscall"); kprint_newline (); + + if (regs->eax >= num_syscalls) + return; + + + + // Get the required syscall location. + void *location = syscalls[regs->eax]; + + // We don't know how many parameters the function wants, so we just + // push them all onto the stack in the correct order. The function will + // use all the parameters it wants, and we can pop them all back off afterwards. + int ret; + asm volatile (" \ + push %1; \ + push %2; \ + push %3; \ + push %4; \ + push %5; \ + call *%6; \ + pop %%ebx; \ + pop %%ebx; \ + pop %%ebx; \ + pop %%ebx; \ + pop %%ebx; \ + " : "=a" (ret) : "r" (regs->edi), "r" (regs->esi), "r" (regs->edx), "r" (regs->ecx), "r" (regs->ebx), "r" (location)); + regs->eax = ret; +} diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h new file mode 100644 index 0000000..63f6aeb --- /dev/null +++ b/src/kernel/syscall.h @@ -0,0 +1,79 @@ +// syscall.h -- Defines the interface for and structures relating to the syscall dispatch system. +// Written for JamesM's kernel development tutorials. + +#ifndef SYSCALL_H +#define SYSCALL_H + +#include "types.h" + +void initialise_syscalls(); + +#define DECL_SYSCALL0(fn) int syscall_##fn(); +#define DECL_SYSCALL1(fn,p1) int syscall_##fn(p1); +#define DECL_SYSCALL2(fn,p1,p2) int syscall_##fn(p1,p2); +#define DECL_SYSCALL3(fn,p1,p2,p3) int syscall_##fn(p1,p2,p3); +#define DECL_SYSCALL4(fn,p1,p2,p3,p4) int syscall_##fn(p1,p2,p3,p4); +#define DECL_SYSCALL5(fn,p1,p2,p3,p4,p5) int syscall_##fn(p1,p2,p3,p4,p5); + +#define DEFN_SYSCALL0(fn, num) \ +int syscall_##fn() \ +{ \ + int a; \ + asm volatile("int $0x26" : "=a" (a) : "0" (num)); \ + return a; \ +} + +#define DEFN_SYSCALL1(fn, num, P1) \ +int syscall_##fn(P1 p1) \ +{ \ + int a; \ + asm volatile("int $0x26" : "=a" (a) : "0" (num), "b" ((int)p1)); \ + return a; \ +} + +#define DEFN_SYSCALL2(fn, num, P1, P2) \ +int syscall_##fn(P1 p1, P2 p2) \ +{ \ + int a; \ + asm volatile("int $0x26" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2)); \ + return a; \ +} + +#define DEFN_SYSCALL3(fn, num, P1, P2, P3) \ +int syscall_##fn(P1 p1, P2 p2, P3 p3) \ +{ \ + int a; \ + asm volatile("int $0x26" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2), "d"((int)p3)); \ + return a; \ +} + +#define DEFN_SYSCALL4(fn, num, P1, P2, P3, P4) \ +int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4) \ +{ \ + int a; \ + asm volatile("int $0x26" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2), "d" ((int)p3), "S" ((int)p4)); \ + return a; \ +} + +#define DEFN_SYSCALL5(fn, num) \ +int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \ +{ \ + int a; \ + asm volatile("int $0x26" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2), "d" ((int)p3), "S" ((int)p4), "D" ((int)p5)); \ + return a; \ +} + +DECL_SYSCALL1(kprint, const char*) +DECL_SYSCALL0(kprint_newline) +DECL_SYSCALL2(kprint_int, int, int) +DECL_SYSCALL2(fb_set_color, char, char) +DECL_SYSCALL0(getch); +DECL_SYSCALL1(thread_kill, uint32_t) +DECL_SYSCALL1(pmem_show_page, uint32_t) +DECL_SYSCALL0(pmem_get_free_ram_info) +DECL_SYSCALL1(kdelay, uint32_t) +DECL_SYSCALL1(get_thread_input_stream, uint8_t) +DECL_SYSCALL3(message_send, uint8_t*, uint32_t, uint32_t) +DECL_SYSCALL2(message_read, uint8_t*, uint32_t) +DECL_SYSCALL1(thread_exit, uint32_t) +#endif diff --git a/src/kernel/thread.c b/src/kernel/thread.c index 940361d..c267d4d 100644 --- a/src/kernel/thread.c +++ b/src/kernel/thread.c @@ -6,7 +6,9 @@ #include "types.h" #include "thread.h" #include "interrupts.h" -#include "protos.h" + +#include "multiboot.h" +#include "elf.h" /* #include @@ -15,9 +17,27 @@ #include */ +extern uint8_t keyboard_ch; + + +static uint8_t *task_base; +static uint8_t *task_context; +static uint8_t *task_stack; + + + ThreadContext* listHead = NULL; ThreadContext* currentContext = NULL; +uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len); +uint8_t message_read (uint8_t *message, uint32_t thread); + +// for message read by a runnning thread, inserts current pid +void thread_message_read (uint8_t *message) +{ + message_read (message, currentContext->pid); +} + uint32_t thread_number_of_threads (void) { /* get number of current threads */ @@ -41,7 +61,7 @@ void thread_init(uint32_t baseContextAddress) { kprint ("Initializing threading..."); kprint_newline (); listHead = currentContext = (ThreadContext*) baseContextAddress; - memset((uint8_t *) listHead, 0, sizeof(ThreadContext)); + memset(listHead, 0, sizeof(ThreadContext)); /*print_string_static("Base thread context is at "); print_integer_hex((uint32_t)currentContext); print_string_static("\n");*/ @@ -52,7 +72,7 @@ void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackS // interrupts_disable (); ThreadContext* context = (ThreadContext*)contextAddress; - memset((uint8_t *) context, 0, sizeof(ThreadContext)); + memset(context, 0, sizeof(ThreadContext)); context->cs = GDT_KERNEL_CODE; context->ds = GDT_KERNEL_DATA; @@ -88,7 +108,7 @@ void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackS * ... */ uint32_t* stack = (uint32_t*)(stackStart); - memset((uint8_t *) stack, 0, stackSize); + memset(stack, 0, stackSize); stack[stackSize/sizeof(uint32_t) - 1] = arg; //Argument stack[stackSize/sizeof(uint32_t) - 2] = 0; //Return Address @@ -118,8 +138,15 @@ void thread_exit (uint32_t ret_code) /* skip current thread in previous thread->next */ search->next = currentContext->next; - kfree ((uint32_t) currentContext->esp); - kfree ((uint32_t) currentContext); + kfree (currentContext->esp); + kfree (currentContext); +} + +uint32_t thread_get_own_pid (void) +{ + // return a threads own PID + + return (currentContext->pid); } uint32_t thread_kill (uint32_t pid) @@ -346,6 +373,74 @@ void switch_to_user_mode () iret; \ 1: \ "); +} + +void init_elf (void* image) +{ + // FIXME needed: length check + + struct elf_header* header = image; + struct elf_program_header* ph; + int i; + + // check if ELF file + if (header->magic != ELF_MAGIC) { + kprint ("No valid ELF-magic!"); kprint_newline (); + return; + } + // copy to memory location + ph = (struct elf_program_header*) (((char*) image) + header->ph_offset); + for (i = 0; i < header->ph_entry_count; i++, ph++) { + void* dest = (void*) ph->virt_addr; + void* src = ((char*) image) + ph->offset; + + // only load type LOAD + if (ph->type != 1) { + continue; + } + + memset(dest, 0, ph->mem_size); + memcpy(dest, src, ph->file_size); + } + + + // task_base = (uint8_t *) kmalloc (8192 * 2); + task_base = (uint8_t *) kmalloc (8192 * 2); + // task_context = (uint8_t *) kmalloc (4096); + // task_stack = (uint8_t *) kmalloc (4096 * 2); + + task_context = (uint8_t *) kmalloc (8192); + task_stack = (uint8_t *) kmalloc (8192 * 2); + + // thread_init(task_base); + thread_create(task_context, task_stack, 4096 * 2, (uint32_t) header->entry, 0, "task"); +} + +void init_multitasking(struct multiboot_info* mb_info) +{ + if (mb_info->mods_count == 0) { + // no modules found, load build in kshell + + run_kshell (); + } else { + // load module + struct module* modules = mb_info->mods_addr; + int i; + + kprint ("loading module..."); kprint_newline (); + + for (i = 0; i < mb_info->mods_count; i++) { + init_elf((void*) modules[i].mod_start); + } + + } +} + +void get_thread_input_stream (uint8_t *ch) +{ + // uint32_t ret = currentContext->input_stream; + ch = keyboard_ch; + // memcpy (ch, keyboard_ch, 1); } diff --git a/src/kernel/thread.c~ b/src/kernel/thread.c~ new file mode 100644 index 0000000..09691ba --- /dev/null +++ b/src/kernel/thread.c~ @@ -0,0 +1,314 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "types.h" +#include "thread.h" + + +/* +#include +#include +#include +#include +*/ + +ThreadContext* listHead = NULL; +ThreadContext* currentContext = NULL; + +uint32_t thread_number_of_threads (void) +{ + /* get number of current threads */ + uint32_t threads = 0; + ThreadContext* search = listHead; + + while (search->next != NULL) + { + threads++; + search = search->next; + } + + return (threads); +} + +void thread_init(uint32_t baseContextAddress) +{ + kprint ("Initializing threading..."); kprint_newline (); + listHead = currentContext = (ThreadContext*) baseContextAddress; + memset(listHead, 0, sizeof(ThreadContext)); + /*print_string_static("Base thread context is at "); + print_integer_hex((uint32_t)currentContext); + print_string_static("\n");*/ +} + +void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) +{ + ThreadContext* context = (ThreadContext*)contextAddress; + memset(context, 0, sizeof(ThreadContext)); + + context->cs = GDT_KERNEL_CODE; + context->ds = GDT_KERNEL_DATA; + context->es = GDT_KERNEL_DATA; + context->fs = GDT_KERNEL_DATA; + context->gs = GDT_KERNEL_DATA; + context->ss = GDT_KERNEL_DATA; + + context->eflags = thread_getEflags(); + + context->eip = entryPoint; + + context->priority = THREAD_PRIORITY_NORMAL; + context->child_of = currentContext->pid; + context->pid = (uint32_t) (thread_number_of_threads () + 1); + context->request = THREAD_NO_REQUEST; + context->signal = THREAD_BREAK_ALLOWED; + context->next_switch = 0; + + strncpy (context->name, name, THREAD_NAME_LEN); + + /* We are building this stack here: + * ... + * | | + * | Stackframe of Caller | <-- Doesn't exist for now + * |------------------------| + * | Parameter | + * |------------------------| + * | Return Address | <-- Fake return address for now + * ESP --> |------------------------| + * | $foo of called function| <-- Pushed EBP, local variables etc. + * | | + * ... + */ + uint32_t* stack = (uint32_t*)(stackStart); + memset(stack, 0, stackSize); + stack[stackSize/sizeof(uint32_t) - 1] = arg; //Argument + stack[stackSize/sizeof(uint32_t) - 2] = 0; //Return Address + + context->esp = stackStart + stackSize - 2*sizeof(uint32_t); + + + ThreadContext* cur = listHead; + while(cur->next != NULL) + cur = cur->next; + + cur->next = context; +} + +void thread_exit (uint32_t ret_code) +{ + /* exit thread, remove thread from list */ + // ThreadContext* currentContext; + ThreadContext* search = listHead; + + while (search->next != currentContext) + { + search = search->next; + } + + /* skip current thread in previous thread->next */ + search->next = currentContext->next; + + kfree (currentContext->esp); + kfree (currentContext); +} + +uint32_t thread_kill (uint32_t pid) +{ + /* kill thread, remove thread from list */ + uint8_t found_pid = 0; + + ThreadContext* search = listHead; + ThreadContext* kill; + + while (found_pid == 0) + { + if (search->pid == pid) + { + found_pid = 1; + kill = search; + } + else + { + search = search->next; + if (search->next == NULL) + { + // end of threads list, no given pid found + break; + } + } + } + + if (found_pid) + { + search = listHead; + while (search->next != kill) + { + search = search->next; + } + + search->next = kill->next; + + kfree (kill->esp); + kfree (kill); + + return (0); + } + else + { + return (1); + } +} + + +void thread_set_priority (int32_t priority) +{ + currentContext->priority = priority; +} + + + +static inline void interrupts_disable(void) __attribute__((always_inline)); +static inline void interrupts_disable(void) +{ + asm volatile ("cli"); +} + + + +void thread_show_info (void) +{ + /* show thread infos */ + uint8_t run = 1; + uint32_t threads = 0, mthreads = 0; + ThreadContext* search = listHead; + + do + { + kprint ("thread: '"); kprint (search->name); kprint ("' thread pid: "); kprint_int (search->pid, 10); kprint (", priority: "); kprint_int (search->priority, 10); + kprint (", child of: "); kprint_int (search->child_of, 10); kprint_newline (); + threads++; + + if (run == 0 && threads == mthreads) break; + search = search->next; + if (search->next == NULL) mthreads = threads + 1, run = 0; + + } while (1); + kprint ("total threads running: "); kprint_int (threads, 10); kprint_newline (); +} + +uint32_t thread_fair_schedule (void) +{ + /* get number of current threads */ + uint32_t threads = 0; + uint32_t ticks_per_sec = 100; + uint32_t fair_schedule = 0; + ThreadContext* search = listHead; + + while (search->next != NULL) + { + threads++; + search = search->next; + } + + if (threads > 0) + { + fair_schedule = ticks_per_sec / threads; + fair_schedule = ticks_per_sec / fair_schedule; + } + + return (fair_schedule); +} + + +void thread_schedule(registers_t* oldState) +{ + uint8_t do_schedule = 0; + uint32_t fair_schedule = thread_fair_schedule (); + + //print_string_static("Scheduling...\n"); + interrupts_disable(); + + if (fair_schedule > 0) + { + // number of tasks greater as zero -> fair_schedule is not zero + if (currentContext->next_switch == 0) + { + if (currentContext->priority >= 0) + { + currentContext->next_switch = clock () + (fair_schedule * currentContext->priority); + } + else + { + if (currentContext->priority < 0) + { + currentContext->next_switch = clock () + 1; + } + else + { + currentContext->next_switch = clock () + fair_schedule; + } + } + } + else + { + if (currentContext->next_switch <= clock ()) + { + /* time to switch to next task */ + currentContext->next_switch = 0; + do_schedule = 1; + } + } + } + + if (do_schedule == 1) + { + thread_saveContext(oldState); + //print_string_static("Old context saved...\n"); + + ThreadContext* next; + if(currentContext->next == NULL) + next = listHead; + else + next = currentContext->next; + + currentContext = next; + /*print_string_static("New thread context is at "); + print_integer_hex((uint32_t)currentContext); + print_string_static("\n");*/ + + thread_switchToContext(currentContext); + } +} + + +void thread_saveContext(registers_t* oldState) +{ + if((oldState->cs & 0x3) == 0x3) //are we coming from usermode? + { + //ss and esp are only valid if we are coming from usermode + currentContext->ss = oldState->ss; + currentContext->esp = oldState->useresp; + } + else + { + currentContext->ss = GDT_KERNEL_DATA; + currentContext->esp = oldState->esp + 0x14; + } + + currentContext->eip = oldState->eip; + currentContext->cs = oldState->cs; + currentContext->eflags = oldState->eflags; + currentContext->eax = oldState->eax; + currentContext->ecx = oldState->ecx; + currentContext->edx = oldState->edx; + currentContext->ebx = oldState->ebx; + + currentContext->ebp = oldState->ebp; + currentContext->esi = oldState->esi; + currentContext->edi = oldState->edi; + currentContext->ds = oldState->ds; + currentContext->es = oldState->es; + currentContext->fs = oldState->fs; + currentContext->gs = oldState->gs; +} diff --git a/src/kernel/thread.h~ b/src/kernel/thread.h~ new file mode 100644 index 0000000..8b0ba74 --- /dev/null +++ b/src/kernel/thread.h~ @@ -0,0 +1,67 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#ifndef _THREADINFO_H_ +#define _THREADINFO_H_ + +#include "registers.h" + +#define THREAD_NAME_LEN 256 + +#define THREAD_MASTER_ID 0 + +// signals +#define THREAD_BREAK_PROTECT 1 +#define THREAD_BREAK_ALLOWED 0 + +// requests +#define THREAD_SHUTDOWN 1 +#define THREAD_NO_REQUEST 0 + +// priority +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_HIGH 10 +#define THREAD_PRIORITY_LOW -5 + +typedef struct ThreadContext_ +{ + uint32_t eip; // 0 + uint32_t cs; // 4 + uint32_t eflags; // 8 + uint32_t eax; // 12 + uint32_t ecx; // 16 + uint32_t edx; // 20 + uint32_t ebx; // 24 + uint32_t esp; // 28 + uint32_t ebp; // 32 + uint32_t esi; // 36 + uint32_t edi; // 40 + uint32_t ds; // 44 + uint32_t es; // 48 + uint32_t fs; // 52 + uint32_t gs; // 56 + uint32_t ss; // 60 + uint32_t cr3; // 64 - unused for now... + uint32_t pid; // process id + uint32_t child_of; // pid of "master" thread + int32_t request; // OS request for shutdown thread + uint32_t signal; // maybe CTRL-C OR CTRL-D allowed or not? + int32_t priority; // task priority + uint32_t next_switch; // next thread switch at this clock + uint8_t name[THREAD_NAME_LEN]; // name of thread, stored if set at thread create + struct ThreadContext_* next; +} ThreadContext; + +uint32_t thread_getEflags(void); + +void thread_init(uint32_t baseContextAddress); +void thread_create(uint32_t infoAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg); + +void thread_schedule(registers_t* oldState); + +void thread_saveContext(registers_t* oldState); +void thread_switchToContext(ThreadContext* newContext); + +#endif diff --git a/src/user/Makefile b/src/user/Makefile new file mode 100644 index 0000000..8a2abb0 --- /dev/null +++ b/src/user/Makefile @@ -0,0 +1,20 @@ +SRCS = $(shell find -name '*.c') +OBJS = $(addsuffix .o,$(basename $(SRCS))) + +CC = gcc +LD = ld + +ASFLAGS = -m32 +CFLAGS = -m32 -Wall -g -fno-stack-protector -nostdinc -I include +LDFLAGS = -melf_i386 -Ttest.ld + +test.bin: $(OBJS) + $(LD) $(LDFLAGS) -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $^ + +clean: + rm $(OBJS) + +.PHONY: clean diff --git a/src/user/lib.c b/src/user/lib.c new file mode 100644 index 0000000..2955d6a --- /dev/null +++ b/src/user/lib.c @@ -0,0 +1,190 @@ +/* lib.c - some helper functions */ + +#include "../kernel/types.h" + +extern uint8_t keyboard_ch; + +void strreverse(uint8_t* begin, uint8_t* end) { + + uint8_t aux; + + while(end>begin) + + aux=*end, *end--=*begin, *begin++=aux; + +} + +void itoa(int32_t value, uint8_t* str, int32_t base) { + + static uint8_t num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + + uint8_t* wstr=str; + + int32_t sign; + + // Validate base + + if (base<2 || base>35){ *wstr='\0'; return; } + + + // Take care of sign + + if ((sign=value) < 0) value = -value; + + + // Conversion. Number is reversed. + + do *wstr++ = num[value%base]; while(value/=base); + + if(sign<0) *wstr++='-'; + + *wstr='\0'; + + + // Reverse string + + + strreverse(str,wstr-1); +} + +// A utility function to check whether x is numeric +uint8_t isNumericChar(uint8_t x) +{ + return (x >= '0' && x <= '9')? TRUE: FALSE; +} + +// A simple atoi() function. If the given string contains +// any invalid character, then this function returns 0 +int32_t atoi(uint8_t *str) +{ + if (*str == NULL) + return 0; + + int32_t res = 0; // Initialize result + int32_t sign = 1; // Initialize sign as positive + int32_t i = 0; // Initialize index of first digit + + // If number is negative, then update sign + if (str[0] == '-') + { + sign = -1; + i++; // Also update index of first digit + } + + // Iterate through all digits of input string and update result + for (; str[i] != '\0'; ++i) + { + if (isNumericChar(str[i]) == FALSE) + return 0; // You may add some lines to write error message + // to error stream + res = res*10 + str[i] - '0'; + } + + // Return result with sign + return sign*res; +} + +int16_t strcmp (const uint8_t * str1, const uint8_t * str2) +{ + while (*str1 == *str2) + { + if (*str1 == '\0' || *str2 == '\0') + break; + + str1++; + str2++; + } + + + if (*str1 == '\0' && *str2 == '\0') + return 0; + else + return -1; +} + +uint32_t strlen (const uint8_t *str) +{ + uint32_t slen = 0; + + while (*str != '\0') + { + slen++; + str++; + } + + return (slen); +} + +uint8_t *strcpy (uint8_t *dest, uint8_t *src) +{ + uint32_t i = 0; + + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + } + + dest[i] = '\0'; + + return (dest); +} + +uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) +{ + uint32_t i = 0; + while (src[i] != '\0') + { + dest[i] = src[i]; + i++; + if (i == len) break; + } + + dest[i] = '\0'; + + return (dest); +} + +uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) +{ + uint32_t i; + + uint8_t *src_ptr = src; + uint8_t *dest_ptr = dest; + + + while (count-- > 0) + { + *dest_ptr++ = *src_ptr++; + } + + return (dest); +} + +uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = val; + } + + return (dest); +} + +uint16_t *memsetw ( uint16_t *dest, uint16_t val, uint32_t count) +{ + uint32_t i; + + for (i = 1; i <= count; i++) + { + dest[i] = val; + } + + return (dest); +} + + + + diff --git a/src/user/test.c b/src/user/test.c new file mode 100644 index 0000000..8abc1b5 --- /dev/null +++ b/src/user/test.c @@ -0,0 +1,246 @@ +#include "../kernel/interrupts.h" +#include "../kernel/syscall.h" +#include "../kernel/pit.h" +#include "../kernel/colors.h" +#include "../kernel/message.h" + +void kprint_int (int32_t n, int32_t base); +void kprint (const char *str); +void kprint_newline (void); +void fb_set_color (unsigned char forecolor, unsigned char backcolor); +uint8_t getch (void); +uint32_t thread_kill (uint32_t pid); +void pmem_show_page (uint32_t page); +uint32_t pmem_get_free_ram_info (void); +void kdelay (uint32_t ticks); +void get_thread_input_stream (uint8_t ch); +uint8_t message_send (uint8_t *message, uint32_t thread, uint32_t len); +uint8_t message_read (uint8_t *message, uint32_t *thread_sender_pid); +void thread_exit (uint32_t ret_code); + +DEFN_SYSCALL1(kprint, 0, const char*); +DEFN_SYSCALL0(kprint_newline, 1); +DEFN_SYSCALL2(kprint_int, 2, int, int); +DEFN_SYSCALL2(fb_set_color, 3, char, char); +DEFN_SYSCALL0(getch, 4); +DEFN_SYSCALL1(thread_kill, 5, uint32_t); +DEFN_SYSCALL1(pmem_show_page, 6, uint32_t); +DEFN_SYSCALL0(pmem_get_free_ram_info, 7); +DEFN_SYSCALL1(kdelay, 8, uint32_t); +DEFN_SYSCALL1(get_thread_input_stream, 9, uint8_t); +DEFN_SYSCALL3(message_send, 10, uint8_t*, uint32_t, uint32_t); +DEFN_SYSCALL2(message_read, 11, uint8_t*, uint32_t); +DEFN_SYSCALL1(thread_exit, 12, uint32_t); + +/* +int i = 0; +void _start(void) +{ + syscall_kprint ("Hello from user module!"); + syscall_kprint_newline (); + + while(1); +} +*/ + +uint8_t getch_sh (void) +{ + uint8_t ch = NULL; + + syscall_kprint ("getch_sh..."); syscall_kprint_newline (); + + do + { + syscall_get_thread_input_stream (&ch); + // syscall_kdelay (50); + syscall_kprint (">"); + syscall_kprint_int (ch, 10); syscall_kprint_newline (); + } + while (ch == NULL); + + return (ch); +} + + +void itoa(int32_t value, uint8_t* str, int32_t base); +int16_t strcmp (const uint8_t * str1, const uint8_t * str2); + +void _startfoo (uint32_t arg) +{ + syscall_kprint ("Welcome to ksh, the kernel shell."); syscall_kprint_newline(); + syscall_thread_exit (0); +} + +void _start (uint32_t arg) +{ + uint8_t ch; + uint8_t command[256]; + uint8_t command_ind = 0; + + command[0] = '\0'; + + uint8_t input[256]; + uint8_t input_ind = 0; + + input[0] = '\0'; + + uint32_t i, page_start, page_end, pid; + uint32_t free_mem_kbytes; + + uint8_t msg_event_key = MSG_EVENT_KEY; + uint8_t msg_reply; + uint32_t msg_sender; + + syscall_kprint ("Welcome to ksh, the kernel shell."); syscall_kprint_newline(); + + + + while (1) + { + syscall_fb_set_color (FB_WHITE, FB_BLACK); + syscall_kprint ("help = list commands "); + syscall_fb_set_color (FB_LIGHT_BLUE, FB_BLACK); + syscall_kprint ("user"); + syscall_fb_set_color (FB_GREEN, FB_BLACK); + syscall_kprint ("@"); + syscall_fb_set_color (FB_RED, FB_BLACK); + syscall_kprint ("ksh> "); + syscall_fb_set_color (FB_WHITE, FB_BLACK); + command_ind = 0; + command[0] = '\0'; + + kshell_loop: + // syscall_kdelay (50); + + // goto kshell_loop; + + syscall_message_send (&msg_event_key, 1, 1); + ch = 0; + do + { + if (syscall_message_read (&msg_reply, &msg_sender) == 0) + { + // syscall_kprint ("msg from 0"); syscall_kprint_newline (); + + if (msg_sender == 1) + { + ch = msg_reply; + } + } + } while (ch == 0); +/* + do + { + syscall_get_thread_input_stream (&ch); + // syscall_kdelay (50); + // syscall_kprint (">"); + // syscall_kprint_int (ch, 10); syscall_kprint_newline (); + } + while (ch == NULL); +*/ + // ch = syscall_getch (); + + syscall_kprint_int (ch, 10); + if (ch != '\r' && ch != '\b') + { + command[command_ind] = ch; + if (command_ind < 256) + { + command_ind++; + syscall_kdelay (10); + goto kshell_loop; + } + } + else + { + if (ch == '\r') + { + // return char, check if command + command[command_ind] = '\0'; + + syscall_kprint ("command: "); syscall_kprint (command); syscall_kprint_newline (); + + if (strcmp (command, "page") == 0) + { + syscall_kprint ("start page block? "); + command_ind = 0; + + input_ind = 0; input[0] = '\0'; + + page_loop: + ch = getch_sh (); // blocking call + if (ch != '\r' && ch != '\b') + { + input[input_ind] = ch; + if (input_ind < 256) + { + input_ind++; + goto page_loop; + } + } + input[input_ind] = '\0'; + page_start = atoi (input); + page_end = page_start + 23; + + for (i = page_start; i <= page_end; i++) + { + syscall_pmem_show_page (i); + } + } + + if (strcmp (command, "mem") == 0) + { + free_mem_kbytes = syscall_pmem_get_free_ram_info (); + syscall_kprint ("free memory: "); + syscall_kprint_int (free_mem_kbytes, 10); + syscall_kprint (" KB"); + syscall_kprint_newline (); + } + + if (strcmp (command, "kill") == 0) + { + syscall_kprint ("pid? "); + command_ind = 0; + + input_ind = 0; input[0] = '\0'; + + kill_loop: + ch = getch_sh (); // blocking call + if (ch != '\r' && ch != '\b') + { + input[input_ind] = ch; + if (input_ind < 256) + { + input_ind++; + goto kill_loop; + } + } + input[input_ind] = '\0'; + pid = atoi (input); + + if (syscall_thread_kill (pid) == 0) + { + syscall_kprint ("thread killed!"); syscall_kprint_newline (); + } + } + + if (strcmp (command, "help") == 0) + { + syscall_kprint ("commands: page [list memory pages], mem [list free memory]"); syscall_kprint_newline (); + syscall_kprint ("kill [kill thread]"); syscall_kprint_newline (); + } + } + else + { + // backspace + + if (command_ind > 0) + { + command_ind = command_ind - 1; + command[command_ind] = '\0'; + goto kshell_loop; + } + } + } + } +} diff --git a/src/user/test.ld b/src/user/test.ld new file mode 100644 index 0000000..a61fb4b --- /dev/null +++ b/src/user/test.ld @@ -0,0 +1,28 @@ +/* Bei _start soll die Ausfuehrung losgehen */ +ENTRY(_start) + +OUTPUT_FORMAT(elf32-i386) + +/* + * Hier wird festgelegt, in welcher Reihenfolge welche Sektionen in die Binary + * geschrieben werden sollen + */ +SECTIONS +{ + /* Das Programm wird an 2 MB geladen */ + /*. = 0x200000; */ + . = 0x200000; + + .text : { + *(.text) + } + .data ALIGN(4096) : { + *(.data) + } + .rodata ALIGN(4096) : { + *(.rodata) + } + .bss ALIGN(4096) : { + *(.bss) + } +} From 7373502e4d54bcb82275bfbdf54fc9e5d94715bc Mon Sep 17 00:00:00 2001 From: Stefan Pietzonke Date: Wed, 25 Nov 2020 20:35:23 +0100 Subject: [PATCH 19/19] rm out.txt --- out.txt | 314 -------------------------------------------- src/kernel/kernel.c | 15 ++- 2 files changed, 11 insertions(+), 318 deletions(-) delete mode 100644 out.txt diff --git a/out.txt b/out.txt deleted file mode 100644 index 9c0ba28..0000000 --- a/out.txt +++ /dev/null @@ -1,314 +0,0 @@ -In Datei, eingebunden von kernel.c:15: -protos.h:20:9: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcmp«; erwartet wurde »int(const char *, const char *)« [-Wbuiltin-declaration-mismatch] - 20 | int16_t strcmp (const uint8_t * str1, const uint8_t * str2); - | ^~~~~~ -protos.h:1:1: Anmerkung: »strcmp« wird in Header »« deklariert - +++ |+#include - 1 | // protos.h - function prototypes -protos.h:21:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strlen«; erwartet wurde »unsigned int(const char *)« [-Wbuiltin-declaration-mismatch] - 21 | uint32_t strlen (const uint8_t *str); - | ^~~~~~ -protos.h:21:10: Anmerkung: »strlen« wird in Header »« deklariert -protos.h:22:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcpy«; erwartet wurde »char *(char *, const char *)« [-Wbuiltin-declaration-mismatch] - 22 | uint8_t *strcpy (uint8_t *dest, uint8_t *src); - | ^~~~~~ -protos.h:22:10: Anmerkung: »strcpy« wird in Header »« deklariert -protos.h:23:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strncpy«; erwartet wurde »char *(char *, const char *, unsigned int)« [-Wbuiltin-declaration-mismatch] - 23 | uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len); - | ^~~~~~~ -protos.h:23:10: Anmerkung: »strncpy« wird in Header »« deklariert -protos.h:24:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memcpy«; erwartet wurde »void *(void *, const void *, unsigned int)« [-Wbuiltin-declaration-mismatch] - 24 | uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); - | ^~~~~~ -protos.h:24:10: Anmerkung: »memcpy« wird in Header »« deklariert -protos.h:25:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memset«; erwartet wurde »void *(void *, int, unsigned int)« [-Wbuiltin-declaration-mismatch] - 25 | uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count); - | ^~~~~~ -protos.h:25:10: Anmerkung: »memset« wird in Header »« deklariert -kernel.c: In Funktion »div_by_zero«: -kernel.c:301:12: Warnung: Division durch Null [-Wdiv-by-zero] - 301 | i = 23 / 0; - | ^ -kernel.c: In Funktion »event_handler«: -kernel.c:482:7: Warnung: Implizite Deklaration der Funktion »message_read«; meinten Sie »thread_message_read«? [-Wimplicit-function-declaration] - 482 | if (message_read (&msg, &sender) == 0) - | ^~~~~~~~~~~~ - | thread_message_read -kernel.c:488:5: Warnung: Implizite Deklaration der Funktion »message_send« [-Wimplicit-function-declaration] - 488 | message_send (keyboard_ch, sender, 1); - | ^~~~~~~~~~~~ -kernel.c: In Funktion »run_event_handler«: -kernel.c:503:17: Warnung: Übergabe des Arguments 1 von »thread_init« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 503 | thread_init(base); - | ^~~~ - | | - | uint8_t * {alias unsigned char *} -In Datei, eingebunden von kernel.c:15: -protos.h:44:27: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} - 44 | void thread_init(uint32_t baseContextAddress); - | ~~~~~~~~~^~~~~~~~~~~~~~~~~~ -kernel.c:504:16: Warnung: Übergabe des Arguments 1 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 504 | thread_create(event_context, event_stack, 4096 * 2, (uint32_t)event_handler, 0, "event_handler"); - | ^~~~~~~~~~~~~ - | | - | uint8_t * {alias unsigned char *} -In Datei, eingebunden von kernel.c:15: -protos.h:45:29: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} - 45 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name); - | ~~~~~~~~~^~~~~~~~~~~~~~ -kernel.c:504:31: Warnung: Übergabe des Arguments 2 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 504 | thread_create(event_context, event_stack, 4096 * 2, (uint32_t)event_handler, 0, "event_handler"); - | ^~~~~~~~~~~ - | | - | uint8_t * {alias unsigned char *} -In Datei, eingebunden von kernel.c:15: -protos.h:45:54: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} - 45 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name); - | ~~~~~~~~~^~~~~~~~~~ -kernel.c: In Funktion »kmain«: -kernel.c:770:5: Warnung: Implizite Deklaration der Funktion »set_gdt« [-Wimplicit-function-declaration] - 770 | set_gdt (); /* init memory GDT */ - | ^~~~~~~ -kernel.c:774:2: Warnung: Implizite Deklaration der Funktion »pic_init« [-Wimplicit-function-declaration] - 774 | pic_init(); //set up PIC - | ^~~~~~~~ -kernel.c:778:2: Warnung: Implizite Deklaration der Funktion »pit_init« [-Wimplicit-function-declaration] - 778 | pit_init(100); - | ^~~~~~~~ -kernel.c:785:36: Warnung: Initialisierung von »multiboot_memory_map_t *« {alias »struct multiboot_memory_map *«} von »long unsigned int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 785 | multiboot_memory_map_t* mmap = mbt->mmap_addr; - | ^~~ -kernel.c:802:13: Warnung: Vergleich zwischen Zeiger und Ganzzahl - 802 | while(mmap < mbt->mmap_addr + mbt->mmap_length) - | ^ -kernel.c:907:5: Warnung: Implizite Deklaration der Funktion »pic_unmask_irq« [-Wimplicit-function-declaration] - 907 | pic_unmask_irq(IRQ_KEYBOARD); - | ^~~~~~~~~~~~~~ -lib.c: In Funktion »kprint_int«: -lib.c:93:5: Warnung: Implizite Deklaration der Funktion »kprint«; meinten Sie »kprint_int«? [-Wimplicit-function-declaration] - 93 | kprint (str); - | ^~~~~~ - | kprint_int -lib.c: Auf höchster Ebene: -lib.c:96:9: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcmp«; erwartet wurde »int(const char *, const char *)« [-Wbuiltin-declaration-mismatch] - 96 | int16_t strcmp (const uint8_t * str1, const uint8_t * str2) - | ^~~~~~ -lib.c:4:1: Anmerkung: »strcmp« wird in Header »« deklariert - 3 | #include "types.h" - +++ |+#include - 4 | -lib.c:114:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strlen«; erwartet wurde »unsigned int(const char *)« [-Wbuiltin-declaration-mismatch] - 114 | uint32_t strlen (const uint8_t *str) - | ^~~~~~ -lib.c:114:10: Anmerkung: »strlen« wird in Header »« deklariert -lib.c:127:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strcpy«; erwartet wurde »char *(char *, const char *)« [-Wbuiltin-declaration-mismatch] - 127 | uint8_t *strcpy (uint8_t *dest, uint8_t *src) - | ^~~~~~ -lib.c:127:10: Anmerkung: »strcpy« wird in Header »« deklariert -lib.c:142:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »strncpy«; erwartet wurde »char *(char *, const char *, unsigned int)« [-Wbuiltin-declaration-mismatch] - 142 | uint8_t *strncpy (uint8_t *dest, uint8_t *src, uint32_t len) - | ^~~~~~~ -lib.c:142:10: Anmerkung: »strncpy« wird in Header »« deklariert -lib.c:157:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memcpy«; erwartet wurde »void *(void *, const void *, unsigned int)« [-Wbuiltin-declaration-mismatch] - 157 | uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count) - | ^~~~~~ -lib.c:157:10: Anmerkung: »memcpy« wird in Header »« deklariert -lib.c: In Funktion »memcpy«: -lib.c:161:24: Warnung: Initialisierung streicht Qualifizierer »const« von Zeiger-Zieltyp [-Wdiscarded-qualifiers] - 161 | uint8_t *src_ptr = src; - | ^~~ -lib.c: Auf höchster Ebene: -lib.c:173:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memset«; erwartet wurde »void *(void *, int, unsigned int)« [-Wbuiltin-declaration-mismatch] - 173 | uint8_t *memset (uint8_t *dest, uint8_t val, uint32_t count) - | ^~~~~~ -lib.c:173:10: Anmerkung: »memset« wird in Header »« deklariert -lib.c: In Funktion »getch«: -lib.c:203:9: Warnung: Implizite Deklaration der Funktion »kdelay« [-Wimplicit-function-declaration] - 203 | kdelay (10); - | ^~~~~~ -gdt.c: In Funktion »write_tss«: -gdt.c:99:4: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] - 99 | memset(&tss_entry, 0, sizeof(tss_entry)); - | ^~~~~~ -gdt.c:99:4: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« -gdt.c:2:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit - 1 | #include "types.h" - +++ |+#include - 2 | -gdt.c: In Funktion »set_gdt«: -gdt.c:119:13: Warnung: Zuweisung an »uint32_t« {alias »unsigned int«} aus »struct gdt_entry_bits (*)[6]« wandelt einen Zeiger in eine Ganzzahl um, ohne explizite Typkonvertierung [-Wint-conversion] - 119 | gp.base = &gdt; - | ^ -gdt.c:153:29: Warnung: Vorzeichenlose Umwandlung von »int« nach »unsigned char:4« ändert den Wert von »65535« nach »15« [-Woverflow] - 153 | null_code->limit_high = 0xFFFF; - | ^~~~~~ -gdt.c:176:22: Warnung: Vorzeichenlose Umwandlung von »int« nach »unsigned char:4« ändert den Wert von »65535« nach »15« [-Woverflow] - 176 | code->limit_high=0xFFFF; - | ^~~~~~ -gdt.c:188:5: Warnung: Implizite Deklaration der Funktion »gdt_flush« [-Wimplicit-function-declaration] - 188 | gdt_flush (); - | ^~~~~~~~~ -gdt.c:190:5: Warnung: Implizite Deklaration der Funktion »tss_flush« [-Wimplicit-function-declaration] - 190 | tss_flush(); //implement this later - | ^~~~~~~~~ -physmem.c: In Funktion »kmalloc«: -physmem.c:346:17: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 346 | ptr = start_free; - | ^ -physmem.c:348:32: Warnung: Rückgabe von »uint32_t« {alias »unsigned int«} aus einer Funktion mit Rückgabetyp »void *« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 348 | return (start_free + sizeof (size_t)); - | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ -physmem.c: In Funktion »kfree«: -physmem.c:368:9: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 368 | ptr = address - sizeof (size_t); - | ^ -physmem.c: In Funktion »pmem_show_page«: -physmem.c:396:5: Warnung: Implizite Deklaration der Funktion »kprint_int« [-Wimplicit-function-declaration] - 396 | kprint_int (page * PAGE_BITS * MEM_BLOCK_SIZE, 16); - | ^~~~~~~~~~ -physmem.c:397:5: Warnung: Implizite Deklaration der Funktion »fb_get_cursor« [-Wimplicit-function-declaration] - 397 | fb_get_cursor (&cursor_x, &cursor_y); - | ^~~~~~~~~~~~~ -physmem.c:402:9: Warnung: Implizite Deklaration der Funktion »fb_set_cursor« [-Wimplicit-function-declaration] - 402 | fb_set_cursor (cursor_x, cursor_y); - | ^~~~~~~~~~~~~ -physmem.c:409:13: Warnung: Implizite Deklaration der Funktion »kprint« [-Wimplicit-function-declaration] - 409 | kprint ("0 "); - | ^~~~~~ -physmem.c:416:5: Warnung: Implizite Deklaration der Funktion »kprint_newline« [-Wimplicit-function-declaration] - 416 | kprint_newline (); - | ^~~~~~~~~~~~~~ -thread.c: In Funktion »thread_init«: -thread.c:62:2: Warnung: Implizite Deklaration der Funktion »kprint« [-Wimplicit-function-declaration] - 62 | kprint ("Initializing threading..."); kprint_newline (); - | ^~~~~~ -thread.c:62:40: Warnung: Implizite Deklaration der Funktion »kprint_newline« [-Wimplicit-function-declaration] - 62 | kprint ("Initializing threading..."); kprint_newline (); - | ^~~~~~~~~~~~~~ -thread.c:64:2: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] - 64 | memset(listHead, 0, sizeof(ThreadContext)); - | ^~~~~~ -thread.c:64:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« -thread.c:12:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit - 11 | #include "elf.h" - +++ |+#include - 12 | -thread.c: In Funktion »thread_create«: -thread.c:75:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« - 75 | memset(context, 0, sizeof(ThreadContext)); - | ^~~~~~ -thread.c:75:2: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit -thread.c:95:5: Warnung: Implizite Deklaration der Funktion »strncpy« [-Wimplicit-function-declaration] - 95 | strncpy (context->name, name, THREAD_NAME_LEN); - | ^~~~~~~ -thread.c:95:5: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »strncpy« -thread.c:95:5: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »strncpy« bereit -thread.c: In Funktion »thread_exit«: -thread.c:141:5: Warnung: Implizite Deklaration der Funktion »kfree« [-Wimplicit-function-declaration] - 141 | kfree (currentContext->esp); - | ^~~~~ -thread.c: In Funktion »thread_show_info«: -thread.c:221:74: Warnung: Implizite Deklaration der Funktion »kprint_int« [-Wimplicit-function-declaration] - 221 | kprint ("thread: '"); kprint (search->name); kprint ("' pid: "); kprint_int (search->pid, 10); kprint (", priority: "); kprint_int (search->priority, 10); - | ^~~~~~~~~~ -thread.c: In Funktion »thread_schedule«: -thread.c:274:47: Warnung: Implizite Deklaration der Funktion »clock« [-Wimplicit-function-declaration] - 274 | currentContext->next_switch = clock () + (fair_schedule * currentContext->priority); - | ^~~~~ -thread.c: In Funktion »switch_to_user_mode«: -thread.c:355:5: Warnung: Implizite Deklaration der Funktion »set_kernel_stack« [-Wimplicit-function-declaration] - 355 | set_kernel_stack(GDT_KERNEL_DATA); - | ^~~~~~~~~~~~~~~~ -thread.c: In Funktion »init_elf«: -thread.c:403:9: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« - 403 | memset(dest, 0, ph->mem_size); - | ^~~~~~ -thread.c:403:9: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit -thread.c:404:9: Warnung: Implizite Deklaration der Funktion »memcpy« [-Wimplicit-function-declaration] - 404 | memcpy(dest, src, ph->file_size); - | ^~~~~~ -thread.c:404:9: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memcpy« -thread.c:404:9: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memcpy« bereit -thread.c:409:29: Warnung: Implizite Deklaration der Funktion »kmalloc« [-Wimplicit-function-declaration] - 409 | task_base = (uint8_t *) kmalloc (8192 * 2); - | ^~~~~~~ -thread.c:417:19: Warnung: Übergabe des Arguments 1 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 417 | thread_create(task_context, task_stack, 4096 * 2, (uint32_t) header->entry, 0, "task"); - | ^~~~~~~~~~~~ - | | - | uint8_t * {alias unsigned char *} -thread.c:70:29: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} - 70 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) - | ~~~~~~~~~^~~~~~~~~~~~~~ -thread.c:417:33: Warnung: Übergabe des Arguments 2 von »thread_create« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 417 | thread_create(task_context, task_stack, 4096 * 2, (uint32_t) header->entry, 0, "task"); - | ^~~~~~~~~~ - | | - | uint8_t * {alias unsigned char *} -thread.c:70:54: Anmerkung: »uint32_t« {alias »unsigned int«} erwartet, aber Argument hat Typ »uint8_t *« {alias »unsigned char *«} - 70 | void thread_create(uint32_t contextAddress, uint32_t stackStart, uint32_t stackSize, uint32_t entryPoint, uint32_t arg, uint8_t *name) - | ~~~~~~~~~^~~~~~~~~~ -thread.c: In Funktion »init_multitasking«: -thread.c:425:3: Warnung: Implizite Deklaration der Funktion »run_kshell« [-Wimplicit-function-declaration] - 425 | run_kshell (); - | ^~~~~~~~~~ -thread.c:428:34: Warnung: Initialisierung von »struct module *« von »long unsigned int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 428 | struct module* modules = mb_info->mods_addr; - | ^~~~~~~ -thread.c: In Funktion »get_thread_input_stream«: -thread.c:444:7: Warnung: Zuweisung an »uint8_t *« {alias »unsigned char *«} von »uint8_t« {alias »unsigned char«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 444 | ch = keyboard_ch; - | ^ -interrupts.c: In Funktion »interrupts_init«: -interrupts.c:13:5: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] - 13 | memset(isrs, 0, sizeof(isrFunction) * I86_IDT_MAX_ENTRY_COUNT); - | ^~~~~~ -interrupts.c:13:5: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« -interrupts.c:8:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit - 7 | #include "pic.h" - +++ |+#include - 8 | -interrupts.c: In Funktion »interrupts_faultHandler«: -interrupts.c:22:9: Warnung: Implizite Deklaration der Funktion »kprint« [-Wimplicit-function-declaration] - 22 | kprint ("Unhandled fault: "); - | ^~~~~~ -interrupts.c:23:9: Warnung: Implizite Deklaration der Funktion »kprint_int« [-Wimplicit-function-declaration] - 23 | kprint_int (regs.int_no, 10); - | ^~~~~~~~~~ -interrupts.c:24:9: Warnung: Implizite Deklaration der Funktion »kprint_newline« [-Wimplicit-function-declaration] - 24 | kprint_newline (); - | ^~~~~~~~~~~~~~ -idt.c: In Funktion »idt_install«: -idt.c:35:2: Warnung: Implizite Deklaration der Funktion »memset« [-Wimplicit-function-declaration] - 35 | memset(&idt, 0, sizeof(struct idt_entry) * I86_IDT_MAX_ENTRY_COUNT); - | ^~~~~~ -idt.c:35:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »memset« -idt.c:9:1: Anmerkung: binden Sie »« ein oder stellen Sie eine Deklaration von »memset« bereit - 8 | #include "types.h" - +++ |+#include - 9 | -message.c:8:10: Warnung: in Konflikt stehende Typen für eingebaute Funktion »memcpy«; erwartet wurde »void *(void *, const void *, unsigned int)« [-Wbuiltin-declaration-mismatch] - 8 | uint8_t *memcpy (uint8_t *dest, const uint8_t *src, uint32_t count); - | ^~~~~~ -message.c:4:1: Anmerkung: »memcpy« wird in Header »« deklariert - 3 | #include "interrupts.h" - +++ |+#include - 4 | -message.c: In Funktion »message_send«: -message.c:26:39: Warnung: Implizite Deklaration der Funktion »kmalloc« [-Wimplicit-function-declaration] - 26 | msg_head = (struct message *) kmalloc (sizeof (struct message)); - | ^~~~~~~ -message.c:35:24: Warnung: Zuweisung an »uint8_t *« {alias »unsigned char *«} von »int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 35 | msg_head->data = kmalloc (len); - | ^ -message.c:67:20: Warnung: Zuweisung an »uint8_t *« {alias »unsigned char *«} von »int« wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 67 | next->data = kmalloc (len); - | ^ -message.c: In Funktion »message_read«: -message.c:103:27: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 103 | thread_sender_pid = current_msg->thread_sender_num; - | ^ -message.c:115:35: Warnung: Zuweisung an »uint32_t *« {alias »unsigned int *«} von »uint32_t« {alias »unsigned int«} wandelt eine Zahl in einen Zeiger um, ohne explizite Typkonvertierung [-Wint-conversion] - 115 | thread_sender_pid = current_msg->thread_sender_num; - | ^ -kernel build end, making .iso diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 6565b7a..c86200c 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -430,10 +430,17 @@ void kshell (uint32_t argument) input[input_ind] = '\0'; pid = atoi (input); - if (thread_kill (pid) == 0) - { - kprint ("thread killed!"); kprint_newline (); - } + if (pid > 1) + { + if (thread_kill (pid) == 0) + { + kprint ("thread killed!"); kprint_newline (); + } + } + else + { + kprint ("can't kill system pid: "); kprint_int (pid, 10); kprint_newline (); + } } if (strcmp (command, (const uint8_t *) "help") == 0)