diff --git a/Part1/.DS_Store b/Part1/.DS_Store index 9d25622..569995f 100644 Binary files a/Part1/.DS_Store and b/Part1/.DS_Store differ diff --git a/Part2/.DS_Store b/Part2/.DS_Store index 6de918b..a168109 100644 Binary files a/Part2/.DS_Store and b/Part2/.DS_Store differ diff --git a/Part2/A4/.DS_Store b/Part2/A4/.DS_Store index 3f2bca8..5e8b2ad 100644 Binary files a/Part2/A4/.DS_Store and b/Part2/A4/.DS_Store differ diff --git a/Part2/A4/01_gdb/.DS_Store b/Part2/A4/01_gdb/.DS_Store index 8e31ed2..d19a948 100644 Binary files a/Part2/A4/01_gdb/.DS_Store and b/Part2/A4/01_gdb/.DS_Store differ diff --git a/Part2/A4/03_strings/.DS_Store b/Part2/A4/03_strings/.DS_Store new file mode 100644 index 0000000..afdf1f2 Binary files /dev/null and b/Part2/A4/03_strings/.DS_Store differ diff --git a/Part2/A4/04_scroll/.DS_Store b/Part2/A4/04_scroll/.DS_Store index 3fb9b9f..ba48463 100644 Binary files a/Part2/A4/04_scroll/.DS_Store and b/Part2/A4/04_scroll/.DS_Store differ diff --git a/Part2/A4/06_irq/.DS_Store b/Part2/A4/06_irq/.DS_Store index 6b62c5f..1561a1d 100644 Binary files a/Part2/A4/06_irq/.DS_Store and b/Part2/A4/06_irq/.DS_Store differ diff --git a/Part2/A4/07_timers/.DS_Store b/Part2/A4/07_timers/.DS_Store index 6b62c5f..3061e1a 100644 Binary files a/Part2/A4/07_timers/.DS_Store and b/Part2/A4/07_timers/.DS_Store differ diff --git a/Part3/.DS_Store b/Part3/.DS_Store new file mode 100644 index 0000000..2dce7a4 Binary files /dev/null and b/Part3/.DS_Store differ diff --git a/Part3/09_memory/Makefile b/Part3/09_memory/Makefile new file mode 100644 index 0000000..ab4485f --- /dev/null +++ b/Part3/09_memory/Makefile @@ -0,0 +1,51 @@ +C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c) +HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h) +# Nice syntax for file extension replacement +OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} + +# Change this if your cross-compiler is somewhere else +CC = /usr/bin/gcc +GDB = /usr/bin/gdb +# -g: Use debugging symbols in gcc +CFLAGS = -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror + +# First rule is run by default +os-image.bin: boot/bootsect.bin kernel.bin + cat $^ > os-image.bin + +# '--oformat binary' deletes all symbols as a collateral, so we don't need +# to 'strip' them manually on this case +kernel.bin: boot/kernel_entry.o ${OBJ} + /usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary + +# Used for debugging purposes +kernel.elf: boot/kernel_entry.o ${OBJ} + /usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ + +run: os-image.bin + qemu-system-i386 -vga std -drive file=$<,index=0,if=floppy,format=raw + +#-s means start server at TCP:1234 +curses: os-image.bin + qemu-system-i386 -curses -s -drive file=$<,index=0,if=floppy,format=raw +# qemu-system-i386 -curses -s -drive file=os-image.bin,index=0,if=floppy,format=raw -d guest_errors,int + +# Open the connection to qemu and load our kernel-object file with symbols +debug: os-image.bin kernel.elf + qemu-system-i386 -s -fda os-image.bin -d guest_errors,int & + ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" + +# Generic rules for wildcards +# To make an object, always compile from its .c +%.o: %.c ${HEADERS} + ${CC} ${CFLAGS} -c $< -o $@ + +%.o: %.asm + nasm $< -f elf -o $@ + +%.bin: %.asm + nasm $< -f bin -o $@ + +clean: + rm -rf *.bin *.dis *.o os-image.bin *.elf + rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o diff --git a/Part3/09_memory/README.md b/Part3/09_memory/README.md new file mode 100644 index 0000000..da2ed0f --- /dev/null +++ b/Part3/09_memory/README.md @@ -0,0 +1,24 @@ +*Concepts you may want to Google beforehand: malloc* + +**Goal: Implement a memory allocator** + +We will add a kernel memory allocator to `libc/mem.c`. It is +implemented as a simple pointer to free memory, which keeps +growing. + +The `kmalloc()` function can be used to request an aligned page, +and it will also return the real, physical address, for later use. + +We'll change the `kernel.c` leaving all the "shell" code there, +Let's just try out the new `kmalloc()`, and check out that +our first page starts at 0x10000 (as hardcoded on `mem.c`) and +subsequent `kmalloc()`'s produce a new address which is +aligned 4096 bytes or 0x1000 from the previous one. + +Note that we added a new `strings.c:hex_to_ascii()` for +nicer printing of hex numbers. + +Another cosmetic modification is to rename `types.c` to +`type.c` for language consistency. + +The rest of the files are unchanged from last lesson. diff --git a/Part3/09_memory/_Makefile b/Part3/09_memory/_Makefile new file mode 100644 index 0000000..f4bd413 --- /dev/null +++ b/Part3/09_memory/_Makefile @@ -0,0 +1,52 @@ +C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c) +HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h) +# Nice syntax for file extension replacement +OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} + +# Change this if your cross-compiler is somewhere else +CC = /usr/bin/gcc +GDB = /usr/bin/gdb +# -g: Use debugging symbols in gcc +CFLAGS = -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror + +# First rule is run by default +os-image.bin: boot/bootsect.bin kernel.bin + cat $^ > os-image.bin + +# '--oformat binary' deletes all symbols as a collateral, so we don't need +# to 'strip' them manually on this case +kernel.bin: boot/kernel_entry.o ${OBJ} + /usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary + +# Used for debugging purposes +kernel.elf: boot/kernel_entry.o ${OBJ} + /usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ + +run: os-image.bin + qemu-system-i386 -vga std -drive file=$<,index=0,if=floppy,format=raw + +#-s means start server at TCP:1234 +curses: os-image.bin + qemu-system-i386 -curses -s -drive file=$<,index=0,if=floppy,format=raw + +# Open the connection to qemu and load our kernel-object file with symbols +debug: os-image.bin kernel.elf + qemu-system-i386 -curses -s -drive file=os-image.bin,index=0,if=floppy,format=raw -d guest_errors,int + +gdb: + ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" + +# Generic rules for wildcards +# To make an object, always compile from its .c +%.o: %.c ${HEADERS} + ${CC} ${CFLAGS} -c $< -o $@ + +%.o: %.asm + nasm $< -f elf -o $@ + +%.bin: %.asm + nasm $< -f bin -o $@ + +clean: + rm -rf *.bin *.dis *.o os-image.bin *.elf + rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o diff --git a/Part3/09_memory/boot/32bit_print.asm b/Part3/09_memory/boot/32bit_print.asm new file mode 100644 index 0000000..4169a17 --- /dev/null +++ b/Part3/09_memory/boot/32bit_print.asm @@ -0,0 +1,26 @@ +[bits 32] ; using 32-bit protected mode + +; this is how constants are defined +VIDEO_MEMORY equ 0xb8000 +WHITE_OB_BLACK equ 0x0f ; the color byte for each character + +print_string_pm: + pusha + mov edx, VIDEO_MEMORY + +print_string_pm_loop: + mov al, [ebx] ; [ebx] is the address of our character + mov ah, WHITE_OB_BLACK + + cmp al, 0 ; check if end of string + je print_string_pm_done + + mov [edx], ax ; store character + attribute in video memory + add ebx, 1 ; next char + add edx, 2 ; next video memory position + + jmp print_string_pm_loop + +print_string_pm_done: + popa + ret diff --git a/Part3/09_memory/boot/bootsect.asm b/Part3/09_memory/boot/bootsect.asm new file mode 100644 index 0000000..cf6a132 --- /dev/null +++ b/Part3/09_memory/boot/bootsect.asm @@ -0,0 +1,51 @@ +; Identical to lesson 13's boot sector, but the %included files have new paths +[org 0x7c00] +KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel + + mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot + mov bp, 0x9000 + mov sp, bp + + mov bx, MSG_REAL_MODE + call print + call print_nl + + call load_kernel ; read the kernel from disk + call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM' + jmp $ ; Never executed + +%include "boot/print.asm" +%include "boot/print_hex.asm" +%include "boot/disk.asm" +%include "boot/gdt.asm" +%include "boot/32bit_print.asm" +%include "boot/switch_pm.asm" + +[bits 16] +load_kernel: + mov bx, MSG_LOAD_KERNEL + call print + call print_nl + + mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000 + mov dh, 32 ; Our future kernel will be larger, make this big + mov dl, [BOOT_DRIVE] + call disk_load + ret + +[bits 32] +BEGIN_PM: + mov ebx, MSG_PROT_MODE + call print_string_pm + call KERNEL_OFFSET ; Give control to the kernel + jmp $ ; Stay here when the kernel returns control to us (if ever) + + +BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten +MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 +MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0 +MSG_LOAD_KERNEL db "Loading kernel into memory", 0 + +; padding +times 510 - ($-$$) db 0 +dw 0xaa55 diff --git a/Part3/09_memory/boot/disk.asm b/Part3/09_memory/boot/disk.asm new file mode 100644 index 0000000..be3d0a9 --- /dev/null +++ b/Part3/09_memory/boot/disk.asm @@ -0,0 +1,46 @@ +; load 'dh' sectors from drive 'dl' into ES:BX +disk_load: + pusha + ; reading from disk requires setting specific values in all registers + ; so we will overwrite our input parameters from 'dx'. Let's save it + ; to the stack for later use. + push dx + + mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' + mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) + mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) + ; 0x01 is our boot sector, 0x02 is the first 'available' sector + mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') + ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS + ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) + mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) + + ; [es:bx] <- pointer to buffer where the data will be stored + ; caller sets it up for us, and it is actually the standard location for int 13h + int 0x13 ; BIOS interrupt + jc disk_error ; if error (stored in the carry bit) + + pop dx + cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. + jne sectors_error + popa + ret + + +disk_error: + mov bx, DISK_ERROR + call print + call print_nl + mov dh, ah ; ah = error code, dl = disk drive that dropped the error + call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html + jmp disk_loop + +sectors_error: + mov bx, SECTORS_ERROR + call print + +disk_loop: + jmp $ + +DISK_ERROR: db "Disk read error", 0 +SECTORS_ERROR: db "Incorrect number of sectors read", 0 diff --git a/Part3/09_memory/boot/gdt.asm b/Part3/09_memory/boot/gdt.asm new file mode 100644 index 0000000..e5db388 --- /dev/null +++ b/Part3/09_memory/boot/gdt.asm @@ -0,0 +1,35 @@ +gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps + ; the GDT starts with a null 8-byte + dd 0x0 ; 4 byte + dd 0x0 ; 4 byte + +; GDT for code segment. base = 0x00000000, length = 0xfffff +; for flags, refer to os-dev.pdf document, page 36 +gdt_code: + dw 0xffff ; segment length, bits 0-15 + dw 0x0 ; segment base, bits 0-15 + db 0x0 ; segment base, bits 16-23 + db 10011010b ; flags (8 bits) + db 11001111b ; flags (4 bits) + segment length, bits 16-19 + db 0x0 ; segment base, bits 24-31 + +; GDT for data segment. base and length identical to code segment +; some flags changed, again, refer to os-dev.pdf +gdt_data: + dw 0xffff + dw 0x0 + db 0x0 + db 10010010b + db 11001111b + db 0x0 + +gdt_end: + +; GDT descriptor +gdt_descriptor: + dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size + dd gdt_start ; address (32 bit) + +; define some constants for later use +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start diff --git a/Part3/09_memory/boot/kernel_entry.asm b/Part3/09_memory/boot/kernel_entry.asm new file mode 100644 index 0000000..86f8fdf --- /dev/null +++ b/Part3/09_memory/boot/kernel_entry.asm @@ -0,0 +1,4 @@ +[bits 32] +[extern _start] ; Define calling point. Must have same name as kernel.c '_start' function +call _start ; Calls the C function. The linker will know where it is placed in memory +jmp $ diff --git a/Part3/09_memory/boot/print.asm b/Part3/09_memory/boot/print.asm new file mode 100644 index 0000000..b066e0d --- /dev/null +++ b/Part3/09_memory/boot/print.asm @@ -0,0 +1,37 @@ +print: + pusha + +; keep this in mind: +; while (string[i] != 0) { print string[i]; i++ } + +; the comparison for string end (null byte) +start: + mov al, [bx] ; 'bx' is the base address for the string + cmp al, 0 + je done + + ; the part where we print with the BIOS help + mov ah, 0x0e + int 0x10 ; 'al' already contains the char + + ; increment pointer and do next loop + add bx, 1 + jmp start + +done: + popa + ret + + + +print_nl: + pusha + + mov ah, 0x0e + mov al, 0x0a ; newline char + int 0x10 + mov al, 0x0d ; carriage return + int 0x10 + + popa + ret diff --git a/Part3/09_memory/boot/print_hex.asm b/Part3/09_memory/boot/print_hex.asm new file mode 100644 index 0000000..b537ef8 --- /dev/null +++ b/Part3/09_memory/boot/print_hex.asm @@ -0,0 +1,46 @@ +; receiving the data in 'dx' +; For the examples we'll assume that we're called with dx=0x1234 +print_hex: + pusha + + mov cx, 0 ; our index variable + +; Strategy: get the last char of 'dx', then convert to ASCII +; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. +; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 +; Then, move the ASCII byte to the correct position on the resulting string +hex_loop: + cmp cx, 4 ; loop 4 times + je end + + ; 1. convert last char of 'dx' to ascii + mov ax, dx ; we will use 'ax' as our working register + and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros + add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" + cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' + jle step2 + add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 + +step2: + ; 2. get the correct position of the string to place our ASCII char + ; bx <- base address + string length - index of char + mov bx, HEX_OUT + 5 ; base + length + sub bx, cx ; our index variable + mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' + ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 + + ; increment index and loop + add cx, 1 + jmp hex_loop + +end: + ; prepare the parameter and call the function + ; remember that print receives parameters in 'bx' + mov bx, HEX_OUT + call print + + popa + ret + +HEX_OUT: + db '0x0000',0 ; reserve memory for our new string diff --git a/Part3/09_memory/boot/switch_pm.asm b/Part3/09_memory/boot/switch_pm.asm new file mode 100644 index 0000000..9394da3 --- /dev/null +++ b/Part3/09_memory/boot/switch_pm.asm @@ -0,0 +1,22 @@ +[bits 16] +switch_to_pm: + cli ; 1. disable interrupts + lgdt [gdt_descriptor] ; 2. load the GDT descriptor + mov eax, cr0 + or eax, 0x1 ; 3. set 32-bit mode bit in cr0 + mov cr0, eax + jmp CODE_SEG:init_pm ; 4. far jump by using a different segment + +[bits 32] +init_pm: ; we are now using 32-bit instructions + mov ax, DATA_SEG ; 5. update the segment registers + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov ebp, 0x90000 ; 6. update the stack right at the top of the free space + mov esp, ebp + + call BEGIN_PM ; 7. Call a well-known label with useful code diff --git a/Part3/09_memory/cpu/idt.c b/Part3/09_memory/cpu/idt.c new file mode 100644 index 0000000..f904a00 --- /dev/null +++ b/Part3/09_memory/cpu/idt.c @@ -0,0 +1,16 @@ +#include "idt.h" + +void set_idt_gate(int n, u32 handler) { + idt[n].low_offset = low_16(handler); + idt[n].sel = KERNEL_CS; + idt[n].always0 = 0; + idt[n].flags = 0x8E; + idt[n].high_offset = high_16(handler); +} + +void set_idt() { + idt_reg.base = (u32) &idt; + idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1; + /* Don't make the mistake of loading &idt -- always load &idt_reg */ + __asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg)); +} diff --git a/Part3/09_memory/cpu/idt.h b/Part3/09_memory/cpu/idt.h new file mode 100644 index 0000000..27bfac5 --- /dev/null +++ b/Part3/09_memory/cpu/idt.h @@ -0,0 +1,39 @@ +#ifndef IDT_H +#define IDT_H + +#include "types.h" + +/* Segment selectors */ +#define KERNEL_CS 0x08 + +/* How every interrupt gate (handler) is defined */ +typedef struct { + u16 low_offset; /* Lower 16 bits of handler function address */ + u16 sel; /* Kernel segment selector */ + u8 always0; + /* First byte + * Bit 7: "Interrupt is present" + * Bits 6-5: Privilege level of caller (0=kernel..3=user) + * Bit 4: Set to 0 for interrupt gates + * Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */ + u8 flags; + u16 high_offset; /* Higher 16 bits of handler function address */ +} __attribute__((packed)) idt_gate_t ; + +/* A pointer to the array of interrupt handlers. + * Assembly instruction 'lidt' will read it */ +typedef struct { + u16 limit; + u32 base; +} __attribute__((packed)) idt_register_t; + +#define IDT_ENTRIES 256 +idt_gate_t idt[IDT_ENTRIES]; +idt_register_t idt_reg; + + +/* Functions implemented in idt.c */ +void set_idt_gate(int n, u32 handler); +void set_idt(); + +#endif diff --git a/Part3/09_memory/cpu/interrupt.asm b/Part3/09_memory/cpu/interrupt.asm new file mode 100644 index 0000000..bf83b7a --- /dev/null +++ b/Part3/09_memory/cpu/interrupt.asm @@ -0,0 +1,425 @@ +; Defined in isr.c +[extern isr_handler] +[extern irq_handler] + +; Common ISR code +isr_common_stub: + ; 1. Save CPU state + pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax + mov ax, ds ; Lower 16-bits of eax = ds. + push eax ; save the data segment descriptor + mov ax, 0x10 ; kernel data segment descriptor + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; 2. Call C handler + call isr_handler + + ; 3. Restore state + pop eax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + popa + add esp, 8 ; Cleans up the pushed error code and pushed ISR number + sti + iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP + +; Common IRQ code. Identical to ISR code except for the 'call' +; and the 'pop ebx' +irq_common_stub: + pusha + mov ax, ds + push eax + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + call irq_handler ; Different than the ISR code + pop ebx ; Different than the ISR code + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + popa + add esp, 8 + sti + iret + +; We don't get information about which interrupt was caller +; when the handler is run, so we will need to have a different handler +; for every interrupt. +; Furthermore, some interrupts push an error code onto the stack but others +; don't, so we will push a dummy error code for those which don't, so that +; we have a consistent stack for all of them. + +; First make the ISRs global +global isr0 +global isr1 +global isr2 +global isr3 +global isr4 +global isr5 +global isr6 +global isr7 +global isr8 +global isr9 +global isr10 +global isr11 +global isr12 +global isr13 +global isr14 +global isr15 +global isr16 +global isr17 +global isr18 +global isr19 +global isr20 +global isr21 +global isr22 +global isr23 +global isr24 +global isr25 +global isr26 +global isr27 +global isr28 +global isr29 +global isr30 +global isr31 +; IRQs +global irq0 +global irq1 +global irq2 +global irq3 +global irq4 +global irq5 +global irq6 +global irq7 +global irq8 +global irq9 +global irq10 +global irq11 +global irq12 +global irq13 +global irq14 +global irq15 + +; 0: Divide By Zero Exception +isr0: + cli + push byte 0 + push byte 0 + jmp isr_common_stub + +; 1: Debug Exception +isr1: + cli + push byte 0 + push byte 1 + jmp isr_common_stub + +; 2: Non Maskable Interrupt Exception +isr2: + cli + push byte 0 + push byte 2 + jmp isr_common_stub + +; 3: Int 3 Exception +isr3: + cli + push byte 0 + push byte 3 + jmp isr_common_stub + +; 4: INTO Exception +isr4: + cli + push byte 0 + push byte 4 + jmp isr_common_stub + +; 5: Out of Bounds Exception +isr5: + cli + push byte 0 + push byte 5 + jmp isr_common_stub + +; 6: Invalid Opcode Exception +isr6: + cli + push byte 0 + push byte 6 + jmp isr_common_stub + +; 7: Coprocessor Not Available Exception +isr7: + cli + push byte 0 + push byte 7 + jmp isr_common_stub + +; 8: Double Fault Exception (With Error Code!) +isr8: + cli + push byte 8 + jmp isr_common_stub + +; 9: Coprocessor Segment Overrun Exception +isr9: + cli + push byte 0 + push byte 9 + jmp isr_common_stub + +; 10: Bad TSS Exception (With Error Code!) +isr10: + cli + push byte 10 + jmp isr_common_stub + +; 11: Segment Not Present Exception (With Error Code!) +isr11: + cli + push byte 11 + jmp isr_common_stub + +; 12: Stack Fault Exception (With Error Code!) +isr12: + cli + push byte 12 + jmp isr_common_stub + +; 13: General Protection Fault Exception (With Error Code!) +isr13: + cli + push byte 13 + jmp isr_common_stub + +; 14: Page Fault Exception (With Error Code!) +isr14: + cli + push byte 14 + jmp isr_common_stub + +; 15: Reserved Exception +isr15: + cli + push byte 0 + push byte 15 + jmp isr_common_stub + +; 16: Floating Point Exception +isr16: + cli + push byte 0 + push byte 16 + jmp isr_common_stub + +; 17: Alignment Check Exception +isr17: + cli + push byte 0 + push byte 17 + jmp isr_common_stub + +; 18: Machine Check Exception +isr18: + cli + push byte 0 + push byte 18 + jmp isr_common_stub + +; 19: Reserved +isr19: + cli + push byte 0 + push byte 19 + jmp isr_common_stub + +; 20: Reserved +isr20: + cli + push byte 0 + push byte 20 + jmp isr_common_stub + +; 21: Reserved +isr21: + cli + push byte 0 + push byte 21 + jmp isr_common_stub + +; 22: Reserved +isr22: + cli + push byte 0 + push byte 22 + jmp isr_common_stub + +; 23: Reserved +isr23: + cli + push byte 0 + push byte 23 + jmp isr_common_stub + +; 24: Reserved +isr24: + cli + push byte 0 + push byte 24 + jmp isr_common_stub + +; 25: Reserved +isr25: + cli + push byte 0 + push byte 25 + jmp isr_common_stub + +; 26: Reserved +isr26: + cli + push byte 0 + push byte 26 + jmp isr_common_stub + +; 27: Reserved +isr27: + cli + push byte 0 + push byte 27 + jmp isr_common_stub + +; 28: Reserved +isr28: + cli + push byte 0 + push byte 28 + jmp isr_common_stub + +; 29: Reserved +isr29: + cli + push byte 0 + push byte 29 + jmp isr_common_stub + +; 30: Reserved +isr30: + cli + push byte 0 + push byte 30 + jmp isr_common_stub + +; 31: Reserved +isr31: + cli + push byte 0 + push byte 31 + jmp isr_common_stub + +; IRQ handlers +irq0: + cli + push byte 0 + push byte 32 + jmp irq_common_stub + +irq1: + cli + push byte 1 + push byte 33 + jmp irq_common_stub + +irq2: + cli + push byte 2 + push byte 34 + jmp irq_common_stub + +irq3: + cli + push byte 3 + push byte 35 + jmp irq_common_stub + +irq4: + cli + push byte 4 + push byte 36 + jmp irq_common_stub + +irq5: + cli + push byte 5 + push byte 37 + jmp irq_common_stub + +irq6: + cli + push byte 6 + push byte 38 + jmp irq_common_stub + +irq7: + cli + push byte 7 + push byte 39 + jmp irq_common_stub + +irq8: + cli + push byte 8 + push byte 40 + jmp irq_common_stub + +irq9: + cli + push byte 9 + push byte 41 + jmp irq_common_stub + +irq10: + cli + push byte 10 + push byte 42 + jmp irq_common_stub + +irq11: + cli + push byte 11 + push byte 43 + jmp irq_common_stub + +irq12: + cli + push byte 12 + push byte 44 + jmp irq_common_stub + +irq13: + cli + push byte 13 + push byte 45 + jmp irq_common_stub + +irq14: + cli + push byte 14 + push byte 46 + jmp irq_common_stub + +irq15: + cli + push byte 15 + push byte 47 + jmp irq_common_stub + diff --git a/Part3/09_memory/cpu/isr.c b/Part3/09_memory/cpu/isr.c new file mode 100644 index 0000000..bbe15b2 --- /dev/null +++ b/Part3/09_memory/cpu/isr.c @@ -0,0 +1,153 @@ +#include "isr.h" +#include "idt.h" +#include "../drivers/screen.h" +#include "../drivers/keyboard.h" +#include "../libc/string.h" +#include "timer.h" +#include "ports.h" + +isr_t interrupt_handlers[256]; + +/* Can't do this with a loop because we need the address + * of the function names */ +void isr_install() { + set_idt_gate(0, (u32)isr0); + set_idt_gate(1, (u32)isr1); + set_idt_gate(2, (u32)isr2); + set_idt_gate(3, (u32)isr3); + set_idt_gate(4, (u32)isr4); + set_idt_gate(5, (u32)isr5); + set_idt_gate(6, (u32)isr6); + set_idt_gate(7, (u32)isr7); + set_idt_gate(8, (u32)isr8); + set_idt_gate(9, (u32)isr9); + set_idt_gate(10, (u32)isr10); + set_idt_gate(11, (u32)isr11); + set_idt_gate(12, (u32)isr12); + set_idt_gate(13, (u32)isr13); + set_idt_gate(14, (u32)isr14); + set_idt_gate(15, (u32)isr15); + set_idt_gate(16, (u32)isr16); + set_idt_gate(17, (u32)isr17); + set_idt_gate(18, (u32)isr18); + set_idt_gate(19, (u32)isr19); + set_idt_gate(20, (u32)isr20); + set_idt_gate(21, (u32)isr21); + set_idt_gate(22, (u32)isr22); + set_idt_gate(23, (u32)isr23); + set_idt_gate(24, (u32)isr24); + set_idt_gate(25, (u32)isr25); + set_idt_gate(26, (u32)isr26); + set_idt_gate(27, (u32)isr27); + set_idt_gate(28, (u32)isr28); + set_idt_gate(29, (u32)isr29); + set_idt_gate(30, (u32)isr30); + set_idt_gate(31, (u32)isr31); + + // Remap the PIC + port_byte_out(0x20, 0x11); + port_byte_out(0xA0, 0x11); + port_byte_out(0x21, 0x20); + port_byte_out(0xA1, 0x28); + port_byte_out(0x21, 0x04); + port_byte_out(0xA1, 0x02); + port_byte_out(0x21, 0x01); + port_byte_out(0xA1, 0x01); + port_byte_out(0x21, 0x0); + port_byte_out(0xA1, 0x0); + + // Install the IRQs + set_idt_gate(32, (u32)irq0); + set_idt_gate(33, (u32)irq1); + set_idt_gate(34, (u32)irq2); + set_idt_gate(35, (u32)irq3); + set_idt_gate(36, (u32)irq4); + set_idt_gate(37, (u32)irq5); + set_idt_gate(38, (u32)irq6); + set_idt_gate(39, (u32)irq7); + set_idt_gate(40, (u32)irq8); + set_idt_gate(41, (u32)irq9); + set_idt_gate(42, (u32)irq10); + set_idt_gate(43, (u32)irq11); + set_idt_gate(44, (u32)irq12); + set_idt_gate(45, (u32)irq13); + set_idt_gate(46, (u32)irq14); + set_idt_gate(47, (u32)irq15); + + set_idt(); // Load with ASM +} + +/* To print the message which defines every exception */ +char *exception_messages[] = { + "Division By Zero", + "Debug", + "Non Maskable Interrupt", + "Breakpoint", + "Into Detected Overflow", + "Out of Bounds", + "Invalid Opcode", + "No Coprocessor", + + "Double Fault", + "Coprocessor Segment Overrun", + "Bad TSS", + "Segment Not Present", + "Stack Fault", + "General Protection Fault", + "Page Fault", + "Unknown Interrupt", + + "Coprocessor Fault", + "Alignment Check", + "Machine Check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" +}; + +void isr_handler(registers_t r) { + kprint("received interrupt: "); + char s[3]; + int_to_ascii(r.int_no, s, 3); + kprint(s); + kprint("\n"); + kprint(exception_messages[r.int_no]); + kprint("\n"); +} + +void register_interrupt_handler(u8 n, isr_t handler) { + interrupt_handlers[n] = handler; +} + +void irq_handler(registers_t r) { + /* After every interrupt we need to send an EOI to the PICs + * or they will not send another interrupt again */ + if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */ + port_byte_out(0x20, 0x20); /* master */ + + /* Handle the interrupt in a more modular way */ + if (interrupt_handlers[r.int_no] != 0) { + isr_t handler = interrupt_handlers[r.int_no]; + handler(r); + } +} + +void irq_install() { + /* Enable interruptions */ + asm volatile("sti"); + /* IRQ0: timer */ + init_timer(50); + /* IRQ1: keyboard */ + init_keyboard(); +} diff --git a/Part3/09_memory/cpu/isr.h b/Part3/09_memory/cpu/isr.h new file mode 100644 index 0000000..337fbf3 --- /dev/null +++ b/Part3/09_memory/cpu/isr.h @@ -0,0 +1,89 @@ +#ifndef ISR_H +#define ISR_H + +#include "types.h" + +/* ISRs reserved for CPU exceptions */ +extern void isr0(); +extern void isr1(); +extern void isr2(); +extern void isr3(); +extern void isr4(); +extern void isr5(); +extern void isr6(); +extern void isr7(); +extern void isr8(); +extern void isr9(); +extern void isr10(); +extern void isr11(); +extern void isr12(); +extern void isr13(); +extern void isr14(); +extern void isr15(); +extern void isr16(); +extern void isr17(); +extern void isr18(); +extern void isr19(); +extern void isr20(); +extern void isr21(); +extern void isr22(); +extern void isr23(); +extern void isr24(); +extern void isr25(); +extern void isr26(); +extern void isr27(); +extern void isr28(); +extern void isr29(); +extern void isr30(); +extern void isr31(); +/* IRQ definitions */ +extern void irq0(); +extern void irq1(); +extern void irq2(); +extern void irq3(); +extern void irq4(); +extern void irq5(); +extern void irq6(); +extern void irq7(); +extern void irq8(); +extern void irq9(); +extern void irq10(); +extern void irq11(); +extern void irq12(); +extern void irq13(); +extern void irq14(); +extern void irq15(); + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +/* Struct which aggregates many registers */ +typedef struct { + u32 ds; /* Data segment selector */ + u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ + u32 int_no, err_code; /* Interrupt number and error code (if applicable) */ + u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */ +} registers_t; + +void isr_install(); +void isr_handler(registers_t r); +void irq_install(); + +typedef void (*isr_t)(registers_t); +void register_interrupt_handler(u8 n, isr_t handler); + +#endif diff --git a/Part3/09_memory/cpu/ports.c b/Part3/09_memory/cpu/ports.c new file mode 100644 index 0000000..f015689 --- /dev/null +++ b/Part3/09_memory/cpu/ports.c @@ -0,0 +1,37 @@ +#include "ports.h" + +/** + * Read a byte from the specified port + */ +u8 port_byte_in (u16 port) { + u8 result; + /* Inline assembler syntax + * !! Notice how the source and destination registers are switched from NASM !! + * + * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x + * '"d" (port)': map the C variable '(port)' into e'd'x register + * + * Inputs and outputs are separated by colons + */ + __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); + return result; +} + +void port_byte_out (u16 port, u8 data) { + /* Notice how here both registers are mapped to C variables and + * nothing is returned, thus, no equals '=' in the asm syntax + * However we see a comma since there are two variables in the input area + * and none in the 'return' area + */ + __asm__ __volatile__("out %%al, %%dx" : : "a" (data), "d" (port)); +} + +u16 port_word_in (u16 port) { + u16 result; + __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); + return result; +} + +void port_word_out (u16 port, u16 data) { + __asm__ __volatile__("out %%ax, %%dx" : : "a" (data), "d" (port)); +} diff --git a/Part3/09_memory/cpu/ports.h b/Part3/09_memory/cpu/ports.h new file mode 100644 index 0000000..0b7fa2c --- /dev/null +++ b/Part3/09_memory/cpu/ports.h @@ -0,0 +1,11 @@ +#ifndef PORTS_H +#define PORTS_H + +#include "../cpu/types.h" + +unsigned char port_byte_in (u16 port); +void port_byte_out (u16 port, u8 data); +unsigned short port_word_in (u16 port); +void port_word_out (u16 port, u16 data); + +#endif diff --git a/Part3/09_memory/cpu/timer.c b/Part3/09_memory/cpu/timer.c new file mode 100644 index 0000000..c0f4357 --- /dev/null +++ b/Part3/09_memory/cpu/timer.c @@ -0,0 +1,25 @@ +#include "timer.h" +#include "isr.h" +#include "ports.h" +#include "../libc/function.h" + +u32 tick = 0; + +static void timer_callback(registers_t regs) { + tick++; + UNUSED(regs); +} + +void init_timer(u32 freq) { + /* Install the function we just wrote */ + register_interrupt_handler(IRQ0, timer_callback); + + /* Get the PIT value: hardware clock at 1193180 Hz */ + u32 divisor = 1193180 / freq; + u8 low = (u8)(divisor & 0xFF); + u8 high = (u8)( (divisor >> 8) & 0xFF); + /* Send the command */ + port_byte_out(0x43, 0x36); /* Command port */ + port_byte_out(0x40, low); + port_byte_out(0x40, high); +} diff --git a/Part3/09_memory/cpu/timer.h b/Part3/09_memory/cpu/timer.h new file mode 100644 index 0000000..5d0195b --- /dev/null +++ b/Part3/09_memory/cpu/timer.h @@ -0,0 +1,8 @@ +#ifndef TIMER_H +#define TIMER_H + +#include "types.h" + +void init_timer(u32 freq); + +#endif diff --git a/Part3/09_memory/cpu/types.h b/Part3/09_memory/cpu/types.h new file mode 100644 index 0000000..53528fe --- /dev/null +++ b/Part3/09_memory/cpu/types.h @@ -0,0 +1,35 @@ +#ifndef TYPES_H +#define TYPES_H + +/* Instead of using 'chars' to allocate non-character bytes, + * we will use these new type with no semantic meaning */ +typedef unsigned int u32; +typedef int s32; +typedef unsigned short u16; +typedef short s16; +typedef unsigned char u8; +typedef char s8; + +#define bool u32 +#define true 1 +#define TRUE 1 +#define false 0 +#define FALSE 1 + +#define NULL ((void *) 0) +#define low_16(address) (u16)((address) & 0xFFFF) +#define high_16(address) (u16)(((address) >> 16) & 0xFFFF) + +typedef struct _node { + u32 id; // generic unique id + u32 base_register; // the memory address start of allocation + u32 limit_register; // the size in bytes of the allocation + bool ft_hole_mem; // FALSE = hole, TRUE = memory allocation + + struct _node *next; + struct _node *previous; + // current size 6 words = 24bytes +} node; +#define NODE_SIZE 24 + +#endif diff --git a/Part3/09_memory/drivers/keyboard.c b/Part3/09_memory/drivers/keyboard.c new file mode 100644 index 0000000..6f40ac6 --- /dev/null +++ b/Part3/09_memory/drivers/keyboard.c @@ -0,0 +1,51 @@ +#include "keyboard.h" +#include "../cpu/ports.h" +#include "../cpu/isr.h" +#include "screen.h" +#include "../libc/string.h" +#include "../libc/function.h" +#include "../kernel/kernel.h" + +#define BACKSPACE 0x0E +#define ENTER 0x1C + +static char key_buffer[256]; + +#define SC_MAX 57 +const char *sc_name[] = { "ERROR", "Esc", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "0", "-", "=", "Backspace", "Tab", "Q", "W", "E", + "R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Lctrl", + "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "`", + "LShift", "\\", "Z", "X", "C", "V", "B", "N", "M", ",", ".", + "/", "RShift", "Keypad *", "LAlt", "Spacebar"}; +const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '}; + +static void keyboard_callback(registers_t regs) { + /* The PIC leaves us the scancode in port 0x60 */ + u8 scancode = port_byte_in(0x60); + + if (scancode > SC_MAX) return; + if (scancode == BACKSPACE) { + backspace(key_buffer); + kprint_backspace(); + } else if (scancode == ENTER) { + kprint("\n"); + user_input(key_buffer); /* kernel-controlled function */ + key_buffer[0] = '\0'; + } else { + char letter = sc_ascii[(int)scancode]; + /* Remember that kprint only accepts char[] */ + char str[2] = {letter, '\0'}; + append(key_buffer, letter); + kprint(str); + } + UNUSED(regs); +} + +void init_keyboard() { + register_interrupt_handler(IRQ1, keyboard_callback); +} diff --git a/Part3/09_memory/drivers/keyboard.h b/Part3/09_memory/drivers/keyboard.h new file mode 100644 index 0000000..ad4fc66 --- /dev/null +++ b/Part3/09_memory/drivers/keyboard.h @@ -0,0 +1,3 @@ +#include "../cpu/types.h" + +void init_keyboard(); diff --git a/Part3/09_memory/drivers/screen.c b/Part3/09_memory/drivers/screen.c new file mode 100644 index 0000000..6f2cf14 --- /dev/null +++ b/Part3/09_memory/drivers/screen.c @@ -0,0 +1,161 @@ +#include "screen.h" +#include "../cpu/ports.h" +#include "../libc/mem.h" + +/* Declaration of private functions */ +int get_cursor_offset(); +void set_cursor_offset(int offset); +int print_char(char c, int col, int row, char attr); +int get_offset(int col, int row); +int get_offset_row(int offset); +int get_offset_col(int offset); + +/********************************************************** + * Public Kernel API functions * + **********************************************************/ + +/** + * Print a message on the specified location + * If col, row, are negative, we will use the current offset + */ +void kprint_at(char *message, int col, int row) { + /* Set cursor if col/row are negative */ + int offset; + if (col >= 0 && row >= 0) + offset = get_offset(col, row); + else { + offset = get_cursor_offset(); + row = get_offset_row(offset); + col = get_offset_col(offset); + } + + /* Loop through message and print it */ + int i = 0; + while (message[i] != 0) { + offset = print_char(message[i++], col, row, WHITE_ON_BLACK); + /* Compute row/col for next iteration */ + row = get_offset_row(offset); + col = get_offset_col(offset); + } +} + +void kprint(char *message) { + kprint_at(message, -1, -1); +} + +void kprint_hex( char *message, u32 hex, u32 digits) +{ + if( digits <= 0) { + return; + } + char hex_str[digits+1]; + hex_to_ascii( hex, hex_str, digits); + kprint( message); + kprint( hex_str); + return; +} + +void kprint_backspace() { + int offset = get_cursor_offset()-2; + int row = get_offset_row(offset); + int col = get_offset_col(offset); + print_char(0x08, col, row, WHITE_ON_BLACK); +} + + +/********************************************************** + * Private kernel functions * + **********************************************************/ + + +/** + * Innermost print function for our kernel, directly accesses the video memory + * + * If 'col' and 'row' are negative, we will print at current cursor location + * If 'attr' is zero it will use 'white on black' as default + * Returns the offset of the next character + * Sets the video cursor to the returned offset + */ +int print_char(char c, int col, int row, char attr) { + u8 *vidmem = (u8*) VIDEO_ADDRESS; + if (!attr) attr = WHITE_ON_BLACK; + + /* Error control: print a red 'E' if the coords aren't right */ + if (col >= MAX_COLS || row >= MAX_ROWS) { + vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E'; + vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE; + return get_offset(col, row); + } + + int offset; + if (col >= 0 && row >= 0) offset = get_offset(col, row); + else offset = get_cursor_offset(); + + if (c == '\n') { + row = get_offset_row(offset); + offset = get_offset(0, row+1); + } else if (c == 0x08) { /* Backspace */ + vidmem[offset] = ' '; + vidmem[offset+1] = attr; + } else { + vidmem[offset] = c; + vidmem[offset+1] = attr; + offset += 2; + } + + /* Check if the offset is over screen size and scroll */ + if (offset >= MAX_ROWS * MAX_COLS * 2) { + int i; + for (i = 1; i < MAX_ROWS; i++) + memory_copy((u8*)(get_offset(0, i) + VIDEO_ADDRESS), + (u8*)(get_offset(0, i-1) + VIDEO_ADDRESS), + MAX_COLS * 2); + + /* Blank last line */ + char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (u8*) VIDEO_ADDRESS); + for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0; + + offset -= 2 * MAX_COLS; + } + + set_cursor_offset(offset); + return offset; +} + +int get_cursor_offset() { + /* Use the VGA ports to get the current cursor position + * 1. Ask for high byte of the cursor offset (data 14) + * 2. Ask for low byte (data 15) + */ + port_byte_out(REG_SCREEN_CTRL, 14); + int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */ + port_byte_out(REG_SCREEN_CTRL, 15); + offset += port_byte_in(REG_SCREEN_DATA); + return offset * 2; /* Position * size of character cell */ +} + +void set_cursor_offset(int offset) { + /* Similar to get_cursor_offset, but instead of reading we write data */ + offset /= 2; + port_byte_out(REG_SCREEN_CTRL, 14); + port_byte_out(REG_SCREEN_DATA, (u8)(offset >> 8)); + port_byte_out(REG_SCREEN_CTRL, 15); + port_byte_out(REG_SCREEN_DATA, (u8)(offset & 0xff)); +} + +void clear_screen() { + int screen_size = MAX_COLS * MAX_ROWS; + int i; + u8 *screen = (u8*) VIDEO_ADDRESS; + + for (i = 0; i < screen_size; i++) { + screen[i*2] = ' '; + screen[i*2+1] = WHITE_ON_BLACK; + } + set_cursor_offset(get_offset(0, 0)); +} + + +int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); } +int get_offset_row(int offset) { return offset / (2 * MAX_COLS); } +int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; } diff --git a/Part3/09_memory/drivers/screen.h b/Part3/09_memory/drivers/screen.h new file mode 100644 index 0000000..0727d49 --- /dev/null +++ b/Part3/09_memory/drivers/screen.h @@ -0,0 +1,23 @@ +#ifndef SCREEN_H +#define SCREEN_H + +#include "../cpu/types.h" + +#define VIDEO_ADDRESS 0xb8000 +#define MAX_ROWS 25 +#define MAX_COLS 80 +#define WHITE_ON_BLACK 0x0f +#define RED_ON_WHITE 0xf4 + +/* Screen i/o ports */ +#define REG_SCREEN_CTRL 0x3d4 +#define REG_SCREEN_DATA 0x3d5 + +/* Public kernel API */ +void clear_screen(); +void kprint_at(char *message, int col, int row); +void kprint(char *message); +void kprint_backspace(); +void kprint_hex( char *message, u32 hex, u32 digits); + +#endif diff --git a/Part3/09_memory/kernel/kernel.c b/Part3/09_memory/kernel/kernel.c new file mode 100644 index 0000000..03ed3ec --- /dev/null +++ b/Part3/09_memory/kernel/kernel.c @@ -0,0 +1,102 @@ +#include "kernel.h" + +// This only runs once and from there on forth only interrupts will cause something to happen +// e.g. displaying to screen and taking input from keyboard +void _start() { +// Initialize Global Variables and set up interrrupt handlers +// These variables are a vulnerability to OS security if someone can adjust +// the lower value in the kernel code (exploiting, for example, an Intel address hack) +// than kernel memory and even kernel code can be overwritten +// https://www.wired.com/story/intel-lab-istare-hack-chips/ +// See libc/globals.h for KMEM_START and KMEM_END values +// See libc/globals.h for UMEM_START and UMEM_END values + kmem_addr = KMEM_START; + kernel_mem_limit = KMEM_END; + + umem_addr = UMEM_START; + user_mem_limit = UMEM_END; + + global_head = NULL; + global_id = 0; + + + isr_install(); + irq_install(); + + kprint("Type something, it will go through the kernel\n" + "Type HELP to list commands\n> "); +} + +// An interrupt calls this function to parse what was typed in, this is, essentially, your +// Command Line Interpreter for now. +void user_input(char *input) { + static u32 delete_id = 0; // static persistent variable for incrementing during test + static node* umem_head = NULL; // static persistent head variable for contiguous block allocations + if (strcmp(input, "END") == 0) { + kprint("Stopping the CPU. Bye!\n"); + asm volatile("hlt"); + } else if (strcmp(input, "ADD") == 0) { + umem_head = add_node( umem_head, 0x10000, 0x100, true, global_id++); + } else if (strcmp(input, "LIST") == 0) { + kprint("***** FORWARD ****\n"); + print_list( umem_head, true); + kprint("***** REVERSE ****\n"); + print_list( umem_head, false); + } else if (strcmp(input, "SHORTLIST") == 0) { + shortprint_list( umem_head, true); + kprint("\n******************\n"); + shortprint_list( umem_head, false); + } else if (strcmp(input, "PAGE") == 0) { + u32 phys_addr = 0; + u32 page = umalloc(0x4200, 0, &phys_addr); + kprint_hex( "Page: ", page, 10); + kprint_hex(", physical address: ", phys_addr, 10); + kprint("\n"); + } else if (strcmp(input, "DELETE") == 0) { + umem_head = remove_node_by_id( umem_head, delete_id++); + } else if (strcmp(input, "INSERT") == 0) { + node *new_node = create_node( 0x15000, 0x1100, true, global_id++); + node *insert_point = find_id( umem_head, 3); + umem_head = insert_node( umem_head, insert_point, new_node, true); + new_node = create_node( 0x18000, 0x2100, true, global_id++); + insert_point = find_id( umem_head, 5); + umem_head = insert_node( umem_head, insert_point, new_node, false); + } else if (strcmp(input, "SORTA") == 0) { + umem_head = hacksort_list( umem_head, true); + } else if (strcmp(input, "SORTD") == 0) { + umem_head = hacksort_list( umem_head, false); + } else if (strcmp(input, "SWAP") == 0) { + node *n1 = find_id( umem_head, 1); + node *n2 = find_id( umem_head, 5); + node *n3 = find_id( umem_head, 3); + node *n4 = find_id( umem_head, 7); + swap_node_data( n1, n2); + swap_node_data( n2, n3); + swap_node_data( n3, n4); + } else if (strcmp(input, "TEST") == 0) { + char s1[10] = "ABCDFFGH\0"; + char s2[10] = "ABCDEGH\0"; + int x = strncmp( s1, s2, 5); + kprint_hex( "STRNCMP: ", x, 16); + kprint("\n"); + + x = sstrlen( s2, 10); + kprint_hex( "SSTRLEN: ", x, 10); + kprint("\n"); + + x = strlen( s2); + kprint_hex( "STRLEN: ", x, 10); + kprint("\n"); + } else if (strcmp(input, "HELP") == 0) { + kprint("Current Commands: ADD, LIST, SHORTLIST, PAGE, DELETE,\n"); + kprint(" : END, INSERT, SORTA, SORTD, SWAP, TEST, HELP\n"); + kprint(" Review the kernel.c source code to see what each command does.\n"); + kprint(" These are hard coded and are just examples, modify as you see fit.\n"); + kprint(" for example - TEST was just added so that I could test the strlen commands.\n"); + } else { + kprint("You said: "); + kprint(input); + kprint("\n"); + } + kprint("> "); +} diff --git a/Part3/09_memory/kernel/kernel.h b/Part3/09_memory/kernel/kernel.h new file mode 100644 index 0000000..b6f309a --- /dev/null +++ b/Part3/09_memory/kernel/kernel.h @@ -0,0 +1,13 @@ +#ifndef KERNEL_H +#define KERNEL_H + +#include "../cpu/isr.h" +#include "../drivers/screen.h" +#include "../libc/string.h" +#include "../libc/globals.h" +#include "../libc/mem.h" +#include "../libc/linked.h" + +void user_input(char *input); + +#endif diff --git a/Part3/09_memory/libc/function.h b/Part3/09_memory/libc/function.h new file mode 100644 index 0000000..bf656ed --- /dev/null +++ b/Part3/09_memory/libc/function.h @@ -0,0 +1,8 @@ +#ifndef FUNCTION_H +#define FUNCTION_H + +/* Sometimes we want to keep parameters to a function for later use + * and this is a solution to avoid the 'unused parameter' compiler warning */ +#define UNUSED(x) (void)(x) + +#endif diff --git a/Part3/09_memory/libc/globals.c b/Part3/09_memory/libc/globals.c new file mode 100644 index 0000000..2fb258a --- /dev/null +++ b/Part3/09_memory/libc/globals.c @@ -0,0 +1,11 @@ +#include "globals.h" + +u32 kmem_addr; +u32 kernel_mem_limit; + +u32 umem_addr; +u32 user_mem_limit; + +u32 global_id; + +node *global_head; diff --git a/Part3/09_memory/libc/globals.h b/Part3/09_memory/libc/globals.h new file mode 100644 index 0000000..b85f77f --- /dev/null +++ b/Part3/09_memory/libc/globals.h @@ -0,0 +1,23 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include "../cpu/types.h" +#include "linked.h" + +// Using defines allows this file to control the ranges here rather than updating the kernel code lines +// somewhere else at various locations throughout the code +// using ALL CAPS is a conventions for defines and constants, that varies from group to group +#define KMEM_START 0x9000 // starts equal to this address for allocation +#define KMEM_END 0x10000 // cannot be equal to or more than this address +extern u32 kmem_addr; // The start value of allocation kernel datastructure allocations +extern u32 kernel_mem_limit; // the upper limit of memory allowed for kernel memory allocations + +#define UMEM_START 0x10000 // kernel memory for linked list nodes starts here +#define UMEM_END 0x40000 // kernel memory for linked list nodes must end before here +extern u32 umem_addr; // start of linked list memory allocation address +extern u32 user_mem_limit; // end of linked list memory allocation address + +extern u32 global_id; // Just an id for now but will eventually become PID + +extern node *global_head; // Kernel data structure for storing memory allocation linked list +#endif // GLOBAL_H diff --git a/Part3/09_memory/libc/linked.c b/Part3/09_memory/libc/linked.c new file mode 100644 index 0000000..e0168b9 --- /dev/null +++ b/Part3/09_memory/libc/linked.c @@ -0,0 +1,480 @@ +// linked.c ANSI C kernel environment linked list +// requires kmalloc to allocate space for linked list + +#include "linked.h" + +// Create a blank node using kmalloc, then use allocated memory to store +// a linked list node with the values passed in the parameters +// You must capture the node returned with an lval result variable +node *create_node( u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id) +{ + node *new_node = NULL; + + new_node = (node *) kmalloc( NODE_SIZE, 0, (u32 *) new_node); + new_node->id = id; + new_node->base_register = base_register; + new_node->limit_register = limit_register * (new_node->id + 1); + new_node->ft_hole_mem = ft_hole_mem; + new_node->next = NULL; + new_node->previous = NULL; + + return( new_node); +} + +// Creates a node from parameters using the above and then passes it to insert the node into the linked list +// changes to *head will only affect a local stack variable +// You must capture the new head returned with an lval result variable to update head changes +// ft_hole_mem is not used currently but is included to maintain a "holes" list +// true means update memory list, false means update holes list (if you were to use it) +node *add_node( node *head, u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id) +{ + node *new_node = NULL; + + new_node = create_node( base_register, limit_register, ft_hole_mem, id); + head = _add_node( head, new_node); + return( head); +} + +// place an already created node at the end of the list +// changes to *head will only affect a local stack variable +// You must capture the new head returned with an lval result variable to update head changes +node *_add_node( node *head, node *new_node) +{ + node *iterator = NULL; + node *previous = NULL; + + if( new_node == NULL) { + return(head); + } + if( head == NULL) { + head = new_node; + return ( head ); + } // Stop here is head is empty and just set to new and return new head. + + iterator = head->next; // Head is not NULL so continue with next node as iterator + while( iterator != NULL) { + previous = iterator; + iterator = iterator->next; + } // until iterator is null keep pointing new node back to previous node and iterator to next node + + if( previous == NULL) { // just head existed, append new node ... + new_node->previous = head; + head->next = new_node; + } else { // iterated to end of list, append new node ... + new_node->previous = previous; + previous->next = new_node; + } // ... and return + return( head); +} + +// Iterate to last node and list and then append new node to it +node *append_node( node *head, node* new_node) +{ + node *iterator = head; + + if( new_node == NULL) { + return( head); + } + if( head == NULL) { + return( new_node); + } + while( iterator->next != NULL) { // step until at last node + iterator = iterator->next; + } + iterator->next = new_node; + new_node->previous = iterator; + new_node->next = NULL; + return(head); +} + +// Given an input id search list until id found +// return lval of that *node value otherwise return a NULL value +node *find_id( node *head, u32 id) +{ + if( head == NULL) { + return( head); + } + node *iterator = head; + + while(( iterator != NULL) && ( iterator->id != id)) { // search and compare id + iterator = iterator->next; + } + return( iterator); +} + +// Given an id, find it with the function above and then use node* value (target) +// to create a gap of previous and next nodes which is then closed. +// if id is not found nothing happens +node *remove_node_by_id( node *head, u32 id) +{ + node *target = NULL; + + target = find_id( head, id); + if( target == NULL) { + return( head); + } + head = zip_list( head, target); + free_node( target); + return( head); +} + +// Given a specific lval node* target value, remove it from the list *head +// and then close the gap created by removing target. +node *zip_list( node *head, node* target) +{ + bool has_previous = true; + bool has_next = true; + + if( target == NULL) { + return( head); + } + if( target->previous == NULL) { + has_previous = false; +#ifdef KDEBUG + kprint("has_previous FALSE\n"); +#endif + } + if ( target->next == NULL) { + has_next = false; +#ifdef KDEBUG + kprint("has_next FALSE\n"); +#endif + } + if( (has_next == true) && (has_previous == true)) { // Node in middle of chain + target->previous->next = target->next; + target->next->previous = target->previous; + } else if ( (has_next == false && has_previous == true)) { // Node at end of chain + target->previous->next = NULL; + } else if ( (has_next == true && has_previous == false)) { // Node at start of chain + target->next->previous = NULL; + head = head->next; + } else { // ( has_next == false && has_previous == false) // Node is isolated + head = NULL; + } + return(head); +} + +// Use find_id to get the insert_point +// decide if insertion is before or after +// if ft_before_after = FALSE then insert before target +// if ft_before_after = TRUE then append after target +node *insert_node( node *head, node* insert_point, node *new_node, bool ft_before_after) +{ + if( new_node == NULL) { // Nothing to add so return unchanged + return( head); + } + if( insert_point == NULL) { // If no point specified either place before head or after tail + if( ft_before_after) { // if after is set append as new tail + head = append_node( head, new_node); + } else { // if before than insert before head as new head + new_node->next = head; + new_node->previous = NULL; + head-> previous = new_node; + return( new_node); // return new head + } + return( head); + } + + if( ft_before_after) { // if adding after then... + if( insert_point->next != NULL) { // insert node after insertion_point + insert_point->next->previous = new_node; + new_node->next = insert_point->next; + } else { // insertion point is last node instead, skip above operations + new_node->next = NULL; + } + new_node->previous = insert_point; + insert_point->next = new_node; + } else { + if( insert_point->previous != NULL) { // insert node before insertion point + insert_point->previous->next = new_node; + new_node->previous = insert_point->previous; + } else { // insertion point is head + new_node->previous = NULL; + head = new_node; // set new head as insertion point + } + insert_point->previous = new_node; + new_node->next = insert_point; + } + return(head); // return changes (if any) to head +} + +node *get_tail( node *head) +{ + if( head == NULL) { + return(head); + } + node *tail = head; + while( tail->next != NULL) { // iterate until the next node is not NULL + tail = tail->next; + } + return( tail); +} + +void get_min_max_id( node *head, u32 *min, u32 *max) +{ + if( head == NULL) { + return; + } + node *iterator = head; + *min = 0xFFFFFFFF; + *max = 0; + + while( iterator != NULL) { // iterate until the next node is not NULL + if( *min > iterator->id) { + *min = iterator->id; + } + if( *max < iterator->id) { + *max = iterator->id; + } + iterator = iterator->next; + } + return; +} + +void swap_node_data( node* left, node *right) +{ + if(( left == NULL) || (right == NULL)) { + kprint("Invalid element.\n"); + return; + } + node swap; + + kprint_hex("SWAP ", left->id, 4); + kprint_hex(" with ", right->id, 4); + kprint("\n"); + + swap.id = left->id; + swap.base_register = left->base_register; + swap.limit_register = left->limit_register; + swap.ft_hole_mem = left->ft_hole_mem; + + left->id = right->id; + left->base_register = right->base_register; + left->limit_register = right->limit_register; + left->ft_hole_mem = right->ft_hole_mem; + + right->id = swap.id; + right->base_register = swap.base_register; + right->limit_register = swap.limit_register; + right->ft_hole_mem = swap.ft_hole_mem; + + return; +} + +node *swap_nodes( node *head, node** left, node **right) +{ + + if( head == NULL) { // don't swap nodes in a list that is empty + return( head); + } + + if( (*left == NULL) || (*right == NULL) ) { // if either or both nodes are NULL do nothing + return( head); + } + if ( head == *left) { // if left happens to be head than set head to right + head = *right; + } + if ( head == *right) { // if right happens to be head then set head to left + head = *left; + } + + node *left_previous = (*left)->previous; + node *left_next = (*left)->next; + node *right_previous = (*right)->previous; + node *right_next = (*right)->next; + + if( (*left)->previous != NULL) { // make sure not to access a NULL value node + (*left)->previous->next = *right; // previous node to left now points to right instead of left + } + + if( (*left)->next != NULL) { // same + (*left)->next->previous = *right; // next node to left now points to right instead of left + } + + if( (*right)->previous != NULL) { // same + (*right)->previous->next = *left; // same as above but right previous to left now + } + if( (*right)->next != NULL) { // same + (*right)->next->previous = *left; // same as above... + } + + (*left)->previous = right_previous; // this will obliterate left->previous so it is preserved in left_previous + (*left)->next = right_next; // this will obliterate left->next so it is preserved in left_next + + (*right)->previous = left_previous; // now point right node back to previous to left node (obliterated above) + (*right)->next = left_next; // now point right node forward to next of left node (obliterated above) + + node* swap; + swap = *left; + *left = *right; + *right = swap; + +#ifdef KDEBUG + kprint("LEFT: "); + print_node( *left); + kprint(" RIGHT: "); + print_node( *right); + kprint("\n"); +#endif + + return( head); // return new head if it changed +} + +// good ways to sort a list, transfer it to an array and merge or quicksort it +// not so good bubble-sort, insertion, select-sort, heap-sort +// worst, this ugly hack +node *hacksort_list( node* head, bool ft_descending_ascending) +{ + if( head == NULL) { + kprint("LIST EMPTY\n"); + return(head); + } + if( ft_descending_ascending) { + kprint("ASCENDING\n"); + } else { + kprint("DESCENDING\n"); + } + node* outer_iterator = head; + node* inner_iterator = head->next; + node* max = head; + + while( outer_iterator != NULL) { + max = outer_iterator; + inner_iterator = outer_iterator->next; + while( inner_iterator != NULL) { + if( ft_descending_ascending) { + if( max->id > inner_iterator->id) { // This decides ASCENDING + max = inner_iterator; + } + } else { + if( max->id < inner_iterator->id) { // This decides SORT DESCENDING + max = inner_iterator; + } + } + inner_iterator = inner_iterator->next; +/******* STOP COUNTER for Infnite Loop Halting + u32 count = 0; + if( count++ > 10) { + return(head); + } +****/ + } + if( ( outer_iterator != NULL) && ( outer_iterator != max)) { + swap_node_data( outer_iterator, max); + } + outer_iterator = outer_iterator->next; + } + return( head); +} + +node *mergesort_list( node* head, node *pivot, node *left, node *right) +{ + return( head); // disabled, not yet implemented + if( ( head == NULL) || ( pivot == NULL) | ( left == NULL) | ( right == NULL)) { + return( head); + } + node *start = left; + node *end = right; + while( (start->next != NULL) || (start->id != end->id)) { + if( start->id < pivot->id) { + ; + } + } + + return( head); +} + +// stand in for delete, this only zeroes all the values of the node allocated by kmalloc +// the memory is not reclaimed, need to add a tracker for these kmalloc allocated blocks +// to implement a proper delete function +void free_node( node *target) +{ + memory_set( (u8 *) target, 0, NODE_SIZE); + target = NULL; + return; +} + +// Print a single node's values +void print_node( node *current) +{ + char numstr[16]; + + if( current->previous != NULL) { + kprint_hex( "(", current->previous->id, 4); + kprint( ")<-"); + } else { + kprint("NULL<-"); + } + + memory_set( (u8 *) numstr, 0, 16); + hex_to_ascii( current->id, numstr, 16); + kprint("ID: "); kprint( numstr); kprint(" --- "); + memory_set( (u8 *) numstr, 0, 16); + hex_to_ascii( current->base_register, numstr, 16); + kprint("BASE: "); kprint( numstr); kprint(" --- "); + memory_set( (u8 *) numstr, 0, 16); + hex_to_ascii( current->limit_register, numstr, 16); + kprint("LIMIT: "); kprint( numstr); kprint(" --- "); + if( current->ft_hole_mem) { + kprint("MEMORY "); + } else { + kprint("HOLE "); + } + if( current->next != NULL) { + kprint_hex( "->", current->next->id, 4); + } else { + kprint("->NULL"); + } + kprint("\n"); + return; +} + +// just print the id separated by commas with a line-feed at the end +void shortprint_list( node *head, bool ft_descending_ascending) +{ + if( head == NULL) { + kprint( "EMPTY."); + kprint("\n"); + return; + } + node *iterator = NULL; + if( ft_descending_ascending) { + iterator = head; + while( iterator != NULL) { + kprint_hex( ",",iterator->id, 4); + iterator = iterator->next; + } + } else { + iterator = get_tail( head); + while( iterator != NULL) { + kprint_hex( ",",iterator->id, 4); + iterator = iterator->previous; + } + } + kprint("\n"); + return; +} + +// iterate through a list printing out the values of each node +void print_list( node *head, bool ft_descending_ascending) +{ + if( head == NULL) { + kprint( "EMPTY."); + return; + } + node *iterator = NULL; + if( ft_descending_ascending) { + iterator = head; + while( iterator != NULL) { + print_node( iterator); + iterator = iterator->next; + } + } else { + iterator = get_tail( head); + while( iterator != NULL) { + print_node( iterator); + iterator = iterator->previous; + } + } + return; +} diff --git a/Part3/09_memory/libc/linked.h b/Part3/09_memory/libc/linked.h new file mode 100644 index 0000000..a44fea2 --- /dev/null +++ b/Part3/09_memory/libc/linked.h @@ -0,0 +1,27 @@ +#ifndef LINKED_H +#define LINKED_H + +#include "../cpu/types.h" +#include "globals.h" +#include "mem.h" + +node *create_node( u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id); +node *add_node( node *head, u32 base_register, u32 limit_register, bool ft_hole_mem, u32 id); +node *_add_node( node *head, node *new_node); +node *append_node( node *head, node* new_node); +node *find_id( node *head, u32 id); +node *remove_node_by_id( node *head, u32 id); +node *zip_list( node *head, node* target); +node *insert_node( node *head, node* insert_point, node *new_node, bool ft_before_after); +node *get_tail( node *head); +void get_min_max_id( node *head, u32 *min, u32 *max); +node *swap_nodes( node *head, node **left, node **right); +void swap_node_data( node* left, node *right); +node *hacksort_list( node* head, bool ft_backward_forward); +node *mergesort_list( node* head, node *pivot, node *left, node *right); +void free_node( node *target); +void print_node( node *current); +void print_list( node *head, bool ft_backward_forward); +void shortprint_list( node *head, bool ft_backward_forward); + +#endif diff --git a/Part3/09_memory/libc/mem.c b/Part3/09_memory/libc/mem.c new file mode 100644 index 0000000..042bb43 --- /dev/null +++ b/Part3/09_memory/libc/mem.c @@ -0,0 +1,76 @@ +#include "mem.h" + +void memory_copy(u8 *source, u8 *dest, int nbytes) { + int i; + for (i = 0; i < nbytes; i++) { + *(dest + i) = *(source + i); + } +} + +void memory_set(u8 *dest, u8 val, u32 len) { + u8 *temp = (u8 *)dest; + for ( ; len != 0; len--) *temp++ = val; +} + +// See globals.h for kmem_addr global declaration +// kmem_addr is reserved block of memory for kernel to store linked list nodes +/* Implementation is just a pointer to some free memory which + * keeps growing */ +u32 kmalloc(u32 size, int align, u32 *phys_addr) { + /* Pages are aligned to 4K, or 0x1000 */ + if (align == 1 && (kmem_addr & 0xFFFFF000)) { + kmem_addr &= 0xFFFFF000; + kmem_addr += 0x1000; + } + + + char c[16]; + hex_to_ascii( *phys_addr, c, 16); + kprint("Inside kmalloc phys_addr (before) = "); + kprint(c); + kprint(" --- "); + + /* Save the physical address */ + *phys_addr = kmem_addr; + + hex_to_ascii( *phys_addr, c, 16); + kprint("(after) phys_addr = "); + kprint(c); + kprint("\n"); + + u32 ret = kmem_addr; + kmem_addr += size; /* Remember to increment the pointer */ + return ret; +} + + +// See globals.h for user_start global declaration +// umem_addr is reserved block of memory for kernel to store linked list nodes +/* Implementation is just a pointer to some free memory which + * keeps growing */ +u32 umalloc(u32 size, int align, u32 *phys_addr) { + /* Pages are aligned to 4K, or 0x1000 */ + if (align == 1 && (umem_addr & 0xFFFFF000)) { + umem_addr &= 0xFFFFF000; + umem_addr += 0x1000; + } + + + char c[16]; + hex_to_ascii( *phys_addr, c, 16); + kprint("Inside umalloc phys_addr (before) = "); + kprint(c); + kprint(" --- "); + + /* Save the physical address */ + *phys_addr = umem_addr; + + hex_to_ascii( *phys_addr, c, 16); + kprint("(after) phys_addr = "); + kprint(c); + kprint("\n"); + + u32 ret = umem_addr; + umem_addr += size; /* Remember to increment the pointer */ + return ret; +} diff --git a/Part3/09_memory/libc/mem.h b/Part3/09_memory/libc/mem.h new file mode 100644 index 0000000..5c1b9d7 --- /dev/null +++ b/Part3/09_memory/libc/mem.h @@ -0,0 +1,15 @@ +#ifndef MEM_H +#define MEM_H + +#include "../cpu/types.h" +#include "../libc/string.h" +#include "../drivers/screen.h" +#include "globals.h" + +void memory_copy(u8 *source, u8 *dest, int nbytes); +void memory_set(u8 *dest, u8 val, u32 len); + +u32 kmalloc(u32 size, int align, u32 *phys_addr); +u32 umalloc(u32 size, int align, u32 *phys_addr); + +#endif diff --git a/Part3/09_memory/libc/string.c b/Part3/09_memory/libc/string.c new file mode 100644 index 0000000..3dea5e0 --- /dev/null +++ b/Part3/09_memory/libc/string.c @@ -0,0 +1,112 @@ +#include "string.h" + +/** + * K&R implementation + */ +void int_to_ascii(int n, char str[], int size) { + memory_set( (u8 *) str, 0, size); + int i, sign; + if ((sign = n) < 0) n = -n; + i = 0; + do { + str[i++] = n % 10 + '0'; + } while ((n /= 10) > 0); + + if (sign < 0) str[i++] = '-'; + str[i] = '\0'; + + reverse(str); +} + +// Convert the numeric value n to a character string of 0x#...# where # is hexadecimal values +// Size is the size of the char str[] array so number of hex digits is 1 less than size +// because you need a terminating 0 +// the char str[] is erased with 0 values at start. +void hex_to_ascii(int n, char str[], int size) { + memory_set( (u8 *) str, 0, size); + append(str, '0'); + append(str, 'x'); + char zeros = 0; + + s32 tmp; + int i; + + for (i = 28; i > 0; i -= 4) { + tmp = (n >> i) & 0xF; + if (tmp == 0 && zeros == 0) continue; + zeros = 1; + if (tmp >= 0xA) append(str, tmp - 0xA + 'A'); + else append(str, tmp + '0'); + } + + tmp = n & 0xF; + if (tmp >= 0xA) append(str, tmp - 0xA + 'A'); + else append(str, tmp + '0'); +} + +/* K&R */ +void reverse(char s[]) { + int c, i, j; + for (i = 0, j = strlen(s)-1; i < j; i++, j--) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } +} + +/* K&R */ +int strlen(char s[]) { + int i = 0; + while (s[i] != '\0') ++i; + return i; +} + +// not K&R +int sstrlen( char s[], u32 size) +{ + if( size <= 0) { + return(0); + } + u32 i = 0; + while ( (s[i] != '\0') && ( i < size)) { ++i; } + return( i); +} + +void append(char s[], char n) { + int len = strlen(s); + s[len] = n; + s[len+1] = '\0'; +} + +void backspace(char s[]) { + int len = strlen(s); + s[len-1] = '\0'; +} + +/* K&R + * Returns <0 if s10 if s1>s2 */ +int strcmp(char s1[], char s2[]) { + int i; + for (i = 0; s1[i] == s2[i]; i++) { + if (s1[i] == '\0') return 0; + } + return s1[i] - s2[i]; +} + +// Not K&R +// Compare strings for first size characters +// if size > both string sizes compare whole string +int strncmp( char s1[], char s2[], u32 size) +{ + u32 i = 0; + while( (s1[i] != '\0') && (s2[i] != '\0') && (i < size) && (s1[i] == s2[i])) { + ++i; + } + if(i >= size) { // match exceeded size, they are same up to size chars + return(0); + } else if ( (s1[i] == s2[i])) { // match ended before size are they same? (includeing NULL matches) + return(0); + } // else don't match return diff + return( s1[i] - s2[i]); +} + diff --git a/Part3/09_memory/libc/string.h b/Part3/09_memory/libc/string.h new file mode 100644 index 0000000..b10db2c --- /dev/null +++ b/Part3/09_memory/libc/string.h @@ -0,0 +1,17 @@ +#ifndef STRINGS_H +#define STRINGS_H + +#include "../cpu/types.h" +#include "mem.h" + +void int_to_ascii(int n, char str[], int size); +void hex_to_ascii(int n, char str[], int size); +void reverse(char s[]); +int strlen(char s[]); +void backspace(char s[]); +void append(char s[], char n); +int strcmp(char s1[], char s2[]); +int strncmp(char s1[], char s2[], u32 size); +int sstrlen( char s1[], u32 size); + +#endif diff --git a/Part3/CS3502S01_Project_III_MemoryManagement.docx b/Part3/CS3502S01_Project_III_MemoryManagement.docx new file mode 100644 index 0000000..77df99e Binary files /dev/null and b/Part3/CS3502S01_Project_III_MemoryManagement.docx differ