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/README.md b/README.md index e4891c6..bb2264b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,26 @@ -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. +- 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 + + +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. diff --git a/binary_x86/kernel b/binary_x86/kernel deleted file mode 100644 index 4dd1747..0000000 Binary files a/binary_x86/kernel and /dev/null differ diff --git a/bochsrc.txt b/bochsrc.txt new file mode 100644 index 0000000..20047c5 --- /dev/null +++ b/bochsrc.txt @@ -0,0 +1,9 @@ + megs: 512 + 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 + boot: cdrom + log: bochslog.txt + clock: sync=realtime, time0=local + cpu: count=1, ips=1000000 diff --git a/kernel.asm b/kernel.asm deleted file mode 100644 index 1c2fee7..0000000 --- a/kernel.asm +++ /dev/null @@ -1,51 +0,0 @@ -; Copyright (C) 2014 Arjun Sreedharan -; License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html - -bits 32 -section .text - ;multiboot spec - align 4 - dd 0x1BADB002 ;magic - dd 0x00 ;flags - dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero - -global start -global keyboard_handler -global read_port -global write_port -global load_idt - -extern kmain ;this is defined in the c file -extern keyboard_handler_main - -read_port: - mov edx, [esp + 4] - ;al is the lower 8 bits of eax - in al, dx ;dx is the lower 16 bits of edx - ret - -write_port: - mov edx, [esp + 4] - mov al, [esp + 4 + 4] - out dx, al - ret - -load_idt: - mov edx, [esp + 4] - lidt [edx] - sti ;turn on interrupts - ret - -keyboard_handler: - call keyboard_handler_main - iretd - -start: - cli ;block interrupts - mov esp, stack_space - call kmain - hlt ;halt the CPU - -section .bss -resb 8192; 8KB for stack -stack_space: diff --git a/kernel.c b/kernel.c deleted file mode 100644 index 3435fc9..0000000 --- a/kernel.c +++ /dev/null @@ -1,163 +0,0 @@ -/* -* Copyright (C) 2014 Arjun Sreedharan -* License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html -*/ -#include "keyboard_map.h" - -/* 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 -#define IDT_SIZE 256 -#define INTERRUPT_GATE 0x8e -#define KERNEL_CODE_SEGMENT_OFFSET 0x08 - -#define ENTER_KEY_CODE 0x1C - -extern unsigned char keyboard_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); - -/* current cursor location */ -unsigned int current_loc = 0; -/* video memory begins at address 0xb8000 */ -char *vidptr = (char*)0xb8000; - -struct IDT_entry { - unsigned short int offset_lowerbits; - unsigned short int selector; - unsigned char zero; - unsigned char type_attr; - unsigned short int offset_higherbits; -}; - -struct IDT_entry IDT[IDT_SIZE]; - - -void idt_init(void) -{ - unsigned long keyboard_address; - unsigned long idt_address; - unsigned long idt_ptr[2]; - - /* populate IDT entry of keyboard's interrupt */ - keyboard_address = (unsigned long)keyboard_handler; - IDT[0x21].offset_lowerbits = keyboard_address & 0xffff; - IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET; - IDT[0x21].zero = 0; - IDT[0x21].type_attr = INTERRUPT_GATE; - IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 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/keyboard_map.h b/keyboard_map.h deleted file mode 100644 index e3b2c32..0000000 --- a/keyboard_map.h +++ /dev/null @@ -1,44 +0,0 @@ -/* The following array is taken from - http://www.osdever.net/bkerndev/Docs/keyboard.htm - All credits where due -*/ - -unsigned char keyboard_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 */ -}; \ No newline at end of file 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 new file mode 100755 index 0000000..f4b4278 --- /dev/null +++ b/make.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 +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 + +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/menu.lst b/menu.lst new file mode 100644 index 0000000..e0cc883 --- /dev/null +++ b/menu.lst @@ -0,0 +1,6 @@ +default=0 + timeout=5 + + title os + kernel /boot/kernel + module /test.bin 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/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 new file mode 100644 index 0000000..0951e70 --- /dev/null +++ b/src/kernel/gdt.c @@ -0,0 +1,191 @@ +#include "types.h" + +void stack_space (void); + +// gdt table +struct gdt_entry_bits +{ + 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)); + +static struct gdt_entry_bits gdt[6]; + +struct gdt_ptr +{ + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +struct gdt_ptr gp; + + +// tss structure + +// A struct describing a Task State Segment. +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 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 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; +} + + +void set_gdt (void) +{ + gp.limit = (sizeof(struct gdt_entry_bits) * 6) - 1; + gp.base = &gdt; + + //....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 = 0xFFFF; + 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=0xFFFF; + 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 +} diff --git a/src/kernel/idt.c b/src/kernel/idt.c new file mode 100644 index 0000000..945e050 --- /dev/null +++ b/src/kernel/idt.c @@ -0,0 +1,88 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "interrupts.h" +#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..1343669 --- /dev/null +++ b/src/kernel/idt.h @@ -0,0 +1,108 @@ +/* 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); + +// void isr128(void); // syscall interruot 80 + +#endif diff --git a/src/kernel/interruptStubs.asm b/src/kernel/interruptStubs.asm new file mode 100644 index 0000000..d513766 --- /dev/null +++ b/src/kernel/interruptStubs.asm @@ -0,0 +1,154 @@ +; 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..8cfe2ed --- /dev/null +++ b/src/kernel/interrupts.h @@ -0,0 +1,57 @@ +#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 + +#define IRQ_SYSCALL 37 + +// 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.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..1bf21df --- /dev/null +++ b/src/kernel/io.c @@ -0,0 +1,23 @@ +#include "io.h" +#include "ports.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) + { + 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/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/kernel.asm b/src/kernel/kernel.asm new file mode 100644 index 0000000..7178987 --- /dev/null +++ b/src/kernel/kernel.asm @@ -0,0 +1,124 @@ +; Copyright (C) 2014 Arjun Sreedharan +; License: GPL version 2 or higher http://www.gnu.org/licenses/gpl.html +; NASM syntax +bits 32 +section .text + ;multiboot spec + align 4 + dd 0x1BADB002 ;magic + dd 0x00 ;flags + dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero + +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: + push ebp + mov ebp, esp + mov eax, [esp + 8] + mov cr3, eax + mov esp, ebp + pop ebp + ret + +global enablePaging +enablePaging: + push ebp + mov ebp, esp + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + mov esp, ebp + pop ebp + ret + +; This will set up our new segment registers. We need to do +; something special in order to set CS. We do what is called a +; far jump. A jump that includes a segment as well as an offset. +; This is declared in C as 'extern void gdt_flush();' +global gdt_flush ; Allows the C code to link to this +extern gp ; Says that '_gp' is in another file +gdt_flush: + lgdt [gp] ; Load the GDT with our '_gp' which is a special pointer + mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump! +flush2: + ret ; Returns back to the C code! + +global tss_flush ; Allows our C code to call tss_flush(). +tss_flush: + mov ax, 0x2B ; Load the index of our TSS structure - The index is + ; 0x28, as it is the 5th selector and each is 8 bytes + ; long, but we set the bottom two bits (making 0x2B) + ; so that it has an RPL of 3, not zero. + ltr ax ; Load 0x2B into the task state register. + ret + + + +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 + mov fs,ax + mov gs,ax ;we don't need to worry about SS. it's handled by iret + + mov eax,esp + push 0x23 ;user data segment with bottom 2 bits set for ring 3 + 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 + iret +;end + + + + +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 + + iretd ; itetd +a: + add esp, 4 ; fix stack + ret + +start: + cli ;block interrupts + mov esp, stack_space + push ebx + call kmain + hlt ;halt the CPU + +section .bss +resb 32768; 8KB for stack +global stack_space +stack_space: diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c new file mode 100644 index 0000000..c86200c --- /dev/null +++ b/src/kernel/kernel.c @@ -0,0 +1,1003 @@ +/* +* 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" +#include "syscall.h" + +#include "protos.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; + uint32_t free_mem_kbytes; + + 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 < 255) + { + command_ind++; + goto kshell_loop; + } + } + else + { + if (ch == '\r') + { + // return char, check if command + command[command_ind] = '\0'; + + if (strcmp (command, (const uint8_t *) "tasks") == 0) + { + // run_threads (); + } + + if (strcmp (command, (const uint8_t *) "btasks") == 0) + { + // run_threads_background (); + } + + 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 < 255) + { + 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, (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, (const uint8_t *) "threads") == 0) + { + thread_show_info (); + } + + 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 < 255) + { + input_ind++; + goto kill_loop; + } + } + input[input_ind] = '\0'; + pid = atoi (input); + + 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) + { + 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 (); + } + } + 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((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) +{ + syscall_kprint ("Hello from userland!"); + syscall_kprint_newline (); +} + + + +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) + { + } +} + + +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); + + 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;thread_sender_pid = current_msg->thread_sender_num; + uint32_t ticks_max = clock () + 10000; + + // normal priority = 0 + for(;;) + { + ticks = clock ();thread_sender_pid = current_msg->thread_sender_num; + 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_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(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;thread_sender_pid = current_msg->thread_sender_num; + uint32_t ticks_max = clock () + 10000; + + for(;;)thread_sender_pid = current_msg->thread_sender_num; + { + 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, 0, 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, 0, 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 (); + } + */ + + 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 (); + + 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); + 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(); + + run_event_handler (); + kdelay (1000); + // init_multitasking (mbt); + + // 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/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/keyboard_map.h b/src/kernel/keyboard_map.h new file mode 100644 index 0000000..c357352 --- /dev/null +++ b/src/kernel/keyboard_map.h @@ -0,0 +1,97 @@ +/* The following array is taken from + http://www.osdever.net/bkerndev/Docs/keyboard.htm + 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 +#define KEY_SCAN_F1 0x3B +#define KEY_SCAN_CTRL_LEFT 0x1D + + +uint8_t keyboard_shift; + +uint8_t keyboard_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 */ +}; + +uint8_t 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 new file mode 100644 index 0000000..5adf82c --- /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/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/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/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/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 */ + diff --git a/src/kernel/physmem.c b/src/kernel/physmem.c new file mode 100644 index 0000000..3b88d0e --- /dev/null +++ b/src/kernel/physmem.c @@ -0,0 +1,426 @@ +// #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; + if (address % MEM_BLOCK_SIZE != 0) + { + page++; + } + + 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; + } + else + { + 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 + { + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + 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 + { + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + 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 + { + if (get_bit (physmem_pages[p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + else + { + clear_bit (&physmem_pages[p], b); + } + } + } + } + } + } + } + else + { + for (b = start_b; b <= end_b; b++) + { + if (state == ALLOCATE) + { + set_bit (&physmem_pages[start_p], b); + } + else + { + if (get_bit (physmem_pages[start_p], b) == FREE) + { + return (MEM_ERR_DOUBLE_FREE); + } + else + { + 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++; + } + } + else + { + 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++) + { + 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++; + 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 + { + 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 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; + uint32_t *ptr; + 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 = pages; /* store size of memoryin pages */ + 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 pages; + + 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); + } + else + { + return (MEM_ERR_BAD_MEM); + } +} + +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) + { + kprint ("0 "); + } + else + { + kprint ("1 "); + } + } + kprint_newline (); +} + + + +/* +void main () +{ + pmem_set_bitmap (1024 * 1024, 0x300000, ALLOCATE); +} +*/ 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/protos.h b/src/kernel/protos.h new file mode 100644 index 0000000..7ceb00f --- /dev/null +++ b/src/kernel/protos.h @@ -0,0 +1,55 @@ +// protos.h - function prototypes +// +// + +void fb_blit (void); +void fb_scroll_down (void); +void fb_move_cursor (unsigned short pos); +void fb_clear_con (void); + +void loop_mark (); + +// 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, 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); + +// 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); 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/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 new file mode 100644 index 0000000..c267d4d --- /dev/null +++ b/src/kernel/thread.c @@ -0,0 +1,446 @@ +/* by Andreas Galauner + * + * https://github.com/G33KatWork + */ + +#include "types.h" +#include "thread.h" +#include "interrupts.h" + +#include "multiboot.h" +#include "elf.h" + +/* +#include +#include +#include +#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 */ + uint32_t threads = 0; + ThreadContext* search = listHead; + + interrupts_disable (); + + while (search->next != NULL) + { + threads++; + search = search->next; + } + + interrupts_enable (); + + 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) +{ + // interrupts_disable (); + + 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); + 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) +{ + /* 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_get_own_pid (void) +{ + // return a threads own PID + + return (currentContext->pid); +} + +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) + { + 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); + + interrupts_enable (); + return (0); + } + else + { + interrupts_enable (); + return (1); + } +} + + +void thread_set_priority (int32_t priority) +{ + currentContext->priority = priority; +} + + +void thread_show_info (void) +{ + /* show thread infos */ + uint8_t run = 1; + uint32_t threads = 0, mthreads = 0; + ThreadContext* search = listHead; + + interrupts_disable (); + + do + { + 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 */ + 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; +} + + +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: \ + "); +} + +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..647adbc --- /dev/null +++ b/src/kernel/thread.h @@ -0,0 +1,69 @@ +/* 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 + uint32_t stack_size; + uint32_t stack_start; + 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, uint8_t *name); + +void thread_schedule(registers_t* oldState); + +void thread_saveContext(registers_t* oldState); +void thread_switchToContext(ThreadContext* newContext); + +#endif 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/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 diff --git a/src/kernel/types.h b/src/kernel/types.h new file mode 100644 index 0000000..270ddd7 --- /dev/null +++ b/src/kernel/types.h @@ -0,0 +1,49 @@ +/* 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 +#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 + +/* 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 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(); +} 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) + } +} diff --git a/stage2_eltorito b/stage2_eltorito new file mode 100644 index 0000000..9e1617c Binary files /dev/null and b/stage2_eltorito differ