Repository: MaaSTaaR/539kernel Branch: master Commit: 82f8164194ce Files: 93 Total size: 103.0 KB Directory structure: gitextract_ir_g73hk/ ├── README.md ├── evolution_by_versions/ │ ├── .gitignore │ ├── 01_539kernel_bootloader/ │ │ ├── Makefile │ │ ├── bootstrap.asm │ │ └── simple_kernel.asm │ ├── 03_539kernel_progenitor/ │ │ ├── Makefile │ │ ├── bootstrap.asm │ │ ├── gdt.asm │ │ ├── idt.asm │ │ ├── linker.ld │ │ ├── main.c │ │ └── starter.asm │ ├── 04_539kernel_version_t/ │ │ ├── Makefile │ │ ├── bootstrap.asm │ │ ├── gdt.asm │ │ ├── idt.asm │ │ ├── linker.ld │ │ ├── main.c │ │ ├── process.c │ │ ├── process.h │ │ ├── scheduler.c │ │ ├── scheduler.h │ │ ├── screen.c │ │ ├── screen.h │ │ └── starter.asm │ ├── 05_539kernel_version_g/ │ │ ├── Makefile │ │ ├── bootstrap.asm │ │ ├── gdt.asm │ │ ├── heap.c │ │ ├── heap.h │ │ ├── idt.asm │ │ ├── linker.ld │ │ ├── main.c │ │ ├── paging.c │ │ ├── paging.h │ │ ├── process.c │ │ ├── process.h │ │ ├── scheduler.c │ │ ├── scheduler.h │ │ ├── screen.c │ │ ├── screen.h │ │ └── starter.asm │ └── 06_539kernel_version_ne/ │ ├── Makefile │ ├── ata.c │ ├── ata.h │ ├── bochs │ ├── bootstrap.asm │ ├── filesystem.c │ ├── filesystem.h │ ├── gdt.asm │ ├── heap.c │ ├── heap.h │ ├── idt.asm │ ├── linker.ld │ ├── main.c │ ├── paging.c │ ├── paging.h │ ├── process.c │ ├── process.h │ ├── scheduler.c │ ├── scheduler.h │ ├── screen.c │ ├── screen.h │ ├── starter.asm │ ├── str.c │ └── str.h └── src/ ├── .gitignore ├── Makefile ├── README ├── ata.c ├── ata.h ├── bochs ├── bootstrap.asm ├── filesystem.c ├── filesystem.h ├── gdt.asm ├── heap.c ├── heap.h ├── idt.asm ├── linker.ld ├── main.c ├── paging.c ├── paging.h ├── process.c ├── process.h ├── scheduler.c ├── scheduler.h ├── screen.c ├── screen.h ├── simple_kernel.asm ├── starter.asm ├── str.c └── str.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ 539kernel is a simple x86 32bit educational kernel which has been written especially for the book "**[A Journey in Creating an Operating System Kernel](https://539kernel.com)**" which is available freely on . * `src/` contains the last version of 539kernel. * `evolution_by_versions/` contains the version of 539kernel while it's under development through the different chapters in the book. ================================================ FILE: evolution_by_versions/.gitignore ================================================ # Created by https://www.gitignore.io/api/c # Edit at https://www.gitignore.io/?templates=c ### C ### # Prerequisites *.d # Object files *.o *.ko *.obj *.elf *.bin # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf # End of https://www.gitignore.io/api/c *.img ================================================ FILE: evolution_by_versions/01_539kernel_bootloader/Makefile ================================================ ASM = nasm BOOTSTRAP_FILE = bootstrap.asm KERNEL_FILE = simple_kernel.asm build: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f bin $(KERNEL_FILE) -o kernel.o dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=kernel.o of=kernel.img bs=512 qemu-system-x86_64 -s kernel.img clean: rm -f *.o ================================================ FILE: evolution_by_versions/01_539kernel_bootloader/bootstrap.asm ================================================ start: mov ax, 07C0h mov ds, ax mov si, title_string call print_string mov si, message_string call print_string call load_kernel_from_disk jmp 0900h:0000 load_kernel_from_disk: mov ax, 0900h mov es, ax mov ah, 02h mov al, 01h mov ch, 0h mov cl, 02h mov dh, 0h mov dl, 80h mov bx, 0h int 13h jc kernel_load_error ret kernel_load_error: mov si, load_error_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je printing_finished int 10h jmp print_char printing_finished: mov al, 10d ; Print new line int 10h ; Reading current cursor position mov ah, 03h mov bh, 0 int 10h ; Move the cursor to the beginning mov ah, 02h mov dl, 0 int 10h ret title_string db 'The Bootloader of 539kernel.', 0 message_string db 'The kernel is loading...', 0 load_error_string db 'The kernel cannot be loaded', 0 times 510-($-$$) db 0 dw 0xAA55 ================================================ FILE: evolution_by_versions/01_539kernel_bootloader/simple_kernel.asm ================================================ start: mov ax, cs mov ds, ax ; --- ; mov si, hello_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je done int 10h jmp print_char done: ret hello_string db 'Hello World!, From Simple Assembly 539kernel!', 0 ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/Makefile ================================================ ASM = nasm CC = gcc BOOTSTRAP_FILE = bootstrap.asm INIT_KERNEL_FILES = starter.asm KERNEL_FILES = main.c KERNEL_FLAGS = -Wall -m32 -c -ffreestanding -fno-asynchronous-unwind-tables -fno-pie KERNEL_OBJECT = -o kernel.elf build: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f elf32 $(INIT_KERNEL_FILES) -o starter.o $(CC) $(KERNEL_FLAGS) $(KERNEL_FILES) $(KERNEL_OBJECT) ld -melf_i386 -Tlinker.ld starter.o kernel.elf -o 539kernel.elf objcopy -O binary 539kernel.elf 539kernel.bin dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=539kernel.bin of=kernel.img bs=512 count=5 dd seek=6 conv=sync if=/dev/zero of=kernel.img bs=512 count=2046 qemu-system-x86_64 -s kernel.img ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/bootstrap.asm ================================================ start: mov ax, 07C0h mov ds, ax mov si, title_string call print_string mov si, message_string call print_string call load_kernel_from_disk jmp 0900h:0000 load_kernel_from_disk: mov ax, [curr_sector_to_load] sub ax, 2 mov bx, 512d mul bx mov bx, ax mov ax, 0900h mov es, ax mov ah, 02h mov al, 1h mov ch, 0h mov cl, [curr_sector_to_load] mov dh, 0h mov dl, 80h int 13h jc kernel_load_error sub byte [number_of_sectors_to_load], 1 add byte [curr_sector_to_load], 1 cmp byte [number_of_sectors_to_load], 0 jne load_kernel_from_disk ret kernel_load_error: mov si, load_error_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je printing_finished int 10h jmp print_char printing_finished: mov al, 10d ; Print new line int 10h ; Reading current cursor position mov ah, 03h mov bh, 0 int 10h ; Move the cursor to the beginning mov ah, 02h mov dl, 0 int 10h ret title_string db 'The Bootloader of 539kernel.', 0 message_string db 'The kernel is loading...', 0 load_error_string db 'The kernel cannot be loaded', 0 number_of_sectors_to_load db 10d curr_sector_to_load db 2d times 510-($-$$) db 0 dw 0xAA55 ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/gdt.asm ================================================ gdt: null_descriptor : dw 0, 0, 0, 0 kernel_code_descriptor : dw 0xffff, 0x0000, 0x9a00, 0x00cf kernel_data_descriptor : dw 0xffff, 0x0000, 0x9200, 0x00cf userspace_code_descriptor : dw 0xffff, 0x0000, 0xfa00, 0x00cf userspace_data_descriptor : dw 0xffff, 0x0000, 0xf200, 0x00cf gdtr: gdt_size_in_bytes : dw ( 5 * 8 ) gdt_base_address : dd gdt ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/idt.asm ================================================ isr_0: cli push 0 jmp isr_basic isr_1: cli push 1 jmp isr_basic isr_2: cli push 2 jmp isr_basic isr_3: cli push 3 jmp isr_basic isr_4: cli push 4 jmp isr_basic isr_5: cli push 5 jmp isr_basic isr_6: cli push 6 jmp isr_basic isr_7: cli push 7 jmp isr_basic isr_8: cli push 8 jmp isr_basic isr_9: cli push 9 jmp isr_basic isr_10: cli push 10 jmp isr_basic isr_11: cli push 11 jmp isr_basic isr_12: cli push 12 jmp isr_basic isr_13: cli push 13 jmp isr_basic isr_14: cli push 14 jmp isr_basic isr_15: cli push 15 jmp isr_basic isr_16: cli push 16 jmp isr_basic isr_17: cli push 17 jmp isr_basic isr_18: cli push 18 jmp isr_basic isr_19: cli push 19 jmp isr_basic isr_20: cli push 20 jmp isr_basic isr_21: cli push 21 jmp isr_basic isr_22: cli push 22 jmp isr_basic isr_23: cli push 23 jmp isr_basic isr_24: cli push 24 jmp isr_basic isr_25: cli push 25 jmp isr_basic isr_26: cli push 26 jmp isr_basic isr_27: cli push 27 jmp isr_basic isr_28: cli push 28 jmp isr_basic isr_29: cli push 29 jmp isr_basic isr_30: cli push 30 jmp isr_basic isr_31: cli push 31 jmp isr_basic isr_32: cli push 32 jmp irq_basic isr_33: cli push 33 jmp irq_basic isr_34: cli push 34 jmp irq_basic isr_35: cli push 35 jmp irq_basic isr_36: cli push 36 jmp irq_basic isr_37: cli push 37 jmp irq_basic isr_38: cli push 38 jmp irq_basic isr_39: cli push 39 jmp irq_basic isr_40: cli push 40 jmp irq_basic isr_41: cli push 41 jmp irq_basic isr_42: cli push 42 jmp irq_basic isr_43: cli push 43 jmp irq_basic isr_44: cli push 44 jmp irq_basic isr_45: cli push 45 jmp irq_basic isr_46: cli push 46 jmp irq_basic isr_47: cli push 47 jmp irq_basic isr_48: cli push 48 jmp irq_basic isr_basic: call interrupt_handler pop eax sti iret irq_basic: call interrupt_handler mov al, 0x20 out 0x20, al cmp byte [esp], 40d jnge irq_basic_end mov al, 0xa0 out 0x20, al irq_basic_end: pop eax sti iret idt: dw isr_0, 8, 0x8e00, 0x0000 dw isr_1, 8, 0x8e00, 0x0000 dw isr_2, 8, 0x8e00, 0x0000 dw isr_3, 8, 0x8e00, 0x0000 dw isr_4, 8, 0x8e00, 0x0000 dw isr_5, 8, 0x8e00, 0x0000 dw isr_6, 8, 0x8e00, 0x0000 dw isr_7, 8, 0x8e00, 0x0000 dw isr_8, 8, 0x8e00, 0x0000 dw isr_9, 8, 0x8e00, 0x0000 dw isr_10, 8, 0x8e00, 0x0000 dw isr_11, 8, 0x8e00, 0x0000 dw isr_12, 8, 0x8e00, 0x0000 dw isr_13, 8, 0x8e00, 0x0000 dw isr_14, 8, 0x8e00, 0x0000 dw isr_15, 8, 0x8e00, 0x0000 dw isr_16, 8, 0x8e00, 0x0000 dw isr_17, 8, 0x8e00, 0x0000 dw isr_18, 8, 0x8e00, 0x0000 dw isr_19, 8, 0x8e00, 0x0000 dw isr_20, 8, 0x8e00, 0x0000 dw isr_21, 8, 0x8e00, 0x0000 dw isr_22, 8, 0x8e00, 0x0000 dw isr_23, 8, 0x8e00, 0x0000 dw isr_24, 8, 0x8e00, 0x0000 dw isr_25, 8, 0x8e00, 0x0000 dw isr_26, 8, 0x8e00, 0x0000 dw isr_27, 8, 0x8e00, 0x0000 dw isr_28, 8, 0x8e00, 0x0000 dw isr_29, 8, 0x8e00, 0x0000 dw isr_30, 8, 0x8e00, 0x0000 dw isr_31, 8, 0x8e00, 0x0000 dw isr_32, 8, 0x8e00, 0x0000 dw isr_33, 8, 0x8e00, 0x0000 dw isr_34, 8, 0x8e00, 0x0000 dw isr_35, 8, 0x8e00, 0x0000 dw isr_36, 8, 0x8e00, 0x0000 dw isr_37, 8, 0x8e00, 0x0000 dw isr_38, 8, 0x8e00, 0x0000 dw isr_39, 8, 0x8e00, 0x0000 dw isr_40, 8, 0x8e00, 0x0000 dw isr_41, 8, 0x8e00, 0x0000 dw isr_42, 8, 0x8e00, 0x0000 dw isr_43, 8, 0x8e00, 0x0000 dw isr_44, 8, 0x8e00, 0x0000 dw isr_45, 8, 0x8e00, 0x0000 dw isr_46, 8, 0x8e00, 0x0000 dw isr_47, 8, 0x8e00, 0x0000 dw isr_48, 8, 0x8e00, 0x0000 idtr: idt_size_in_bytes : dw idtr - idt idt_base_address : dd idt ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/linker.ld ================================================ /* Based on: http://www.jamesmolloy.co.uk/tutorial_html/1.-Environment%20setup.html */ SECTIONS { .text 0x09000 : { code = .; _code = .; __code = .; *(.text) } .data : { data = .; _data = .; __data = .; *(.data) *(.rodata) } .bss : { bss = .; _bss = .; __bss = .; *(.bss) } end = .; _end = .; __end = .; } ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/main.c ================================================ volatile unsigned char *video = 0xB8000; int nextTextPos = 0; int currLine = 0; void print( char * ); void println(); void printi( int ); void kernel_main() { print( "Welcome to 539kernel!" ); println(); print( "We are now in Protected-mode" ); println(); printi( 539 ); println(); while( 1 ); } void interrupt_handler( int interrupt_number ) { println(); print( "Interrupt Received " ); printi( interrupt_number ); } void print( char *str ) { int currCharLocationInVidMem, currColorLocationInVidMem; while ( *str != '\0' ) { currCharLocationInVidMem = nextTextPos * 2; currColorLocationInVidMem = currCharLocationInVidMem + 1; video[ currCharLocationInVidMem ] = *str; video[ currColorLocationInVidMem ] = 15; nextTextPos++; str++; } } void println() { nextTextPos = ++currLine * 80; } void printi( int number ) { char* digitToStr[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; if ( number >= 0 && number <= 9 ) { print( digitToStr[ number ] ); return; } else { int remaining = number % 10; number = number / 10; printi( number ); printi( remaining ); } } ================================================ FILE: evolution_by_versions/03_539kernel_progenitor/starter.asm ================================================ bits 16 extern kernel_main extern interrupt_handler start: mov ax, cs mov ds, ax call load_gdt call init_video_mode call enter_protected_mode call setup_interrupts call 08h:start_kernel load_gdt: cli lgdt [gdtr - start] ret enter_protected_mode: mov eax, cr0 or eax, 1 mov cr0, eax ret init_video_mode: mov ah, 0h mov al, 03h int 10h mov ah, 01h mov cx, 2000h int 10h ret setup_interrupts: call remap_pic call load_idt ret remap_pic: mov al, 11h send_init_cmd_to_pic_master: out 0x20, al send_init_cmd_to_pic_slave: out 0xa0, al ; ... ; make_irq_starts_from_intr_32_in_pic_master: mov al, 32d out 0x21, al make_irq_starts_from_intr_40_in_pic_slave: mov al, 40d out 0xa1, al ; ... ; tell_pic_master_where_pic_slave_is_connected: mov al, 04h out 0x21, al tell_pic_slave_where_pic_master_is_connected: mov al, 02h out 0xa1, al ; ... ; mov al, 01h tell_pic_master_the_arch_is_x86: out 0x21, al tell_pic_slave_the_arch_is_x86: out 0xa1, al ; ... ; mov al, 0h make_pic_master_enables_all_irqs: out 0x21, al make_pic_slave_enables_all_irqs: out 0xa1, al ; ... ; ret load_idt: lidt [idtr - start] ret bits 32 start_kernel: mov eax, 10h mov ds, eax mov ss, eax mov eax, 0h mov es, eax mov fs, eax mov gs, eax sti call kernel_main %include "gdt.asm" %include "idt.asm" ================================================ FILE: evolution_by_versions/04_539kernel_version_t/Makefile ================================================ ASM = nasm CC = gcc BOOTSTRAP_FILE = bootstrap.asm INIT_KERNEL_FILES = starter.asm KERNEL_FILES = main.c KERNEL_FLAGS = -Wall -m32 -c -ffreestanding -fno-asynchronous-unwind-tables -fno-pie KERNEL_OBJECT = -o kernel.elf build: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f elf32 $(INIT_KERNEL_FILES) -o starter.o $(CC) $(KERNEL_FLAGS) $(KERNEL_FILES) $(KERNEL_OBJECT) $(CC) $(KERNEL_FLAGS) screen.c -o screen.elf $(CC) $(KERNEL_FLAGS) process.c -o process.elf $(CC) $(KERNEL_FLAGS) scheduler.c -o scheduler.elf ld -melf_i386 -Tlinker.ld starter.o kernel.elf screen.elf process.elf scheduler.elf -o 539kernel.elf objcopy -O binary 539kernel.elf 539kernel.bin dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=539kernel.bin of=kernel.img bs=512 count=8 dd seek=9 conv=sync if=/dev/zero of=kernel.img bs=512 count=2046 qemu-system-x86_64 -s kernel.img ================================================ FILE: evolution_by_versions/04_539kernel_version_t/bootstrap.asm ================================================ start: mov ax, 07C0h mov ds, ax mov si, title_string call print_string mov si, message_string call print_string call load_kernel_from_disk jmp 0900h:0000 load_kernel_from_disk: mov ax, [curr_sector_to_load] sub ax, 2 mov bx, 512d mul bx mov bx, ax mov ax, 0900h mov es, ax mov ah, 02h mov al, 1h mov ch, 0h mov cl, [curr_sector_to_load] mov dh, 0h mov dl, 80h int 13h jc kernel_load_error sub byte [number_of_sectors_to_load], 1 add byte [curr_sector_to_load], 1 cmp byte [number_of_sectors_to_load], 0 jne load_kernel_from_disk ret kernel_load_error: mov si, load_error_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je printing_finished int 10h jmp print_char printing_finished: mov al, 10d ; Print new line int 10h ; Reading current cursor position mov ah, 03h mov bh, 0 int 10h ; Move the cursor to the beginning mov ah, 02h mov dl, 0 int 10h ret title_string db 'The Bootloader of 539kernel.', 0 message_string db 'The kernel is loading...', 0 load_error_string db 'The kernel cannot be loaded', 0 number_of_sectors_to_load db 10d curr_sector_to_load db 2d times 510-($-$$) db 0 dw 0xAA55 ================================================ FILE: evolution_by_versions/04_539kernel_version_t/gdt.asm ================================================ gdt: null_descriptor : dw 0, 0, 0, 0 kernel_code_descriptor : dw 0xffff, 0x0000, 0x9a00, 0x00cf kernel_data_descriptor : dw 0xffff, 0x0000, 0x9200, 0x00cf userspace_code_descriptor : dw 0xffff, 0x0000, 0xfa00, 0x00cf userspace_data_descriptor : dw 0xffff, 0x0000, 0xf200, 0x00cf tss_descriptor : dw tss + 3, tss, 0x8900, 0x0000 gdtr: gdt_size_in_bytes : dw ( 6 * 8 ) gdt_base_address : dd gdt ================================================ FILE: evolution_by_versions/04_539kernel_version_t/idt.asm ================================================ isr_0: cli push 0 jmp isr_basic isr_1: cli push 1 jmp isr_basic isr_2: cli push 2 jmp isr_basic isr_3: cli push 3 jmp isr_basic isr_4: cli push 4 jmp isr_basic isr_5: cli push 5 jmp isr_basic isr_6: cli push 6 jmp isr_basic isr_7: cli push 7 jmp isr_basic isr_8: cli push 8 jmp isr_basic isr_9: cli push 9 jmp isr_basic isr_10: cli push 10 jmp isr_basic isr_11: cli push 11 jmp isr_basic isr_12: cli push 12 jmp isr_basic isr_13: cli push 13 jmp isr_basic isr_14: cli push 14 jmp isr_basic isr_15: cli push 15 jmp isr_basic isr_16: cli push 16 jmp isr_basic isr_17: cli push 17 jmp isr_basic isr_18: cli push 18 jmp isr_basic isr_19: cli push 19 jmp isr_basic isr_20: cli push 20 jmp isr_basic isr_21: cli push 21 jmp isr_basic isr_22: cli push 22 jmp isr_basic isr_23: cli push 23 jmp isr_basic isr_24: cli push 24 jmp isr_basic isr_25: cli push 25 jmp isr_basic isr_26: cli push 26 jmp isr_basic isr_27: cli push 27 jmp isr_basic isr_28: cli push 28 jmp isr_basic isr_29: cli push 29 jmp isr_basic isr_30: cli push 30 jmp isr_basic isr_31: cli push 31 jmp isr_basic isr_32: ; Part 1 cli ; Step 1 pusha ; Step 2 ; Step 3 mov eax, [esp + 32] push eax call scheduler ; Step 4 ; ... ; ; Part 2 ; Step 5 mov al, 0x20 out 0x20, al ; Step 6 add esp, 40d push run_next_process iret ; Step 7 isr_33: cli push 33 jmp irq_basic isr_34: cli push 34 jmp irq_basic isr_35: cli push 35 jmp irq_basic isr_36: cli push 36 jmp irq_basic isr_37: cli push 37 jmp irq_basic isr_38: cli push 38 jmp irq_basic isr_39: cli push 39 jmp irq_basic isr_40: cli push 40 jmp irq_basic isr_41: cli push 41 jmp irq_basic isr_42: cli push 42 jmp irq_basic isr_43: cli push 43 jmp irq_basic isr_44: cli push 44 jmp irq_basic isr_45: cli push 45 jmp irq_basic isr_46: cli push 46 jmp irq_basic isr_47: cli push 47 jmp irq_basic isr_48: cli push 48 jmp irq_basic isr_basic: call interrupt_handler pop eax sti iret irq_basic: call interrupt_handler mov al, 0x20 out 0x20, al cmp byte [esp], 40d jnge irq_basic_end mov al, 0xa0 out 0x20, al irq_basic_end: pop eax sti iret idt: dw isr_0, 8, 0x8e00, 0x0000 dw isr_1, 8, 0x8e00, 0x0000 dw isr_2, 8, 0x8e00, 0x0000 dw isr_3, 8, 0x8e00, 0x0000 dw isr_4, 8, 0x8e00, 0x0000 dw isr_5, 8, 0x8e00, 0x0000 dw isr_6, 8, 0x8e00, 0x0000 dw isr_7, 8, 0x8e00, 0x0000 dw isr_8, 8, 0x8e00, 0x0000 dw isr_9, 8, 0x8e00, 0x0000 dw isr_10, 8, 0x8e00, 0x0000 dw isr_11, 8, 0x8e00, 0x0000 dw isr_12, 8, 0x8e00, 0x0000 dw isr_13, 8, 0x8e00, 0x0000 dw isr_14, 8, 0x8e00, 0x0000 dw isr_15, 8, 0x8e00, 0x0000 dw isr_16, 8, 0x8e00, 0x0000 dw isr_17, 8, 0x8e00, 0x0000 dw isr_18, 8, 0x8e00, 0x0000 dw isr_19, 8, 0x8e00, 0x0000 dw isr_20, 8, 0x8e00, 0x0000 dw isr_21, 8, 0x8e00, 0x0000 dw isr_22, 8, 0x8e00, 0x0000 dw isr_23, 8, 0x8e00, 0x0000 dw isr_24, 8, 0x8e00, 0x0000 dw isr_25, 8, 0x8e00, 0x0000 dw isr_26, 8, 0x8e00, 0x0000 dw isr_27, 8, 0x8e00, 0x0000 dw isr_28, 8, 0x8e00, 0x0000 dw isr_29, 8, 0x8e00, 0x0000 dw isr_30, 8, 0x8e00, 0x0000 dw isr_31, 8, 0x8e00, 0x0000 dw isr_32, 8, 0x8e00, 0x0000 dw isr_33, 8, 0x8e00, 0x0000 dw isr_34, 8, 0x8e00, 0x0000 dw isr_35, 8, 0x8e00, 0x0000 dw isr_36, 8, 0x8e00, 0x0000 dw isr_37, 8, 0x8e00, 0x0000 dw isr_38, 8, 0x8e00, 0x0000 dw isr_39, 8, 0x8e00, 0x0000 dw isr_40, 8, 0x8e00, 0x0000 dw isr_41, 8, 0x8e00, 0x0000 dw isr_42, 8, 0x8e00, 0x0000 dw isr_43, 8, 0x8e00, 0x0000 dw isr_44, 8, 0x8e00, 0x0000 dw isr_45, 8, 0x8e00, 0x0000 dw isr_46, 8, 0x8e00, 0x0000 dw isr_47, 8, 0x8e00, 0x0000 dw isr_48, 8, 0x8e00, 0x0000 idtr: idt_size_in_bytes : dw idtr - idt idt_base_address : dd idt ================================================ FILE: evolution_by_versions/04_539kernel_version_t/linker.ld ================================================ /* Based on: http://www.jamesmolloy.co.uk/tutorial_html/1.-Environment%20setup.html */ SECTIONS { .text 0x09000 : { code = .; _code = .; __code = .; *(.text) } .data : { data = .; _data = .; __data = .; *(.data) *(.rodata) } .bss : { bss = .; _bss = .; __bss = .; *(.bss) } end = .; _end = .; __end = .; } ================================================ FILE: evolution_by_versions/04_539kernel_version_t/main.c ================================================ #include "screen.h" #include "scheduler.h" void processA(); void processB(); void processC(); void processD(); void kernel_main() { process_t p1, p2, p3, p4; screen_init(); process_init(); scheduler_init(); print( "Welcome to 539kernel!" ); println(); print( "We are now in Protected-mode" ); println(); printi( 539 ); println(); process_create( &processA, &p1 ); process_create( &processB, &p2 ); process_create( &processC, &p3 ); process_create( &processD, &p4 ); while( 1 ); } void interrupt_handler( int interrupt_number ) { println(); print( "Interrupt Received " ); printi( interrupt_number ); } void processA() { print( "Process A," ); while ( 1 ) asm( "mov $5390, %eax" ); } void processB() { print( "Process B," ); while ( 1 ) asm( "mov $5391, %eax" ); } void processC() { print( "Process C," ); while ( 1 ) asm( "mov $5392, %eax" ); } void processD() { print( "Process D," ); while ( 1 ) asm( "mov $5393, %eax" ); } ================================================ FILE: evolution_by_versions/04_539kernel_version_t/process.c ================================================ #include "process.h" void process_init() { processes_count = 0; curr_pid = 0; } void process_create( int *base_address, process_t *process ) { process->pid = curr_pid++; process->context.eax = 0; process->context.ecx = 0; process->context.edx = 0; process->context.ebx = 0; process->context.esp = 0; process->context.ebp = 0; process->context.esi = 0; process->context.edi = 0; process->context.eip = base_address; process->state = READY; process->base_address = base_address; processes[ process->pid ] = process; processes_count++; } ================================================ FILE: evolution_by_versions/04_539kernel_version_t/process.h ================================================ typedef enum process_state { READY, RUNNING } process_state_t; typedef struct process_context { int eax, ecx, edx, ebx, esp, ebp, esi, edi, eip; } process_context_t; typedef struct process { int pid; process_context_t context; process_state_t state; int *base_address; } process_t; process_t *processes[ 15 ]; int processes_count, curr_pid; void process_init(); void process_create( int *, process_t * ); ================================================ FILE: evolution_by_versions/04_539kernel_version_t/scheduler.c ================================================ #include "scheduler.h" void scheduler_init() { next_sch_pid = 0; curr_sch_pid = 0; } process_t *get_next_process() { process_t *next_process = processes[ next_sch_pid ]; curr_sch_pid = next_sch_pid; next_sch_pid++; next_sch_pid = next_sch_pid % processes_count; return next_process; } void scheduler( int eip, int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax ) { process_t *curr_process; print( " EAX = " ); // For Testing Purpose printi( eax ); // For Testing Purpose // ... // // PART 1 curr_process = processes[ curr_sch_pid ]; next_process = get_next_process(); // ... // // PART 2 if ( curr_process->state == RUNNING ) { curr_process->context.eax = eax; curr_process->context.ecx = ecx; curr_process->context.edx = edx; curr_process->context.ebx = ebx; curr_process->context.esp = esp; curr_process->context.ebp = ebp; curr_process->context.esi = esi; curr_process->context.edi = edi; curr_process->context.eip = eip; } curr_process->state = READY; // ... // // PART 3 asm( " mov %0, %%eax; \ mov %0, %%ecx; \ mov %0, %%edx; \ mov %0, %%ebx; \ mov %0, %%esi; \ mov %0, %%edi;" : : "r" ( next_process->context.eax ), "r" ( next_process->context.ecx ), "r" ( next_process->context.edx ), "r" ( next_process->context.ebx ), "r" ( next_process->context.esi ), "r" ( next_process->context.edi ) ); next_process->state = RUNNING; } void run_next_process() { asm( " sti; \ jmp *%0" : : "r" ( next_process->context.eip ) ); } ================================================ FILE: evolution_by_versions/04_539kernel_version_t/scheduler.h ================================================ #include "process.h" int next_sch_pid, curr_sch_pid; process_t *next_process; void scheduler_init(); process_t *get_next_process(); void scheduler( int, int, int, int, int, int, int, int, int ); void run_next_process(); ================================================ FILE: evolution_by_versions/04_539kernel_version_t/screen.c ================================================ #include "screen.h" void screen_init() { video = 0xB8000; nextTextPos = 0; currLine = 0; } void print( char *str ) { int currCharLocationInVidMem, currColorLocationInVidMem; while ( *str != '\0' ) { currCharLocationInVidMem = nextTextPos * 2; currColorLocationInVidMem = currCharLocationInVidMem + 1; video[ currCharLocationInVidMem ] = *str; video[ currColorLocationInVidMem ] = 15; nextTextPos++; str++; } } void println() { nextTextPos = ++currLine * 80; } void printi( int number ) { char* digitToStr[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; if ( number >= 0 && number <= 9 ) { print( digitToStr[ number ] ); return; } else { int remaining = number % 10; number = number / 10; printi( number ); printi( remaining ); } } ================================================ FILE: evolution_by_versions/04_539kernel_version_t/screen.h ================================================ volatile unsigned char *video; int nextTextPos; int currLine; void screen_init(); void print( char * ); void println(); void printi( int ); ================================================ FILE: evolution_by_versions/04_539kernel_version_t/starter.asm ================================================ bits 16 extern kernel_main extern interrupt_handler extern scheduler extern run_next_process start: mov ax, cs mov ds, ax call load_gdt call init_video_mode call enter_protected_mode call setup_interrupts call load_task_register call 08h:start_kernel load_gdt: cli lgdt [gdtr - start] ret enter_protected_mode: mov eax, cr0 or eax, 1 mov cr0, eax ret init_video_mode: mov ah, 0h mov al, 03h int 10h mov ah, 01h mov cx, 2000h int 10h ret setup_interrupts: call remap_pic call load_idt ret remap_pic: mov al, 11h send_init_cmd_to_pic_master: out 0x20, al send_init_cmd_to_pic_slave: out 0xa0, al ; ... ; make_irq_starts_from_intr_32_in_pic_master: mov al, 32d out 0x21, al make_irq_starts_from_intr_40_in_pic_slave: mov al, 40d out 0xa1, al ; ... ; tell_pic_master_where_pic_slave_is_connected: mov al, 04h out 0x21, al tell_pic_slave_where_pic_master_is_connected: mov al, 02h out 0xa1, al ; ... ; mov al, 01h tell_pic_master_the_arch_is_x86: out 0x21, al tell_pic_slave_the_arch_is_x86: out 0xa1, al ; ... ; mov al, 0h make_pic_master_enables_all_irqs: out 0x21, al make_pic_slave_enables_all_irqs: out 0xa1, al ; ... ; ret load_idt: lidt [idtr - start] ret load_task_register: mov ax, 40d ltr ax ret bits 32 start_kernel: mov eax, 10h mov ds, eax mov ss, eax mov eax, 0h mov es, eax mov fs, eax mov gs, eax sti call kernel_main %include "gdt.asm" %include "idt.asm" tss: dd 0 ================================================ FILE: evolution_by_versions/05_539kernel_version_g/Makefile ================================================ ASM = nasm CC = gcc BOOTSTRAP_FILE = bootstrap.asm SIMPLE_KERNEL = simple_kernel.asm INIT_KERNEL_FILES = starter.asm KERNEL_FILES = main.c KERNEL_FLAGS = -Wall -m32 -c -ffreestanding -fno-asynchronous-unwind-tables -fno-pie KERNEL_OBJECT = -o kernel.elf build: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f elf32 $(INIT_KERNEL_FILES) -o starter.o $(CC) $(KERNEL_FLAGS) $(KERNEL_FILES) $(KERNEL_OBJECT) $(CC) $(KERNEL_FLAGS) screen.c -o screen.elf $(CC) $(KERNEL_FLAGS) process.c -o process.elf $(CC) $(KERNEL_FLAGS) scheduler.c -o scheduler.elf $(CC) $(KERNEL_FLAGS) heap.c -o heap.elf $(CC) $(KERNEL_FLAGS) paging.c -o paging.elf ld -melf_i386 -Tlinker.ld starter.o kernel.elf screen.elf process.elf scheduler.elf heap.elf paging.elf -o 539kernel.elf objcopy -O binary 539kernel.elf 539kernel.bin dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=539kernel.bin of=kernel.img bs=512 count=8 dd seek=9 conv=sync if=/dev/zero of=kernel.img bs=512 count=2046 qemu-system-x86_64 -s kernel.img ================================================ FILE: evolution_by_versions/05_539kernel_version_g/bootstrap.asm ================================================ start: mov ax, 07C0h mov ds, ax mov si, title_string call print_string mov si, message_string call print_string call load_kernel_from_disk jmp 0900h:0000 load_kernel_from_disk: mov ax, [curr_sector_to_load] sub ax, 2 mov bx, 512d mul bx mov bx, ax mov ax, 0900h mov es, ax mov ah, 02h mov al, 1h mov ch, 0h mov cl, [curr_sector_to_load] mov dh, 0h mov dl, 80h int 13h jc kernel_load_error sub byte [number_of_sectors_to_load], 1 add byte [curr_sector_to_load], 1 cmp byte [number_of_sectors_to_load], 0 jne load_kernel_from_disk ret kernel_load_error: mov si, load_error_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je printing_finished int 10h jmp print_char printing_finished: mov al, 10d ; Print new line int 10h ; Reading current cursor position mov ah, 03h mov bh, 0 int 10h ; Move the cursor to the beginning mov ah, 02h mov dl, 0 int 10h ret title_string db 'The Bootloader of 539kernel.', 0 message_string db 'The kernel is loading...', 0 load_error_string db 'The kernel cannot be loaded', 0 number_of_sectors_to_load db 10d curr_sector_to_load db 2d times 510-($-$$) db 0 dw 0xAA55 ================================================ FILE: evolution_by_versions/05_539kernel_version_g/gdt.asm ================================================ gdt: null_descriptor : dw 0, 0, 0, 0 kernel_code_descriptor : dw 0xffff, 0x0000, 0x9a00, 0x00cf kernel_data_descriptor : dw 0xffff, 0x0000, 0x9200, 0x00cf userspace_code_descriptor : dw 0xffff, 0x0000, 0xfa00, 0x00cf userspace_data_descriptor : dw 0xffff, 0x0000, 0xf200, 0x00cf tss_descriptor : dw tss + 3, tss, 0x8900, 0x0000 gdtr: gdt_size_in_bytes : dw ( 6 * 8 ) gdt_base_address : dd gdt ================================================ FILE: evolution_by_versions/05_539kernel_version_g/heap.c ================================================ #include "heap.h" void heap_init() { heap_base = 0x100000; } int kalloc( int bytes ) { unsigned int new_object_address = heap_base; heap_base += bytes; return new_object_address; } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/heap.h ================================================ unsigned int heap_base; void heap_init(); int kalloc( int ); ================================================ FILE: evolution_by_versions/05_539kernel_version_g/idt.asm ================================================ isr_0: cli push 0 jmp isr_basic isr_1: cli push 1 jmp isr_basic isr_2: cli push 2 jmp isr_basic isr_3: cli push 3 jmp isr_basic isr_4: cli push 4 jmp isr_basic isr_5: cli push 5 jmp isr_basic isr_6: cli push 6 jmp isr_basic isr_7: cli push 7 jmp isr_basic isr_8: cli push 8 jmp isr_basic isr_9: cli push 9 jmp isr_basic isr_10: cli push 10 jmp isr_basic isr_11: cli push 11 jmp isr_basic isr_12: cli push 12 jmp isr_basic isr_13: cli push 13 jmp isr_basic isr_14: cli push 14 jmp isr_basic isr_15: cli push 15 jmp isr_basic isr_16: cli push 16 jmp isr_basic isr_17: cli push 17 jmp isr_basic isr_18: cli push 18 jmp isr_basic isr_19: cli push 19 jmp isr_basic isr_20: cli push 20 jmp isr_basic isr_21: cli push 21 jmp isr_basic isr_22: cli push 22 jmp isr_basic isr_23: cli push 23 jmp isr_basic isr_24: cli push 24 jmp isr_basic isr_25: cli push 25 jmp isr_basic isr_26: cli push 26 jmp isr_basic isr_27: cli push 27 jmp isr_basic isr_28: cli push 28 jmp isr_basic isr_29: cli push 29 jmp isr_basic isr_30: cli push 30 jmp isr_basic isr_31: cli push 31 jmp isr_basic isr_32: ; Part 1 cli ; Step 1 pusha ; Step 2 ; Step 3 mov eax, [esp + 32] push eax call scheduler ; Step 4 ; ... ; ; Part 2 ; Step 5 mov al, 0x20 out 0x20, al ; Step 6 add esp, 40d push run_next_process iret ; Step 7 isr_33: cli push 33 jmp irq_basic isr_34: cli push 34 jmp irq_basic isr_35: cli push 35 jmp irq_basic isr_36: cli push 36 jmp irq_basic isr_37: cli push 37 jmp irq_basic isr_38: cli push 38 jmp irq_basic isr_39: cli push 39 jmp irq_basic isr_40: cli push 40 jmp irq_basic isr_41: cli push 41 jmp irq_basic isr_42: cli push 42 jmp irq_basic isr_43: cli push 43 jmp irq_basic isr_44: cli push 44 jmp irq_basic isr_45: cli push 45 jmp irq_basic isr_46: cli push 46 jmp irq_basic isr_47: cli push 47 jmp irq_basic isr_48: cli push 48 jmp irq_basic isr_basic: call interrupt_handler pop eax sti iret irq_basic: call interrupt_handler mov al, 0x20 out 0x20, al cmp byte [esp], 40d jnge irq_basic_end mov al, 0xa0 out 0x20, al irq_basic_end: pop eax sti iret idt: dw isr_0, 8, 0x8e00, 0x0000 dw isr_1, 8, 0x8e00, 0x0000 dw isr_2, 8, 0x8e00, 0x0000 dw isr_3, 8, 0x8e00, 0x0000 dw isr_4, 8, 0x8e00, 0x0000 dw isr_5, 8, 0x8e00, 0x0000 dw isr_6, 8, 0x8e00, 0x0000 dw isr_7, 8, 0x8e00, 0x0000 dw isr_8, 8, 0x8e00, 0x0000 dw isr_9, 8, 0x8e00, 0x0000 dw isr_10, 8, 0x8e00, 0x0000 dw isr_11, 8, 0x8e00, 0x0000 dw isr_12, 8, 0x8e00, 0x0000 dw isr_13, 8, 0x8e00, 0x0000 dw isr_14, 8, 0x8e00, 0x0000 dw isr_15, 8, 0x8e00, 0x0000 dw isr_16, 8, 0x8e00, 0x0000 dw isr_17, 8, 0x8e00, 0x0000 dw isr_18, 8, 0x8e00, 0x0000 dw isr_19, 8, 0x8e00, 0x0000 dw isr_20, 8, 0x8e00, 0x0000 dw isr_21, 8, 0x8e00, 0x0000 dw isr_22, 8, 0x8e00, 0x0000 dw isr_23, 8, 0x8e00, 0x0000 dw isr_24, 8, 0x8e00, 0x0000 dw isr_25, 8, 0x8e00, 0x0000 dw isr_26, 8, 0x8e00, 0x0000 dw isr_27, 8, 0x8e00, 0x0000 dw isr_28, 8, 0x8e00, 0x0000 dw isr_29, 8, 0x8e00, 0x0000 dw isr_30, 8, 0x8e00, 0x0000 dw isr_31, 8, 0x8e00, 0x0000 dw isr_32, 8, 0x8e00, 0x0000 dw isr_33, 8, 0x8e00, 0x0000 dw isr_34, 8, 0x8e00, 0x0000 dw isr_35, 8, 0x8e00, 0x0000 dw isr_36, 8, 0x8e00, 0x0000 dw isr_37, 8, 0x8e00, 0x0000 dw isr_38, 8, 0x8e00, 0x0000 dw isr_39, 8, 0x8e00, 0x0000 dw isr_40, 8, 0x8e00, 0x0000 dw isr_41, 8, 0x8e00, 0x0000 dw isr_42, 8, 0x8e00, 0x0000 dw isr_43, 8, 0x8e00, 0x0000 dw isr_44, 8, 0x8e00, 0x0000 dw isr_45, 8, 0x8e00, 0x0000 dw isr_46, 8, 0x8e00, 0x0000 dw isr_47, 8, 0x8e00, 0x0000 dw isr_48, 8, 0x8e00, 0x0000 idtr: idt_size_in_bytes : dw idtr - idt idt_base_address : dd idt ================================================ FILE: evolution_by_versions/05_539kernel_version_g/linker.ld ================================================ /* Based on: http://www.jamesmolloy.co.uk/tutorial_html/1.-Environment%20setup.html */ SECTIONS { .text 0x09000 : { code = .; _code = .; __code = .; *(.text) } .data : { data = .; _data = .; __data = .; *(.data) *(.rodata) } .bss : { bss = .; _bss = .; __bss = .; *(.bss) } end = .; _end = .; __end = .; } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/main.c ================================================ #include "screen.h" #include "scheduler.h" #include "heap.h" void processA(); void processB(); void processC(); void processD(); void kernel_main() { heap_init(); paging_init(); screen_init(); process_init(); scheduler_init(); print( "Welcome to 539kernel!" ); println(); print( "We are now in Protected-mode" ); println(); printi( 539 ); println(); process_create( &processA ); process_create( &processB ); process_create( &processC ); process_create( &processD ); while( 1 ); } void interrupt_handler( int interrupt_number ) { println(); print( "Interrupt Received " ); printi( interrupt_number ); } void processA() { print( "Process A," ); while ( 1 ) asm( "mov $5390, %eax" ); } void processB() { print( "Process B," ); while ( 1 ) asm( "mov $5391, %eax" ); } void processC() { print( "Process C," ); while ( 1 ) asm( "mov $5392, %eax" ); } void processD() { print( "Process D," ); while ( 1 ) asm( "mov $5393, %eax" ); } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/paging.c ================================================ #include "paging.h" int create_page_entry( int base_address, char present, char writable, char privilege_level, char cache_enabled, char write_through_cache, char accessed, char page_size, char dirty ) { int entry = 0; entry |= present; entry |= writable << 1; entry |= privilege_level << 2; entry |= write_through_cache << 3; entry |= cache_enabled << 4; entry |= accessed << 5; entry |= dirty << 6; entry |= page_size << 7; return base_address | entry; } void paging_init() { // PART 1: unsigned int curr_page_frame = 0; page_directory = kalloc( 4 * 1024 ); for ( int currPDE = 0; currPDE < PDE_NUM; currPDE++ ) { unsigned int *pagetable = kalloc( 4 * PTE_NUM ); for ( int currPTE = 0; currPTE < PTE_NUM; currPTE++, curr_page_frame++ ) pagetable[ currPTE ] = create_page_entry( curr_page_frame * 4096, 1, 0, 0, 1, 1, 0, 0, 0 ); page_directory[ currPDE ] = create_page_entry( pagetable, 1, 0, 0, 1, 1, 0, 0, 0 ); } // ... // // PART 2 load_page_directory(); enable_paging(); } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/paging.h ================================================ #define PDE_NUM 3 #define PTE_NUM 1024 extern void load_page_directory(); extern void enable_paging(); unsigned int *page_directory; void paging_init(); int create_page_entry( int, char, char, char, char, char, char, char, char ); ================================================ FILE: evolution_by_versions/05_539kernel_version_g/process.c ================================================ #include "process.h" void process_init() { processes_count = 0; curr_pid = 0; } process_t *process_create( int *base_address ) { process_t *process = kalloc( sizeof( process_t ) ); process->pid = curr_pid++; process->context.eax = 0; process->context.ecx = 0; process->context.edx = 0; process->context.ebx = 0; process->context.esp = 0; process->context.ebp = 0; process->context.esi = 0; process->context.edi = 0; process->context.eip = base_address; process->state = READY; process->base_address = base_address; processes[ process->pid ] = process; processes_count++; } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/process.h ================================================ typedef enum process_state { READY, RUNNING } process_state_t; typedef struct process_context { int eax, ecx, edx, ebx, esp, ebp, esi, edi, eip; } process_context_t; typedef struct process { int pid; process_context_t context; process_state_t state; int *base_address; } process_t; process_t *processes[ 15 ]; int processes_count, curr_pid; void process_init(); process_t *process_create( int * ); ================================================ FILE: evolution_by_versions/05_539kernel_version_g/scheduler.c ================================================ #include "scheduler.h" void scheduler_init() { next_sch_pid = 0; curr_sch_pid = 0; } process_t *get_next_process() { process_t *next_process = processes[ next_sch_pid ]; curr_sch_pid = next_sch_pid; next_sch_pid++; next_sch_pid = next_sch_pid % processes_count; return next_process; } void scheduler( int eip, int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax ) { process_t *curr_process; print( " EAX = " ); // For Testing Purpose printi( eax ); // For Testing Purpose // ... // // PART 1 curr_process = processes[ curr_sch_pid ]; next_process = get_next_process(); // ... // // PART 2 if ( curr_process->state == RUNNING ) { curr_process->context.eax = eax; curr_process->context.ecx = ecx; curr_process->context.edx = edx; curr_process->context.ebx = ebx; curr_process->context.esp = esp; curr_process->context.ebp = ebp; curr_process->context.esi = esi; curr_process->context.edi = edi; curr_process->context.eip = eip; } curr_process->state = READY; // ... // // PART 3 asm( " mov %0, %%eax; \ mov %0, %%ecx; \ mov %0, %%edx; \ mov %0, %%ebx; \ mov %0, %%esi; \ mov %0, %%edi;" : : "r" ( next_process->context.eax ), "r" ( next_process->context.ecx ), "r" ( next_process->context.edx ), "r" ( next_process->context.ebx ), "r" ( next_process->context.esi ), "r" ( next_process->context.edi ) ); next_process->state = RUNNING; } void run_next_process() { asm( " sti; \ jmp *%0" : : "r" ( next_process->context.eip ) ); } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/scheduler.h ================================================ #include "process.h" int next_sch_pid, curr_sch_pid; process_t *next_process; void scheduler_init(); process_t *get_next_process(); void scheduler( int, int, int, int, int, int, int, int, int ); void run_next_process(); ================================================ FILE: evolution_by_versions/05_539kernel_version_g/screen.c ================================================ #include "screen.h" void screen_init() { video = 0xB8000; nextTextPos = 0; currLine = 0; } void print( char *str ) { int currCharLocationInVidMem, currColorLocationInVidMem; while ( *str != '\0' ) { currCharLocationInVidMem = nextTextPos * 2; currColorLocationInVidMem = currCharLocationInVidMem + 1; video[ currCharLocationInVidMem ] = *str; video[ currColorLocationInVidMem ] = 15; nextTextPos++; str++; } } void println() { nextTextPos = ++currLine * 80; } void printi( int number ) { char* digitToStr[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; if ( number >= 0 && number <= 9 ) { print( digitToStr[ number ] ); return; } else { int remaining = number % 10; number = number / 10; printi( number ); printi( remaining ); } } ================================================ FILE: evolution_by_versions/05_539kernel_version_g/screen.h ================================================ volatile unsigned char *video; int nextTextPos; int currLine; void screen_init(); void print( char * ); void println(); void printi( int ); ================================================ FILE: evolution_by_versions/05_539kernel_version_g/starter.asm ================================================ bits 16 extern kernel_main extern interrupt_handler extern scheduler extern run_next_process extern page_directory global load_page_directory global enable_paging start: mov ax, cs mov ds, ax call load_gdt call init_video_mode call enter_protected_mode call setup_interrupts call load_task_register call 08h:start_kernel load_gdt: cli lgdt [gdtr - start] ret enter_protected_mode: mov eax, cr0 or eax, 1 mov cr0, eax ret init_video_mode: mov ah, 0h mov al, 03h int 10h mov ah, 01h mov cx, 2000h int 10h ret setup_interrupts: call remap_pic call load_idt ret remap_pic: mov al, 11h send_init_cmd_to_pic_master: out 0x20, al send_init_cmd_to_pic_slave: out 0xa0, al ; ... ; make_irq_starts_from_intr_32_in_pic_master: mov al, 32d out 0x21, al make_irq_starts_from_intr_40_in_pic_slave: mov al, 40d out 0xa1, al ; ... ; tell_pic_master_where_pic_slave_is_connected: mov al, 04h out 0x21, al tell_pic_slave_where_pic_master_is_connected: mov al, 02h out 0xa1, al ; ... ; mov al, 01h tell_pic_master_the_arch_is_x86: out 0x21, al tell_pic_slave_the_arch_is_x86: out 0xa1, al ; ... ; mov al, 0h make_pic_master_enables_all_irqs: out 0x21, al make_pic_slave_enables_all_irqs: out 0xa1, al ; ... ; ret load_idt: lidt [idtr - start] ret load_task_register: mov ax, 40d ltr ax ret bits 32 load_page_directory: mov eax, [page_directory] mov cr3, eax ret enable_paging: mov eax, cr0 or eax, 80000000h mov cr0, eax ret start_kernel: mov eax, 10h mov ds, eax mov ss, eax mov eax, 0h mov es, eax mov fs, eax mov gs, eax sti call kernel_main %include "gdt.asm" %include "idt.asm" tss: dd 0 ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/Makefile ================================================ ASM = nasm CC = gcc BOOTSTRAP_FILE = bootstrap.asm SIMPLE_KERNEL = simple_kernel.asm INIT_KERNEL_FILES = starter.asm KERNEL_FILES = main.c KERNEL_FLAGS = -Wall -m32 -c -ffreestanding -fno-asynchronous-unwind-tables -fno-pie KERNEL_OBJECT = -o kernel.elf build: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f elf32 $(INIT_KERNEL_FILES) -o starter.o $(CC) $(KERNEL_FLAGS) $(KERNEL_FILES) $(KERNEL_OBJECT) $(CC) $(KERNEL_FLAGS) screen.c -o screen.elf $(CC) $(KERNEL_FLAGS) process.c -o process.elf $(CC) $(KERNEL_FLAGS) scheduler.c -o scheduler.elf $(CC) $(KERNEL_FLAGS) heap.c -o heap.elf $(CC) $(KERNEL_FLAGS) paging.c -o paging.elf $(CC) $(KERNEL_FLAGS) ata.c -o ata.elf $(CC) $(KERNEL_FLAGS) str.c -o str.elf $(CC) $(KERNEL_FLAGS) filesystem.c -o filesystem.elf ld -melf_i386 -Tlinker.ld starter.o kernel.elf screen.elf process.elf scheduler.elf heap.elf paging.elf ata.elf str.elf filesystem.elf -o 539kernel.elf objcopy -O binary 539kernel.elf 539kernel.bin dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=539kernel.bin of=kernel.img bs=512 count=20 dd seek=21 conv=sync if=/dev/zero of=kernel.img bs=512 count=2046 bochs -f bochs #qemu-system-x86_64 -machine pc kernel.img ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/ata.c ================================================ #include "ata.h" void wait_drive_until_ready() { int status = 0; do { status = dev_read( BASE_PORT + 7 ); } while ( ( status ^ 0x80 ) == 128 ); } void *read_disk_chs( int sector ) { // Part 1 dev_write( BASE_PORT + 6, 0x0a0 ); dev_write( BASE_PORT + 2, 1 ); dev_write( BASE_PORT + 3, sector ); dev_write( BASE_PORT + 4, 0 ); dev_write( BASE_PORT + 5, 0 ); dev_write( BASE_PORT + 7, 0x20 ); // ... // // Part 2 wait_drive_until_ready(); // ... // // Part 3 short *buffer = kalloc( SECTOR_SIZE ); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) buffer[ currByte ] = dev_read( BASE_PORT ); return buffer; } void *read_disk( int address ) { dev_write( BASE_PORT + 6, ( 0x0e0 | ( ( address & 0x0F000000 ) >> 24 ) ) ); dev_write( BASE_PORT + 2, 1 ); dev_write( BASE_PORT + 3, address & 0x000000FF ); dev_write( BASE_PORT + 4, ( address & 0x0000FF00 ) >> 8 ); dev_write( BASE_PORT + 5, ( address & 0x00FF0000 ) >> 16 ); dev_write( BASE_PORT + 7, 0x20 ); // ... // wait_drive_until_ready(); // ... // short *buffer = kalloc( SECTOR_SIZE ); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) buffer[ currByte ] = dev_read( BASE_PORT ); return buffer; } void write_disk_chs( int sector, short *buffer ) { dev_write( BASE_PORT + 6, 0x0a0 ); dev_write( BASE_PORT + 2, 1 ); dev_write( BASE_PORT + 3, sector ); dev_write( BASE_PORT + 4, 0 ); dev_write( BASE_PORT + 5, 0 ); dev_write( BASE_PORT + 7, 0x30 ); wait_drive_until_ready(); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) dev_write_word( BASE_PORT, buffer[ currByte ] ); wait_drive_until_ready(); } void write_disk( int address, short *buffer ) { dev_write( BASE_PORT + 6, ( 0x0e0 | ( ( address & 0x0F000000 ) >> 24 ) ) ); dev_write( BASE_PORT + 2, 1 ); dev_write( BASE_PORT + 3, address & 0x000000FF ); dev_write( BASE_PORT + 4, ( address & 0x0000FF00 ) >> 8 ); dev_write( BASE_PORT + 5, ( address & 0x00FF0000 ) >> 16 ); dev_write( BASE_PORT + 7, 0x30 ); wait_drive_until_ready(); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) dev_write_word( BASE_PORT, buffer[ currByte ] ); wait_drive_until_ready(); } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/ata.h ================================================ #define BASE_PORT 0x1F0 #define SECTOR_SIZE 512 void wait_drive_until_ready(); void *read_disk( int ); void write_disk( int, short * ); void *read_disk_chs( int ); void write_disk_chs( int, short * ); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/bochs ================================================ plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, gameport=1, iodebug=1 config_interface: textconfig display_library: x, options="gui_debug" memory: host=32, guest=32 romimage: file="/usr/share/bochs/BIOS-bochs-latest" vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" boot: disk ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 ata0-master: type=disk, mode=flat, translation=auto, path="kernel.img", cylinders=2, heads=16, spt=63, biosdetect=auto, model="Generic 1234" pci: enabled=1, chipset=i440fx vga: extension=vbe, update_freq=5 cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 cpuid: family=6, model=0x03, stepping=3, mmx=1, apic=xapic, sse=sse2, sse4a=0, sep=1, aes=0, xsave=0, xsaveopt=0, movbe=0, adx=0, smep=0, avx=0, avx_f16c=0, avx_fma=0, bmi=0, xop=0, tbm=0, fma4=0, vmx=1, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0, mwait=1 cpuid: vendor_string="GenuineIntel" cpuid: brand_string=" Intel(R) Pentium(R) 4 CPU " ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/bootstrap.asm ================================================ start: mov ax, 07C0h mov ds, ax mov si, title_string call print_string mov si, message_string call print_string call load_kernel_from_disk jmp 0900h:0000 load_kernel_from_disk: mov ax, [curr_sector_to_load] sub ax, 2 mov bx, 512d mul bx mov bx, ax mov ax, 0900h mov es, ax mov ah, 02h mov al, 1h mov ch, 0h mov cl, [curr_sector_to_load] mov dh, 0h mov dl, 80h int 13h jc kernel_load_error sub byte [number_of_sectors_to_load], 1 add byte [curr_sector_to_load], 1 cmp byte [number_of_sectors_to_load], 0 jne load_kernel_from_disk ret kernel_load_error: mov si, load_error_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je printing_finished int 10h jmp print_char printing_finished: mov al, 10d ; Print new line int 10h ; Reading current cursor position mov ah, 03h mov bh, 0 int 10h ; Move the cursor to the beginning mov ah, 02h mov dl, 0 int 10h ret title_string db 'The Bootloader of 539kernel.', 0 message_string db 'The kernel is loading...', 0 load_error_string db 'The kernel cannot be loaded', 0 number_of_sectors_to_load db 15d curr_sector_to_load db 2d times 510-($-$$) db 0 dw 0xAA55 ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/filesystem.c ================================================ #include "filesystem.h" void filesystem_init() { base_block = read_disk( BASE_BLOCK_ADDRESS ); } void create_file( char *filename, char *buffer ) { int metadata_lba = ( base_block->head == 0 ) ? BASE_BLOCK_ADDRESS + 1 : base_block->tail + 2; int file_lba = metadata_lba + 1; metadata_t *metadata = kalloc( sizeof( metadata_t ) ); metadata->next_file_address = 0; int currIdx; for ( currIdx = 0; *filename != '\0' && currIdx < FILENAME_LENGTH - 1; currIdx++, filename++ ) metadata->filename[ currIdx ] = *filename; metadata->filename[ currIdx ] = '\0'; write_disk( metadata_lba, metadata ); write_disk( file_lba, buffer ); if ( base_block->head == 0 ) { update_base_block( metadata_lba, metadata_lba ); } else { metadata_t *tail_metadata = load_metadata( base_block->tail ); tail_metadata->next_file_address = metadata_lba; write_disk( base_block->tail, tail_metadata ); update_base_block( base_block->head, metadata_lba ); } } char **list_files() { // Part 1 if ( base_block->head == 0 ) return -1; // Part 2 char **list; list = kalloc( get_files_number() * sizeof( char * ) ); // Part 3 metadata_t *curr_file = load_metadata( base_block->head ); int idx = 0; while ( 1 ) { list[ idx ] = curr_file->filename; if ( curr_file->next_file_address == 0 ) break; curr_file = load_metadata( curr_file->next_file_address ); idx++; } return list; } char *read_file( char *filename ) { int address = get_address_by_filename( filename ); if ( address == 0 ) return 0; char *buffer = read_disk( address + 1 ); return buffer; } void delete_file( char *filename ) { // Part 1 int curr_file_address = get_address_by_filename( filename ); if ( curr_file_address == 0 ) return; metadata_t *curr_file_metadata = read_disk( curr_file_address ); // Part 2 if ( get_files_number() == 1 ) { update_base_block( 0, 0 ); return; } // Part 3 if ( curr_file_address == base_block->head ) { update_base_block( curr_file_metadata->next_file_address, base_block->tail ); } // Part 4 else { int prev_file_address = get_prev_file_address( curr_file_address ); metadata_t *prev_file = load_metadata( prev_file_address ); prev_file->next_file_address = curr_file_metadata->next_file_address; write_disk( prev_file_address, prev_file ); if ( curr_file_address == base_block->tail ) update_base_block( base_block->head, prev_file_address ); } } // ... // void update_base_block( int new_head, int new_tail ) { base_block->head = new_head; base_block->tail = new_tail; write_disk( BASE_BLOCK_ADDRESS, base_block ); } metadata_t *load_metadata( int address ) { metadata_t *metadata = read_disk( address ); return metadata; } int get_address_by_filename( char *filename ) { metadata_t *curr_file = load_metadata( base_block->head ); int curr_file_address = base_block->head; int idx = 0; while ( 1 ) { if ( strcmp( curr_file->filename, filename ) == 1 ) return curr_file_address; if ( curr_file->next_file_address == 0 ) break; curr_file_address = curr_file->next_file_address; curr_file = load_metadata( curr_file->next_file_address ); } return 0; } int get_prev_file_address( int address ) { metadata_t *prev_file = load_metadata( base_block->head ); int prev_file_address = base_block->head; while ( 1 ) { if ( prev_file->next_file_address == address ) return prev_file_address; prev_file_address = prev_file->next_file_address; prev_file = load_metadata( prev_file->next_file_address ); } return -1; } int get_files_number() { if ( base_block->head == 0 ) return 0; int files_number = 0; // ... // metadata_t *curr_file = load_metadata( base_block->head ); while ( 1 ) { files_number++; if ( curr_file->next_file_address == 0 ) break; curr_file = load_metadata( curr_file->next_file_address ); } return files_number; } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/filesystem.h ================================================ #define BASE_BLOCK_ADDRESS 100 #define FILENAME_LENGTH 256 typedef struct { int head, tail; } base_block_t; typedef struct { char filename[ FILENAME_LENGTH ]; int next_file_address; } metadata_t; base_block_t *base_block; void filesystem_init(); void create_file( char *, char * ); char **list_files(); char *read_file( char * ); // Auxiliary Functions metadata_t *load_metadata( int ); int get_address_by_filename( char * ); int get_prev_file_address( int ); int get_files_number(); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/gdt.asm ================================================ gdt: null_descriptor : dw 0, 0, 0, 0 kernel_code_descriptor : dw 0xffff, 0x0000, 0x9a00, 0x00cf kernel_data_descriptor : dw 0xffff, 0x0000, 0x9200, 0x00cf userspace_code_descriptor : dw 0xffff, 0x0000, 0xfa00, 0x00cf userspace_data_descriptor : dw 0xffff, 0x0000, 0xf200, 0x00cf tss_descriptor : dw tss + 3, tss, 0x8900, 0x0000 gdtr: gdt_size_in_bytes : dw ( 6 * 8 ) gdt_base_address : dd gdt ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/heap.c ================================================ #include "heap.h" void heap_init() { heap_base = 0x100000; } int kalloc( int bytes ) { unsigned int new_object_address = heap_base; heap_base += bytes; return new_object_address; } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/heap.h ================================================ unsigned int heap_base; void heap_init(); int kalloc( int ); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/idt.asm ================================================ isr_0: cli push 0 jmp isr_basic isr_1: cli push 1 jmp isr_basic isr_2: cli push 2 jmp isr_basic isr_3: cli push 3 jmp isr_basic isr_4: cli push 4 jmp isr_basic isr_5: cli push 5 jmp isr_basic isr_6: cli push 6 jmp isr_basic isr_7: cli push 7 jmp isr_basic isr_8: cli push 8 jmp isr_basic isr_9: cli push 9 jmp isr_basic isr_10: cli push 10 jmp isr_basic isr_11: cli push 11 jmp isr_basic isr_12: cli push 12 jmp isr_basic isr_13: cli push 13 jmp isr_basic isr_14: cli push 14 jmp isr_basic isr_15: cli push 15 jmp isr_basic isr_16: cli push 16 jmp isr_basic isr_17: cli push 17 jmp isr_basic isr_18: cli push 18 jmp isr_basic isr_19: cli push 19 jmp isr_basic isr_20: cli push 20 jmp isr_basic isr_21: cli push 21 jmp isr_basic isr_22: cli push 22 jmp isr_basic isr_23: cli push 23 jmp isr_basic isr_24: cli push 24 jmp isr_basic isr_25: cli push 25 jmp isr_basic isr_26: cli push 26 jmp isr_basic isr_27: cli push 27 jmp isr_basic isr_28: cli push 28 jmp isr_basic isr_29: cli push 29 jmp isr_basic isr_30: cli push 30 jmp isr_basic isr_31: cli push 31 jmp isr_basic isr_32: ; Part 1 cli ; Step 1 pusha ; Step 2 ; Step 3 mov eax, [esp + 32] push eax call scheduler ; Step 4 ; ... ; ; Part 2 ; Step 5 mov al, 0x20 out 0x20, al ; Step 6 add esp, 40d push run_next_process iret ; Step 7 isr_33: cli push 33 jmp irq_basic isr_34: cli push 34 jmp irq_basic isr_35: cli push 35 jmp irq_basic isr_36: cli push 36 jmp irq_basic isr_37: cli push 37 jmp irq_basic isr_38: cli push 38 jmp irq_basic isr_39: cli push 39 jmp irq_basic isr_40: cli push 40 jmp irq_basic isr_41: cli push 41 jmp irq_basic isr_42: cli push 42 jmp irq_basic isr_43: cli push 43 jmp irq_basic isr_44: cli push 44 jmp irq_basic isr_45: cli push 45 jmp irq_basic isr_46: cli push 46 jmp irq_basic isr_47: cli push 47 jmp irq_basic isr_48: cli push 48 jmp irq_basic isr_basic: call interrupt_handler pop eax sti iret irq_basic: call interrupt_handler mov al, 0x20 out 0x20, al cmp byte [esp], 40d jnge irq_basic_end mov al, 0xa0 out 0x20, al irq_basic_end: pop eax sti iret idt: dw isr_0, 8, 0x8e00, 0x0000 dw isr_1, 8, 0x8e00, 0x0000 dw isr_2, 8, 0x8e00, 0x0000 dw isr_3, 8, 0x8e00, 0x0000 dw isr_4, 8, 0x8e00, 0x0000 dw isr_5, 8, 0x8e00, 0x0000 dw isr_6, 8, 0x8e00, 0x0000 dw isr_7, 8, 0x8e00, 0x0000 dw isr_8, 8, 0x8e00, 0x0000 dw isr_9, 8, 0x8e00, 0x0000 dw isr_10, 8, 0x8e00, 0x0000 dw isr_11, 8, 0x8e00, 0x0000 dw isr_12, 8, 0x8e00, 0x0000 dw isr_13, 8, 0x8e00, 0x0000 dw isr_14, 8, 0x8e00, 0x0000 dw isr_15, 8, 0x8e00, 0x0000 dw isr_16, 8, 0x8e00, 0x0000 dw isr_17, 8, 0x8e00, 0x0000 dw isr_18, 8, 0x8e00, 0x0000 dw isr_19, 8, 0x8e00, 0x0000 dw isr_20, 8, 0x8e00, 0x0000 dw isr_21, 8, 0x8e00, 0x0000 dw isr_22, 8, 0x8e00, 0x0000 dw isr_23, 8, 0x8e00, 0x0000 dw isr_24, 8, 0x8e00, 0x0000 dw isr_25, 8, 0x8e00, 0x0000 dw isr_26, 8, 0x8e00, 0x0000 dw isr_27, 8, 0x8e00, 0x0000 dw isr_28, 8, 0x8e00, 0x0000 dw isr_29, 8, 0x8e00, 0x0000 dw isr_30, 8, 0x8e00, 0x0000 dw isr_31, 8, 0x8e00, 0x0000 dw isr_32, 8, 0x8e00, 0x0000 dw isr_33, 8, 0x8e00, 0x0000 dw isr_34, 8, 0x8e00, 0x0000 dw isr_35, 8, 0x8e00, 0x0000 dw isr_36, 8, 0x8e00, 0x0000 dw isr_37, 8, 0x8e00, 0x0000 dw isr_38, 8, 0x8e00, 0x0000 dw isr_39, 8, 0x8e00, 0x0000 dw isr_40, 8, 0x8e00, 0x0000 dw isr_41, 8, 0x8e00, 0x0000 dw isr_42, 8, 0x8e00, 0x0000 dw isr_43, 8, 0x8e00, 0x0000 dw isr_44, 8, 0x8e00, 0x0000 dw isr_45, 8, 0x8e00, 0x0000 dw isr_46, 8, 0x8e00, 0x0000 dw isr_47, 8, 0x8e00, 0x0000 dw isr_48, 8, 0x8e00, 0x0000 idtr: idt_size_in_bytes : dw idtr - idt idt_base_address : dd idt ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/linker.ld ================================================ /* Based on: http://www.jamesmolloy.co.uk/tutorial_html/1.-Environment%20setup.html */ SECTIONS { .text 0x09000 : { code = .; _code = .; __code = .; *(.text) } .data : { data = .; _data = .; __data = .; *(.data) *(.rodata) } .bss : { bss = .; _bss = .; __bss = .; *(.bss) } end = .; _end = .; __end = .; } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/main.c ================================================ #include "screen.h" #include "scheduler.h" #include "heap.h" void processA(); void processB(); void processC(); void processD(); void print_fs(); void kernel_main() { heap_init(); paging_init(); screen_init(); process_init(); scheduler_init(); filesystem_init(); print( "Welcome to 539kernel!" ); println(); print( "We are now in Protected-mode" ); println(); printi( 539 ); println(); process_create( &processA ); process_create( &processB ); process_create( &processC ); process_create( &processD ); // ... // char *data = kalloc( 512 ); strcpy( data, "The content of the first file on 539filesystem" ); create_file( "first_file", data ); // ... // char *data2 = kalloc( 512 ); strcpy( data2, "SECOND FILE in 539filesystem" ); create_file( "second_file", data2 ); // ... // char *data3 = kalloc( 512 ); strcpy( data3, "THIRD FILE in 539filesystem" ); create_file( "third_file", data3 ); // ... // print( read_file( "first_file" ) ); println(); print( read_file( "second_file" ) ); println(); print( read_file( "third_file" ) ); println(); // ... // print_fs(); delete_file( "first_file" ); print_fs(); // ... // while( 1 ); } void print_fs() { char **files = list_files(); for ( int currIdx = 0; currIdx < get_files_number(); currIdx++ ) { print( "File: " ); print( files[ currIdx ] ); println(); } print( "==" ); println(); } void interrupt_handler( int interrupt_number ) { //println(); //print( "Interrupt Received " ); //printi( interrupt_number ); } void processA() { print( "Process A," ); while ( 1 ) asm( "mov $5390, %eax" ); } void processB() { print( "Process B," ); while ( 1 ) asm( "mov $5391, %eax" ); } void processC() { print( "Process C," ); while ( 1 ) asm( "mov $5392, %eax" ); } void processD() { print( "Process D," ); while ( 1 ) asm( "mov $5393, %eax" ); } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/paging.c ================================================ #include "paging.h" int create_page_entry( int base_address, char present, char writable, char privilege_level, char cache_enabled, char write_through_cache, char accessed, char page_size, char dirty ) { int entry = 0; entry |= present; entry |= writable << 1; entry |= privilege_level << 2; entry |= write_through_cache << 3; entry |= cache_enabled << 4; entry |= accessed << 5; entry |= dirty << 6; entry |= page_size << 7; return base_address | entry; } void paging_init() { // PART 1: unsigned int curr_page_frame = 0; page_directory = kalloc( 4 * 1024 ); for ( int currPDE = 0; currPDE < PDE_NUM; currPDE++ ) { unsigned int *pagetable = kalloc( 4 * PTE_NUM ); for ( int currPTE = 0; currPTE < PTE_NUM; currPTE++, curr_page_frame++ ) pagetable[ currPTE ] = create_page_entry( curr_page_frame * 4096, 1, 0, 0, 1, 1, 0, 0, 0 ); page_directory[ currPDE ] = create_page_entry( pagetable, 1, 0, 0, 1, 1, 0, 0, 0 ); } // ... // // PART 2 load_page_directory(); enable_paging(); } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/paging.h ================================================ #define PDE_NUM 3 #define PTE_NUM 1024 extern void load_page_directory(); extern void enable_paging(); unsigned int *page_directory; void paging_init(); int create_page_entry( int, char, char, char, char, char, char, char, char ); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/process.c ================================================ #include "process.h" void process_init() { processes_count = 0; curr_pid = 0; } process_t *process_create( int *base_address ) { process_t *process = kalloc( sizeof( process_t ) ); process->pid = curr_pid++; process->context.eax = 0; process->context.ecx = 0; process->context.edx = 0; process->context.ebx = 0; process->context.esp = 0; process->context.ebp = 0; process->context.esi = 0; process->context.edi = 0; process->context.eip = base_address; process->state = READY; process->base_address = base_address; processes[ process->pid ] = process; processes_count++; } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/process.h ================================================ typedef enum process_state { READY, RUNNING } process_state_t; typedef struct process_context { int eax, ecx, edx, ebx, esp, ebp, esi, edi, eip; } process_context_t; typedef struct process { int pid; process_context_t context; process_state_t state; int *base_address; } process_t; process_t *processes[ 15 ]; int processes_count, curr_pid; void process_init(); process_t *process_create( int * ); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/scheduler.c ================================================ #include "scheduler.h" void scheduler_init() { next_sch_pid = 0; curr_sch_pid = 0; } process_t *get_next_process() { process_t *next_process = processes[ next_sch_pid ]; curr_sch_pid = next_sch_pid; next_sch_pid++; next_sch_pid = next_sch_pid % processes_count; return next_process; } void scheduler( int eip, int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax ) { process_t *curr_process; print( " EAX = " ); // For Testing Purpose printi( eax ); // For Testing Purpose // ... // // PART 1 curr_process = processes[ curr_sch_pid ]; next_process = get_next_process(); // ... // // PART 2 if ( curr_process->state == RUNNING ) { curr_process->context.eax = eax; curr_process->context.ecx = ecx; curr_process->context.edx = edx; curr_process->context.ebx = ebx; curr_process->context.esp = esp; curr_process->context.ebp = ebp; curr_process->context.esi = esi; curr_process->context.edi = edi; curr_process->context.eip = eip; } curr_process->state = READY; // ... // // PART 3 asm( " mov %0, %%eax; \ mov %0, %%ecx; \ mov %0, %%edx; \ mov %0, %%ebx; \ mov %0, %%esi; \ mov %0, %%edi;" : : "r" ( next_process->context.eax ), "r" ( next_process->context.ecx ), "r" ( next_process->context.edx ), "r" ( next_process->context.ebx ), "r" ( next_process->context.esi ), "r" ( next_process->context.edi ) ); next_process->state = RUNNING; } void run_next_process() { asm( " sti; \ jmp *%0" : : "r" ( next_process->context.eip ) ); } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/scheduler.h ================================================ #include "process.h" int next_sch_pid, curr_sch_pid; process_t *next_process; void scheduler_init(); process_t *get_next_process(); void scheduler( int, int, int, int, int, int, int, int, int ); void run_next_process(); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/screen.c ================================================ #include "screen.h" void screen_init() { video = 0xB8000; nextTextPos = 0; currLine = 0; } void print( char *str ) { int currCharLocationInVidMem, currColorLocationInVidMem; while ( *str != '\0' ) { currCharLocationInVidMem = nextTextPos * 2; currColorLocationInVidMem = currCharLocationInVidMem + 1; video[ currCharLocationInVidMem ] = *str; video[ currColorLocationInVidMem ] = 15; nextTextPos++; str++; } } void println() { nextTextPos = ++currLine * 80; } void printi( int number ) { char* digitToStr[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; if ( number >= 0 && number <= 9 ) { print( digitToStr[ number ] ); return; } else { int remaining = number % 10; number = number / 10; printi( number ); printi( remaining ); } } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/screen.h ================================================ volatile unsigned char *video; int nextTextPos; int currLine; void screen_init(); void print( char * ); void println(); void printi( int ); ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/starter.asm ================================================ bits 16 extern kernel_main extern interrupt_handler extern scheduler extern run_next_process extern page_directory global load_page_directory global enable_paging global dev_write global dev_write_word global dev_read start: mov ax, cs mov ds, ax call load_gdt call init_video_mode call enter_protected_mode call setup_interrupts call load_task_register call 08h:start_kernel load_gdt: cli lgdt [gdtr - start] ret enter_protected_mode: mov eax, cr0 or eax, 1 mov cr0, eax ret init_video_mode: mov ah, 0h mov al, 03h int 10h mov ah, 01h mov cx, 2000h int 10h ret setup_interrupts: call remap_pic call load_idt ret remap_pic: mov al, 11h send_init_cmd_to_pic_master: out 0x20, al send_init_cmd_to_pic_slave: out 0xa0, al ; ... ; make_irq_starts_from_intr_32_in_pic_master: mov al, 32d out 0x21, al make_irq_starts_from_intr_40_in_pic_slave: mov al, 40d out 0xa1, al ; ... ; tell_pic_master_where_pic_slave_is_connected: mov al, 04h out 0x21, al tell_pic_slave_where_pic_master_is_connected: mov al, 02h out 0xa1, al ; ... ; mov al, 01h tell_pic_master_the_arch_is_x86: out 0x21, al tell_pic_slave_the_arch_is_x86: out 0xa1, al ; ... ; mov al, 0h make_pic_master_enables_all_irqs: out 0x21, al make_pic_slave_enables_all_irqs: out 0xa1, al ; ... ; ret load_idt: lidt [idtr - start] ret load_task_register: mov ax, 40d ltr ax ret bits 32 load_page_directory: mov eax, [page_directory] mov cr3, eax ret enable_paging: mov eax, cr0 or eax, 80000000h mov cr0, eax ret dev_write: ; Part 1 push edx push eax ; Part 2 xor edx, edx xor eax, eax ; Part 3 mov dx, [esp + 12] mov al, [esp + 16] ; Part 4 out dx, al ; Part 5 pop eax pop edx ret dev_write_word: push edx push eax xor edx, edx xor eax, eax mov dx, [esp + 12] mov ax, [esp + 16] out dx, ax pop eax pop edx ret dev_read: push edx xor edx, edx xor eax, eax mov dx, [esp + 8] in ax, dx pop edx ret start_kernel: mov eax, 10h mov ds, eax mov ss, eax mov eax, 0h mov es, eax mov fs, eax mov gs, eax ;sti call kernel_main %include "gdt.asm" %include "idt.asm" tss: dd 0 ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/str.c ================================================ #include "str.h" void strcpy( char *dest, char *src ) { int idx = 0; while ( *src != '\0' ) { dest[ idx ] = *src; src++; idx++; } } int strcmp( char *str1, char *str2 ) { while ( *str1 != '\0' ) { if ( *str1 != *str2 ) return 0; str1++; str2++; } if ( *str2 != '\0' ) return 0; return 1; } ================================================ FILE: evolution_by_versions/06_539kernel_version_ne/str.h ================================================ void strcpy( char *, char * ); int strcmp( char *, char * ); ================================================ FILE: src/.gitignore ================================================ # Created by https://www.gitignore.io/api/c # Edit at https://www.gitignore.io/?templates=c ### C ### # Prerequisites *.d # Object files *.o *.ko *.obj *.elf *.bin # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf # End of https://www.gitignore.io/api/c *.img ================================================ FILE: src/Makefile ================================================ ASM = nasm CC = gcc BOOTSTRAP_FILE = bootstrap.asm SIMPLE_KERNEL = simple_kernel.asm INIT_KERNEL_FILES = starter.asm KERNEL_FILES = main.c # -m32 means generate 32-bit code. See "Machine-Dependent Options" section in gcc man page for details. Under "x86 Options" section "-m flag". # # -Wall: # "This enables all the warnings about constructions that some # users consider questionable, and that are easy to avoid (or # modify to prevent the warning), even in conjunction with # macros. This also enables some language-specific warnings # described in C++ Dialect Options and Objective-C and # Objective-C++ Dialect Options." # ~ GCC man page # # -c: # "If any of these options is used [-c, -S, -E], then the linker is not run, and object file names should not be used as arguments." # ~ GCC man page # # -ffreestanding: # "Assert that compilation targets a freestanding environment. # This implies -fno-builtin. A freestanding environment is one # in which the standard library may not exist, and program # startup may not necessarily be at "main". The most obvious # example is an OS kernel. This is equivalent to -fno-hosted." # ~ GCC man page # # -no-pie: # "Don't produce a dynamically linked position independent executable." # ~ GCC man page # # -g: # "To tell GCC to emit extra information for use by a debugger, in almost all cases you need only to add -g to your other options." # ~ GCC man page # # -std= # "Determine the language standard. This option is currently only supported when compiling C or C++." # gnu99: "GNU dialect of ISO C99. The name gnu9x is deprecated." # gnu18: "GNU dialect of ISO C17. This is the default for C code." # ~ GCC man page #-std=gnu99 # -c -Wall -no-pie -g -std=gnu99 KERNEL_FLAGS = -Wall -m32 -c -ffreestanding -fno-asynchronous-unwind-tables -fno-pie -fcommon -fno-stack-protector # -O1 -fno-pie KERNEL_OBJECT = -o kernel.elf build: echo Please Choose Type kernel: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f elf32 $(INIT_KERNEL_FILES) -o starter.o $(CC) $(KERNEL_FLAGS) $(KERNEL_FILES) $(KERNEL_OBJECT) $(CC) $(KERNEL_FLAGS) screen.c -o screen.elf $(CC) $(KERNEL_FLAGS) process.c -o process.elf $(CC) $(KERNEL_FLAGS) scheduler.c -o scheduler.elf $(CC) $(KERNEL_FLAGS) heap.c -o heap.elf $(CC) $(KERNEL_FLAGS) paging.c -o paging.elf $(CC) $(KERNEL_FLAGS) ata.c -o ata.elf $(CC) $(KERNEL_FLAGS) str.c -o str.elf $(CC) $(KERNEL_FLAGS) filesystem.c -o filesystem.elf ld -melf_i386 -Tlinker.ld starter.o kernel.elf screen.elf process.elf scheduler.elf heap.elf paging.elf ata.elf str.elf filesystem.elf -o 539kernel.elf objcopy -O binary 539kernel.elf 539kernel.bin dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=539kernel.bin of=kernel.img bs=512 count=20 dd seek=21 conv=sync if=/dev/zero of=kernel.img bs=512 count=2046 bochs -f bochs #qemu-system-x86_64 -s kernel.img debug: $(BOOTSTRAP_FILE) $(KERNEL_FILE) $(ASM) -f bin $(BOOTSTRAP_FILE) -o bootstrap.o $(ASM) -f elf32 $(INIT_KERNEL_FILES) -o starter.o $(CC) $(KERNEL_FLAGS) $(KERNEL_FILES) $(KERNEL_OBJECT) $(CC) $(KERNEL_FLAGS) screen.c -o screen.elf $(CC) $(KERNEL_FLAGS) process.c -o process.elf $(CC) $(KERNEL_FLAGS) scheduler.c -o scheduler.elf $(CC) $(KERNEL_FLAGS) heap.c -o heap.elf $(CC) $(KERNEL_FLAGS) paging.c -o paging.elf $(CC) $(KERNEL_FLAGS) ata.c -o ata.elf $(CC) $(KERNEL_FLAGS) str.c -o str.elf $(CC) $(KERNEL_FLAGS) filesystem.c -o filesystem.elf ld -melf_i386 -Tlinker.ld starter.o kernel.elf screen.elf process.elf scheduler.elf heap.elf paging.elf ata.elf str.elf filesystem.elf -o 539kernel.elf objcopy -O binary 539kernel.elf 539kernel.bin dd if=bootstrap.o of=kernel.img dd seek=1 conv=sync if=539kernel.bin of=kernel.img bs=512 count=20 dd seek=21 conv=sync if=/dev/zero of=kernel.img bs=512 count=2045 bochs -f bochs rerun-debug: bochs -f bochs rerun-kernel: qemu-system-x86_64 -s kernel.img clean: rm -f *.o rm -f *.elf rm -f *.img rm -f *.bin ================================================ FILE: src/README ================================================ [MQH] 25 Nov 2019 Here is the final code of 539kernel. Any incremental steps, examples and excercises can be found in ../examples. ================================================ FILE: src/ata.c ================================================ #include "ata.h" void wait_drive_until_ready() { int status = 0; do { status = dev_read( BASE_PORT + 7 ); } while ( ( status ^ 0x80 ) == 128 ); } // LBA void *read_disk( int address ) { dev_write( BASE_PORT + 6, ( 0x0e0 | ( ( address & 0x0F000000 ) >> 24 ) ) ); // Drive 0. Bits 0-3 = Bits 24-27 of LBA dev_write( BASE_PORT + 2, 1 ); // Sector count dev_write( BASE_PORT + 3, address & 0x000000FF ); // LBA's 0-7 bits dev_write( BASE_PORT + 4, ( address & 0x0000FF00 ) >> 8 ); // LBA's 8-15 bits dev_write( BASE_PORT + 5, ( address & 0x00FF0000 ) >> 16 ); // LBA's 16-23 bits dev_write( BASE_PORT + 7, 0x20 ); // Command: Read with Retry // ... // wait_drive_until_ready(); // ... // short *buffer = kalloc( SECTOR_SIZE ); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) buffer[ currByte ] = dev_read( BASE_PORT ); return buffer; } // LBA void write_disk( int address, short *buffer ) { dev_write( BASE_PORT + 6, ( 0x0e0 | ( ( address & 0x0F000000 ) >> 24 ) ) ); // Drive 0. Bits 0-3 = Bits 24-27 of LBA dev_write( BASE_PORT + 2, 1 ); // Sector count dev_write( BASE_PORT + 3, address & 0x000000FF ); // LBA's 0-7 bits dev_write( BASE_PORT + 4, ( address & 0x0000FF00 ) >> 8 ); // LBA's 8-15 bits dev_write( BASE_PORT + 5, ( address & 0x00FF0000 ) >> 16 ); // LBA's 16-23 bits dev_write( BASE_PORT + 7, 0x30 ); // Command: Write with Retry wait_drive_until_ready(); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) dev_write_word( BASE_PORT, buffer[ currByte ] ); wait_drive_until_ready(); } void *read_disk_chs( int sector ) { dev_write( BASE_PORT + 6, 0x0a0 ); // Drive 0 and Head 0 dev_write( BASE_PORT + 2, 1 ); // Sector count dev_write( BASE_PORT + 3, sector ); // Sector to read dev_write( BASE_PORT + 4, 0 ); // Cylinder - Low dev_write( BASE_PORT + 5, 0 ); // Cylinder - High dev_write( BASE_PORT + 7, 0x20 ); // Command: Read with Retry // ... // wait_drive_until_ready(); // ... // short *buffer = kalloc( SECTOR_SIZE ); for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) buffer[ currByte ] = dev_read( BASE_PORT ); return buffer; } void write_disk_chs( int sector, short *buffer ) { dev_write( BASE_PORT + 6, 0x0a0 ); // Drive 0 and Head 0 dev_write( BASE_PORT + 2, 1 ); // Sector count dev_write( BASE_PORT + 3, sector ); // Sector to read dev_write( BASE_PORT + 4, 0 ); // Cylinder - Low dev_write( BASE_PORT + 5, 0 ); // Cylinder - High dev_write( BASE_PORT + 7, 0x30 ); // Command: Write with Retry // ... // wait_drive_until_ready(); // ... // for ( int currByte = 0; currByte < ( SECTOR_SIZE / 2 ); currByte++ ) dev_write_word( BASE_PORT, buffer[ currByte ] ); // ... // wait_drive_until_ready(); } ================================================ FILE: src/ata.h ================================================ #define BASE_PORT 0x1F0 #define SECTOR_SIZE 512 void wait_drive_until_ready(); void *read_disk( int ); void write_disk( int, short * ); void *read_disk_chs( int ); void write_disk_chs( int, short * ); ================================================ FILE: src/bochs ================================================ # configuration file generated by Bochs plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, gameport=1, iodebug=1 config_interface: textconfig display_library: x, options="gui_debug" memory: host=32, guest=32 romimage: file="/usr/share/bochs/BIOS-bochs-latest" vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" boot: disk floppy_bootsig_check: disabled=0 # no floppya # no floppyb ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 ata0-master: type=disk, mode=flat, translation=auto, path="kernel.img", cylinders=2, heads=16, spt=63, biosdetect=auto, model="Generic 1234" ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 ata2: enabled=0 ata3: enabled=0 pci: enabled=1, chipset=i440fx vga: extension=vbe, update_freq=5 cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 cpuid: family=6, model=0x03, stepping=3, mmx=1, apic=xapic, sse=sse2, sse4a=0, sep=1, aes=0, xsave=0, xsaveopt=0, movbe=0, adx=0, smep=0, avx=0, avx_f16c=0, avx_fma=0, bmi=0, xop=0, tbm=0, fma4=0, vmx=1, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0, mwait=1 cpuid: vendor_string="GenuineIntel" cpuid: brand_string=" Intel(R) Pentium(R) 4 CPU " print_timestamps: enabled=0 debugger_log: - magic_break: enabled=0 port_e9_hack: enabled=0 private_colormap: enabled=0 clock: sync=none, time0=local, rtc_sync=0 # no cmosimage # no loader log: - logprefix: %t%e%d panic: action=ask error: action=report info: action=report debug: action=ignore keyboard: type=mf, serial_delay=250, paste_delay=100000, keymap= user_shortcut: keys=none mouse: enabled=0, type=ps2, toggle=ctrl+mbutton parport1: enabled=1, file="" parport2: enabled=0 com1: enabled=1, mode=null, dev="" com2: enabled=0 com3: enabled=0 com4: enabled=0 ================================================ FILE: src/bootstrap.asm ================================================ start: ; [MQH] 3 Sep 2019 ; When a the boot sector is loaded by BIOS. It will be loaded on the location 07C0h in the memory. ; What we do here is setting the register "ds" to the value "07C0" which is the same of the memory address we are on now. ; Our variable "hello_string" will be loaded as a part of the bootloader, when we use it in the line "mov si, hello_string" ; we're actually loading the memory address of the first character, let's assume that this memory address is (1000). Because ; it is considered as a "data" by the processor. This offset (the memory address of "hello_string") will be added to the data segment ; selector which resides in the register "ds". According to "Protected Mode Software Architecture", the initial value of "ds" is 0000h, ; so, if we keep "ds" as it is, the CPU is going to load the data from the memory 00001000, BUT we are actually on 07C0, so the data ; segment selector should be 07C0h so we can get the correct address of "hello_string" which is 07C01000 and not 00001000. ; For backward compatibility, when the bootloader starts, the execution environment will be in "Real Mode", 16-bit. mov ax, 07C0h mov ds, ax ; ... ; ; == Printing == ; mov si, title_string call print_string ; "CALL" is used instead of "JMP" because we would like to return to the next instruction after "print_string" finishes. mov si, message_string call print_string ; ... ; ; === Loading the Kernel === ; call load_kernel_from_disk ; If the loading has been performed correctly. Jump to the kernel's code which resides on 0900h:0000 according ; to ES:BX values before calling (int 13h). ; ; There is a difference between "JMP" and "CALL" inctructions. The first one doesn't store returning information ; in the stack while the later does. Because we are not going to return from kernel to the bootloader, we don't ; need to store return information. jmp 0900h:0000 ; ... ; ; ... ; load_kernel_from_disk: ; [MQH] 26 Nov 2019 ; BIOS provides disk services through intrrupt 13h. The value of the register "ah" decides which disk service that we are requesting from BIOS. ; The Disk Service "02h" loads sectors to the memory. The memory location that we would like to load our kernel in should be decided ; before calling "int 13h". There are two parts of this location. The first part (segment selector) resides in the register "ES" while ; the other part (offset) resides in the register "BX". ; ; x86 Segment Registers: CS: for code segment. ; DS, ES, FS, and GS: for 4 data segments. ; SS: for stack segment. ; They are 16-bit registers that hold "segment selectors" mov ax, [curr_sector_to_load] sub ax, 2 mov bx, 512d mul bx mov bx, ax mov ax, 0900h mov es, ax mov ah, 02h ; Requesting the service of reading disk sectors mov al, 1h ; Number of sectors to read (How many sectors to read?) mov ch, 0h ; Track number mov cl, [curr_sector_to_load] ; Sector number mov dh, 0h ; Head number mov dl, 80h ; Drive to read from. (0 = Floppy. 80h = Drive #0. 81h = Drive #1) int 13h ; BIOS Disk Services ; The instruction "jc" jumps to a memory location when CF = 1 (jc = jump if carry). ; CF (or carry flag) is the first bit of EFLAG register in x86. The BIOS service (13h,02h) ; clear CF (that is, put 0 in CF) when everything is fine. But if there is some error ; it's going to set CF (that is, put 1 in CF). Error code will be in "ah". ; ; If any error happens in loading our kernel. The bootloader is going to jump to the label "kernel_load_error". jc kernel_load_error sub byte [number_of_sectors_to_load], 1 add byte [curr_sector_to_load], 1 cmp byte [number_of_sectors_to_load], 0 jne load_kernel_from_disk ret ; When the bootloader fails to load the kernel. A nice message is printted for the user. kernel_load_error: mov si, load_error_string call print_string ; "$" is a special expression in NASM. It means the starting memory address (or assembly position?) of current instruction, that means ; the following instruction is going to loop forever. jmp $ ; ... ; ; ... ; ; [MQH] 3 Sep 2019 ; BIOS provides video services through intrrupt 10h. The value of the register "ah" decides which video service that we are requesting from BIOS. ; Those BIOS services are only available on Real Mode (https://stackoverflow.com/questions/26448480/bios-interrupts-in-protected-mode) ; ; ah = 0Eh means that we wish to print a character in TTY mode. ; For an example of using service "0Eh" to print "Hello" character by character, please refer to "../examples/bootstrap/1/". print_string: mov ah, 0Eh print_char: ; [MQH] 26 Nov 2019 ; It's known that a byte = 8 bits. In x86 there are more two units: a "word" = 16 bits and "doubleword" = 32 bits. Some x86 instructions have multiple variants that work ; with a specific aforementioned unit. "lods" is an example of these instructions, there is "lodsb" which works with a byte, "lodsw" which works with a word and "lodsd" which ; works with doubleword. ; ; The instruction "lods" uses the value which is stored in register "si" (when using lodsb or loadsw) or register "esi" (when using lodsw) and assume that this ; value as memory location. Then according to the used unit a (b)yte (8-bits), (w)ord (16-bits) or (d)oubleword (32-bit) will be read from the memory location DS:(E)SI ; and store these bits in al, ax or eax according to the size of read data. lodsb cmp al, 0 je printing_finished int 10h jmp print_char printing_finished: mov al, 10d ; Print new line int 10h ; [MQH] NEW 25 June 2021 ; Move the cursor to the beginning mov ah, 03h mov bh, 0 int 10h mov ah, 02h mov dl, 0 int 10h ; print_string is a procedure (or function), therefore we should return. It is called by using "CALL" instead of "JMP". ret ; ... ; ; [MQH] 9 Dec 2019 ; "DB" is one of pseudo-instructions which is provided by NASM. A source line in NASM follows the following format ; label: instruction operands ; comment ; The label is optional and even the colon after the label is optional. ; "DB" is used to declare initialized data. The "B" in "DB" means byte. ; the part ", 0" means that the last byte will be 0. In some way resembles ; nul character in C strings. ; ; Put the string in the output of this assembly file. Byte by byte, and the address of ; this string will be in the label, so we can reach the string inside the assembly source code. title_string db 'The Bootloader of 539kernel.', 0 message_string db 'The kernel is loading...', 0 load_error_string db 'The kernel cannot be loaded', 0 number_of_sectors_to_load db 15d ; 255 sectors = 127.5KB ; [MQH] NEW 4 July 2021 curr_sector_to_load db 2d ; [MQH] 9 Dec 2019 ; "TIMES" is an NASM pseudo-instruction which repeats an instruction a number of specific times. ; The first operand of "TIMES" is the number of repetitions. ; As we mentioned before "$" means the starting address (or assembly position?) of current instruction. ; "$$" is another special expression which means the starting address (or assembly position?) of current section. ; So, we know that the size of bootstrap should be 512 byte. Two bytes are represent the magic code in the last line ; so 510 bytes remains for us. Our code starts from position $$ and we reached position $. Therefore, $ - $$ gives ; us the size of our code. So, 510 - ( $ - $$ ) gives us the remaining size of the 512 bytes and fills it with zeros ; by using "DB". times 510-($-$$) db 0 ; Put the magic code of bootloader in the end of assembly file's output. "W" means word. dw 0xAA55 ================================================ FILE: src/filesystem.c ================================================ #include "filesystem.h" void filesystem_init() { base_block = read_disk( BASE_BLOCK_ADDRESS ); } void update_base_block( int new_head, int new_tail ) { base_block->head = new_head; base_block->tail = new_tail; write_disk( BASE_BLOCK_ADDRESS, base_block ); } metadata_t *load_metadata( int address ) { metadata_t *metadata = read_disk( address ); return metadata; } int get_files_number() { if ( base_block->head == 0 ) return 0; int files_number = 0; // ... // metadata_t *curr_file = load_metadata( base_block->head ); while ( 1 ) { files_number++; if ( curr_file->next_file_address == 0 ) break; curr_file = load_metadata( curr_file->next_file_address ); } return files_number; } void create_file( char *filename, char *buffer ) { int metadata_lba = ( base_block->head == 0 ) ? BASE_BLOCK_ADDRESS + 1 : base_block->tail + 2; int file_lba = metadata_lba + 1; metadata_t *metadata = kalloc( sizeof( metadata_t ) ); metadata->next_file_address = 0; // ... // int currIdx; for ( currIdx = 0; *filename != '\0' && currIdx < FILENAME_LENGTH - 1; currIdx++, filename++ ) metadata->filename[ currIdx ] = *filename; metadata->filename[ currIdx ] = '\0'; // ... // write_disk( metadata_lba, metadata ); write_disk( file_lba, buffer ); // ... // if ( base_block->head == 0 ) { update_base_block( metadata_lba, metadata_lba ); } else { metadata_t *tail_metadata = load_metadata( base_block->tail ); tail_metadata->next_file_address = metadata_lba; write_disk( base_block->tail, tail_metadata ); update_base_block( base_block->head, metadata_lba ); } } char **list_files() { if ( base_block->head == 0 ) return -1; char **list; list = kalloc( get_files_number() * sizeof( char * ) ); metadata_t *curr_file = load_metadata( base_block->head ); int idx = 0; while ( 1 ) { list[ idx ] = curr_file->filename; if ( curr_file->next_file_address == 0 ) break; curr_file = load_metadata( curr_file->next_file_address ); idx++; } return list; } int get_address_by_filename( char *filename ) { metadata_t *curr_file = load_metadata( base_block->head ); int curr_file_address = base_block->head; int idx = 0; while ( 1 ) { if ( strcmp( curr_file->filename, filename ) == 1 ) return curr_file_address; if ( curr_file->next_file_address == 0 ) break; curr_file_address = curr_file->next_file_address; curr_file = load_metadata( curr_file->next_file_address ); } return 0; } char *read_file( char *filename ) { int address = get_address_by_filename( filename ); if ( address == 0 ) return 0; printi( address ); char *buffer = read_disk( address + 1 ); return buffer; } int get_prev_file_address( int address ) { metadata_t *prev_file = load_metadata( base_block->head ); int prev_file_address = base_block->head; while ( 1 ) { if ( prev_file->next_file_address == address ) return prev_file_address; prev_file_address = prev_file->next_file_address; prev_file = load_metadata( prev_file->next_file_address ); } return -1; } void delete_file( char *filename ) { int curr_file_address = get_address_by_filename( filename ); if ( curr_file_address == 0 ) return; // ... // if ( get_files_number() == 1 ) { update_base_block( 0, 0 ); return; } metadata_t *curr_file_metadata = read_disk( curr_file_address ); if ( curr_file_address == base_block->head ) { update_base_block( curr_file_metadata->next_file_address, base_block->tail ); } else { int prev_file_address = get_prev_file_address( curr_file_address ); metadata_t *prev_file = load_metadata( prev_file_address ); prev_file->next_file_address = curr_file_metadata->next_file_address; write_disk( prev_file_address, prev_file ); if ( curr_file_address == base_block->tail ) update_base_block( base_block->head, prev_file_address ); } } ================================================ FILE: src/filesystem.h ================================================ #define BASE_BLOCK_ADDRESS 100 #define FILENAME_LENGTH 256 typedef struct { int head, tail; } base_block_t; typedef struct { char filename[ FILENAME_LENGTH ]; int next_file_address; } metadata_t; base_block_t *base_block; void filesystem_init(); void create_file( char *, char * ); char **list_files(); char *read_file( char * ); // Auxiliary Functions metadata_t *load_metadata( int ); int get_address_by_filename( char * ); int get_prev_file_address( int ); int get_files_number(); ================================================ FILE: src/gdt.asm ================================================ ; The values of the decriptors from Basekernel (kernelcode.S) (https://github.com/dthain/basekernel) gdt: null_descriptor : dw 0, 0, 0, 0 kernel_code_descriptor : dw 0xffff, 0x0000, 0x9a00, 0x00cf kernel_data_descriptor : dw 0xffff, 0x0000, 0x9200, 0x00cf userspace_code_descriptor : dw 0xffff, 0x0000, 0xfa00, 0x00cf userspace_data_descriptor : dw 0xffff, 0x0000, 0xf200, 0x00cf ; DON'T FORGET TO CHANGE THE SIZE OF THE GDT ; TODO: I think the limit isn't correct tss_descriptor : dw tss + 3, tss, 0x8900, 0x0000 gdtr: gdt_size_in_bytes : dw ( 6 * 8 ) ;= 28h gdt_base_address : dd gdt ================================================ FILE: src/heap.c ================================================ #include "heap.h" void heap_init() { heap_base = 0x100000; } int kalloc( int bytes ) { // Note: Not necessarily page-aligned unsigned int new_object_address = heap_base; heap_base += bytes; return new_object_address; } ================================================ FILE: src/heap.h ================================================ unsigned int heap_base; void heap_init(); int kalloc( int ); ================================================ FILE: src/idt.asm ================================================ isr_0: cli push 0 jmp isr_basic isr_1: cli push 1 jmp isr_basic isr_2: cli push 2 jmp isr_basic isr_3: cli push 3 jmp isr_basic isr_4: cli push 4 jmp isr_basic isr_5: cli push 5 jmp isr_basic isr_6: cli push 6 jmp isr_basic isr_7: cli push 7 jmp isr_basic isr_8: cli push 8 jmp isr_basic isr_9: cli push 9 jmp isr_basic isr_10: cli push 10 jmp isr_basic isr_11: cli push 11 jmp isr_basic isr_12: cli push 12 jmp isr_basic isr_13: cli push 13 jmp isr_basic isr_14: cli push 14 jmp isr_basic isr_15: cli push 15 jmp isr_basic isr_16: cli push 16 jmp isr_basic isr_17: cli push 17 jmp isr_basic isr_18: cli push 18 jmp isr_basic isr_19: cli push 19 jmp isr_basic isr_20: cli push 20 jmp isr_basic isr_21: cli push 21 jmp isr_basic isr_22: cli push 22 jmp isr_basic isr_23: cli push 23 jmp isr_basic isr_24: cli push 24 jmp isr_basic isr_25: cli push 25 jmp isr_basic isr_26: cli push 26 jmp isr_basic isr_27: cli push 27 jmp isr_basic isr_28: cli push 28 jmp isr_basic isr_29: cli push 29 jmp isr_basic isr_30: cli push 30 jmp isr_basic isr_31: cli push 31 jmp isr_basic ; System Timer isr_32: cli pusha ; Store the context of current process mov eax, [esp + 32] ; EIP before interrupt. Could be the EIP of the current process push eax call scheduler mov al, 0x20 out 0x20, al add esp, 40d ; Remove return address from stack, EIP and all general purpose registers of previous process. push run_next_process iret isr_33: cli push 33 jmp irq_basic isr_34: cli push 34 jmp irq_basic isr_35: cli push 35 jmp irq_basic isr_36: cli push 36 jmp irq_basic isr_37: cli push 37 jmp irq_basic isr_38: cli push 38 jmp irq_basic isr_39: cli push 39 jmp irq_basic isr_40: cli push 40 jmp irq_basic isr_41: cli push 41 jmp irq_basic isr_42: cli push 42 jmp irq_basic isr_43: cli push 43 jmp irq_basic isr_44: cli push 44 jmp irq_basic isr_45: cli push 45 jmp irq_basic isr_46: cli push 46 jmp irq_basic isr_47: cli push 47 jmp irq_basic isr_48: cli push 48 jmp irq_basic isr_basic: ; cli call interrupt_handler pop eax sti iret irq_basic: ; cli call interrupt_handler mov al, 0x20 out 0x20, al cmp byte [esp], 40d ; Interrupt number jnge irq_basic_end mov al, 0xa0 out 0x20, al irq_basic_end: pop eax sti iret ; The value of the flags from Basekernel (kernelcode.S) (https://github.com/dthain/basekernel) idt: dw isr_0, 8, 0x8e00, 0x0000 dw isr_1, 8, 0x8e00, 0x0000 dw isr_2, 8, 0x8e00, 0x0000 dw isr_3, 8, 0x8e00, 0x0000 dw isr_4, 8, 0x8e00, 0x0000 dw isr_5, 8, 0x8e00, 0x0000 dw isr_6, 8, 0x8e00, 0x0000 dw isr_7, 8, 0x8e00, 0x0000 dw isr_8, 8, 0x8e00, 0x0000 dw isr_9, 8, 0x8e00, 0x0000 dw isr_10, 8, 0x8e00, 0x0000 dw isr_11, 8, 0x8e00, 0x0000 dw isr_12, 8, 0x8e00, 0x0000 dw isr_13, 8, 0x8e00, 0x0000 dw isr_14, 8, 0x8e00, 0x0000 dw isr_15, 8, 0x8e00, 0x0000 dw isr_16, 8, 0x8e00, 0x0000 dw isr_17, 8, 0x8e00, 0x0000 dw isr_18, 8, 0x8e00, 0x0000 dw isr_19, 8, 0x8e00, 0x0000 dw isr_20, 8, 0x8e00, 0x0000 dw isr_21, 8, 0x8e00, 0x0000 dw isr_22, 8, 0x8e00, 0x0000 dw isr_23, 8, 0x8e00, 0x0000 dw isr_24, 8, 0x8e00, 0x0000 dw isr_25, 8, 0x8e00, 0x0000 dw isr_26, 8, 0x8e00, 0x0000 dw isr_27, 8, 0x8e00, 0x0000 dw isr_28, 8, 0x8e00, 0x0000 dw isr_29, 8, 0x8e00, 0x0000 dw isr_30, 8, 0x8e00, 0x0000 dw isr_31, 8, 0x8e00, 0x0000 dw isr_32, 8, 0x8e00, 0x0000 dw isr_33, 8, 0x8e00, 0x0000 dw isr_34, 8, 0x8e00, 0x0000 dw isr_35, 8, 0x8e00, 0x0000 dw isr_36, 8, 0x8e00, 0x0000 dw isr_37, 8, 0x8e00, 0x0000 dw isr_38, 8, 0x8e00, 0x0000 dw isr_39, 8, 0x8e00, 0x0000 dw isr_40, 8, 0x8e00, 0x0000 dw isr_41, 8, 0x8e00, 0x0000 dw isr_42, 8, 0x8e00, 0x0000 dw isr_43, 8, 0x8e00, 0x0000 dw isr_44, 8, 0x8e00, 0x0000 dw isr_45, 8, 0x8e00, 0x0000 dw isr_46, 8, 0x8e00, 0x0000 dw isr_47, 8, 0x8e00, 0x0000 dw isr_48, 8, 0x8e00, 0x0000 idtr: idt_size_in_bytes : dw idtr - idt idt_base_address : dd idt ================================================ FILE: src/linker.ld ================================================ /* Based on: http://www.jamesmolloy.co.uk/tutorial_html/1.-Environment%20setup.html */ SECTIONS { .text 0x09000 : { code = .; _code = .; __code = .; *(.text) /*. = ALIGN(4096);*/ } .data : { data = .; _data = .; __data = .; *(.data) *(.rodata) /*. = ALIGN(4096);*/ } .bss : { bss = .; _bss = .; __bss = .; *(.bss) /*. = ALIGN(4096);*/ } end = .; _end = .; __end = .; } ================================================ FILE: src/main.c ================================================ #include "heap.h" #include "paging.h" #include "screen.h" #include "scheduler.h" #include "ata.h" #include "filesystem.h" #include "str.h" void processA(); void processB(); void processC(); void processD(); void print_fs() { char **files = list_files(); for ( int currIdx = 0; currIdx < get_files_number(); currIdx++ ) { print( "File: " ); print( files[ currIdx ] ); println(); } print( "==" ); println(); } void kernel_main() { heap_init(); paging_init(); screen_init(); process_init(); scheduler_init(); filesystem_init(); // ... // print( "Welcome to 539kernel!" ); println(); print( "We are now in Protected-mode" ); println(); printi( 539 ); println(); // ... // char *data = kalloc( 512 ); strcpy( data, "The content of the first file on 539filesystem" ); create_file( "first_file", data ); // ... // char *data2 = kalloc( 512 ); strcpy( data2, "SECOND FILE in 539filesystem" ); create_file( "second_file", data2 ); // ... // char *data3 = kalloc( 512 ); strcpy( data3, "THIRD FILE in 539filesystem" ); create_file( "third_file", data3 ); // ... // char *data4 = kalloc( 512 ); strcpy( data4, "FOURTH FILE in 539filesystem" ); create_file( "fourth_file", data4 ); // ... // //print_fs(); //delete_file( "first_file" ); //print_fs(); //delete_file( "second_file" ); //print_fs(); //delete_file( "third_file" ); //print_fs(); //delete_file( "fourth_file" ); //print_fs(); // ... // print( read_file( "first_file" ) ); println(); print( read_file( "second_file" ) ); println(); print( read_file( "third_file" ) ); println(); print( read_file( "fourth_file" ) ); println(); // ... // /*void *data = //read_disk_chs( 1 ); read_disk( 100 ); printi( data );*/ /* char *data = kalloc( 512 ); for ( int currIdx = 0; currIdx < 512; currIdx++ ) data[ currIdx ] = 'X'; data[ 511 ] = 'D'; write_disk( 0, data ); char *data2 = kalloc( 512 ); for ( int currIdx = 0; currIdx < 512; currIdx++ ) data2[ currIdx ] = 'Y'; data2[ 511 ] = 'D'; write_disk( 1, data2 ); char *data3 = kalloc( 512 ); for ( int currIdx = 0; currIdx < 512; currIdx++ ) data3[ currIdx ] = 'Z'; data3[ 511 ] = 'D'; write_disk( 2, data3 ); read_disk( 0 ); read_disk( 1 ); read_disk( 2 ); */ // write_disk_chs( 1, data ); /* println(); print( "=============" ); */ /*process_create( &processA ); process_create( &processB ); process_create( &processC ); process_create( &processD ); asm( "sti" );*/ while( 1 ); } void interrupt_handler( int interrupt_number ) { print( "Interrupt Received " ); printi( interrupt_number ); println(); } void processA() { print( "Process A," ); while ( 1 ) asm( "mov $5390, %eax" ); } void processB() { print( "Process B," ); while ( 1 ) asm( "mov $5391, %eax" ); } void processC() { print( "Process C," ); while ( 1 ) asm( "mov $5392, %eax" ); } void processD() { print( "Process D," ); while ( 1 ) asm( "mov $5393, %eax" ); } ================================================ FILE: src/paging.c ================================================ #include "paging.h" void paging_init() { // Initializing Kernel's Page Directory (1 to 1 mapping) unsigned int curr_page_frame = 0; // We allocate 4 * 1024 bytes here, whatever the size of // the page directory, to make sure that the page tables // that are coming next are in page aligned-addresses. page_directory = kalloc( 4 * 1024 ); for ( int currPDE = 0; currPDE < PDE_NUM; currPDE++ ) { unsigned int *pagetable = kalloc( 4 * PTE_NUM ); for ( int currPTE = 0; currPTE < PTE_NUM; currPTE++, curr_page_frame++ ) pagetable[ currPTE ] = create_page_entry( curr_page_frame * 4096, 1, 0, 0, 1, 1, 0, 0, 0 ); page_directory[ currPDE ] = create_page_entry( pagetable, 1, 0, 0, 1, 1, 0, 0, 0 ); } // ... // load_page_directory(); enable_paging(); } int create_page_entry( int base_address, char present, char writable, char privilege_level, char cache_enabled, char write_through_cache, char accessed, char page_size, char dirty ) { int entry = 0; entry |= present; entry |= writable << 1; entry |= privilege_level << 2; entry |= write_through_cache << 3; entry |= cache_enabled << 4; entry |= accessed << 5; entry |= dirty << 6; entry |= page_size << 7; return base_address | entry; } ================================================ FILE: src/paging.h ================================================ #define PDE_NUM 3 #define PTE_NUM 1024 extern void load_page_directory(); extern void enable_paging(); unsigned int *page_directory; void paging_init(); int create_page_entry( int, char, char, char, char, char, char, char, char ); ================================================ FILE: src/process.c ================================================ #include "process.h" void process_init() { processes_count = 0; curr_pid = 0; } process_t *process_create( int *base_address ) { process_t *process = kalloc( sizeof( process_t ) ); process->pid = curr_pid++; process->context.eax = 0; process->context.ecx = 0; process->context.edx = 0; process->context.ebx = 0; process->context.esp = 0; process->context.ebp = 0; process->context.esi = 0; process->context.edi = 0; process->context.eip = base_address; process->state = READY; process->base_address = base_address; processes[ process->pid ] = process; processes_count++; return process; } ================================================ FILE: src/process.h ================================================ typedef enum process_state { READY, RUNNING } process_state_t; typedef struct process_context { int eax, ecx, edx, ebx, esp, ebp, esi, edi, eip; } process_context_t; typedef struct process { int pid; process_context_t context; process_state_t state; int *base_address; } process_t; process_t *processes[ 15 ]; // TODO: Dynamic int processes_count, curr_pid; void process_init(); process_t *process_create( int * ); ================================================ FILE: src/scheduler.c ================================================ #include "scheduler.h" void scheduler_init() { next_sch_pid = 0; curr_sch_pid = 0; } process_t *get_next_process() { process_t *next_process = processes[ next_sch_pid ]; curr_sch_pid = next_sch_pid; next_sch_pid++; next_sch_pid = next_sch_pid % processes_count; return next_process; } void scheduler( int eip, int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax ) { process_t *curr_process; print( " EAX = " ); printi( eax ); curr_process = processes[ curr_sch_pid ]; next_process = get_next_process(); // Copy Current Process Context to The Memory if ( curr_process->state == RUNNING ) { curr_process->context.eax = eax; curr_process->context.ecx = ecx; curr_process->context.edx = edx; curr_process->context.ebx = ebx; curr_process->context.esp = esp; curr_process->context.ebp = ebp; curr_process->context.esi = esi; curr_process->context.edi = edi; curr_process->context.eip = eip; } curr_process->state = READY; // ... // // Copy Next Process Context to The Processor // TODO: In Memory-Management Version. Copy ESP and EBP asm( " mov %0, %%eax; \ mov %0, %%ecx; \ mov %0, %%edx; \ mov %0, %%ebx; \ mov %0, %%esi; \ mov %0, %%edi;" : : "r" ( next_process->context.eax ), "r" ( next_process->context.ecx ), "r" ( next_process->context.edx ), "r" ( next_process->context.ebx ), "r" ( next_process->context.esi ), "r" ( next_process->context.edi ) ); next_process->state = RUNNING; } void run_next_process() { // No code should reside here at all. Otherwise, // we will not be sure that the context of the process // to run will be correct. asm( " sti; \ jmp *%0" : : "r" ( next_process->context.eip ) ); } ================================================ FILE: src/scheduler.h ================================================ #include "process.h" int next_sch_pid, curr_sch_pid; process_t *next_process; void scheduler_init(); process_t *get_next_process(); void scheduler( int, int, int, int, int, int, int, int, int ); void run_next_process(); ================================================ FILE: src/screen.c ================================================ #include "screen.h" void screen_init() { // VGA Text Mode = 0xB8000 // VGA Graphics Mode = 0xA0000 video = 0xB8000; nextTextPos = 0; currLine = 0; } void print( char *str ) { int currCharLocationInVidMem, currColorLocationInVidMem; while ( *str != '\0' ) { currCharLocationInVidMem = nextTextPos * 2; currColorLocationInVidMem = currCharLocationInVidMem + 1; video[ currCharLocationInVidMem ] = *str; video[ currColorLocationInVidMem ] = 15; nextTextPos++; str++; } } void println() { if ( currLine > 23 ) { cls(); return; } nextTextPos = ++currLine * 80; } void printi( int number ) { char* digitToStr[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; if ( number >= 0 && number <= 9 ) { print( digitToStr[ number ] ); return; } else { int remaining = number % 10; number = number / 10; printi( number ); printi( remaining ); } } void cls() { for ( int currPos = 0; currPos < 3999; currPos = currPos + 2 ) { video[ currPos ] = 0; video[ currPos + 1 ] = 0; } nextTextPos = 0; currLine = 0; } ================================================ FILE: src/screen.h ================================================ volatile unsigned char *video; int nextTextPos; int currLine; void screen_init(); void print( char * ); void println(); void printi( int ); void cls(); ================================================ FILE: src/simple_kernel.asm ================================================ start: mov ax, cs mov ds, ax ; --- ; mov si, hello_string call print_string jmp $ print_string: mov ah, 0Eh print_char: lodsb cmp al, 0 je done int 10h jmp print_char done: ret hello_string db 'Hello World!, From Simple Assembly 539kernel!', 0 ================================================ FILE: src/starter.asm ================================================ bits 16 extern kernel_main extern interrupt_handler extern scheduler extern run_next_process extern page_directory global load_page_directory global enable_paging global dev_write global dev_write_word global dev_read start: mov ax, cs mov ds, ax ; --- ; call load_gdt call init_video_mode call enter_protected_mode call setup_interrupts call load_task_register ; --- ; call 08h:start_kernel load_gdt: cli lgdt [gdtr - start] ret setup_interrupts: call remap_pic call load_idt ret init_video_mode: ;; Set Video Mode mov ah, 0h mov al, 03h ; 03h For Text Mode. 13h For Graphics Mode. int 10h ;; Disable Text Cursor mov ah, 01h mov cx, 2000h int 10h ret enter_protected_mode: mov eax, cr0 or eax, 1 mov cr0, eax ret remap_pic: mov al, 11h send_init_cmd_to_pic_master: out 0x20, al send_init_cmd_to_pic_slave: out 0xa0, al ; ... ; make_irq_starts_from_intr_32_in_pic_master: mov al, 32d out 0x21, al make_irq_starts_from_intr_40_in_pic_slave: mov al, 40d out 0xa1, al ; ... ; tell_pic_master_where_pic_slave_is_connected: mov al, 04h out 0x21, al tell_pic_slave_where_pic_master_is_connected: mov al, 02h out 0xa1, al ; ... ; mov al, 01h tell_pic_master_the_arch_is_x86: out 0x21, al tell_pic_slave_the_arch_is_x86: out 0xa1, al ; ... ; mov al, 0h make_pic_master_enables_all_irqs: out 0x21, al make_pic_slave_enables_all_irqs: out 0xa1, al ; ... ; ret load_idt: lidt [idtr - start] ret load_task_register: mov ax, 40d ltr ax ret bits 32 load_page_directory: mov eax, [page_directory] mov cr3, eax ret enable_paging: mov eax, cr0 or eax, 80000000h mov cr0, eax ret ; --- ; ; dev_write( int port, int cmd ); dev_write: push edx push eax xor edx, edx xor eax, eax mov dx, [esp + 12] mov al, [esp + 16] out dx, al pop eax pop edx ret dev_write_word: push edx push eax xor edx, edx xor eax, eax mov dx, [esp + 12] mov ax, [esp + 16] out dx, ax pop eax pop edx ret ; dev_read( int port ); dev_read: push edx xor edx, edx xor eax, eax mov dx, [esp + 8] in ax, dx ;jmp $ pop edx ret start_kernel: mov eax, 10h mov ds, eax mov ss, eax mov es, eax mov eax, 0h mov fs, eax mov gs, eax ;sti ; TODO: I think we need to enable it. Does the multitasking work? 13 Nov 2022. Check if we enabled interrupts elsewhere? call kernel_main %include "gdt.asm" %include "idt.asm" tss: dd 0 ================================================ FILE: src/str.c ================================================ #include "str.h" void strcpy( char *dest, char *src ) { int idx = 0; while ( *src != '\0' ) { dest[ idx ] = *src; src++; idx++; } } int strcmp( char *str1, char *str2 ) { while ( *str1 != '\0' ) { if ( *str1 != *str2 ) return 0; str1++; str2++; } if ( *str2 != '\0' ) return 0; return 1; } ================================================ FILE: src/str.h ================================================ void strcpy( char *, char * ); int strcmp( char *, char * );