├── .gitignore ├── Makefile ├── ata_pio_drv.c ├── ata_pio_drv.h ├── boot.nasm ├── boot.s ├── common.c ├── common.h ├── common_asm.nasm ├── common_asm.s ├── fat32.c ├── fat32.h ├── fat32_console.c ├── fat32_console.h ├── font.c ├── font.h ├── frame.c ├── frame.h ├── gdt.c ├── gdt.h ├── gdt_asm.nasm ├── gdt_asm.s ├── idt.c ├── idt.h ├── idt_asm.nasm ├── idt_asm.s ├── isr.c ├── isr.h ├── kernel.c ├── kernio.c ├── kernio.h ├── keyboard.c ├── keyboard.h ├── kheap.c ├── kheap.h ├── kmalloc_early.c ├── kmalloc_early.h ├── linker.ld ├── multiboot.h ├── paging.c ├── paging.h ├── pic.c ├── pic.h ├── pit.c ├── pit.h ├── port.h ├── port.s ├── realmode.h ├── realmode.s ├── stddef.h ├── stdint.h ├── stdio.c ├── stdio.h ├── terminal.c ├── terminal.h ├── vesa.c └── vesa.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | AS=as 3 | KERNEL_IMG=myos.bin 4 | 5 | CFLAGS = -ggdb -m32 -O0 -Wall -Wextra -std=gnu99 -ffreestanding 6 | AFLAGS = -m32 -ggdb 7 | LDFLAGS = $(CFLAGS) -nostdlib -lgcc -Wl,--build-id=none -lssp 8 | 9 | ## END CONFIGURABLE ## 10 | 11 | ## Gather the necessary assembly files 12 | ASM_FILES=$(shell ls *.s) 13 | ASM_OBJ=$(patsubst %.s,%.o,$(ASM_FILES)) 14 | 15 | ## Gather the necessary C files 16 | CFILES=$(shell ls *.c) 17 | C_OBJ=$(patsubst %.c,%.o,$(CFILES)) 18 | DEPFILES=$(patsubst %.c,./deps/%.d,$(CFILES)) 19 | 20 | OBJECTS=$(ASM_OBJ) $(C_OBJ) 21 | 22 | all: $(KERNEL_IMG) 23 | 24 | ## Depfiles contain inter-file dependencies 25 | -include $(DEPFILES) 26 | 27 | clean: 28 | -@rm *.o *~ 29 | -@rm -R deps 30 | 31 | nuke: clean 32 | -@rm $(KERNEL_IMG) 33 | -@rm f32.disk 34 | 35 | run: $(KERNEL_IMG) f32.disk 36 | qemu-system-i386 --kernel $(KERNEL_IMG) -drive file=f32.disk,format=raw -m size=4096 37 | 38 | 39 | run-debug: $(KERNEL_IMG) f32.disk 40 | @echo "gdb target localhost:1234" 41 | qemu-system-i386 --kernel $(KERNEL_IMG) -drive file=f32.disk,format=raw -m size=4096 -S -s 42 | 43 | boot.o : boot.nasm 44 | nasm -f elf32 boot.nasm -o boot.o 45 | 46 | realmode.o : realmode.s 47 | nasm -f elf realmode.s -o realmode.o 48 | 49 | common_asm.o : common_asm.nasm 50 | nasm -f elf common_asm.nasm -o common_asm.o 51 | 52 | gdt_asm.o : gdt_asm.nasm 53 | nasm -f elf gdt_asm.nasm -o gdt_asm.o 54 | 55 | idt_asm.o : idt_asm.nasm 56 | nasm -f elf idt_asm.nasm -o idt_asm.o 57 | 58 | port.o : port.s 59 | $(AS) --32 -ggdb port.s -o port.o 60 | 61 | run-kvm: $(KERNEL_IMG) f32.disk 62 | sudo qemu-system-i386 --kernel $(KERNEL_IMG) -drive file=f32.disk,format=raw -m size=4096 --enable-kvm 63 | 64 | f32.disk: 65 | -rm f32.disk 66 | dd if=/dev/zero of=f32.disk bs=1M count=100 67 | mkfs.fat -F32 f32.disk -s 1 68 | 69 | mount_disk: f32.disk 70 | mkdir -p fat32 71 | sudo mount -rw f32.disk fat32 72 | 73 | populate_disk: mount_disk 74 | sudo cp *.c *.h fat32 75 | sudo cp -R deps fat32/ 76 | sudo mkdir -p fat32/foo/bar/baz/boo/dep/doo/poo/goo/ 77 | sudo cp common.h fat32/foo/bar/baz/boo/dep/doo/poo/goo/tood.txt 78 | sleep 1 79 | sudo umount fat32 80 | -@rm -Rf fat32 81 | 82 | $(KERNEL_IMG) : $(OBJECTS) linker.ld 83 | $(CC) $(LDFLAGS) -T linker.ld -o myos.bin $(OBJECTS) 84 | 85 | ## Realmode uses nasm 86 | #realmode.o : realmode.s 87 | # nasm -f elf32 realmode.s -o realmode.o 88 | 89 | ## Generic assembly rule 90 | #%.o: %.s 91 | # $(AS) $(AFLAGS) $< -o $@ 92 | 93 | deps/%.d : %.c 94 | @mkdir -p deps 95 | @mkdir -p `dirname $@` 96 | @echo -e "[MM]\t\t" $@ 97 | @$(CC) $(CFLAGS) -MM $< -MF $@ 98 | -------------------------------------------------------------------------------- /ata_pio_drv.c: -------------------------------------------------------------------------------- 1 | #include "ata_pio_drv.h" 2 | #include "port.h" 3 | #include "kheap.h" 4 | #include "common.h" 5 | #include "kernio.h" 6 | 7 | #define ATA_PRIMARY_DATA 0x1F0 8 | #define ATA_PRIMARY_ERR 0x1F1 9 | #define ATA_PRIMARY_SECCOUNT 0x1F2 10 | #define ATA_PRIMARY_LBA_LO 0x1F3 11 | #define ATA_PRIMARY_LBA_MID 0x1F4 12 | #define ATA_PRIMARY_LBA_HI 0x1F5 13 | #define ATA_PRIMARY_DRIVE_HEAD 0x1F6 14 | #define ATA_PRIMARY_COMM_REGSTAT 0x1F7 15 | #define ATA_PRIMARY_ALTSTAT_DCR 0x3F6 16 | 17 | 18 | #define STAT_ERR (1 << 0) // Indicates an error occurred. Send a new command to clear it 19 | #define STAT_DRQ (1 << 3) // Set when the drive has PIO data to transfer, or is ready to accept PIO data. 20 | #define STAT_SRV (1 << 4) // Overlapped Mode Service Request. 21 | #define STAT_DF (1 << 5) // Drive Fault Error (does not set ERR). 22 | #define STAT_RDY (1 << 6) // Bit is clear when drive is spun down, or after an error. Set otherwise. 23 | #define STAT_BSY (1 << 7) // Indicates the drive is preparing to send/receive data (wait for it to clear). 24 | // In case of 'hang' (it never clears), do a software reset. 25 | 26 | 27 | /** 28 | * To use the IDENTIFY command, select a target drive by sending 0xA0 for the master drive, 29 | * or 0xB0 for the slave, to the "drive select" IO port. On the Primary bus, this would be port 0x1F6. 30 | * Then set the Sectorcount, LBAlo, LBAmid, and LBAhi IO ports to 0 (port 0x1F2 to 0x1F5). Then send 31 | * the IDENTIFY command (0xEC) to the Command IO port (0x1F7). 32 | * 33 | * Then read the Status port (0x1F7) again. 34 | * If the value read is 0, the drive does not exist. 35 | * For any other value: poll the Status port (0x1F7) until bit 7 (BSY, value = 0x80) clears. 36 | * Because of some ATAPI drives that do not follow spec, at this point you need to check the 37 | * LBAmid and LBAhi ports (0x1F4 and 0x1F5) to see if they are non-zero. If so, the drive is not ATA, 38 | * and you should stop polling. Otherwise, continue polling one of the Status 39 | * ports until bit 3 (DRQ, value = 8) sets, or until bit 0 (ERR, value = 1) sets. 40 | * 41 | * At that point, if ERR is clear, the data is ready to read from the Data port (0x1F0). 42 | * Read 256 16-bit values, and store them. 43 | */ 44 | 45 | uint8_t identify() { 46 | inb(ATA_PRIMARY_COMM_REGSTAT); 47 | outb(ATA_PRIMARY_DRIVE_HEAD, 0xA0); 48 | inb(ATA_PRIMARY_COMM_REGSTAT); 49 | outb(ATA_PRIMARY_SECCOUNT, 0); 50 | inb(ATA_PRIMARY_COMM_REGSTAT); 51 | outb(ATA_PRIMARY_LBA_LO, 0); 52 | inb(ATA_PRIMARY_COMM_REGSTAT); 53 | outb(ATA_PRIMARY_LBA_MID, 0); 54 | inb(ATA_PRIMARY_COMM_REGSTAT); 55 | outb(ATA_PRIMARY_LBA_HI, 0); 56 | inb(ATA_PRIMARY_COMM_REGSTAT); 57 | outb(ATA_PRIMARY_COMM_REGSTAT, 0xEC); 58 | outb(ATA_PRIMARY_COMM_REGSTAT, 0xE7); 59 | 60 | // Read the status port. If it's zero, the drive does not exist. 61 | uint8_t status = inb(ATA_PRIMARY_COMM_REGSTAT); 62 | 63 | printf("Waiting for status.\n"); 64 | while(status & STAT_BSY) { 65 | uint32_t i = 0; 66 | while(1) { 67 | printf("Printing stuff %d\n", i); 68 | i++; 69 | } 70 | for(i = 0; i < 0x0FFFFFFF; i++) {} 71 | printf("Checking regstat.\n"); 72 | status = inb(ATA_PRIMARY_COMM_REGSTAT); 73 | } 74 | 75 | if(status == 0) return 0; 76 | 77 | printf("Status indicates presence of a drive. Polling while STAT_BSY... "); 78 | while(status & STAT_BSY) { 79 | printf("\ninb(ATA_PRIMARY_COMM_REGSTAT);... "); 80 | status = inb(ATA_PRIMARY_COMM_REGSTAT); 81 | } 82 | printf("Done.\n"); 83 | 84 | uint8_t mid = inb(ATA_PRIMARY_LBA_MID); 85 | uint8_t hi = inb(ATA_PRIMARY_LBA_HI); 86 | if(mid || hi) { 87 | // The drive is not ATA. (Who knows what it is.) 88 | return 0; 89 | } 90 | 91 | printf("Waiting for ERR or DRQ.\n"); 92 | // Wait for ERR or DRQ 93 | while(!(status & (STAT_ERR | STAT_DRQ))) { 94 | status = inb(ATA_PRIMARY_COMM_REGSTAT); 95 | } 96 | 97 | if(status & STAT_ERR) { 98 | // There was an error on the drive. Forget about it. 99 | return 0; 100 | } 101 | 102 | printf("Reading IDENTIFY structure.\n"); 103 | //uint8_t *buff = kmalloc(40960, 0, NULL); 104 | uint8_t buff[256 * 2]; 105 | insw(ATA_PRIMARY_DATA, buff, 256); 106 | printf("Success. Disk is ready to go.\n"); 107 | // We read it! 108 | return 1; 109 | } 110 | 111 | /** 112 | * An example of a 28 bit LBA PIO mode read on the Primary bus: 113 | * 114 | * Send 0xE0 for the "master" or 0xF0 for the "slave", ORed with the highest 4 bits of the LBA to port 0x1F6: outb(0x1F6, 0xE0 | (slavebit << 4) | ((LBA >> 24) & 0x0F)) 115 | * Send a NULL byte to port 0x1F1, if you like (it is ignored and wastes lots of CPU time): outb(0x1F1, 0x00) 116 | * Send the sectorcount to port 0x1F2: outb(0x1F2, (unsigned char) count) 117 | * Send the low 8 bits of the LBA to port 0x1F3: outb(0x1F3, (unsigned char) LBA)) 118 | * Send the next 8 bits of the LBA to port 0x1F4: outb(0x1F4, (unsigned char)(LBA >> 8)) 119 | * Send the next 8 bits of the LBA to port 0x1F5: outb(0x1F5, (unsigned char)(LBA >> 16)) 120 | * Send the "READ SECTORS" command (0x20) to port 0x1F7: outb(0x1F7, 0x20) 121 | * Wait for an IRQ or poll. 122 | * Transfer 256 16-bit values, a uint16_t at a time, into your buffer from I/O port 0x1F0. (In assembler, REP INSW works well for this.) 123 | * Then loop back to waiting for the next IRQ (or poll again -- see next note) for each successive sector. 124 | */ 125 | 126 | 127 | void ata_pio_read28(uint32_t LBA, uint8_t sectorcount, uint8_t *target) { 128 | // HARD CODE MASTER (for now) 129 | outb(ATA_PRIMARY_DRIVE_HEAD, 0xE0 | ((LBA >> 24) & 0x0F)); 130 | outb(ATA_PRIMARY_ERR, 0x00); 131 | outb(ATA_PRIMARY_SECCOUNT, sectorcount); 132 | outb(ATA_PRIMARY_LBA_LO, LBA & 0xFF); 133 | outb(ATA_PRIMARY_LBA_MID, (LBA >> 8) & 0xFF); 134 | outb(ATA_PRIMARY_LBA_HI, (LBA >> 16) & 0xFF); 135 | outb(ATA_PRIMARY_COMM_REGSTAT, 0x20); 136 | 137 | uint8_t i; 138 | for(i = 0; i < sectorcount; i++) { 139 | // POLL! 140 | while(1) { 141 | uint8_t status = inb(ATA_PRIMARY_COMM_REGSTAT); 142 | if(status & STAT_DRQ) { 143 | // Drive is ready to transfer data! 144 | break; 145 | } 146 | } 147 | // Transfer the data! 148 | insw(ATA_PRIMARY_DATA, (void *)target, 256); 149 | target += 256; 150 | } 151 | 152 | } 153 | 154 | /** 155 | * 48-bit LBA read 156 | * 157 | * Send 0x40 for the "master" or 0x50 for the "slave" to port 0x1F6: outb(0x1F6, 0x40 | (slavebit << 4)) 158 | * outb (0x1F2, sectorcount high byte) 159 | * outb (0x1F3, LBA4) 160 | * outb (0x1F4, LBA5) 161 | * outb (0x1F5, LBA6) 162 | * outb (0x1F2, sectorcount low byte) 163 | * outb (0x1F3, LBA1) 164 | * outb (0x1F4, LBA2) 165 | * outb (0x1F5, LBA3) 166 | * Send the "READ SECTORS EXT" command (0x24) to port 0x1F7: outb(0x1F7, 0x24) 167 | */ 168 | 169 | void ata_pio_read48(uint64_t LBA, uint16_t sectorcount, uint8_t *target) { 170 | // HARD CODE MASTER (for now) 171 | outb(ATA_PRIMARY_DRIVE_HEAD, 0x40); // Select master 172 | outb(ATA_PRIMARY_SECCOUNT, (sectorcount >> 8) & 0xFF ); // sectorcount high 173 | outb(ATA_PRIMARY_LBA_LO, (LBA >> 24) & 0xFF); // LBA4 174 | outb(ATA_PRIMARY_LBA_MID, (LBA >> 32) & 0xFF); // LBA5 175 | outb(ATA_PRIMARY_LBA_HI, (LBA >> 40) & 0xFF); // LBA6 176 | outb(ATA_PRIMARY_SECCOUNT, sectorcount & 0xFF); // sectorcount low 177 | outb(ATA_PRIMARY_LBA_LO, LBA & 0xFF); // LBA1 178 | outb(ATA_PRIMARY_LBA_MID, (LBA >> 8) & 0xFF); // LBA2 179 | outb(ATA_PRIMARY_LBA_HI, (LBA >> 16) & 0xFF); // LBA3 180 | outb(ATA_PRIMARY_COMM_REGSTAT, 0x24); // READ SECTORS EXT 181 | 182 | 183 | uint8_t i; 184 | for(i = 0; i < sectorcount; i++) { 185 | // POLL! 186 | while(1) { 187 | uint8_t status = inb(ATA_PRIMARY_COMM_REGSTAT); 188 | if(status & STAT_DRQ) { 189 | // Drive is ready to transfer data! 190 | break; 191 | } 192 | } 193 | // Transfer the data! 194 | insw(ATA_PRIMARY_DATA, (void *)target, 256); 195 | target += 256; 196 | } 197 | 198 | } 199 | 200 | /** 201 | * To write sectors in 48 bit PIO mode, send command "WRITE SECTORS EXT" (0x34), instead. 202 | * (As before, do not use REP OUTSW when writing.) And remember to do a Cache Flush after 203 | * each write command completes. 204 | */ 205 | void ata_pio_write48(uint64_t LBA, uint16_t sectorcount, uint8_t *target) { 206 | 207 | // HARD CODE MASTER (for now) 208 | outb(ATA_PRIMARY_DRIVE_HEAD, 0x40); // Select master 209 | outb(ATA_PRIMARY_SECCOUNT, (sectorcount >> 8) & 0xFF ); // sectorcount high 210 | outb(ATA_PRIMARY_LBA_LO, (LBA >> 24) & 0xFF); // LBA4 211 | outb(ATA_PRIMARY_LBA_MID, (LBA >> 32) & 0xFF); // LBA5 212 | outb(ATA_PRIMARY_LBA_HI, (LBA >> 40) & 0xFF); // LBA6 213 | outb(ATA_PRIMARY_SECCOUNT, sectorcount & 0xFF); // sectorcount low 214 | outb(ATA_PRIMARY_LBA_LO, LBA & 0xFF); // LBA1 215 | outb(ATA_PRIMARY_LBA_MID, (LBA >> 8) & 0xFF); // LBA2 216 | outb(ATA_PRIMARY_LBA_HI, (LBA >> 16) & 0xFF); // LBA3 217 | outb(ATA_PRIMARY_COMM_REGSTAT, 0x34); // READ SECTORS EXT 218 | 219 | uint8_t i; 220 | for(i = 0; i < sectorcount; i++) { 221 | // POLL! 222 | while(1) { 223 | uint8_t status = inb(ATA_PRIMARY_COMM_REGSTAT); 224 | if(status & STAT_DRQ) { 225 | // Drive is ready to transfer data! 226 | break; 227 | } 228 | else if(status & STAT_ERR) { 229 | PANIC("DISK SET ERROR STATUS!"); 230 | } 231 | } 232 | // Transfer the data! 233 | outsw(ATA_PRIMARY_DATA, (void *)target, 256); 234 | target += 256; 235 | } 236 | 237 | // Flush the cache. 238 | outb(ATA_PRIMARY_COMM_REGSTAT, 0xE7); 239 | // Poll for BSY. 240 | while(inb(ATA_PRIMARY_COMM_REGSTAT) & STAT_BSY) {} 241 | } 242 | -------------------------------------------------------------------------------- /ata_pio_drv.h: -------------------------------------------------------------------------------- 1 | #ifndef ATA_PIO_DRV_H 2 | #define ATA_PIO_DRV_H 3 | 4 | #include "stdint.h" 5 | 6 | uint8_t identify(); 7 | void ata_pio_read28(uint32_t LBA, uint8_t sectorcount, uint8_t *target); 8 | void ata_pio_read48(uint64_t LBA, uint16_t sectorcount, uint8_t *target); 9 | void ata_pio_write48(uint64_t LBA, uint16_t sectorcount, uint8_t *target); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /boot.nasm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | MALIGN equ 1<<0 4 | MEMINFO equ 1<<1 5 | FLAGS equ MALIGN | MEMINFO 6 | MAGIC equ 0x1BADB002 7 | CHECKSUM equ -(MAGIC + FLAGS) 8 | 9 | section .multiboot 10 | align 4 11 | dd MAGIC 12 | dd FLAGS 13 | dd CHECKSUM 14 | 15 | 16 | section .bootstrap_stack nobits write align=16 17 | align 16 18 | stack_bottom: 19 | resb 32768 20 | stack_top: 21 | 22 | extern kernel_main 23 | section .text 24 | global _start:function (_start.end - _start) 25 | _start: 26 | mov esp, stack_top 27 | ;sti 28 | push ebx 29 | call kernel_main 30 | .end 31 | global halt 32 | halt: 33 | cli 34 | hlt 35 | .Lhang: 36 | jmp halt 37 | .end 38 | 39 | section .text 40 | global pause 41 | pause: 42 | hlt 43 | ret 44 | 45 | section .text 46 | global sys_cli 47 | sys_cli: 48 | hlt 49 | ret 50 | 51 | 52 | section .text 53 | global sys_sti 54 | sys_sti: 55 | hlt 56 | ret 57 | 58 | section .kend 59 | global end_of_kernel 60 | end_of_kernel: 61 | -------------------------------------------------------------------------------- /boot.s: -------------------------------------------------------------------------------- 1 | # Declare constants used for creating a multiboot header. 2 | .set ALIGN, 1<<0 # align loaded modules on page boundaries 3 | .set MEMINFO, 1<<1 # provide memory map 4 | .set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field 5 | .set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header 6 | .set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot 7 | 8 | # Declare a header as in the Multiboot Standard. We put this into a special 9 | # section so we can force the header to be in the start of the final program. 10 | # You don't need to understand all these details as it is just magic values that 11 | # is documented in the multiboot standard. The bootloader will search for this 12 | # magic sequence and recognize us as a multiboot kernel. 13 | .section .multiboot 14 | .align 4 15 | .long MAGIC 16 | .long FLAGS 17 | .long CHECKSUM 18 | 19 | # Currently the stack pointer register (esp) points at anything and using it may 20 | # cause massive harm. Instead, we'll provide our own stack. We will allocate 21 | # room for a small temporary stack by creating a symbol at the bottom of it, 22 | # then allocating 16384 bytes for it, and finally creating a symbol at the top. 23 | .section .bootstrap_stack, "aw", @nobits 24 | stack_bottom: 25 | .skip 32768 #32 KiB 26 | stack_top: 27 | 28 | .section .text 29 | .global _start 30 | .type _start, @function 31 | _start: 32 | # Welcome to kernel mode! We now have sufficient code for the bootloader to 33 | # load and run our operating system. It doesn't do anything interesting yet. 34 | # Perhaps we would like to call printf("Hello, World\n"). You should now 35 | # realize one of the profound truths about kernel mode: There is nothing 36 | # there unless you provide it yourself. There is no printf function. There 37 | # is no header. If you want a function, you will have to code it 38 | # yourself. And that is one of the best things about kernel development: 39 | # you get to make the entire system yourself. You have absolute and complete 40 | # power over the machine, there are no security restrictions, no safe 41 | # guards, no debugging mechanisms, there is nothing but what you build. 42 | 43 | # By now, you are perhaps tired of assembly language. You realize some 44 | # things simply cannot be done in C, such as making the multiboot header in 45 | # the right section and setting up the stack. However, you would like to 46 | # write the operating system in a higher level language, such as C or C++. 47 | # To that end, the next task is preparing the processor for execution of 48 | # such code. C doesn't expect much at this point and we only need to set up 49 | # a stack. Note that the processor is not fully initialized yet and stuff 50 | # such as floating point instructions are not available yet. 51 | 52 | # To set up a stack, we simply set the esp register to point to the top of 53 | # our stack (as it grows downwards). 54 | movl $stack_top, %esp 55 | 56 | # We are now ready to actually execute C code. We cannot embed that in an 57 | # assembly file, so we'll create a kernel.c file in a moment. In that file, 58 | # we'll create a C entry point called kernel_main and call it here. 59 | sti 60 | pushl %ebx # EBX contains a pointer to the multiboot info structure. 61 | call kernel_main 62 | 63 | # In case the function returns, we'll want to put the computer into an 64 | # infinite loop. To do that, we use the clear interrupt ('cli') instruction 65 | # to disable interrupts, the halt instruction ('hlt') to stop the CPU until 66 | # the next interrupt arrives, and jumping to the halt instruction if it ever 67 | # continues execution, just to be safe. We will create a local label rather 68 | # than real symbol and jump to there endlessly. 69 | .global halt 70 | halt: 71 | cli 72 | hlt 73 | .Lhang: 74 | jmp .Lhang 75 | 76 | .section .text 77 | .global pause 78 | .type pause @function 79 | pause: 80 | hlt 81 | ret 82 | 83 | .section .text 84 | .global sys_cli 85 | .type sys_cli @function 86 | sys_cli: 87 | hlt 88 | ret 89 | 90 | 91 | .section .text 92 | .global sys_sti 93 | .type sys_sti @function 94 | sys_sti: 95 | hlt 96 | ret 97 | 98 | # Set the size of the _start symbol to the current location '.' minus its start. 99 | # This is useful when debugging or when you implement call tracing. 100 | .size _start, . - _start 101 | 102 | .section .kend 103 | .global end_of_kernel 104 | end_of_kernel: 105 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "kernio.h" 3 | #include "terminal.h" 4 | #include "vesa.h" 5 | #include "kheap.h" 6 | 7 | extern void halt(); 8 | 9 | void *memset(void *p, int c, size_t count) 10 | { 11 | for(unsigned int i = 0; i < count; i++) 12 | { 13 | ((char *)p)[i] = c; 14 | } 15 | return p; 16 | } 17 | 18 | void *memcpy(void *dest, const void *src, size_t n) { 19 | 20 | char *cdest = dest; 21 | const char *csrc = src; 22 | 23 | size_t i; 24 | for(i = 0; i < n; i++) { 25 | cdest[i] = csrc[i]; 26 | } 27 | 28 | return dest; 29 | } 30 | 31 | int k_toupper(int c) { 32 | if(c >= 97 && c <= 122) { 33 | return c - 32; 34 | } 35 | return c; 36 | } 37 | int k_tolower(int c) { 38 | if(c >=65 && c <= 90) { 39 | return c + 32; 40 | } 41 | return c; 42 | } 43 | 44 | char * itos(uint32_t myint, char buffer[], int bufflen) 45 | { 46 | int i = bufflen - 2; 47 | buffer[bufflen-1] = 0; 48 | 49 | if(myint == 0) { 50 | buffer[i--] = '0'; 51 | } 52 | 53 | while(myint > 0 && i >= 0) 54 | { 55 | buffer[i--] = (myint % 10) + '0'; 56 | myint/=10; 57 | } 58 | 59 | return &buffer[i+1]; 60 | } 61 | 62 | size_t strlen(const char* str) { 63 | size_t ret = 0; 64 | while ( str[ret] != 0 ) 65 | ret++; 66 | return ret; 67 | } 68 | 69 | int strcmp(const char *s1, const char *s2) { 70 | while(*s1 && *s2) { 71 | if(*s1 != *s2) { 72 | return s1 - s2; 73 | } 74 | s1++; 75 | s2++; 76 | } 77 | if(*s1) return 1; 78 | if(*s2) return -1; 79 | return 0; 80 | } 81 | 82 | char *strdup(const char *s) { 83 | char *news = kmalloc(strlen(s) + 1); 84 | char *temp = news; 85 | while(*temp++ = *s++); 86 | return news; 87 | } 88 | 89 | char *strchr(const char *s, int c) { 90 | while(*s) { 91 | if(*s == c) return s; 92 | s++; 93 | } 94 | return NULL; 95 | } 96 | 97 | char *strtok_r(char *str, const char *delim, char **saveptr) { 98 | char *begin; 99 | if(str) { 100 | begin = str; 101 | } 102 | else if (*saveptr) { 103 | begin = *saveptr; 104 | } 105 | else { 106 | return NULL; 107 | } 108 | 109 | while(strchr(delim, begin[0])) { 110 | begin++; 111 | } 112 | 113 | 114 | char *next = NULL; 115 | for(int i = 0; i < strlen(delim); i++) { 116 | char *temp = strchr(begin, delim[i]); 117 | if(temp < next || next == NULL) { 118 | next = temp; 119 | } 120 | } 121 | if(!next) { 122 | *saveptr = NULL; 123 | return begin; 124 | } 125 | *next = 0; 126 | *saveptr=next+1; 127 | return begin; 128 | } 129 | 130 | int coerce_int(char *s, uint32_t *val) { 131 | uint32_t result = 0; 132 | 133 | while(*s && *s != '\n') { 134 | if(*s >= 48 && *s <= 57) { 135 | result *= 10; 136 | result = result + (*s - 48); 137 | } 138 | else { 139 | return 0; 140 | } 141 | s++; 142 | } 143 | *val = result; 144 | return 1; 145 | } 146 | 147 | uint8_t hex_char(uint8_t byte) 148 | { 149 | byte = byte & 0x0F; 150 | if(byte < 0xA) 151 | { 152 | char buff[2]; 153 | itos(byte, buff, 2); 154 | return buff[0]; 155 | } 156 | else 157 | { 158 | switch(byte) 159 | { 160 | case 0x0A: 161 | return 'A'; 162 | break; 163 | case 0x0B: 164 | return 'B'; 165 | break; 166 | case 0x0C: 167 | return 'C'; 168 | break; 169 | case 0x0D: 170 | return 'D'; 171 | break; 172 | case 0x0E: 173 | return 'E'; 174 | break; 175 | case 0x0F: 176 | return 'F'; 177 | break; 178 | } 179 | } 180 | return 0; 181 | } 182 | 183 | void PANIC(char *err) { 184 | terminal_set_cursor(0, 1); 185 | set_vesa_color(make_vesa_color(0, 0, 0)); 186 | set_vesa_background(make_vesa_color(255, 0, 0)); 187 | // terminal_setcolor(make_color(COLOR_DARK_GREY, COLOR_BLACK)); 188 | // terminal_settextcolor(make_color(COLOR_RED, COLOR_BLACK)); 189 | printf("PANIC: %s", err); 190 | halt(); 191 | } 192 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | 7 | void *memset(void *p, int c, size_t count); 8 | void *memcpy(void *dest, const void *src, size_t n); 9 | extern void fastcp(char *dest, char *src, uint32_t count); 10 | 11 | int k_toupper(int c); 12 | int k_tolower(int c); 13 | 14 | char * itos(uint32_t myint, char buffer[], int bufflen); 15 | 16 | size_t strlen(const char* str); 17 | int strcmp(const char *s1, const char *s2); 18 | char *strdup(const char *s); 19 | char *strchr(const char *s, int c); 20 | char *strtok_r(char *str, const char *delim, char **saveptr); 21 | 22 | int coerce_int(char *s, uint32_t *val); 23 | uint8_t hex_char(uint8_t byte); 24 | 25 | void PANIC(char *err); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /common_asm.nasm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | section .text 4 | global fastcp 5 | 6 | fastcp: 7 | push esi 8 | push edi 9 | mov edi, DWORD [esp + 12] 10 | mov esi, DWORD [esp + 16] 11 | mov ecx, DWORD [esp + 20] 12 | rep movsb 13 | pop edi 14 | pop esi 15 | ret 16 | -------------------------------------------------------------------------------- /common_asm.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | .global fastcp 3 | 4 | .type fastcp, @function 5 | # void fastcp(char *dest, char *src, uint32_t count) 6 | fastcp: 7 | pushl %esi 8 | pushl %edi 9 | movl 12(%esp), %edi # dest 10 | movl 16(%esp), %esi # src 11 | movl 20(%esp), %ecx # count 12 | rep movsb 13 | popl %edi 14 | popl %esi 15 | ret 16 | -------------------------------------------------------------------------------- /fat32.c: -------------------------------------------------------------------------------- 1 | #include "fat32.h" 2 | #include "ata_pio_drv.h" 3 | #include "kheap.h" 4 | #include "common.h" 5 | #include "kernio.h" 6 | 7 | f32 *master_fs; 8 | //int x; 9 | 10 | //struct f32 { 11 | // //FILE *f; 12 | // uint32_t *FAT; 13 | // struct bios_parameter_block bpb; 14 | // uint32_t partition_begin_sector; 15 | // uint32_t fat_begin_sector; 16 | // uint32_t cluster_begin_sector; 17 | // uint32_t cluster_size; 18 | // uint32_t cluster_alloc_hint; 19 | //}; 20 | 21 | static void read_bpb(f32 *fs, struct bios_parameter_block *bpb); 22 | static uint32_t sector_for_cluster(f32 *fs, uint32_t cluster); 23 | 24 | static void trim_spaces(char *c, int max) { 25 | int i = 0; 26 | while(*c != ' ' && i++ < max) { 27 | c++; 28 | } 29 | if(*c == ' ') *c = 0; 30 | } 31 | 32 | static void getSector(f32 *fs, uint8_t *buff, uint32_t sector, uint32_t count) { 33 | ata_pio_read48(sector, count, buff); 34 | } 35 | 36 | static void putSector(f32 *fs, uint8_t *buff, uint32_t sector, uint32_t count) { 37 | uint32_t i; 38 | for(i = 0; i < count; i++) { 39 | ata_pio_write48(sector + i, 1, buff + (i * 512)); 40 | } 41 | } 42 | 43 | static void flushFAT(f32 *fs) { 44 | // TODO: This is not endian-safe. Must marshal the integers into a byte buffer. 45 | putSector(fs, (uint8_t *)fs->FAT, fs->fat_begin_sector, fs->bpb.count_sectors_per_FAT32); 46 | } 47 | 48 | static uint16_t readi16(uint8_t *buff, size_t offset) 49 | { 50 | uint8_t *ubuff = buff + offset; 51 | return ubuff[1] << 8 | ubuff[0]; 52 | } 53 | static uint32_t readi32(uint8_t *buff, size_t offset) { 54 | uint8_t *ubuff = buff + offset; 55 | return 56 | ((ubuff[3] << 24) & 0xFF000000) | 57 | ((ubuff[2] << 16) & 0x00FF0000) | 58 | ((ubuff[1] << 8) & 0x0000FF00) | 59 | (ubuff[0] & 0x000000FF); 60 | } 61 | 62 | /** 63 | * 11 2 The number of Bytes per sector (remember, all numbers are in the little-endian format). 64 | * 13 1 Number of sectors per cluster. 65 | * 14 2 Number of reserved sectors. The boot record sectors are included in this value. 66 | * 16 1 Number of File Allocation Tables (FAT's) on the storage media. Often this value is 2. 67 | * 17 2 Number of directory entries (must be set so that the root directory occupies entire sectors). 68 | * 19 2 The total sectors in the logical volume. If this value is 0, it means there are more than 65535 sectors in the volume, and the actual count is stored in "Large Sectors (bytes 32-35). 69 | * 21 1 This Byte indicates the media descriptor type. 70 | * 22 2 Number of sectors per FAT. FAT12/FAT16 only. 71 | * 24 2 Number of sectors per track. 72 | * 26 2 Number of heads or sides on the storage media. 73 | * 28 4 Number of hidden sectors. (i.e. the LBA of the beginning of the partition.) 74 | * 32 4 Large amount of sector on media. This field is set if there are more than 65535 sectors in the volume. 75 | */ 76 | 77 | /** 78 | * 36 4 Sectors per FAT. The size of the FAT in sectors. 79 | * 40 2 Flags. 80 | * 42 2 FAT version number. The high byte is the major version and the low byte is the minor version. FAT drivers should respect this field. 81 | * 44 4 The cluster number of the root directory. Often this field is set to 2. 82 | * 48 2 The sector number of the FSInfo structure. 83 | * 50 2 The sector number of the backup boot sector. 84 | * 52 12 Reserved. When the volume is formated these bytes should be zero. 85 | * 64 1 Drive number. The values here are identical to the values returned by the BIOS interrupt 0x13. 0x00 for a floppy disk and 0x80 for hard disks. 86 | * 65 1 Flags in Windows NT. Reserved otherwise. 87 | * 66 1 Signature (must be 0x28 or 0x29). 88 | * 67 4 VolumeID 'Serial' number. Used for tracking volumes between computers. You can ignore this if you want. 89 | * 71 11 Volume label string. This field is padded with spaces. 90 | * 82 8 System identifier string. Always "FAT32 ". The spec says never to trust the contents of this string for any use. 91 | * 90 420 Boot code. 92 | */ 93 | 94 | static void read_bpb(f32 *fs, struct bios_parameter_block *bpb) { 95 | uint8_t sector0[512]; 96 | getSector(fs, sector0, 0, 1); 97 | 98 | bpb->bytes_per_sector = readi16(sector0, 11);; 99 | bpb->sectors_per_cluster = sector0[13]; 100 | bpb->reserved_sectors = readi16(sector0, 14); 101 | bpb->FAT_count = sector0[16]; 102 | bpb->dir_entries = readi16(sector0, 17); 103 | bpb->total_sectors = readi16(sector0, 19); 104 | bpb->media_descriptor_type = sector0[21]; 105 | bpb->count_sectors_per_FAT12_16 = readi16(sector0, 22); 106 | bpb->count_sectors_per_track = readi16(sector0, 24); 107 | bpb->count_heads_or_sizes_on_media = readi16(sector0, 26); 108 | bpb->count_hidden_sectors = readi32(sector0, 28); 109 | bpb->large_sectors_on_media = readi32(sector0, 32); 110 | // EBR 111 | bpb->count_sectors_per_FAT32 = readi32(sector0, 36); 112 | bpb->flags = readi16(sector0, 40); 113 | bpb->FAT_version = readi16(sector0, 42); 114 | bpb->cluster_number_root_dir = readi32(sector0, 44); 115 | bpb->sector_number_FSInfo = readi16(sector0, 48); 116 | bpb->sector_number_backup_boot_sector = readi16(sector0, 50); 117 | // Skip 12 bytes 118 | bpb->drive_number = sector0[64]; 119 | bpb->windows_flags = sector0[65]; 120 | bpb->signature = sector0[66]; 121 | bpb->volume_id = readi32(sector0, 67); 122 | memcpy(&bpb->volume_label, sector0 + 71, 11); bpb->volume_label[11] = 0; 123 | memcpy(&bpb->system_id, sector0 + 82, 8); bpb->system_id[8] = 0; 124 | } 125 | 126 | static uint32_t sector_for_cluster(f32 *fs, uint32_t cluster) { 127 | return fs->cluster_begin_sector + ((cluster - 2) * fs->bpb.sectors_per_cluster); 128 | } 129 | 130 | // CLUSTER NUMBERS START AT 2 (for some reason...) 131 | void getCluster(f32 *fs, uint8_t *buff, uint32_t cluster_number) { // static 132 | if(cluster_number >= EOC) { 133 | PANIC("Can't get cluster. Hit End Of Chain."); 134 | } 135 | uint32_t sector = sector_for_cluster(fs, cluster_number); 136 | uint32_t sector_count = fs->bpb.sectors_per_cluster; 137 | getSector(fs, buff, sector, sector_count); 138 | } 139 | 140 | static void putCluster(f32 *fs, uint8_t *buff, uint32_t cluster_number) { 141 | uint32_t sector = sector_for_cluster(fs, cluster_number); 142 | uint32_t sector_count = fs->bpb.sectors_per_cluster; 143 | putSector(fs, buff, sector, sector_count); 144 | } 145 | 146 | uint32_t get_next_cluster_id(f32 *fs, uint32_t cluster) { // static 147 | return fs->FAT[cluster] & 0x0FFFFFFF; 148 | } 149 | 150 | static char *parse_long_name(uint8_t *entries, uint8_t entry_count) { 151 | // each entry can hold 13 characters. 152 | char *name = kmalloc(entry_count * 13); 153 | int i, j; 154 | for(i = 0; i < entry_count; i++) { 155 | uint8_t *entry = entries + (i * 32); 156 | uint8_t entry_no = (uint8_t)entry[0] & 0x0F; 157 | char *name_offset = name + ((entry_no - 1) * 13); 158 | 159 | for(j = 1; j < 10; j+=2) { 160 | if(entry[j] >= 32 && entry[j] <= 127) { 161 | *name_offset = entry[j]; 162 | } 163 | else { 164 | *name_offset = 0; 165 | } 166 | name_offset++; 167 | } 168 | for(j = 14; j < 25; j+=2) { 169 | if(entry[j] >= 32 && entry[j] <= 127) { 170 | *name_offset = entry[j]; 171 | } 172 | else { 173 | *name_offset = 0; 174 | } 175 | name_offset++; 176 | } 177 | for(j = 28; j < 31; j+=2) { 178 | if(entry[j] >= 32 && entry[j] <= 127) { 179 | *name_offset = entry[j]; 180 | } 181 | else { 182 | *name_offset = 0; 183 | } 184 | name_offset++; 185 | } 186 | } 187 | return name; 188 | } 189 | 190 | static void clear_cluster(f32 *fs, uint32_t cluster) { 191 | uint8_t buffer[fs->cluster_size]; 192 | memset(buffer, 0, fs->cluster_size); 193 | putCluster(fs, buffer, cluster); 194 | } 195 | 196 | static uint32_t allocateCluster(f32 *fs) { 197 | uint32_t i, ints_per_fat = (512 * fs->bpb.count_sectors_per_FAT32) / 4; 198 | for(i = fs->cluster_alloc_hint; i < ints_per_fat; i++) { 199 | if(fs->FAT[i] == 0) { 200 | fs->FAT[i] = 0x0FFFFFFF; 201 | clear_cluster(fs, i); 202 | fs->cluster_alloc_hint = i+1; 203 | return i; 204 | } 205 | } 206 | for(i = 0; i < fs->cluster_alloc_hint; i++) { 207 | if(fs->FAT[i] == 0) { 208 | fs->FAT[i] = 0x0FFFFFFF; 209 | clear_cluster(fs, i); 210 | fs->cluster_alloc_hint = i+1; 211 | return i; 212 | } 213 | } 214 | return 0; 215 | } 216 | 217 | // Creates a checksum for an 8.3 filename 218 | // must be in directory-entry format, i.e. 219 | // fat32.c -> "FAT32 C " 220 | static uint8_t checksum_fname(char *fname) { 221 | uint32_t i; 222 | uint8_t checksum = 0; 223 | for(i = 0; i < 11; i++) { 224 | uint8_t highbit = (checksum & 0x1) << 7; 225 | checksum = ((checksum >> 1) & 0x7F) | highbit; 226 | checksum = checksum + fname[i]; 227 | } 228 | return checksum; 229 | } 230 | 231 | static void write_8_3_filename(char *fname, uint8_t *buffer) { 232 | memset(buffer, ' ', 11); 233 | uint32_t namelen = strlen(fname); 234 | // find the extension 235 | int i; 236 | int dot_index = -1; 237 | for(i = namelen-1; i >= 0; i--) { 238 | if(fname[i] == '.') { 239 | // Found it! 240 | dot_index = i; 241 | break; 242 | } 243 | } 244 | 245 | // Write the extension 246 | if(dot_index >= 0) { 247 | for(i = 0; i < 3; i++) { 248 | uint32_t c_index = dot_index + 1 + i; 249 | uint8_t c = c_index >= namelen ? ' ' : k_toupper(fname[c_index]); 250 | buffer[8 + i] = c; 251 | } 252 | } 253 | else { 254 | for(i = 0; i < 3; i++) { 255 | buffer[8 + i] = ' '; 256 | } 257 | } 258 | 259 | // Write the filename. 260 | uint32_t firstpart_len = namelen; 261 | if(dot_index >= 0) { 262 | firstpart_len = dot_index; 263 | } 264 | if(firstpart_len > 8) { 265 | // Write the weird tilde thing. 266 | for(i = 0; i < 6; i++) { 267 | buffer[i] = k_toupper(fname[i]); 268 | } 269 | buffer[6] = '~'; 270 | buffer[7] = '1'; // probably need to enumerate like files and increment. 271 | } 272 | else { 273 | // Just write the file name. 274 | uint32_t j; 275 | for(j = 0; j < firstpart_len; j++) { 276 | buffer[j] = k_toupper(fname[j]); 277 | } 278 | } 279 | } 280 | 281 | static uint8_t *locate_entries(f32 *fs, uint8_t *cluster_buffer, struct directory *dir, uint32_t count, uint32_t *found_cluster) { 282 | uint32_t dirs_per_cluster = fs->cluster_size / 32; 283 | 284 | uint32_t i; 285 | int64_t index = -1; 286 | uint32_t cluster = dir->cluster; 287 | while(1) { 288 | getCluster(fs, cluster_buffer, cluster); 289 | 290 | uint32_t in_a_row = 0; 291 | for(i = 0; i < dirs_per_cluster; i++) { 292 | uint8_t *entry = cluster_buffer + (i * 32); 293 | uint8_t first_byte = entry[0]; 294 | if(first_byte == 0x00 || first_byte == 0xE5) { 295 | in_a_row++; 296 | } 297 | else { 298 | in_a_row = 0; 299 | } 300 | 301 | if(in_a_row == count) { 302 | index = i - (in_a_row - 1); 303 | break; 304 | } 305 | } 306 | if(index >= 0) { 307 | // We found a spot to put our crap! 308 | break; 309 | } 310 | 311 | uint32_t next_cluster = fs->FAT[cluster]; 312 | if(next_cluster >= EOC) { 313 | next_cluster = allocateCluster(fs); 314 | if(!next_cluster) { 315 | return 0; 316 | } 317 | fs->FAT[cluster] = next_cluster; 318 | } 319 | cluster = next_cluster; 320 | } 321 | *found_cluster = cluster; 322 | return cluster_buffer + (index * 32); 323 | } 324 | 325 | static void write_long_filename_entries(uint8_t *start, uint32_t num_entries, char *fname) { 326 | // Create a short filename to use for the checksum. 327 | char shortfname[12]; 328 | shortfname[11] = 0; 329 | write_8_3_filename(fname, (uint8_t *)shortfname); 330 | uint8_t checksum = checksum_fname(shortfname); 331 | 332 | /* Write the long-filename entries */ 333 | // tracks the number of characters we've written into 334 | // the long-filename entries. 335 | uint32_t writtenchars = 0; 336 | char *nameptr = fname; 337 | uint32_t namelen = strlen(fname); 338 | uint8_t *entry = NULL; 339 | uint32_t i; 340 | for(i = 0; i < num_entries; i++) { 341 | // reverse the entry order 342 | entry = start + ((num_entries - 1 - i) * 32); 343 | // Set the entry number 344 | entry[0] = i+1; 345 | entry[13] = checksum; 346 | 347 | // Characters are 16 bytes in long-filename entries (j+=2) 348 | // And they only go in certain areas in the 32-byte 349 | // block. (why we have three loops) 350 | uint32_t j; 351 | for(j = 1; j < 10; j+=2) { 352 | if(writtenchars < namelen) { 353 | entry[j] = *nameptr; 354 | } 355 | else { 356 | entry[j] = 0; 357 | } 358 | nameptr++; 359 | writtenchars++; 360 | } 361 | for(j = 14; j < 25; j+=2) { 362 | if(writtenchars < namelen) { 363 | entry[j] = *nameptr; 364 | } 365 | else { 366 | entry[j] = 0; 367 | } 368 | nameptr++; 369 | writtenchars++; 370 | } 371 | for(j = 28; j < 31; j+=2) { 372 | if(writtenchars < namelen) { 373 | entry[j] = *nameptr; 374 | } 375 | else { 376 | entry[j] = 0; 377 | } 378 | nameptr++; 379 | writtenchars++; 380 | } 381 | // Mark the attributes byte as LFN (Long File Name) 382 | entry[11] = LFN; 383 | } 384 | // Mark the last(first) entry with the end-of-long-filename bit 385 | entry[0] |= 0x40; 386 | } 387 | 388 | f32 *makeFilesystem(char *fatSystem) { 389 | f32 *fs = kmalloc(sizeof (struct f32)); 390 | if(!identify()) { 391 | return NULL; 392 | } 393 | printf("Filesystem identified!\n"); 394 | read_bpb(fs, &fs->bpb); 395 | 396 | trim_spaces(fs->bpb.system_id, 8); 397 | if(strcmp(fs->bpb.system_id, "FAT32") != 0) { 398 | kfree(fs); 399 | return NULL; 400 | } 401 | 402 | printf("Sectors per cluster: %d\n", fs->bpb.sectors_per_cluster); 403 | 404 | fs->partition_begin_sector = 0; 405 | fs->fat_begin_sector = fs->partition_begin_sector + fs->bpb.reserved_sectors; 406 | fs->cluster_begin_sector = fs->fat_begin_sector + (fs->bpb.FAT_count * fs->bpb.count_sectors_per_FAT32); 407 | fs->cluster_size = 512 * fs->bpb.sectors_per_cluster; 408 | fs->cluster_alloc_hint = 0; 409 | 410 | // Load the FAT 411 | uint32_t bytes_per_fat = 512 * fs->bpb.count_sectors_per_FAT32; 412 | fs->FAT = kmalloc(bytes_per_fat); 413 | uint32_t sector_i; 414 | for(sector_i = 0; sector_i < fs->bpb.count_sectors_per_FAT32; sector_i++) { 415 | uint8_t sector[512]; 416 | getSector(fs, sector, fs->fat_begin_sector + sector_i, 1); 417 | uint32_t integer_j; 418 | for(integer_j = 0; integer_j < 512/4; integer_j++) { 419 | fs->FAT[sector_i * (512 / 4) + integer_j] 420 | = readi32(sector, integer_j * 4); 421 | } 422 | } 423 | return fs; 424 | } 425 | 426 | void destroyFilesystem(f32 *fs) { 427 | printf("Destroying filesystem.\n"); 428 | flushFAT(fs); 429 | kfree(fs->FAT); 430 | kfree(fs); 431 | } 432 | 433 | const struct bios_parameter_block *getBPB(f32 *fs) { 434 | return &fs->bpb; 435 | } 436 | 437 | void populate_root_dir(f32 *fs, struct directory *dir) { 438 | populate_dir(fs, dir, 2); 439 | } 440 | 441 | // Populates dirent with the directory entry starting at start 442 | // Returns a pointer to the next 32-byte chunk after the entry 443 | // or NULL if either start does not point to a valid entry, or 444 | // there are not enough entries to build a struct dir_entry 445 | static uint8_t *read_dir_entry(f32 *fs, uint8_t *start, uint8_t *end, struct dir_entry *dirent) { 446 | uint8_t first_byte = start[0]; 447 | uint8_t *entry = start; 448 | if(first_byte == 0x00 || first_byte == 0xE5) { 449 | // NOT A VALID ENTRY! 450 | return NULL; 451 | } 452 | 453 | uint32_t LFNCount = 0; 454 | while(entry[11] == LFN) { 455 | LFNCount++; 456 | entry += 32; 457 | if(entry == end) { 458 | return NULL; 459 | } 460 | } 461 | if(LFNCount > 0) { 462 | dirent->name = parse_long_name(start, LFNCount); 463 | } 464 | else { 465 | // There's no long file name. 466 | // Trim up the short filename. 467 | dirent->name = kmalloc(13); 468 | memcpy(dirent->name, entry, 11); 469 | dirent->name[11] = 0; 470 | char extension[4]; 471 | memcpy(extension, dirent->name + 8, 3); 472 | extension[3] = 0; 473 | trim_spaces(extension, 3); 474 | 475 | dirent->name[8] = 0; 476 | trim_spaces(dirent->name, 8); 477 | 478 | if(strlen(extension) > 0) { 479 | uint32_t len = strlen(dirent->name); 480 | dirent->name[len++] = '.'; 481 | memcpy(dirent->name + len, extension, 4); 482 | } 483 | } 484 | 485 | dirent->dir_attrs = entry[11];; 486 | uint16_t first_cluster_high = readi16(entry, 20); 487 | uint16_t first_cluster_low = readi16(entry, 26); 488 | dirent->first_cluster = first_cluster_high << 16 | first_cluster_low; 489 | dirent->file_size = readi32(entry, 28); 490 | return entry + 32; 491 | } 492 | 493 | // This is a complicated one. It parses a directory entry into the dir_entry pointed to by target_dirent. 494 | // root_cluster must point to a buffer big enough for two clusters. 495 | // entry points to the entry the caller wants to parse, and must point to a spot within root_cluster. 496 | // nextentry will be modified to hold the next spot within root_entry to begin looking for entries. 497 | // cluster is the cluster number of the cluster loaded into root_cluster. 498 | // secondcluster will be modified IF this function needs to load another cluster to continue parsing 499 | // the entry, in which case, it will be set to the value of the cluster loaded. 500 | // 501 | void next_dir_entry(f32 *fs, uint8_t *root_cluster, uint8_t *entry, uint8_t **nextentry, struct dir_entry *target_dirent, uint32_t cluster, uint32_t *secondcluster) { 502 | 503 | uint8_t *end_of_cluster = root_cluster + fs->cluster_size; 504 | *nextentry = read_dir_entry(fs, entry, end_of_cluster, target_dirent); 505 | if(!*nextentry) { 506 | // Something went wrong! 507 | // Either the directory entry spans the bounds of a cluster, 508 | // or the directory entry is invalid. 509 | // Load the next cluster and retry. 510 | 511 | // Figure out how much of the last cluster to "replay" 512 | uint32_t bytes_from_prev_chunk = end_of_cluster - entry; 513 | 514 | *secondcluster = get_next_cluster_id(fs, cluster); 515 | if(*secondcluster >= EOC) { 516 | // There's not another directory cluster to load 517 | // and the previous entry was invalid! 518 | // It's possible the filesystem is corrupt or... you know... 519 | // my software could have bugs. 520 | PANIC("FOUND BAD DIRECTORY ENTRY!"); 521 | } 522 | // Load the cluster after the previous saved entries. 523 | getCluster(fs, root_cluster + fs->cluster_size, *secondcluster); 524 | // Set entry to its new location at the beginning of root_cluster. 525 | entry = root_cluster + fs->cluster_size - bytes_from_prev_chunk; 526 | 527 | // Retry reading the entry. 528 | *nextentry = read_dir_entry(fs, entry, end_of_cluster + fs->cluster_size, target_dirent); 529 | if(!*nextentry) { 530 | // Still can't parse the directory entry. 531 | // Something is very wrong. 532 | PANIC("FAILED TO READ DIRECTORY ENTRY! THE SOFTWARE IS BUGGY!\n"); 533 | } 534 | } 535 | } 536 | 537 | // TODO: Refactor this. It is so similar to delFile that it would be nice 538 | // to combine the similar elements. 539 | // WARN: If you fix a bug in this function, it's likely you will find the same 540 | // bug in delFile. 541 | void populate_dir(f32 *fs, struct directory *dir, uint32_t cluster) { 542 | dir->cluster = cluster; 543 | uint32_t dirs_per_cluster = fs->cluster_size / 32; 544 | uint32_t max_dirs = 0; 545 | dir->entries = 0; 546 | uint32_t entry_count = 0; 547 | 548 | while(1) { 549 | max_dirs += dirs_per_cluster; 550 | dir->entries = krealloc(dir->entries, max_dirs * sizeof (struct dir_entry)); 551 | // Double the size in case we need to read a directory entry that 552 | // spans the bounds of a cluster. 553 | uint8_t root_cluster[fs->cluster_size * 2]; 554 | getCluster(fs, root_cluster, cluster); 555 | 556 | uint8_t *entry = root_cluster; 557 | while((uint32_t)(entry - root_cluster) < fs->cluster_size) { 558 | uint8_t first_byte = *entry; 559 | if(first_byte == 0x00 || first_byte == 0xE5) { 560 | // This directory entry has never been written 561 | // or it has been deleted. 562 | entry += 32; 563 | continue; 564 | } 565 | 566 | uint32_t secondcluster = 0; 567 | uint8_t *nextentry = NULL; 568 | struct dir_entry *target_dirent = dir->entries + entry_count; 569 | next_dir_entry(fs, root_cluster, entry, &nextentry, target_dirent, cluster, &secondcluster); 570 | entry = nextentry; 571 | if(secondcluster) { 572 | cluster = secondcluster; 573 | } 574 | 575 | entry_count++; 576 | } 577 | cluster = get_next_cluster_id(fs, cluster); 578 | if(cluster >= EOC) break; 579 | } 580 | dir->num_entries = entry_count; 581 | } 582 | 583 | static void zero_FAT_chain(f32 *fs, uint32_t start_cluster) { 584 | uint32_t cluster = start_cluster; 585 | while(cluster < EOC && cluster != 0) { 586 | uint32_t next_cluster = fs->FAT[cluster]; 587 | fs->FAT[cluster] = 0; 588 | cluster = next_cluster; 589 | } 590 | flushFAT(fs); 591 | } 592 | 593 | // TODO: Refactor this. It is so similar to populate_dir that it would be nice 594 | // to combine the similar elements. 595 | // WARN: If you fix a bug in this function, it's likely you will find the same 596 | // bug in populate_dir. 597 | void delFile(f32 *fs, struct directory *dir, char *filename) { //struct dir_entry *dirent) { 598 | uint32_t cluster = dir->cluster; 599 | 600 | // Double the size in case we need to read a directory entry that 601 | // spans the bounds of a cluster. 602 | uint8_t root_cluster[fs->cluster_size * 2]; 603 | struct dir_entry target_dirent; 604 | 605 | // Try to locate and invalidate the directory entries corresponding to the 606 | // filename in dirent. 607 | while(1) { 608 | getCluster(fs, root_cluster, cluster); 609 | 610 | uint8_t *entry = root_cluster; 611 | while((uint32_t)(entry - root_cluster) < fs->cluster_size) { 612 | uint8_t first_byte = *entry; 613 | if(first_byte == 0x00 || first_byte == 0xE5) { 614 | // This directory entry has never been written 615 | // or it has been deleted. 616 | entry += 32; 617 | continue; 618 | } 619 | 620 | uint32_t secondcluster = 0; 621 | uint8_t *nextentry = NULL; 622 | next_dir_entry(fs, root_cluster, entry, &nextentry, &target_dirent, cluster, &secondcluster); 623 | 624 | // We have a target dirent! see if it's the one we want! 625 | if(strcmp(target_dirent.name, filename) == 0) { 626 | // We found it! Invalidate all the entries. 627 | memset(entry, 0, nextentry - entry); 628 | putCluster(fs, root_cluster, cluster); 629 | if(secondcluster) { 630 | putCluster(fs, root_cluster + fs->cluster_size, secondcluster); 631 | } 632 | zero_FAT_chain(fs, target_dirent.first_cluster); 633 | kfree(target_dirent.name); 634 | return; 635 | } 636 | else { 637 | // We didn't find it. Continue. 638 | entry = nextentry; 639 | if(secondcluster) { 640 | cluster = secondcluster; 641 | } 642 | } 643 | kfree(target_dirent.name); 644 | 645 | } 646 | cluster = get_next_cluster_id(fs, cluster); 647 | if(cluster >= EOC) return; 648 | } 649 | } 650 | 651 | void free_directory(f32 *fs, struct directory *dir){ 652 | uint32_t i; 653 | for(i = 0; i < dir->num_entries; i++) { 654 | kfree(dir->entries[i].name); 655 | } 656 | kfree(dir->entries); 657 | } 658 | 659 | uint8_t *readFile(f32 *fs, struct dir_entry *dirent) { 660 | uint8_t *file = kmalloc(dirent->file_size); 661 | uint8_t *filecurrptr = file; 662 | uint32_t cluster = dirent->first_cluster; 663 | uint32_t copiedbytes = 0; 664 | while(1) { 665 | uint8_t cbytes[fs->cluster_size]; 666 | getCluster(fs, cbytes, cluster); 667 | 668 | uint32_t remaining = dirent->file_size - copiedbytes; 669 | uint32_t to_copy = remaining > fs->cluster_size ? fs->cluster_size : remaining; 670 | 671 | memcpy(filecurrptr, cbytes, to_copy); 672 | 673 | filecurrptr += fs->cluster_size; 674 | copiedbytes += to_copy; 675 | 676 | cluster = get_next_cluster_id(fs, cluster); 677 | if(cluster >= EOC) break; 678 | } 679 | return file; 680 | } 681 | 682 | static void writeFile_impl(f32 *fs, struct directory *dir, uint8_t *file, char *fname, uint32_t flen, uint8_t attrs, uint32_t setcluster) { 683 | uint32_t required_clusters = flen / fs->cluster_size; 684 | if(flen % fs->cluster_size != 0) required_clusters++; 685 | if(required_clusters == 0) required_clusters++; // Allocate at least one cluster. 686 | // One for the traditional 8.3 name, one for each 13 charaters in the extended name. 687 | // Int division truncates, so if there's a remainder from length / 13, add another entry. 688 | uint32_t required_entries_long_fname = (strlen(fname) / 13); 689 | if(strlen(fname) % 13 > 0) { 690 | required_entries_long_fname++; 691 | } 692 | 693 | uint32_t required_entries_total = required_entries_long_fname + 1; 694 | 695 | uint32_t cluster; // The cluster number that the entries are found in 696 | uint8_t root_cluster[fs->cluster_size]; 697 | uint8_t *start_entries = locate_entries(fs, root_cluster, dir, required_entries_total, &cluster); 698 | write_long_filename_entries(start_entries, required_entries_long_fname, fname); 699 | 700 | // Write the actual file entry; 701 | uint8_t *actual_entry = start_entries + (required_entries_long_fname * 32); 702 | write_8_3_filename(fname, actual_entry); 703 | 704 | // Actually write the file! 705 | uint32_t writtenbytes = 0; 706 | uint32_t prevcluster = 0; 707 | uint32_t firstcluster = 0; 708 | uint32_t i; 709 | if(setcluster) { 710 | // Caller knows where the first cluster is. 711 | // Don't allocate or write anything. 712 | firstcluster = setcluster; 713 | } 714 | else { 715 | for(i = 0; i < required_clusters; i++) { 716 | uint32_t currcluster = allocateCluster(fs); 717 | if(!firstcluster) { 718 | firstcluster = currcluster; 719 | } 720 | uint8_t cluster_buffer[fs->cluster_size]; 721 | memset(cluster_buffer, 0, fs->cluster_size); 722 | uint32_t bytes_to_write = flen - writtenbytes; 723 | if(bytes_to_write > fs->cluster_size) { 724 | bytes_to_write = fs->cluster_size; 725 | } 726 | memcpy(cluster_buffer, file + writtenbytes, bytes_to_write); 727 | writtenbytes += bytes_to_write; 728 | putCluster(fs, cluster_buffer, currcluster); 729 | if(prevcluster) { 730 | fs->FAT[prevcluster] = currcluster; 731 | } 732 | prevcluster = currcluster; 733 | } 734 | } 735 | 736 | // Write the other fields of the actual entry 737 | // We do it down here because we need the first cluster 738 | // number. 739 | 740 | // attrs 741 | actual_entry[11] = attrs; 742 | 743 | // high cluster bits 744 | actual_entry[20] = (firstcluster >> 16) & 0xFF; 745 | actual_entry[21] = (firstcluster >> 24) & 0xFF; 746 | 747 | // low cluster bits 748 | actual_entry[26] = (firstcluster) & 0xFF; 749 | actual_entry[27] = (firstcluster >> 8) & 0xFF; 750 | 751 | // file size 752 | actual_entry[28] = flen & 0xFF; 753 | actual_entry[29] = (flen >> 8) & 0xFF; 754 | actual_entry[30] = (flen >> 16) & 0xFF; 755 | actual_entry[31] = (flen >> 24) & 0xFF; 756 | 757 | // Write the cluster back to disk 758 | putCluster(fs, root_cluster, cluster); 759 | flushFAT(fs); 760 | } 761 | 762 | void writeFile(f32 *fs, struct directory *dir, uint8_t *file, char *fname, uint32_t flen) { 763 | writeFile_impl(fs, dir, file, fname, flen, 0, 0); 764 | } 765 | 766 | static void mkdir_subdirs(f32 *fs, struct directory *dir, uint32_t parentcluster) { 767 | writeFile_impl(fs, dir, NULL, ".", 0, DIRECTORY, dir->cluster); 768 | writeFile_impl(fs, dir, NULL, "..", 0, DIRECTORY, parentcluster); 769 | } 770 | 771 | void mkdir(f32 *fs, struct directory *dir, char *dirname) { 772 | writeFile_impl(fs, dir, NULL, dirname, 0, DIRECTORY, 0); 773 | 774 | // We need to add the subdirectories '.' and '..' 775 | struct directory subdir; 776 | populate_dir(fs, &subdir, dir->cluster); 777 | uint32_t i; 778 | for(i = 0; i < subdir.num_entries; i++) { 779 | if(strcmp(subdir.entries[i].name, dirname) == 0) { 780 | struct directory newsubdir; 781 | populate_dir(fs, &newsubdir, subdir.entries[i].first_cluster); 782 | mkdir_subdirs(fs, &newsubdir, subdir.cluster); 783 | free_directory(fs, &newsubdir); 784 | } 785 | } 786 | free_directory(fs, &subdir); 787 | } 788 | 789 | void print_directory(f32 *fs, struct directory *dir) { 790 | uint32_t i; 791 | uint32_t max_name = 0; 792 | for(i = 0; i < dir->num_entries; i++) { 793 | uint32_t namelen = strlen(dir->entries[i].name); 794 | max_name = namelen > max_name ? namelen : max_name; 795 | } 796 | 797 | char *namebuff = kmalloc(max_name + 1); 798 | for(i = 0; i < dir->num_entries; i++) { 799 | // printf("[%d] %*s %c %8d bytes ", 800 | // i, 801 | // -max_name, 802 | // dir->entries[i].name, 803 | // dir->entries[i].dir_attrs & DIRECTORY?'D':' ', 804 | // dir->entries[i].file_size, dir->entries[i].first_cluster); 805 | printf("[%d] ", i); 806 | 807 | 808 | uint32_t j; 809 | for(j = 0; j < max_name; j++) { 810 | namebuff[j] = ' '; 811 | } 812 | namebuff[max_name] = 0; 813 | for(j = 0; j < strlen(dir->entries[i].name); j++) { 814 | namebuff[j] = dir->entries[i].name[j]; 815 | } 816 | 817 | printf("%s %c %d ", 818 | namebuff, 819 | dir->entries[i].dir_attrs & DIRECTORY?'D':' ', 820 | dir->entries[i].file_size); 821 | 822 | uint32_t cluster = dir->entries[i].first_cluster; 823 | uint32_t cluster_count = 1; 824 | while(1) { 825 | cluster = fs->FAT[cluster]; 826 | if(cluster >= EOC) break; 827 | if(cluster == 0) { 828 | PANIC("BAD CLUSTER CHAIN! FS IS CORRUPT!"); 829 | } 830 | cluster_count++; 831 | } 832 | printf("clusters: [%d]\n", cluster_count); 833 | } 834 | kfree(namebuff); 835 | } 836 | 837 | uint32_t count_free_clusters(f32 *fs) { 838 | uint32_t clusters_in_fat = (fs->bpb.count_sectors_per_FAT32 * 512) / 4; 839 | uint32_t i; 840 | uint32_t count = 0; 841 | for(i = 0; i < clusters_in_fat; i++) { 842 | if(fs->FAT[i] == 0) { 843 | count++; 844 | } 845 | } 846 | return count; 847 | } 848 | -------------------------------------------------------------------------------- /fat32.h: -------------------------------------------------------------------------------- 1 | #ifndef FAT32_H 2 | #define FAT32_H 3 | 4 | #include "stdint.h" 5 | 6 | struct bios_parameter_block { 7 | uint16_t bytes_per_sector; // IMPORTANT 8 | uint8_t sectors_per_cluster; // IMPORTANT 9 | uint16_t reserved_sectors; // IMPORTANT 10 | uint8_t FAT_count; // IMPORTANT 11 | uint16_t dir_entries; 12 | uint16_t total_sectors; 13 | uint8_t media_descriptor_type; 14 | uint16_t count_sectors_per_FAT12_16; // FAT12/FAT16 only. 15 | uint16_t count_sectors_per_track; 16 | uint16_t count_heads_or_sizes_on_media; 17 | uint32_t count_hidden_sectors; 18 | uint32_t large_sectors_on_media; // This is set instead of total_sectors if it's > 65535 19 | 20 | // Extended Boot Record 21 | uint32_t count_sectors_per_FAT32; // IMPORTANT 22 | uint16_t flags; 23 | uint16_t FAT_version; 24 | uint32_t cluster_number_root_dir; // IMPORTANT 25 | uint16_t sector_number_FSInfo; 26 | uint16_t sector_number_backup_boot_sector; 27 | uint8_t drive_number; 28 | uint8_t windows_flags; 29 | uint8_t signature; // IMPORTANT 30 | uint32_t volume_id; 31 | char volume_label[12]; 32 | char system_id[9]; 33 | }; 34 | 35 | #define READONLY 1 36 | #define HIDDEN (1 << 1) 37 | #define SYSTEM (1 << 2) 38 | #define VolumeID (1 << 3) 39 | #define DIRECTORY (1 << 4) 40 | #define ARCHIVE (1 << 5) 41 | #define LFN (READONLY | HIDDEN | SYSTEM | VolumeID) 42 | 43 | struct dir_entry { 44 | char *name; 45 | uint8_t dir_attrs; 46 | uint32_t first_cluster; 47 | uint32_t file_size; 48 | }; 49 | 50 | struct directory { 51 | uint32_t cluster; 52 | struct dir_entry *entries; 53 | uint32_t num_entries; 54 | }; 55 | 56 | // REFACTOR 57 | // I want to get rid of this from the header. This should be internal 58 | // implementation, but for now, it's too convenient for stdio.c impl. 59 | 60 | // EOC = End Of Chain 61 | #define EOC 0x0FFFFFF8 62 | 63 | struct f32 { 64 | //FILE *f; 65 | uint32_t *FAT; 66 | struct bios_parameter_block bpb; 67 | uint32_t partition_begin_sector; 68 | uint32_t fat_begin_sector; 69 | uint32_t cluster_begin_sector; 70 | uint32_t cluster_size; 71 | uint32_t cluster_alloc_hint; 72 | }; 73 | 74 | typedef struct f32 f32; 75 | 76 | void getCluster(f32 *fs, uint8_t *buff, uint32_t cluster_number); 77 | uint32_t get_next_cluster_id(f32 *fs, uint32_t cluster); 78 | 79 | // END REFACTOR 80 | 81 | f32 *makeFilesystem(char *fatSystem); 82 | void destroyFilesystem(f32 *fs); 83 | 84 | const struct bios_parameter_block *getBPB(f32 *fs); 85 | 86 | void populate_root_dir(f32 *fs, struct directory *dir); 87 | void populate_dir(f32 *fs, struct directory *dir, uint32_t cluster); 88 | void free_directory(f32 *fs, struct directory *dir); 89 | 90 | uint8_t *readFile(f32 *fs, struct dir_entry *dirent); 91 | void writeFile(f32 *fs, struct directory *dir, uint8_t *file, char *fname, uint32_t flen); 92 | void mkdir(f32 *fs, struct directory *dir, char *dirname); 93 | void delFile(f32 *fs, struct directory *dir, char *filename); 94 | 95 | void print_directory(f32 *fs, struct directory *dir); 96 | uint32_t count_free_clusters(f32 *fs); 97 | 98 | extern f32 *master_fs; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /fat32_console.c: -------------------------------------------------------------------------------- 1 | #include "stddef.h" 2 | #include "common.h" 3 | #include "fat32.h" 4 | #include "keyboard.h" 5 | #include "kheap.h" 6 | #include "kernio.h" 7 | #include "terminal.h" 8 | 9 | int handle_commands(f32 *fs, struct directory *dir, char *buffer); 10 | 11 | void fat32_console(f32 *fs) { 12 | printf("\n\nStarting up Fat32 console...\n"); 13 | printf("Reading root directory... "); 14 | struct directory dir; 15 | populate_root_dir(fs, &dir); 16 | printf("Done.\n"); 17 | 18 | uint32_t bufflen = 24; 19 | 20 | if(dir.num_entries > 0) { 21 | printf("Root directory:\n"); 22 | print_directory(fs, &dir); 23 | } 24 | else { 25 | printf("Root directory empty.\n"); 26 | } 27 | printf("Welcome to the Version 1 Fat32 console.\n"); 28 | printf("Type 'help' to see available commands.\n"); 29 | 30 | char buffer[bufflen + 1]; 31 | while(1) { 32 | printf(">> "); 33 | uint32_t i; 34 | for(i = 0; i < bufflen; i++) { 35 | char c = get_ascii_char(); 36 | if(c == BS) { 37 | if(i == 0) { 38 | i--; 39 | continue; 40 | } 41 | printf("%c", c); 42 | i-=2; 43 | continue; 44 | } 45 | if(c == EOT || c == ESC) { 46 | i--; 47 | continue; 48 | } 49 | printf("%c", c); 50 | 51 | buffer[i] = c; 52 | if(c == '\n') break; 53 | } 54 | 55 | if(i == bufflen) { 56 | printf("Input too long.\n"); 57 | while(get_ascii_char() != '\n'); 58 | continue; 59 | } 60 | buffer[i] = 0; 61 | 62 | // If it's just a return, print the current directory. 63 | if(strlen(buffer) == 0) { 64 | print_directory(fs, &dir); 65 | continue; 66 | } 67 | 68 | uint32_t x; 69 | int scanned = coerce_int(buffer, &x); 70 | if(scanned == 0) { 71 | int command_ret = handle_commands(fs, &dir, buffer); 72 | if(!command_ret) { 73 | printf("Invalid input. Enter a number or command.\n"); 74 | } 75 | else if(command_ret == -1) { 76 | break; 77 | } 78 | continue; 79 | } 80 | 81 | if(dir.num_entries <= x) { 82 | printf("Invalid selection.\n"); 83 | continue; 84 | } 85 | 86 | if(dir.entries[x].dir_attrs & DIRECTORY) { 87 | // It's a directory. chdir to that one. 88 | uint32_t cluster = dir.entries[x].first_cluster; 89 | if(cluster == 0) cluster = 2; 90 | free_directory(fs, &dir); 91 | populate_dir(fs, &dir, cluster); 92 | print_directory(fs, &dir); 93 | continue; 94 | } 95 | else { 96 | uint8_t *file = readFile(fs, &dir.entries[x]); 97 | for(i = 0; i < dir.entries[x].file_size; i++) { 98 | printf("%c", file[i]); 99 | } 100 | kfree(file); 101 | } 102 | } 103 | printf("Shutting down filesystem.\n"); 104 | free_directory(fs, &dir); 105 | destroyFilesystem(fs); 106 | } 107 | 108 | struct bytes { 109 | char *buff; 110 | size_t len; 111 | char *err; 112 | }; 113 | 114 | void do_delete(f32 *fs, struct directory *dir, char *filename) { 115 | printf("do_delete(%s)\n", filename); 116 | uint32_t i; 117 | for(i = 0; i < dir->num_entries; i++) { 118 | if(strcmp(filename, dir->entries[i].name) == 0) { 119 | if(dir->entries[i].dir_attrs & DIRECTORY) { 120 | struct directory subdir; 121 | populate_dir(fs, &subdir, dir->entries[i].first_cluster); 122 | uint32_t j; 123 | for(j = 0; j < subdir.num_entries; j++) { 124 | // Delete these last! 125 | if(strcmp(subdir.entries[j].name, ".") == 0) { 126 | // Don't recur on current directory! 127 | continue; 128 | } 129 | if(strcmp(subdir.entries[j].name, "..") == 0) { 130 | // Don't recur on parent directory. 131 | continue; 132 | } 133 | do_delete(fs, &subdir, subdir.entries[j].name); 134 | } 135 | // Now delete '.' and '..' 136 | for(j = 0; j < subdir.num_entries; j++) { 137 | if(strcmp(subdir.entries[j].name, ".") == 0) { 138 | // Don't recur on current directory! 139 | delFile(fs, dir, subdir.entries[j].name); 140 | continue; 141 | } 142 | if(strcmp(subdir.entries[j].name, "..") == 0) { 143 | // Don't recur on parent directory. 144 | delFile(fs, dir, subdir.entries[j].name); 145 | continue; 146 | } 147 | } 148 | free_directory(fs, &subdir); 149 | // Finally, delete the directory itself. 150 | delFile(fs, dir, filename); 151 | } 152 | else { 153 | delFile(fs, dir, dir->entries[i].name); 154 | } 155 | } 156 | } 157 | } 158 | 159 | void do_cat(f32 *fs, struct directory *dir, char *filename) { 160 | uint32_t i; 161 | for(i = 0; i < dir->num_entries; i++) { 162 | if(strcmp(filename, dir->entries[i].name) == 0) { 163 | printf("File already exists. del the file first.\n"); 164 | return; 165 | } 166 | } 167 | printf("Ctrl+D to end file.\n"); 168 | 169 | uint32_t index = 0; 170 | uint32_t buffsize = 1024; 171 | uint32_t currlinelen = 0; 172 | uint8_t *file = kmalloc(buffsize); 173 | while(1) { 174 | char c = get_ascii_char(); 175 | if(c == EOT) { 176 | break; 177 | } 178 | printf("%c", c); 179 | if(c == BS) { 180 | if(currlinelen > 0) { 181 | index--; 182 | currlinelen--; 183 | } 184 | continue; 185 | } 186 | file[index++] = c; 187 | currlinelen++; 188 | if(c == '\n') { 189 | currlinelen = 0; 190 | } 191 | if(index == buffsize) { 192 | buffsize *= 2; 193 | file = krealloc(file, buffsize); 194 | } 195 | } 196 | writeFile(fs, dir, file, filename, index); 197 | kfree(file); 198 | } 199 | 200 | void do_touch(f32 *fs, struct directory *dir, char *filename) { 201 | writeFile(fs, dir, (uint8_t *)"", filename, 0); 202 | } 203 | 204 | int scan_command(char *buffer, char **comm, char **fname) { 205 | char *buffscan = buffer; 206 | if(!*buffscan) { 207 | // There's nothing in the buffer. 208 | return 0; 209 | } 210 | 211 | // skip any whitespace 212 | while(*buffscan && *buffscan == ' ') { 213 | buffscan++; 214 | } 215 | 216 | // Make sure there's something afterwards 217 | if(!*buffscan) { 218 | return 0; 219 | } 220 | 221 | // Point comm at the first non-whitespace character. 222 | *comm = buffscan; 223 | 224 | // Find a space. 225 | while(*buffscan && *buffscan != ' ') { 226 | buffscan++; 227 | } 228 | 229 | if(!*buffscan) { 230 | // There's no more in the string 231 | return 1; 232 | } 233 | // Terminate the string. 234 | *buffscan = 0; 235 | buffscan++; 236 | 237 | // skip any whitespace 238 | while(*buffscan && *buffscan == ' ') { 239 | buffscan++; 240 | } 241 | 242 | // If there's no more string left, return 1. 243 | if(!*buffscan) { 244 | return 1; 245 | } 246 | 247 | *fname = buffscan; 248 | 249 | // Chop any space off the end. 250 | while(*buffscan && *buffscan != ' ') { 251 | buffscan++; 252 | } 253 | *buffscan = 0; 254 | 255 | return 2; 256 | } 257 | 258 | int handle_commands(f32 *fs, struct directory *dir, char *buffer) { 259 | char *command = NULL; 260 | char *filename = NULL; 261 | int scanned = scan_command(buffer, &command, &filename); 262 | if(scanned == 0) { printf("Failed to parse command.\n"); return 0; } 263 | 264 | int ret = 0; 265 | if(strcmp(command, "mkdir") == 0) { 266 | if(filename != NULL && strlen(filename) > 0) { 267 | printf("Making directory [%s].\n", filename); 268 | mkdir(fs, dir, filename); 269 | } 270 | else { 271 | printf("Need a directory name.\n"); 272 | } 273 | ret = 1; 274 | } 275 | if(strcmp(command, "touch") == 0) { 276 | if(filename != NULL && strlen(filename) > 0) { 277 | do_touch(fs, dir, filename); 278 | } 279 | ret = 1; 280 | } 281 | if(strcmp(command, "del") == 0) { 282 | if(filename != NULL && strlen(filename) > 0) { 283 | do_delete(fs, dir, filename); 284 | } 285 | else { 286 | printf("Need a file/directory name.\n"); 287 | } 288 | ret = 1; 289 | } 290 | if(strcmp(command, "cat") == 0) { 291 | if(filename != NULL && strlen(filename) > 0) { 292 | do_cat(fs, dir, filename); 293 | } 294 | else { 295 | printf("Need a filename.\n"); 296 | } 297 | ret = 1; 298 | } 299 | if(strcmp(command, "freeclusters") == 0) { 300 | printf("Free clusters in FAT: %d\n", count_free_clusters(fs)); 301 | ret = 1; 302 | } 303 | if(strcmp(command, "exit") == 0) { 304 | printf("See ya!\n"); 305 | ret = -1; 306 | } 307 | if(strcmp(command, "help") == 0) { 308 | printf("Commands are: \n"); 309 | printf("\t[a number] -> a number corresponding with a file or directory will print that file or enter that directory.\n"); 310 | printf("\t(return) -> pressing return without entering a command will list the current directory. Entries marked with a 'D' next to their names are directories.\n"); 311 | printf("\tmkdir [dirname] -> Create a directory in the current directory.\n"); 312 | printf("\ttouch [filename] -> Create an empty file in the current directory.\n"); 313 | printf("\tcat [filename] -> Creates a file named 'filename' and lets you type into it. Ctrl+D ends the file.\n"); 314 | printf("\tdel [filename | dirname] -> Delete a file or (recursively) a directory.\n"); 315 | printf("\tfreeclusters -> Count the free clusters available in the filesystem.\n"); 316 | printf("\texit -> Exit the FAT32 shell. This gracefully closes the filesystem. Always do this before shutting down the kernel.\n"); 317 | ret = 1; 318 | } 319 | free_directory(fs, dir); 320 | populate_dir(fs, dir, dir->cluster); 321 | return ret; 322 | } 323 | -------------------------------------------------------------------------------- /fat32_console.h: -------------------------------------------------------------------------------- 1 | #ifndef FAT32_CONSOLE_H 2 | #define FAT32_CONSOLE_H 3 | 4 | void fat32_console(f32 *fs); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /font.h: -------------------------------------------------------------------------------- 1 | // (c) 2009, 2010 Lutz Sammer, License: AGPLv3 2 | 3 | /// bitmap font structure 4 | struct bitmap_font { 5 | unsigned char Width; ///< max. character width 6 | unsigned char Height; ///< character height 7 | unsigned short Chars; ///< number of characters in font 8 | const unsigned char *Widths; ///< width of each character 9 | const unsigned short *Index; ///< encoding to character index 10 | const unsigned char *Bitmap; ///< bitmap of all characters 11 | }; 12 | 13 | extern const struct bitmap_font font; 14 | 15 | /// @{ defines to have human readable font files 16 | #define ________ 0x00 17 | #define _______X 0x01 18 | #define ______X_ 0x02 19 | #define ______XX 0x03 20 | #define _____X__ 0x04 21 | #define _____X_X 0x05 22 | #define _____XX_ 0x06 23 | #define _____XXX 0x07 24 | #define ____X___ 0x08 25 | #define ____X__X 0x09 26 | #define ____X_X_ 0x0A 27 | #define ____X_XX 0x0B 28 | #define ____XX__ 0x0C 29 | #define ____XX_X 0x0D 30 | #define ____XXX_ 0x0E 31 | #define ____XXXX 0x0F 32 | #define ___X____ 0x10 33 | #define ___X___X 0x11 34 | #define ___X__X_ 0x12 35 | #define ___X__XX 0x13 36 | #define ___X_X__ 0x14 37 | #define ___X_X_X 0x15 38 | #define ___X_XX_ 0x16 39 | #define ___X_XXX 0x17 40 | #define ___XX___ 0x18 41 | #define ___XX__X 0x19 42 | #define ___XX_X_ 0x1A 43 | #define ___XX_XX 0x1B 44 | #define ___XXX__ 0x1C 45 | #define ___XXX_X 0x1D 46 | #define ___XXXX_ 0x1E 47 | #define ___XXXXX 0x1F 48 | #define __X_____ 0x20 49 | #define __X____X 0x21 50 | #define __X___X_ 0x22 51 | #define __X___XX 0x23 52 | #define __X__X__ 0x24 53 | #define __X__X_X 0x25 54 | #define __X__XX_ 0x26 55 | #define __X__XXX 0x27 56 | #define __X_X___ 0x28 57 | #define __X_X__X 0x29 58 | #define __X_X_X_ 0x2A 59 | #define __X_X_XX 0x2B 60 | #define __X_XX__ 0x2C 61 | #define __X_XX_X 0x2D 62 | #define __X_XXX_ 0x2E 63 | #define __X_XXXX 0x2F 64 | #define __XX____ 0x30 65 | #define __XX___X 0x31 66 | #define __XX__X_ 0x32 67 | #define __XX__XX 0x33 68 | #define __XX_X__ 0x34 69 | #define __XX_X_X 0x35 70 | #define __XX_XX_ 0x36 71 | #define __XX_XXX 0x37 72 | #define __XXX___ 0x38 73 | #define __XXX__X 0x39 74 | #define __XXX_X_ 0x3A 75 | #define __XXX_XX 0x3B 76 | #define __XXXX__ 0x3C 77 | #define __XXXX_X 0x3D 78 | #define __XXXXX_ 0x3E 79 | #define __XXXXXX 0x3F 80 | #define _X______ 0x40 81 | #define _X_____X 0x41 82 | #define _X____X_ 0x42 83 | #define _X____XX 0x43 84 | #define _X___X__ 0x44 85 | #define _X___X_X 0x45 86 | #define _X___XX_ 0x46 87 | #define _X___XXX 0x47 88 | #define _X__X___ 0x48 89 | #define _X__X__X 0x49 90 | #define _X__X_X_ 0x4A 91 | #define _X__X_XX 0x4B 92 | #define _X__XX__ 0x4C 93 | #define _X__XX_X 0x4D 94 | #define _X__XXX_ 0x4E 95 | #define _X__XXXX 0x4F 96 | #define _X_X____ 0x50 97 | #define _X_X___X 0x51 98 | #define _X_X__X_ 0x52 99 | #define _X_X__XX 0x53 100 | #define _X_X_X__ 0x54 101 | #define _X_X_X_X 0x55 102 | #define _X_X_XX_ 0x56 103 | #define _X_X_XXX 0x57 104 | #define _X_XX___ 0x58 105 | #define _X_XX__X 0x59 106 | #define _X_XX_X_ 0x5A 107 | #define _X_XX_XX 0x5B 108 | #define _X_XXX__ 0x5C 109 | #define _X_XXX_X 0x5D 110 | #define _X_XXXX_ 0x5E 111 | #define _X_XXXXX 0x5F 112 | #define _XX_____ 0x60 113 | #define _XX____X 0x61 114 | #define _XX___X_ 0x62 115 | #define _XX___XX 0x63 116 | #define _XX__X__ 0x64 117 | #define _XX__X_X 0x65 118 | #define _XX__XX_ 0x66 119 | #define _XX__XXX 0x67 120 | #define _XX_X___ 0x68 121 | #define _XX_X__X 0x69 122 | #define _XX_X_X_ 0x6A 123 | #define _XX_X_XX 0x6B 124 | #define _XX_XX__ 0x6C 125 | #define _XX_XX_X 0x6D 126 | #define _XX_XXX_ 0x6E 127 | #define _XX_XXXX 0x6F 128 | #define _XXX____ 0x70 129 | #define _XXX___X 0x71 130 | #define _XXX__X_ 0x72 131 | #define _XXX__XX 0x73 132 | #define _XXX_X__ 0x74 133 | #define _XXX_X_X 0x75 134 | #define _XXX_XX_ 0x76 135 | #define _XXX_XXX 0x77 136 | #define _XXXX___ 0x78 137 | #define _XXXX__X 0x79 138 | #define _XXXX_X_ 0x7A 139 | #define _XXXX_XX 0x7B 140 | #define _XXXXX__ 0x7C 141 | #define _XXXXX_X 0x7D 142 | #define _XXXXXX_ 0x7E 143 | #define _XXXXXXX 0x7F 144 | #define X_______ 0x80 145 | #define X______X 0x81 146 | #define X_____X_ 0x82 147 | #define X_____XX 0x83 148 | #define X____X__ 0x84 149 | #define X____X_X 0x85 150 | #define X____XX_ 0x86 151 | #define X____XXX 0x87 152 | #define X___X___ 0x88 153 | #define X___X__X 0x89 154 | #define X___X_X_ 0x8A 155 | #define X___X_XX 0x8B 156 | #define X___XX__ 0x8C 157 | #define X___XX_X 0x8D 158 | #define X___XXX_ 0x8E 159 | #define X___XXXX 0x8F 160 | #define X__X____ 0x90 161 | #define X__X___X 0x91 162 | #define X__X__X_ 0x92 163 | #define X__X__XX 0x93 164 | #define X__X_X__ 0x94 165 | #define X__X_X_X 0x95 166 | #define X__X_XX_ 0x96 167 | #define X__X_XXX 0x97 168 | #define X__XX___ 0x98 169 | #define X__XX__X 0x99 170 | #define X__XX_X_ 0x9A 171 | #define X__XX_XX 0x9B 172 | #define X__XXX__ 0x9C 173 | #define X__XXX_X 0x9D 174 | #define X__XXXX_ 0x9E 175 | #define X__XXXXX 0x9F 176 | #define X_X_____ 0xA0 177 | #define X_X____X 0xA1 178 | #define X_X___X_ 0xA2 179 | #define X_X___XX 0xA3 180 | #define X_X__X__ 0xA4 181 | #define X_X__X_X 0xA5 182 | #define X_X__XX_ 0xA6 183 | #define X_X__XXX 0xA7 184 | #define X_X_X___ 0xA8 185 | #define X_X_X__X 0xA9 186 | #define X_X_X_X_ 0xAA 187 | #define X_X_X_XX 0xAB 188 | #define X_X_XX__ 0xAC 189 | #define X_X_XX_X 0xAD 190 | #define X_X_XXX_ 0xAE 191 | #define X_X_XXXX 0xAF 192 | #define X_XX____ 0xB0 193 | #define X_XX___X 0xB1 194 | #define X_XX__X_ 0xB2 195 | #define X_XX__XX 0xB3 196 | #define X_XX_X__ 0xB4 197 | #define X_XX_X_X 0xB5 198 | #define X_XX_XX_ 0xB6 199 | #define X_XX_XXX 0xB7 200 | #define X_XXX___ 0xB8 201 | #define X_XXX__X 0xB9 202 | #define X_XXX_X_ 0xBA 203 | #define X_XXX_XX 0xBB 204 | #define X_XXXX__ 0xBC 205 | #define X_XXXX_X 0xBD 206 | #define X_XXXXX_ 0xBE 207 | #define X_XXXXXX 0xBF 208 | #define XX______ 0xC0 209 | #define XX_____X 0xC1 210 | #define XX____X_ 0xC2 211 | #define XX____XX 0xC3 212 | #define XX___X__ 0xC4 213 | #define XX___X_X 0xC5 214 | #define XX___XX_ 0xC6 215 | #define XX___XXX 0xC7 216 | #define XX__X___ 0xC8 217 | #define XX__X__X 0xC9 218 | #define XX__X_X_ 0xCA 219 | #define XX__X_XX 0xCB 220 | #define XX__XX__ 0xCC 221 | #define XX__XX_X 0xCD 222 | #define XX__XXX_ 0xCE 223 | #define XX__XXXX 0xCF 224 | #define XX_X____ 0xD0 225 | #define XX_X___X 0xD1 226 | #define XX_X__X_ 0xD2 227 | #define XX_X__XX 0xD3 228 | #define XX_X_X__ 0xD4 229 | #define XX_X_X_X 0xD5 230 | #define XX_X_XX_ 0xD6 231 | #define XX_X_XXX 0xD7 232 | #define XX_XX___ 0xD8 233 | #define XX_XX__X 0xD9 234 | #define XX_XX_X_ 0xDA 235 | #define XX_XX_XX 0xDB 236 | #define XX_XXX__ 0xDC 237 | #define XX_XXX_X 0xDD 238 | #define XX_XXXX_ 0xDE 239 | #define XX_XXXXX 0xDF 240 | #define XXX_____ 0xE0 241 | #define XXX____X 0xE1 242 | #define XXX___X_ 0xE2 243 | #define XXX___XX 0xE3 244 | #define XXX__X__ 0xE4 245 | #define XXX__X_X 0xE5 246 | #define XXX__XX_ 0xE6 247 | #define XXX__XXX 0xE7 248 | #define XXX_X___ 0xE8 249 | #define XXX_X__X 0xE9 250 | #define XXX_X_X_ 0xEA 251 | #define XXX_X_XX 0xEB 252 | #define XXX_XX__ 0xEC 253 | #define XXX_XX_X 0xED 254 | #define XXX_XXX_ 0xEE 255 | #define XXX_XXXX 0xEF 256 | #define XXXX____ 0xF0 257 | #define XXXX___X 0xF1 258 | #define XXXX__X_ 0xF2 259 | #define XXXX__XX 0xF3 260 | #define XXXX_X__ 0xF4 261 | #define XXXX_X_X 0xF5 262 | #define XXXX_XX_ 0xF6 263 | #define XXXX_XXX 0xF7 264 | #define XXXXX___ 0xF8 265 | #define XXXXX__X 0xF9 266 | #define XXXXX_X_ 0xFA 267 | #define XXXXX_XX 0xFB 268 | #define XXXXXX__ 0xFC 269 | #define XXXXXX_X 0xFD 270 | #define XXXXXXX_ 0xFE 271 | #define XXXXXXXX 0xFF 272 | /// @} 273 | 274 | -------------------------------------------------------------------------------- /frame.c: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "stddef.h" 3 | #include "isr.h" 4 | #include "paging.h" 5 | #include "frame.h" 6 | #include "kmalloc_early.h" 7 | #include "common.h" 8 | 9 | 10 | // We're going to track free frames in a stack. (array) 11 | uint32_t stack_count = 0; // The current capacity of the stack 12 | uint32_t *free_frames = NULL; 13 | int32_t top_of_stack = -1; 14 | 15 | // If the stack is empty, we allocate from the end of physical memory. 16 | // Since we've not allocated any pages, end_of_mem begins at 0. 17 | uint32_t end_of_mem = 0; 18 | 19 | uint32_t allocated_frames = 0; 20 | uint32_t total_frames; 21 | 22 | void init_frame_allocator(uint32_t system_frames) 23 | { 24 | total_frames = system_frames; 25 | if(free_frames != NULL) 26 | { 27 | // We've already initialized the frame allocator! 28 | return; 29 | } 30 | 31 | // Allocate a big enough stack to hold all the frames on the system. 32 | // Should be at most 4 MiB. 33 | free_frames = (uint32_t *)e_kmalloc(system_frames * sizeof (uint32_t)); 34 | stack_count = system_frames; 35 | } 36 | 37 | void alloc_frame(struct page *page, int is_kernel, int is_writeable) 38 | { 39 | if (page->frame != 0) 40 | { 41 | return; // Frame was already allocated, return straight away. 42 | } 43 | allocated_frames++; 44 | uint32_t idx; 45 | if(top_of_stack >= 0) 46 | { 47 | // There are free frames in the stack! 48 | idx = free_frames[top_of_stack]; 49 | top_of_stack--; // POP 50 | } 51 | else 52 | { 53 | // Otherwise, there were no free frames on the stack. 54 | // Grab one from the end of memory. 55 | if(end_of_mem >= total_frames) { 56 | // There are no free frames in the frame stack 57 | // and we're at the limit of our physical memory. 58 | PANIC("Cannot alloc frame. Out of memory!"); 59 | } 60 | idx = end_of_mem; 61 | end_of_mem++; 62 | } 63 | page->present = 1; // Mark it as present. 64 | page->rw = (is_writeable)?1:0; // Should the page be writeable? 65 | page->user = (is_kernel)?0:1; // Should the page be user-mode? 66 | page->frame = idx; 67 | } 68 | 69 | void free_frame(struct page *page) 70 | { 71 | top_of_stack++; // Advance to the next spot 72 | // Put the frame into the stack. 73 | if(((uint16_t)top_of_stack) >= stack_count) 74 | { 75 | // This should never happen because we're allocating 76 | // the full stack during initialization. 77 | PANIC("Frame pool is full! Something weird happened!"); 78 | } 79 | 80 | free_frames[top_of_stack] = page->frame; 81 | page->frame = 0x0; 82 | allocated_frames--; 83 | } 84 | -------------------------------------------------------------------------------- /frame.h: -------------------------------------------------------------------------------- 1 | #ifndef FRAME_H 2 | #define FRAME_H 3 | 4 | /** 5 | * Functions for allocating frames 6 | */ 7 | 8 | void init_frame_allocator(uint32_t system_frames); 9 | void alloc_frame(struct page *page, int is_kernel, int is_writeable); 10 | void free_frame(struct page *page); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /gdt.c: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "stdint.h" 3 | 4 | // This structure contains the value of one GDT entry. 5 | // We use the attribute 'packed' to tell GCC not to change 6 | // any of the alignment in the structure. 7 | struct gdt_entry_struct 8 | { 9 | uint16_t limit_low; // The lower 16 bits of the limit. 10 | uint16_t base_low; // The lower 16 bits of the base. 11 | uint8_t base_middle; // The next 8 bits of the base. 12 | uint8_t access; // Access flags, determine what ring this segment can be used in. 13 | uint8_t granularity; // low 4 bits are high 4 bits of limit 14 | uint8_t base_high; // The last 8 bits of the base. 15 | } __attribute__((packed)); 16 | typedef struct gdt_entry_struct gdt_entry_t; 17 | 18 | struct gdt_ptr_struct 19 | { 20 | uint16_t limit; // The upper 16 bits of all selector limits. 21 | uint32_t base; // The address of the first gdt_entry_t struct. 22 | } 23 | __attribute__((packed)); 24 | typedef struct gdt_ptr_struct gdt_ptr_t; 25 | 26 | // Lets us access our ASM functions from our C code. 27 | //extern void load_gdt(uint32_t); 28 | extern void load_gdt(gdt_ptr_t * gdt_ptr); 29 | 30 | // Internal function prototypes. 31 | static void gdt_set_gate(int32_t,uint32_t,uint32_t,uint8_t,uint8_t); 32 | 33 | gdt_entry_t gdt_entries[5]; 34 | gdt_ptr_t gdt_ptr; 35 | //idt_entry_t idt_entries[256]; 36 | //idt_ptr_t idt_ptr; 37 | 38 | 39 | void init_gdt() 40 | { 41 | gdt_ptr.limit = (sizeof(gdt_entry_t)*5) - 1; 42 | gdt_ptr.base = (uint32_t)&gdt_entries; 43 | 44 | /* 45 | Pr Priv 1 Ex DC RW Ac 46 | 0x9A == 1001 1010 == 1 00 1 1 0 1 0 47 | 0x92 == 1001 0010 == 1 00 1 0 0 1 0 48 | 0xFA == 1111 1010 == 1 11 1 1 0 1 0 49 | 0xF2 == 1111 0010 == 1 11 1 0 0 1 0 50 | 51 | We have page-granularity and 32-bit mode 52 | G D 0 Av 53 | 0xCF == 1100 1111 == 1 1 0 0 ~ 54 | */ 55 | 56 | gdt_set_gate(0,0,0,0,0); //Null segment 57 | gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); //Code segment 58 | gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); //Data segment 59 | gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); //User mode code segment 60 | gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); //User mode data segment 61 | 62 | //terminal_writestring("Flushing GDT.\n"); 63 | load_gdt(&gdt_ptr); 64 | } 65 | 66 | static void gdt_set_gate(int32_t entry, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) 67 | { 68 | gdt_entries[entry].base_low = (base & 0xFFFF); 69 | gdt_entries[entry].base_middle = (base >> 16) & 0xFF; 70 | gdt_entries[entry].base_high = (base >> 24) & 0xFF; 71 | 72 | gdt_entries[entry].limit_low = (limit & 0xFFFF); 73 | gdt_entries[entry].granularity = (limit >> 16) & 0x0F; 74 | 75 | gdt_entries[entry].granularity |= gran & 0xF0; 76 | gdt_entries[entry].access = access; 77 | } 78 | -------------------------------------------------------------------------------- /gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | 4 | void init_gdt(); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /gdt_asm.nasm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | section .text 4 | global load_gdt 5 | 6 | load_gdt: 7 | mov eax, DWORD [esp + 4] 8 | lgdt [eax] 9 | 10 | mov ax, 10h 11 | mov ds, ax 12 | mov es, ax 13 | mov fs, ax 14 | mov gs, ax 15 | mov ss, ax 16 | jmp 08h:flush 17 | flush: 18 | ret 19 | -------------------------------------------------------------------------------- /gdt_asm.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | .global load_gdt 3 | 4 | .type load_gdt, @function 5 | # void load_gdt(gdt_ptr_t * gdt_ptr) 6 | load_gdt: 7 | movl 4(%esp), %eax 8 | lgdt (%eax) 9 | 10 | # 0x10 is the offset in the GDT to our data segment 11 | movw $0x10, %ax 12 | movw %ax, %ds 13 | movw %ax, %es 14 | movw %ax, %fs 15 | movw %ax, %gs 16 | movw %ax, %ss 17 | ljmp $0x08, $.flush # Long jump to our new code segment 18 | .flush: 19 | ret 20 | 21 | 22 | .size load_gdt, . - load_gdt 23 | -------------------------------------------------------------------------------- /idt.c: -------------------------------------------------------------------------------- 1 | /** 2 | * This file sets up the Interrupt Descriptor Table. 3 | * Both the IRQs and ISRs are handled in isr.c. 4 | */ 5 | 6 | #include "idt.h" 7 | #include "common.h" 8 | #include "kernio.h" 9 | #include "stdint.h" 10 | 11 | // A struct describing an interrupt gate. 12 | struct idt_entry_struct 13 | { 14 | uint16_t base_lo; // The lower 16 bits of the address to jump to when this interrupt fires. 15 | uint16_t sel; // Kernel segment selector. 16 | uint8_t always0; // This must always be zero. 17 | uint8_t flags; // More flags. See documentation. 18 | uint16_t base_hi; // The upper 16 bits of the address to jump to. 19 | } __attribute__((packed)); 20 | typedef struct idt_entry_struct idt_entry_t; 21 | 22 | // A struct describing a pointer to an array of interrupt handlers. 23 | // This is in a format suitable for giving to 'lidt'. 24 | struct idt_ptr_struct 25 | { 26 | uint16_t limit; 27 | uint32_t base; // The address of the first element in our idt_entry_t array. 28 | } __attribute__((packed)); 29 | typedef struct idt_ptr_struct idt_ptr_t; 30 | 31 | // These extern directives let us access the addresses of our ASM ISR handlers. 32 | extern void isr0 (); 33 | extern void isr1 (); 34 | extern void isr2 (); 35 | extern void isr3 (); 36 | extern void isr4 (); 37 | extern void isr5 (); 38 | extern void isr6 (); 39 | extern void isr7 (); 40 | extern void isr8 (); 41 | extern void isr9 (); 42 | extern void isr10(); 43 | extern void isr11(); 44 | extern void isr12(); 45 | extern void isr13(); 46 | extern void isr14(); 47 | extern void isr15(); 48 | extern void isr16(); 49 | extern void isr17(); 50 | extern void isr18(); 51 | extern void isr19(); 52 | extern void isr20(); 53 | extern void isr21(); 54 | extern void isr22(); 55 | extern void isr23(); 56 | extern void isr24(); 57 | extern void isr25(); 58 | extern void isr26(); 59 | extern void isr27(); 60 | extern void isr28(); 61 | extern void isr29(); 62 | extern void isr30(); 63 | extern void isr31(); 64 | 65 | 66 | // IRQ Handlers 67 | extern void irq0(); 68 | extern void irq1(); 69 | extern void irq2(); 70 | extern void irq3(); 71 | extern void irq4(); 72 | extern void irq5(); 73 | extern void irq6(); 74 | extern void irq7(); 75 | extern void irq8(); 76 | extern void irq9(); 77 | extern void irq10(); 78 | extern void irq11(); 79 | extern void irq12(); 80 | extern void irq13(); 81 | extern void irq14(); 82 | extern void irq15(); 83 | 84 | extern void load_idt(idt_ptr_t *); 85 | static void idt_set_gate(uint8_t,uint32_t,uint16_t,uint8_t); 86 | 87 | idt_entry_t idt_entries[256]; 88 | idt_ptr_t idt_ptr; 89 | 90 | 91 | void init_idt() 92 | { 93 | idt_ptr.limit = sizeof(idt_entry_t) * 256 -1; 94 | idt_ptr.base = (uint32_t)&idt_entries; 95 | 96 | memset(&idt_entries, 0, sizeof(idt_entry_t)*256); 97 | 98 | idt_set_gate( 0, (uint32_t)isr0 , 0x08, 0x8E); 99 | idt_set_gate( 1, (uint32_t)isr1 , 0x08, 0x8E); 100 | idt_set_gate( 2, (uint32_t)isr2, 0x08, 0x8E); 101 | idt_set_gate( 3, (uint32_t)isr3, 0x08, 0x8E); 102 | idt_set_gate( 4, (uint32_t)isr4, 0x08, 0x8E); 103 | idt_set_gate( 5, (uint32_t)isr5, 0x08, 0x8E); 104 | idt_set_gate( 6, (uint32_t)isr6, 0x08, 0x8E); 105 | idt_set_gate( 7, (uint32_t)isr7, 0x08, 0x8E); 106 | idt_set_gate( 8, (uint32_t)isr8, 0x08, 0x8E); 107 | idt_set_gate( 9, (uint32_t)isr9, 0x08, 0x8E); 108 | idt_set_gate(10, (uint32_t)isr10, 0x08, 0x8E); 109 | idt_set_gate(11, (uint32_t)isr11, 0x08, 0x8E); 110 | idt_set_gate(12, (uint32_t)isr12, 0x08, 0x8E); 111 | idt_set_gate(13, (uint32_t)isr13, 0x08, 0x8E); 112 | idt_set_gate(14, (uint32_t)isr14, 0x08, 0x8E); 113 | idt_set_gate(15, (uint32_t)isr15, 0x08, 0x8E); 114 | idt_set_gate(16, (uint32_t)isr16, 0x08, 0x8E); 115 | idt_set_gate(17, (uint32_t)isr17, 0x08, 0x8E); 116 | idt_set_gate(18, (uint32_t)isr18, 0x08, 0x8E); 117 | idt_set_gate(19, (uint32_t)isr19, 0x08, 0x8E); 118 | idt_set_gate(20, (uint32_t)isr20, 0x08, 0x8E); 119 | idt_set_gate(21, (uint32_t)isr21, 0x08, 0x8E); 120 | idt_set_gate(22, (uint32_t)isr22, 0x08, 0x8E); 121 | idt_set_gate(23, (uint32_t)isr23, 0x08, 0x8E); 122 | idt_set_gate(24, (uint32_t)isr24, 0x08, 0x8E); 123 | idt_set_gate(25, (uint32_t)isr25, 0x08, 0x8E); 124 | idt_set_gate(26, (uint32_t)isr26, 0x08, 0x8E); 125 | idt_set_gate(27, (uint32_t)isr27, 0x08, 0x8E); 126 | idt_set_gate(28, (uint32_t)isr28, 0x08, 0x8E); 127 | idt_set_gate(29, (uint32_t)isr29, 0x08, 0x8E); 128 | idt_set_gate(30, (uint32_t)isr30, 0x08, 0x8E); 129 | idt_set_gate(31, (uint32_t)isr31, 0x08, 0x8E); 130 | 131 | 132 | // IRQ entries 133 | idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E); 134 | idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E); 135 | idt_set_gate(34, (uint32_t)irq2, 0x08, 0x8E); 136 | idt_set_gate(35, (uint32_t)irq3, 0x08, 0x8E); 137 | idt_set_gate(36, (uint32_t)irq4, 0x08, 0x8E); 138 | idt_set_gate(37, (uint32_t)irq5, 0x08, 0x8E); 139 | idt_set_gate(38, (uint32_t)irq6, 0x08, 0x8E); 140 | idt_set_gate(39, (uint32_t)irq7, 0x08, 0x8E); 141 | idt_set_gate(40, (uint32_t)irq8, 0x08, 0x8E); 142 | idt_set_gate(41, (uint32_t)irq9, 0x08, 0x8E); 143 | idt_set_gate(42, (uint32_t)irq10, 0x08, 0x8E); 144 | idt_set_gate(43, (uint32_t)irq11, 0x08, 0x8E); 145 | idt_set_gate(44, (uint32_t)irq12, 0x08, 0x8E); 146 | idt_set_gate(45, (uint32_t)irq13, 0x08, 0x8E); 147 | idt_set_gate(46, (uint32_t)irq14, 0x08, 0x8E); 148 | idt_set_gate(47, (uint32_t)irq15, 0x08, 0x8E); 149 | 150 | printf("Flushing IDT.\n"); 151 | load_idt(&idt_ptr); 152 | } 153 | 154 | static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) 155 | { 156 | idt_entries[num].base_lo = base & 0xFFFF; 157 | idt_entries[num].base_hi = (base >> 16) & 0xFFFF; 158 | 159 | idt_entries[num].sel = sel; 160 | idt_entries[num].always0 = 0; 161 | // We must uncomment the OR below when we get to using user-mode. 162 | // It sets the interrupt gate's privilege level to 3. 163 | idt_entries[num].flags = flags /* | 0x60 */; 164 | } 165 | -------------------------------------------------------------------------------- /idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | void init_idt(); 5 | 6 | // 0 - Division by zero exception 7 | // 1 - Debug exception 8 | // 2 - Non maskable interrupt 9 | // 3 - Breakpoint exception 10 | // 4 - 'Into detected overflow' 11 | // 5 - Out of bounds exception 12 | // 6 - Invalid opcode exception 13 | // 7 - No coprocessor exception 14 | // 8 - Double fault (pushes an error code) 15 | // 9 - Coprocessor segment overrun 16 | // 10 - Bad TSS (pushes an error code) 17 | // 11 - Segment not present (pushes an error code) 18 | // 12 - Stack fault (pushes an error code) 19 | // 13 - General protection fault (pushes an error code) 20 | // 14 - Page fault (pushes an error code) 21 | // 15 - Unknown interrupt exception 22 | // 16 - Coprocessor fault 23 | // 17 - Alignment check exception 24 | // 18 - Machine check exception 25 | // 19-31 - Reserved 26 | 27 | #define DIVISION_BY_ZERO 0 28 | #define DEBUG_EXCEPTION 1 29 | #define NON_MASKABLE_INTERRUPT 2 30 | #define BREAKPOINT_EXCEPTION 3 31 | #define INTO_DETECTED_OVERFLOW 4 32 | #define OUT_OF_BOUNDS_EXCEPTION 5 33 | #define INVALID_OPCODE_EXCEPTION 6 34 | #define NO_COPROCESSOR_EXCEPTION 7 35 | #define DOUBLE_FAULT 8 36 | #define COPROCESSOR_SEGMENT_OVERRUN 9 37 | #define BAD_TSS 10 38 | #define SEGMENT_NOT_PRESENT 11 39 | #define STACK_FAULT 12 40 | #define GENERAL_PROTECTION_FAULT 13 41 | #define PAGE_FAULT 14 42 | #define UNKNOWN_INTERRUPT_EXCEPTION 15 43 | #define COPROCESSOR_FAULT 16 44 | #define ALIGNMENT_CHECK_EXCEPTION 17 45 | #define MACHINE_CHECK_EXCEPTION 18 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /idt_asm.nasm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | section .text 3 | global load_idt 4 | 5 | load_idt: 6 | mov eax, [esp + 4] 7 | lidt [eax] 8 | ret 9 | 10 | ;; num %1 11 | %macro isr_noerr 1 12 | global isr%1 13 | isr%1: 14 | cli 15 | push 0 16 | push %1 17 | jmp isr_stub 18 | %endmacro 19 | 20 | 21 | %macro isr_err 1 22 | global isr%1 23 | isr%1: 24 | cli 25 | push %1 26 | jmp isr_stub 27 | %endmacro 28 | 29 | isr_noerr 0 30 | isr_noerr 1 31 | isr_noerr 2 32 | isr_noerr 3 33 | isr_noerr 4 34 | isr_noerr 5 35 | isr_noerr 6 36 | isr_noerr 7 37 | isr_err 8 38 | isr_noerr 9 39 | isr_err 10 40 | isr_err 11 41 | isr_err 12 42 | isr_err 13 43 | isr_err 14 44 | isr_noerr 15 45 | isr_noerr 16 46 | isr_noerr 17 47 | isr_noerr 18 48 | isr_noerr 19 49 | isr_noerr 20 50 | isr_noerr 21 51 | isr_noerr 22 52 | isr_noerr 23 53 | isr_noerr 24 54 | isr_noerr 25 55 | isr_noerr 26 56 | isr_noerr 27 57 | isr_noerr 28 58 | isr_noerr 29 59 | isr_noerr 30 60 | isr_noerr 31 61 | 62 | extern isr_handler 63 | isr_stub: 64 | pusha 65 | 66 | mov ax, ds 67 | push eax 68 | mov esi, eax 69 | 70 | mov ax, 10h 71 | mov ds, ax 72 | mov es, ax 73 | mov fs, ax 74 | mov gs, ax 75 | 76 | call isr_handler 77 | 78 | pop eax 79 | mov eax, esi 80 | 81 | mov ds, ax 82 | mov ds, ax 83 | mov es, ax 84 | mov fs, ax 85 | mov gs, ax 86 | 87 | popa 88 | add esp, 8 89 | sti 90 | iret 91 | 92 | ;; num %1 map %2 93 | %macro irq 2 94 | global irq%1 95 | irq%1: 96 | cli 97 | push 0 98 | push %2 99 | jmp irq_stub 100 | %endmacro 101 | 102 | irq 0, 32 103 | irq 1, 33 104 | irq 2, 34 105 | irq 3, 35 106 | irq 4, 36 107 | irq 5, 37 108 | irq 6, 38 109 | irq 7, 39 110 | irq 8, 40 111 | irq 9, 41 112 | irq 10, 42 113 | irq 11, 43 114 | irq 12, 44 115 | irq 13, 45 116 | irq 14, 46 117 | irq 15, 47 118 | 119 | 120 | extern irq_handler 121 | irq_stub: 122 | pusha 123 | 124 | mov ax, ds 125 | push eax 126 | mov esi, eax 127 | 128 | mov ax, 10h 129 | mov ds, ax 130 | mov es, ax 131 | mov fs, ax 132 | mov gs, ax 133 | 134 | call irq_handler 135 | 136 | pop ebx 137 | mov ebx, esi 138 | 139 | mov ds, bx 140 | mov es, bx 141 | mov fs, bx 142 | mov gs, bx 143 | 144 | popa 145 | add esp, 8 146 | sti 147 | iret 148 | -------------------------------------------------------------------------------- /idt_asm.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | .global load_idt 3 | 4 | .type load_idt, @function 5 | # void load_idt(idt_ptr_t *) 6 | load_idt: 7 | movl 4(%esp),%eax 8 | lidt (%eax) 9 | ret 10 | 11 | .size load_idt, . - load_idt 12 | 13 | # Macro for interrupt handler which does not push an error (we push a dummy val) 14 | .macro isr_noerr num 15 | .global isr\num 16 | isr\num: 17 | cli 18 | push $0 19 | push $\num 20 | jmp isr_stub 21 | .endm 22 | 23 | # Macro for interrupt handler which pushes an error 24 | .macro isr_err num 25 | .global isr\num 26 | isr\num: 27 | cli 28 | push $\num 29 | jmp isr_stub 30 | .endm 31 | 32 | isr_noerr 0 33 | isr_noerr 1 34 | isr_noerr 2 35 | isr_noerr 3 36 | isr_noerr 4 37 | isr_noerr 5 38 | isr_noerr 6 39 | isr_noerr 7 40 | isr_err 8 41 | isr_noerr 9 42 | isr_err 10 43 | isr_err 11 44 | isr_err 12 45 | isr_err 13 46 | isr_err 14 47 | isr_noerr 15 48 | isr_noerr 16 49 | isr_noerr 17 50 | isr_noerr 18 51 | isr_noerr 19 52 | isr_noerr 20 53 | isr_noerr 21 54 | isr_noerr 22 55 | isr_noerr 23 56 | isr_noerr 24 57 | isr_noerr 25 58 | isr_noerr 26 59 | isr_noerr 27 60 | isr_noerr 28 61 | isr_noerr 29 62 | isr_noerr 30 63 | isr_noerr 31 64 | 65 | 66 | .extern isr_handler 67 | isr_stub: 68 | pusha 69 | 70 | mov %ds, %ax 71 | pushl %eax 72 | movl %eax, %esi 73 | 74 | movw $0x10, %ax 75 | movw %ax, %ds 76 | movw %ax, %es 77 | movw %ax, %fs 78 | movw %ax, %gs 79 | 80 | call isr_handler 81 | 82 | pop %eax 83 | movl %esi, %eax 84 | 85 | mov %ax, %ds 86 | movw %ax, %ds 87 | movw %ax, %es 88 | movw %ax, %fs 89 | movw %ax, %gs 90 | 91 | popa 92 | add $8, %esp 93 | sti 94 | iret 95 | 96 | 97 | 98 | # Interrupt Request Handlers 99 | 100 | .macro irq num, map 101 | .global irq\num 102 | irq\num: 103 | cli 104 | push $0 105 | push $\map 106 | jmp irq_stub 107 | .endm 108 | 109 | 110 | irq 0, 32 111 | irq 1, 33 112 | irq 2, 34 113 | irq 3, 35 114 | irq 4, 36 115 | irq 5, 37 116 | irq 6, 38 117 | irq 7, 39 118 | irq 8, 40 119 | irq 9, 41 120 | irq 10, 42 121 | irq 11, 43 122 | irq 12, 44 123 | irq 13, 45 124 | irq 14, 46 125 | irq 15, 47 126 | 127 | .extern irq_handler 128 | irq_stub: 129 | pusha 130 | 131 | mov %ds, %ax 132 | pushl %eax 133 | movl %eax, %esi 134 | 135 | movw $0x10, %ax 136 | movw %ax, %ds 137 | movw %ax, %es 138 | movw %ax, %fs 139 | movw %ax, %gs 140 | 141 | call irq_handler 142 | 143 | pop %ebx 144 | movl %esi, %ebx 145 | 146 | movw %bx, %ds 147 | movw %bx, %es 148 | movw %bx, %fs 149 | movw %bx, %gs 150 | 151 | popa 152 | add $8, %esp 153 | sti 154 | iret 155 | -------------------------------------------------------------------------------- /isr.c: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains the minimal C code for dispatching 3 | * interrupts to handlers. 4 | */ 5 | 6 | #include "isr.h" 7 | #include "idt.h" 8 | #include "kernio.h" 9 | #include "pic.h" 10 | #include "port.h" 11 | #include "stdint.h" 12 | 13 | extern void halt(); 14 | 15 | isr_handler_t interrupt_handlers[256]; 16 | 17 | void register_interrupt_handler(uint8_t interrupt, isr_handler_t handler) 18 | { 19 | interrupt_handlers[interrupt] = handler; 20 | } 21 | 22 | void isr_handler(registers_t regs) 23 | { 24 | if(regs.int_no == GENERAL_PROTECTION_FAULT) 25 | { 26 | printf("General Protection Fault. Code: %d", regs.err_code); 27 | PANIC("General Protection Fault!"); 28 | } 29 | 30 | if(interrupt_handlers[regs.int_no]) 31 | { 32 | //printf("Handling %d!\n", regs.int_no); 33 | interrupt_handlers[regs.int_no](regs); 34 | //printf("Returning!\n"); 35 | } 36 | // else { 37 | // printf("Got ISR.\n"); 38 | // PANIC("Unhandled ISR.\n"); 39 | // } 40 | } 41 | 42 | 43 | void irq_handler(registers_t regs) 44 | { 45 | //If int_no >= 40, we must reset the slave as well as the master 46 | if(regs.int_no >= 40) 47 | { 48 | //reset slave 49 | outb(SLAVE_COMMAND, PIC_RESET); 50 | } 51 | 52 | outb(MASTER_COMMAND, PIC_RESET); 53 | 54 | if(interrupt_handlers[regs.int_no]) 55 | { 56 | interrupt_handlers[regs.int_no](regs); 57 | } 58 | // else { 59 | // if(regs.int_no == 33) return; 60 | // printf("Got IRQ.\n"); 61 | // char buff[1024]; 62 | // sprintf(buff, "Unhandled IRQ %d\n", regs.int_no); 63 | // PANIC(buff); 64 | // } 65 | } 66 | -------------------------------------------------------------------------------- /isr.h: -------------------------------------------------------------------------------- 1 | #ifndef ISR_H 2 | #define ISR_H 3 | 4 | #include "stdint.h" 5 | 6 | typedef struct registers 7 | { 8 | uint32_t ds; // Data segment selector 9 | uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha. 10 | uint32_t int_no, err_code; // Interrupt number and error code (if applicable) 11 | uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically. 12 | } registers_t; 13 | 14 | // This intentionally accepts a *COPY* of the registers. 15 | // It's slower, but it prevents service routines from messing with them. 16 | // Maybe messing with them is useful, and we'll change this later. 17 | typedef void (*isr_handler_t)(registers_t); 18 | void register_interrupt_handler(uint8_t interrupt, isr_handler_t handler); 19 | 20 | #define IRQ0 32 21 | #define IRQ1 33 22 | #define IRQ2 34 23 | #define IRQ3 35 24 | #define IRQ4 36 25 | #define IRQ5 37 26 | #define IRQ6 38 27 | #define IRQ7 39 28 | #define IRQ8 40 29 | #define IRQ9 41 30 | #define IRQ10 42 31 | #define IRQ11 43 32 | #define IRQ12 44 33 | #define IRQ13 45 34 | #define IRQ14 46 35 | #define IRQ15 47 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /kernel.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "stddef.h" 3 | #include "stdint.h" 4 | #include "terminal.h" 5 | #include "gdt.h" 6 | #include "idt.h" 7 | #include "pit.h" 8 | #include "pic.h" 9 | #include "isr.h" 10 | #include "paging.h" 11 | #include "common.h" 12 | #include "kheap.h" 13 | #include "multiboot.h" 14 | #include "keyboard.h" 15 | #include "fat32.h" 16 | #include "fat32_console.h" 17 | #include "kernio.h" 18 | #include "vesa.h" 19 | #include "terminal.h" 20 | 21 | /* This tutorial will only work for the 32-bit ix86 targets. */ 22 | #if !defined(__i386__) 23 | #error "This tutorial needs to be compiled with a ix86-elf compiler" 24 | #endif 25 | 26 | 27 | #if defined(__cplusplus) 28 | extern "C" /* Use C linkage for kernel_main. */ 29 | #endif 30 | 31 | extern void pause(); 32 | extern char _binary_f32_disk_start; 33 | 34 | 35 | 36 | void kernel_main(struct multiboot_info *mi) 37 | { 38 | terminal_initialize(COLOR_WHITE); 39 | uint32_t low_pages = 256; // 1024 * 1024 bytes / 4096 40 | uint32_t high_pages = (mi->mem_upper * 1024) / 4096; 41 | 42 | uint32_t total_frames = high_pages + low_pages; 43 | 44 | set_vmode(); 45 | set_vesa_color(make_vesa_color(0x8F, 0x8F, 0x8F)); 46 | init_gdt(); 47 | 48 | remap_pic(); 49 | init_idt(); 50 | 51 | init_timer(100); 52 | 53 | initialize_keyboard(); 54 | 55 | initialize_paging(total_frames, get_framebuffer_addr(), get_framebuffer_length()); 56 | malloc_stats(); 57 | printf("Done setting up paging.\n"); 58 | 59 | set_vesa_color(make_vesa_color(0xFF, 0xFF, 0xFF)); 60 | printf("Kernel is ready to go!!!\n\n"); 61 | 62 | // Kernel ready to go! 63 | 64 | printf("Creating fat32 filesystem.\n"); 65 | master_fs = makeFilesystem(""); 66 | if(master_fs == NULL) { 67 | printf("Failed to create fat32 filesystem. Disk may be corrupt.\n"); 68 | return; 69 | } 70 | 71 | printf("Finding /foo/bar/baz/boo/dep/doo/poo/goo/tood.txt.\n"); 72 | 73 | FILE *f = fopen("/foo/bar/baz/boo/dep/doo/poo/goo/tood.txt", NULL); 74 | if(f) { 75 | #define BCOUNT 1000 76 | uint8_t c[BCOUNT]; 77 | printf("READING:.................................\n"); 78 | int count, total; 79 | while((count = fread(&c, BCOUNT, 1, f)), count > 0) { 80 | for(int i = 0; i < count; i++) { 81 | printf("%c", c[i]); 82 | } 83 | total += count; 84 | } 85 | fclose(f); 86 | printf("Read %d bytes.\n", total); 87 | } 88 | else { 89 | printf("File not found. Continuing.\n"); 90 | } 91 | 92 | printf("Starting fat32 console.\n"); 93 | 94 | fat32_console(master_fs); 95 | 96 | printf("FAT32 shell exited. It is safe to power off.\nSystem is in free-typing mode.\n"); 97 | 98 | while(1) { 99 | char c = get_ascii_char(); 100 | printf("%c", c); 101 | }; 102 | printf("Halting.\n"); 103 | 104 | } 105 | -------------------------------------------------------------------------------- /kernio.c: -------------------------------------------------------------------------------- 1 | #include "kernio.h" 2 | #include "terminal.h" 3 | #include "common.h" 4 | #include 5 | 6 | int BOOTED; 7 | 8 | // Potential base for stdarg. Doesn't work. 9 | // We need to know more about how gcc handles the stack. 10 | //typedef char *va_list; 11 | //#define va_start(ap,parmn) (void)((ap) = (char*)(&(parmn) + 1)) 12 | //#define va_end(ap) (void)((ap) = 0) 13 | //#define va_arg(ap, type) \ 14 | // (((type*)((ap) = ((ap) + sizeof(type))))[-1]) 15 | 16 | int printf(char *fmt, ...) { 17 | //return; 18 | va_list argp; 19 | va_start(argp, fmt); 20 | 21 | char *p; 22 | for(p = fmt; *p != 0; p++) { 23 | if(*p != '%') { 24 | terminal_putchar(*p); 25 | continue; 26 | } 27 | p++; // Skip the % 28 | int i; 29 | char *s; 30 | switch(*p) { 31 | case 'c': 32 | i = va_arg(argp, int); 33 | terminal_putchar(i); 34 | break; 35 | case 's': 36 | s = va_arg(argp, char *); 37 | terminal_writestring(s); 38 | break; 39 | case 'd': 40 | i = va_arg(argp, int); 41 | terminal_write_dec(i); 42 | break; 43 | case 'x': 44 | i = va_arg(argp, int); 45 | terminal_write_hex(i); 46 | break; 47 | case '%': 48 | terminal_putchar('%'); 49 | break; 50 | default: 51 | terminal_putchar('%'); 52 | terminal_putchar(*p); 53 | break; 54 | } 55 | } 56 | return 0; 57 | } 58 | 59 | int sprintf(char *str, char *fmt, ...) { 60 | va_list argp; 61 | va_start(argp, fmt); 62 | 63 | char *p; 64 | for(p = fmt; *p != 0; p++) { 65 | if(*p != '%') { 66 | *str++ = *p; 67 | continue; 68 | } 69 | p++; // Skip the % 70 | int i; 71 | char *s; 72 | switch(*p) { 73 | case 'c': 74 | i = va_arg(argp, int); 75 | *str++ = i; 76 | break; 77 | case 's': 78 | s = va_arg(argp, char *); 79 | while(*s) { 80 | *str++ = *s++; 81 | } 82 | break; 83 | case 'd': 84 | i = va_arg(argp, int); 85 | char decbuff[13]; // At most 12 decimal places for 32 bit int. 86 | char *dec = itos(i, decbuff, 13); 87 | while(*dec) { 88 | *str++ = *dec++; 89 | } 90 | break; 91 | case 'x': 92 | i = va_arg(argp, int); 93 | for(int j = 28; j >= 0; j-=4) 94 | { 95 | *str++ = hex_char(i>>j); 96 | } 97 | break; 98 | case '%': 99 | *str++ = '%'; 100 | break; 101 | default: 102 | *str++ = '%'; 103 | *str++ = *p; 104 | break; 105 | } 106 | } 107 | *str++ = 0; 108 | return 0; 109 | } 110 | 111 | void set_status(char *str) { 112 | if(terminal_set_status) { 113 | terminal_set_status(str); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /kernio.h: -------------------------------------------------------------------------------- 1 | #ifndef KERNIO_H 2 | #define KERNIO_H 3 | 4 | /** 5 | * Supports %c %s %d and %x 6 | */ 7 | int printf(char *fmt, ...); 8 | int sprintf(char *str, char *fmt, ...); 9 | void set_status(char *str); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /keyboard.c: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "keyboard.h" 3 | #include "kernio.h" 4 | #include "isr.h" 5 | #include "port.h" 6 | #include "terminal.h" 7 | 8 | /* 9 | * Scan code Key Scan code Key Scan code Key Scan code Key 10 | * 0x01 escape pressed 0x02 1 pressed 0x03 2 pressed 11 | * 0x04 3 pressed 0x05 4 pressed 0x06 5 pressed 0x07 6 pressed 12 | * 0x08 7 pressed 0x09 8 pressed 0x0A 9 pressed 0x0B 0 (zero) pressed 13 | * 0x0C - pressed 0x0D = pressed 0x0E backspace pressed 0x0F tab pressed 14 | * 0x10 Q pressed 0x11 W pressed 0x12 E pressed 0x13 R pressed 15 | * 0x14 T pressed 0x15 Y pressed 0x16 U pressed 0x17 I pressed 16 | * 0x18 O pressed 0x19 P pressed 0x1A [ pressed 0x1B ] pressed 17 | * 0x1C enter pressed 0x1D left control pressed 0x1E A pressed 0x1F S pressed 18 | * 0x20 D pressed 0x21 F pressed 0x22 G pressed 0x23 H pressed 19 | * 0x24 J pressed 0x25 K pressed 0x26 L pressed 0x27 ; pressed 20 | * 0x28 ' (single quote) pressed 0x29 ` (back tick) pressed 0x2A left shift pressed 0x2B \ pressed 21 | * 0x2C Z pressed 0x2D X pressed 0x2E C pressed 0x2F V pressed 22 | * 0x30 B pressed 0x31 N pressed 0x32 M pressed 0x33 , pressed 23 | * 0x34 . pressed 0x35 / pressed 0x36 right shift pressed 0x37 (keypad) * pressed 24 | * 0x38 left alt pressed 0x39 space pressed 0x3A CapsLock pressed 0x3B F1 pressed 25 | * 0x3C F2 pressed 0x3D F3 pressed 0x3E F4 pressed 0x3F F5 pressed 26 | * 0x40 F6 pressed 0x41 F7 pressed 0x42 F8 pressed 0x43 F9 pressed 27 | * 0x44 F10 pressed 0x45 NumberLock pressed 0x46 ScrollLock pressed 0x47 (keypad) 7 pressed 28 | * 0x48 (keypad) 8 pressed 0x49 (keypad) 9 pressed 0x4A (keypad) - pressed 0x4B (keypad) 4 pressed 29 | * 0x4C (keypad) 5 pressed 0x4D (keypad) 6 pressed 0x4E (keypad) + pressed 0x4F (keypad) 1 pressed 30 | * 0x50 (keypad) 2 pressed 0x51 (keypad) 3 pressed 0x52 (keypad) 0 pressed 0x53 (keypad) . pressed 31 | * 0x57 F11 pressed 0x58 F12 pressed 32 | */ 33 | 34 | // Scancode -> ASCII 35 | const uint8_t lower_ascii_codes[256] = { 36 | 0x00, ESC, '1', '2', /* 0x00 */ 37 | '3', '4', '5', '6', /* 0x04 */ 38 | '7', '8', '9', '0', /* 0x08 */ 39 | '-', '=', BS, '\t', /* 0x0C */ 40 | 'q', 'w', 'e', 'r', /* 0x10 */ 41 | 't', 'y', 'u', 'i', /* 0x14 */ 42 | 'o', 'p', '[', ']', /* 0x18 */ 43 | '\n', 0x00, 'a', 's', /* 0x1C */ 44 | 'd', 'f', 'g', 'h', /* 0x20 */ 45 | 'j', 'k', 'l', ';', /* 0x24 */ 46 | '\'', '`', 0x00, '\\', /* 0x28 */ 47 | 'z', 'x', 'c', 'v', /* 0x2C */ 48 | 'b', 'n', 'm', ',', /* 0x30 */ 49 | '.', '/', 0x00, '*', /* 0x34 */ 50 | 0x00, ' ', 0x00, 0x00, /* 0x38 */ 51 | 0x00, 0x00, 0x00, 0x00, /* 0x3C */ 52 | 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 53 | 0x00, 0x00, 0x00, '7', /* 0x44 */ 54 | '8', '9', '-', '4', /* 0x48 */ 55 | '5', '6', '+', '1', /* 0x4C */ 56 | '2', '3', '0', '.', /* 0x50 */ 57 | 0x00, 0x00, 0x00, 0x00, /* 0x54 */ 58 | 0x00, 0x00, 0x00, 0x00 /* 0x58 */ 59 | }; 60 | 61 | // Scancode -> ASCII 62 | const uint8_t upper_ascii_codes[256] = { 63 | 0x00, ESC, '!', '@', /* 0x00 */ 64 | '#', '$', '%', '^', /* 0x04 */ 65 | '&', '*', '(', ')', /* 0x08 */ 66 | '_', '+', BS, '\t', /* 0x0C */ 67 | 'Q', 'W', 'E', 'R', /* 0x10 */ 68 | 'T', 'Y', 'U', 'I', /* 0x14 */ 69 | 'O', 'P', '{', '}', /* 0x18 */ 70 | '\n', 0x00, 'A', 'S', /* 0x1C */ 71 | 'D', 'F', 'G', 'H', /* 0x20 */ 72 | 'J', 'K', 'L', ':', /* 0x24 */ 73 | '"', '~', 0x00, '|', /* 0x28 */ 74 | 'Z', 'X', 'C', 'V', /* 0x2C */ 75 | 'B', 'N', 'M', '<', /* 0x30 */ 76 | '>', '?', 0x00, '*', /* 0x34 */ 77 | 0x00, ' ', 0x00, 0x00, /* 0x38 */ 78 | 0x00, 0x00, 0x00, 0x00, /* 0x3C */ 79 | 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 80 | 0x00, 0x00, 0x00, '7', /* 0x44 */ 81 | '8', '9', '-', '4', /* 0x48 */ 82 | '5', '6', '+', '1', /* 0x4C */ 83 | '2', '3', '0', '.', /* 0x50 */ 84 | 0x00, 0x00, 0x00, 0x00, /* 0x54 */ 85 | 0x00, 0x00, 0x00, 0x00 /* 0x58 */ 86 | }; 87 | 88 | 89 | const uint8_t lower_ascii_codes_dvorak[256] = { 90 | 0x00, ESC, '1', '2', /* 0x00 */ 91 | '3', '4', '5', '6', /* 0x04 */ 92 | '7', '8', '9', '0', /* 0x08 */ 93 | '[', ']', BS, '\t', /* 0x0C */ 94 | '\'', ',', '.', 'p', /* 0x10 */ 95 | 'y', 'f', 'g', 'c', /* 0x14 */ 96 | 'r', 'l', '/', '=', /* 0x18 */ 97 | '\n', 0x00, 'a', 'o', /* 0x1C */ 98 | 'e', 'u', 'i', 'd', /* 0x20 */ 99 | 'h', 't', 'n', 's', /* 0x24 */ 100 | '-', '`', 0x00, '\\', /* 0x28 */ 101 | ';', 'q', 'j', 'k', /* 0x2C */ 102 | 'x', 'b', 'm', 'w', /* 0x30 */ 103 | 'v', 'z', 0x00, '*', /* 0x34 */ 104 | 0x00, ' ', 0x00, 0x00, /* 0x38 */ 105 | 0x00, 0x00, 0x00, 0x00, /* 0x3C */ 106 | 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 107 | 0x00, 0x00, 0x00, '7', /* 0x44 */ 108 | '8', '9', '[', '4', /* 0x48 */ 109 | '5', '6', '}', '1', /* 0x4C */ 110 | '2', '3', '0', 'v', /* 0x50 */ 111 | 0x00, 0x00, 0x00, 0x00, /* 0x54 */ 112 | 0x00, 0x00, 0x00, 0x00 /* 0x58 */ 113 | }; 114 | 115 | // Scancode -> ASCII 116 | const uint8_t upper_ascii_codes_dvorak[256] = { 117 | 0x00, ESC, '!', '@', /* 0x00 */ 118 | '#', '$', '%', '^', /* 0x04 */ 119 | '&', '*', '(', ')', /* 0x08 */ 120 | '{', '}', BS, '\t', /* 0x0C */ 121 | '"', '<', '>', 'P', /* 0x10 */ 122 | 'Y', 'F', 'G', 'C', /* 0x14 */ 123 | 'R', 'L', '?', '+', /* 0x18 */ 124 | '\n', 0x00, 'A', 'O', /* 0x1C */ 125 | 'E', 'U', 'I', 'D', /* 0x20 */ 126 | 'H', 'T', 'N', 'S', /* 0x24 */ 127 | '_', '~', 0x00, '|', /* 0x28 */ 128 | ':', 'Q', 'J', 'K', /* 0x2C */ 129 | 'X', 'B', 'M', 'W', /* 0x30 */ 130 | 'V', 'Z', 0x00, '*', /* 0x34 */ 131 | 0x00, ' ', 0x00, 0x00, /* 0x38 */ 132 | 0x00, 0x00, 0x00, 0x00, /* 0x3C */ 133 | 0x00, 0x00, 0x00, 0x00, /* 0x40 */ 134 | 0x00, 0x00, 0x00, '7', /* 0x44 */ 135 | '8', '9', '[', '4', /* 0x48 */ 136 | '5', '6', '}', '1', /* 0x4C */ 137 | '2', '3', '0', 'v', /* 0x50 */ 138 | 0x00, 0x00, 0x00, 0x00, /* 0x54 */ 139 | 0x00, 0x00, 0x00, 0x00 /* 0x58 */ 140 | }; 141 | 142 | // shift flags. left shift is bit 0, right shift is bit 1. 143 | uint8_t shift; 144 | // control flags just like shift flags. 145 | uint8_t ctrl; 146 | uint8_t keypresses[256]; 147 | 148 | #define BUFFLEN 128 149 | // New characters are added to hd. characters are pulled off of tl. 150 | uint8_t kb_buff[BUFFLEN]; 151 | uint8_t kb_buff_hd; 152 | uint8_t kb_buff_tl; 153 | 154 | static void poll_keyboard_input() { 155 | // See if there's room in the key buffer, else bug out. 156 | uint8_t next_hd = (kb_buff_hd + 1) % BUFFLEN; 157 | if(next_hd == kb_buff_tl) { 158 | return; 159 | } 160 | 161 | uint8_t byte = inb(0x60); 162 | if(byte == 0) { 163 | return; 164 | } 165 | 166 | if(byte & 0x80) { 167 | // Key release 168 | uint8_t pressedbyte = byte & 0x7F; 169 | // Check if we're releasing a shift key. 170 | if(pressedbyte == 0x2A) { 171 | // left 172 | shift = shift & 0x02; 173 | } 174 | else if(pressedbyte == 0x36) { 175 | // right 176 | shift = shift & 0x01; 177 | } 178 | else if(pressedbyte == 0x1D) { 179 | ctrl = 0; 180 | } 181 | 182 | keypresses[pressedbyte] = 0; 183 | return; 184 | } 185 | 186 | if(keypresses[byte] < 10 && keypresses[byte] > 0) { 187 | // Key is already pressed. Ignore it. 188 | keypresses[byte]++; // Increment anyway, so we can roll over and repeat. 189 | return; 190 | } 191 | keypresses[byte]++; 192 | 193 | if(byte == 0x2A) { 194 | shift = shift | 0x01; 195 | return; 196 | } 197 | else if(byte == 0x36) { 198 | shift = shift | 0x02; 199 | return; 200 | } 201 | else if(byte == 0x1D) { 202 | ctrl = 1; 203 | return; 204 | } 205 | 206 | const uint8_t *codes; 207 | if(ctrl) { 208 | if(lower_ascii_codes_dvorak[byte] == 'd') { 209 | // Ctrl+d 210 | kb_buff[kb_buff_hd] = EOT; 211 | kb_buff_hd = next_hd; 212 | return; 213 | } 214 | } 215 | else if(shift) { 216 | codes = upper_ascii_codes_dvorak; 217 | } 218 | else { 219 | codes = lower_ascii_codes_dvorak; 220 | } 221 | 222 | uint8_t ascii = codes[byte]; 223 | if(ascii != 0) { 224 | kb_buff[kb_buff_hd] = ascii; 225 | kb_buff_hd = next_hd; 226 | return; 227 | } 228 | } 229 | 230 | void keyboard_handler(registers_t regs) { 231 | poll_keyboard_input(); 232 | } 233 | 234 | void initialize_keyboard() { 235 | printf("Initializing keyboard.\n"); 236 | 237 | outb(0x64, 0xFF); 238 | uint8_t status = inb(0x64); 239 | printf("Got status (%x) after reset.\n", status); 240 | 241 | status = inb(0x64); 242 | if(status & (1 << 0)) { 243 | printf("Output buffer full.\n"); 244 | } 245 | else { 246 | printf("Output buffer empty.\n"); 247 | } 248 | 249 | if(status & (1 << 1)) { 250 | printf("Input buffer full.\n"); 251 | } 252 | else { 253 | printf("Input buffer empty.\n"); 254 | } 255 | 256 | if(status & (1 << 2)) { 257 | printf("System flag set.\n"); 258 | } 259 | else { 260 | printf("System flag unset.\n"); 261 | } 262 | 263 | if(status & (1 << 3)) { 264 | printf("Command/Data -> PS/2 device.\n"); 265 | } 266 | else { 267 | printf("Command/Data -> PS/2 controller.\n"); 268 | } 269 | 270 | if(status & (1 << 6)) { 271 | printf("Timeout error.\n"); 272 | } 273 | else { 274 | printf("No timeout error.\n"); 275 | } 276 | 277 | if(status & (1 << 7)) { 278 | printf("Parity error.\n"); 279 | } 280 | else { 281 | printf("No parity error.\n"); 282 | } 283 | 284 | // Test the controller. 285 | outb(0x64, 0xAA); 286 | uint8_t result = inb(0x60); 287 | if(result == 0x55) { 288 | printf("PS/2 controller test passed.\n"); 289 | } 290 | else if(result == 0xFC) { 291 | printf("PS/2 controller test failed.\n"); 292 | // return; 293 | } 294 | else { 295 | printf("PS/2 controller responded to test with unknown code %x\n", result); 296 | printf("Trying to continue.\n"); 297 | // return; 298 | } 299 | 300 | // Check the PS/2 controller configuration byte. 301 | outb(0x64, 0x20); 302 | result = inb(0x60); 303 | printf("PS/2 config byte: %x\n", result); 304 | 305 | register_interrupt_handler(IRQ1, keyboard_handler); 306 | 307 | printf("Keyboard ready to go!\n\n"); 308 | } 309 | 310 | extern void pause(); 311 | extern void sys_cli(); 312 | extern void sys_sti(); 313 | 314 | // This can race with the keyboard_handler, so we have to stop interrupts while we check stuff. 315 | char get_ascii_char() { 316 | 317 | while(1) { 318 | sys_cli(); 319 | if(kb_buff_hd != kb_buff_tl) { 320 | char c = kb_buff[kb_buff_tl]; 321 | kb_buff_tl = (kb_buff_tl + 1) % BUFFLEN; 322 | poll_keyboard_input(); 323 | sys_sti(); 324 | return c; 325 | } 326 | sys_sti(); 327 | pause(); 328 | } 329 | 330 | } 331 | -------------------------------------------------------------------------------- /keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARD_H 2 | #define KEYBOARD_H 3 | 4 | void initialize_keyboard(); 5 | char get_ascii_char(); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /kheap.c: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "common.h" 3 | #include "kheap.h" 4 | #include "kernio.h" 5 | #include "isr.h" 6 | #include "paging.h" 7 | 8 | #define INITIAL_HEAP_SIZE (0x1000 * INITIAL_HEAP_PAGE_COUNT) 9 | 10 | struct header { 11 | uint8_t free; 12 | uint32_t size; 13 | }; 14 | 15 | struct free_header { 16 | struct header h; 17 | struct free_header *next; 18 | struct free_header *prev; 19 | }; 20 | 21 | 22 | struct footer { 23 | uint32_t size; 24 | }; 25 | 26 | char *memhead; 27 | char *memend; // memend MUST always be page aligned. 28 | 29 | struct free_header *head; 30 | 31 | uint32_t allocations = 0; 32 | uint32_t heap_free = 0; 33 | 34 | static void do_kfree(void *p); 35 | 36 | void malloc_stats() { 37 | printf("Heap starts @ %x ends @ %x and contains ", memhead, memend); 38 | 39 | uint32_t bytes = memend - memhead; 40 | if(bytes / 1024 / 1024 > 0) { 41 | printf("%d MiB.\nCurrent allocations: [%d]\n", bytes / 1024 / 1024, allocations); 42 | } 43 | else if(bytes / 1024 > 0) { 44 | printf("%d KiB.\nCurrent allocations: [%d]\n", bytes / 1024, allocations); 45 | } 46 | else { 47 | printf("%d B.\nCurrent allocations: [%d]\n", bytes, allocations); 48 | } 49 | 50 | uint32_t free = 0; 51 | struct free_header *current = head; 52 | while(current) { 53 | free += current->h.size; 54 | current = current->next; 55 | } 56 | 57 | if(free / 1024 / 1024 > 0) { 58 | printf("Free: [%d MiB]\n", free / 1024 / 1024); 59 | } 60 | else if(free / 1024 > 0) { 61 | printf("Free: [%d KiB]\n", free / 1024); 62 | } 63 | else { 64 | printf("Free: [%d Bytes]\n", free); 65 | } 66 | } 67 | 68 | static struct header *set_header_footer(char *block, uint32_t block_size) { 69 | struct header *header = (struct header *)block; 70 | header->free = 1; 71 | header->size = block_size; 72 | struct footer *foot = (struct footer *)(block + block_size - sizeof (struct footer)); 73 | foot->size = block_size; 74 | 75 | // I'm going to leave this here even though it's odd 76 | // because it's a hard bug to track down. 77 | // If we map pages past the end of RAM, we end up 78 | // here. If we don't catch this here, we end up panicing 79 | // on heap corruption. 80 | if(foot->size != block_size) { 81 | PANIC("OOM!"); 82 | } 83 | 84 | return header; 85 | } 86 | 87 | static void expand(uint32_t at_least) { 88 | uint32_t size = 0x2000; // expand by at least two pages. 89 | if(at_least > size) { 90 | // If at_least is more than two pages, 91 | // map enough frames to supply at_least bytes. 92 | size = (at_least & 0xFFFFF000) + 0x1000; 93 | } 94 | 95 | // Number of frames we're going to map. 96 | uint32_t num_frames = size / 0x1000; 97 | char *block_header = memend; 98 | 99 | uint32_t i; 100 | // If we're mapping more than one frame, we may end up with fewer than we asked for. 101 | // We still want to insert them into the heap and try an allocation. 102 | uint32_t actually_allocated = 0; 103 | for(i = 0; i < num_frames; i++) { 104 | struct page *page = map_kernel_page((uint32_t)memend, 0); 105 | if(page == NULL) { 106 | // We failed to grab a page-table entry for the desired page. 107 | break; 108 | } 109 | memend += 0x1000; 110 | actually_allocated += 0x1000; 111 | } 112 | if(actually_allocated == 0) { 113 | // If we didn't manage to map anything, just return. 114 | return; 115 | } 116 | 117 | set_header_footer((char *)block_header, actually_allocated); 118 | 119 | do_kfree(block_header + sizeof (struct header)); 120 | } 121 | 122 | void initialize_kheap(uint32_t start_addr) { 123 | memhead = (char *)start_addr; 124 | memend = memhead + INITIAL_HEAP_SIZE; 125 | set_header_footer(memhead, INITIAL_HEAP_SIZE); 126 | head = (struct free_header *)memhead; 127 | head->next = NULL; 128 | head->prev = NULL; 129 | heap_free = head->h.size; 130 | } 131 | 132 | static struct free_header *find_block_with_page_aligned_addr(uint32_t needed_size) { 133 | struct free_header *block = head; 134 | while(block != NULL) { 135 | char *start_addr = (char *)block; 136 | char *end_addr = (char *)(start_addr + block->h.size); 137 | 138 | uint32_t next_page = (((uint32_t)block) & 0xFFFFF000) + 0x1000; 139 | 140 | // Move the blockstart back by the size of the header. The 141 | // address we RETURN has to be page-aligned. 142 | char *blockstart = (char *)(next_page - sizeof (struct header)); 143 | 144 | if(blockstart < (start_addr + block->h.size - sizeof (struct footer))) { 145 | // page-aligned address is within block bounds! 146 | // Check if there is enough space. 147 | uint32_t total_space = end_addr - blockstart; 148 | if(total_space >= needed_size) { 149 | // WE FOUND ONE! 150 | if(blockstart != start_addr) { 151 | // Now we need to break up the blocks. 152 | if((uint32_t)(blockstart - start_addr) < (uint32_t)(sizeof(struct header) + sizeof(struct footer))) { 153 | // There's not enough room before blockstart to split the blocks. 154 | // Try to find another. 155 | continue; 156 | } 157 | else { 158 | // There's enough room. Break up the block. 159 | uint32_t space_in_prev_block = blockstart - start_addr; 160 | set_header_footer((char *)block, space_in_prev_block); 161 | struct free_header *newblock = (struct free_header *)blockstart; 162 | set_header_footer((char *)newblock, total_space); 163 | newblock->next = block->next; 164 | if(newblock->next) { 165 | newblock->next->prev = newblock; 166 | } 167 | block->next = newblock; 168 | newblock->prev = block; 169 | // We've broken up the block. Return the new one! 170 | return newblock; 171 | } 172 | } 173 | else { 174 | // The block is already page-aligned. Just return it. 175 | return block; 176 | } 177 | } 178 | } 179 | block = block->next; 180 | } 181 | return NULL; 182 | } 183 | 184 | // Find a free block of suitable size and alignment. 185 | static struct free_header *find_block(uint32_t size, uint8_t align) { 186 | struct free_header *block = head; 187 | while(block != NULL) { 188 | // If we're not looking for alignment, Just check size: 189 | if(!align) { 190 | if(block->h.size >= size) { 191 | break; 192 | } 193 | } 194 | else { 195 | // We need an aligned block. 196 | block = find_block_with_page_aligned_addr(size); 197 | break; 198 | } 199 | block = block->next; 200 | } 201 | return block; 202 | } 203 | 204 | void *kmalloc(uint32_t size) { 205 | return kmalloc_ap(size, 0, NULL); 206 | } 207 | 208 | void *krealloc(void *p, uint32_t size) { 209 | if(p == NULL) { 210 | return kmalloc(size); 211 | } 212 | struct header *header = (struct header *)(((char *)p) - sizeof (struct header)); 213 | void *newchunk = kmalloc(size); 214 | if(newchunk == NULL) return NULL; // Don't know if this can actually happen 215 | 216 | memcpy(newchunk, p, header->size); 217 | kfree(p); 218 | return newchunk; 219 | } 220 | 221 | void *kmalloc_ap(uint32_t size, uint8_t align, uint32_t *phys) { 222 | if(size <= 0) { 223 | return NULL; 224 | } 225 | 226 | if(memhead == NULL) { 227 | PANIC("kheap not initialized."); 228 | } 229 | 230 | // The block we need needs to fit user data + metadata. 231 | uint32_t needed_size = size + sizeof (struct free_header) + sizeof (struct footer); 232 | 233 | // Find a suitable free block. 234 | struct free_header *block = find_block(needed_size, align); 235 | if(block == NULL) { 236 | // We couldn't find a suitable block. 237 | // Try to expand the heap 238 | expand(needed_size); 239 | 240 | // Try to find a block again, now that we've 241 | // hopefully expanded. 242 | block = find_block(needed_size, align); 243 | if(block == NULL) { 244 | // If we still can't find a suitable block, PANIC! 245 | PANIC("Failed to expand kheap."); 246 | } 247 | } 248 | // Now we have found the block we are going to use! 249 | 250 | block->h.free = 0; 251 | 252 | if(head == block) { 253 | head = block->next; 254 | } 255 | 256 | if(block->prev) { 257 | block->prev->next = block->next; 258 | } 259 | if(block->next) { 260 | block->next->prev = block->prev; 261 | } 262 | 263 | 264 | if(block->h.size - needed_size < sizeof (struct free_header) + sizeof (struct footer)) { 265 | // Not enough room left in the remaining block to put a free entry. Just include the 266 | // rest of the space in the alloc'd block. 267 | needed_size = block->h.size; 268 | } 269 | else { 270 | // Chop up the block and insert the remainder into the free list. 271 | 272 | // Thse may get clobbered when writing the remainder header. 273 | // Save them now so we can use them to insert the remainder. 274 | void *blocknext = block->next; 275 | void *blockprev = block->prev; 276 | 277 | struct free_header *remainder_block = (struct free_header *)(((char *) block) + needed_size); 278 | set_header_footer((char *)remainder_block, block->h.size - needed_size); 279 | 280 | remainder_block->next = blocknext; 281 | if(remainder_block->next) { 282 | remainder_block->next->prev = remainder_block; 283 | } 284 | remainder_block->prev = blockprev; 285 | if(remainder_block->prev) { 286 | remainder_block->prev->next = remainder_block; 287 | } 288 | else { 289 | // Blockprev was head. Set head to remainder. 290 | head = remainder_block; 291 | } 292 | } 293 | 294 | block->h.size = needed_size; 295 | struct footer *block_footer = (struct footer *)((char *)block + needed_size - sizeof (struct footer)); 296 | block_footer->size = needed_size; 297 | 298 | // We have oficcially made an allocation. 299 | allocations++; 300 | heap_free -= block->h.size; 301 | 302 | char *return_addr = ((char *)block) + sizeof (struct header); 303 | if(phys) { 304 | struct page *p = get_kernel_page((uint32_t)return_addr, 0); 305 | *phys = (p->frame << 12) | (((uint32_t)return_addr) & 0xFFF); 306 | } 307 | return return_addr; 308 | } 309 | 310 | static struct free_header *get_previous_block(struct header *block_head) { 311 | char *block = (char *)block_head; 312 | if(block == memhead) { 313 | // We're at the beginning of the heap. 314 | // No previous block! 315 | return NULL; 316 | } 317 | 318 | struct footer *prev_foot = (struct footer *)(block - sizeof (struct footer)); 319 | struct header *prev_head = (struct header *)(block - prev_foot->size); 320 | if(prev_head == block_head) { 321 | // Something is really wrong. header/footer sizes should NEVER be zero. 322 | PANIC("Detected kheap corruption."); 323 | } 324 | return (struct free_header *)prev_head; 325 | } 326 | 327 | static struct free_header *get_next_block(struct header *block_head) { 328 | char *block = (char *)block_head; 329 | void *nextblock = block + block_head->size; 330 | if(nextblock == memend) { 331 | return NULL; 332 | } 333 | return nextblock; 334 | } 335 | 336 | static void do_kfree(void *p) { 337 | if(p == NULL) { 338 | return; 339 | } 340 | 341 | struct free_header *block_header = (struct free_header *)(((char *)p) - sizeof (struct header)); 342 | block_header->h.free = 1; 343 | heap_free += block_header->h.size; 344 | 345 | struct free_header *prev_block = get_previous_block((struct header *)block_header); 346 | struct free_header *next_block = get_next_block((struct header *)block_header); 347 | 348 | // Try to join the prev block first. 349 | if(prev_block && prev_block->h.free) { 350 | // If the previous block is free, just add our size to it. 351 | set_header_footer((char *)prev_block, prev_block->h.size + block_header->h.size); 352 | 353 | // If the next block is also free, we can join it simply. 354 | if(next_block && next_block->h.free) { 355 | prev_block->next = next_block->next; 356 | if(prev_block->next) { 357 | prev_block->next->prev = prev_block; 358 | } 359 | set_header_footer((char *)prev_block, prev_block->h.size + next_block->h.size); 360 | } 361 | return; 362 | } 363 | 364 | //Try to join with the next block 365 | if(next_block && next_block->h.free) { 366 | block_header->next = next_block->next; 367 | if(block_header->next) { 368 | block_header->next->prev = block_header; 369 | } 370 | block_header->prev = next_block->prev; 371 | if(block_header->prev) { 372 | block_header->prev->next = block_header; 373 | } 374 | 375 | set_header_footer((char *)block_header, block_header->h.size + next_block->h.size); 376 | if(head == next_block) { 377 | head = block_header; 378 | block_header->prev = NULL; 379 | } 380 | return; 381 | } 382 | 383 | // Blocks on both sides are used. Look backwards for closest free block. 384 | 385 | // We couldn't get pointers from either the previous or the next block since neither were free. 386 | // We won't be merging with any other blocks. We just want to find the closest previous block 387 | // So we can insert the block into the free list. 388 | 389 | // First check if there even ARE any free blocks before this one. 390 | // This is an optimization to avoid scanning backwards through 391 | // The entire heap if we don't need to. 392 | if(head == NULL || head > block_header) { 393 | // There are no blocks before this one. 394 | prev_block = NULL; 395 | } 396 | 397 | while(prev_block) { 398 | if(prev_block->h.free) { 399 | break; 400 | } 401 | prev_block = get_previous_block((struct header *)prev_block); 402 | } 403 | 404 | if(prev_block == NULL) { 405 | // There are no previous free blocks. This is the first free block. Set it at the head. 406 | block_header->next = head; 407 | head = block_header; 408 | if(block_header->next) { 409 | block_header->next->prev = block_header; 410 | } 411 | block_header->prev = NULL; 412 | } 413 | else { 414 | // Insert the block after prev_block 415 | block_header->next = prev_block->next; 416 | if(block_header->next) { 417 | block_header->next->prev = block_header; 418 | } 419 | block_header->prev = prev_block; 420 | prev_block->next = block_header; 421 | } 422 | set_header_footer((char *)block_header, block_header->h.size); 423 | 424 | } 425 | 426 | // Tries to unmap the last block in the heap. 427 | // Returns 1 on success. 428 | static int unmap_blocks() { 429 | // Check the last block to see if it's 1. free, 2. big enough that we can 430 | // unmap it. 431 | struct footer *f = (struct footer *)(memend - sizeof (struct footer)); 432 | if(f->size < 0x1000) { 433 | // The block has to be at least a page large, otherwise, we can't 434 | // unmap a page (duh) 435 | return 0; 436 | } 437 | 438 | struct header *h = (struct header *)(memend - f->size); 439 | if(!h->free) { 440 | // The block isn't free anyway. We can't unmap it. 441 | return 0; 442 | } 443 | 444 | char *page_addr = memend - 0x1000; 445 | if(page_addr < (char *)h 446 | || (uint32_t)(page_addr - (char *)h) < (sizeof (struct free_header) + sizeof (struct footer))) { 447 | // There's not enough room left if we free the page to make a free block. 448 | return 0; 449 | } 450 | 451 | // We can free a page! 452 | uint32_t newsize = h->size - 0x1000; 453 | memend -= 0x1000; 454 | heap_free -= 0x1000; 455 | set_header_footer((char *)h, newsize); 456 | unmap_kernel_page((uint32_t)memend); 457 | return 1; 458 | } 459 | 460 | void kfree(void *p) { 461 | do_kfree(p); 462 | allocations--; 463 | while(unmap_blocks()){} 464 | } 465 | -------------------------------------------------------------------------------- /kheap.h: -------------------------------------------------------------------------------- 1 | #ifndef KHEAP_H 2 | #define KHEAP_H 3 | 4 | #define INITIAL_HEAP_PAGE_COUNT 4 5 | 6 | /** 7 | * initialize the kernel heap. 8 | * start_addr must be a pointer to a mapped kernel page. 9 | */ 10 | void initialize_kheap(uint32_t start_addr); 11 | 12 | /** 13 | * Allocates a contiguout region of memory 'size' bytes in size. 14 | * equivalent to kmalloc_ap(size, 0, NULL); 15 | */ 16 | void *kmalloc(uint32_t size); 17 | 18 | void *krealloc(void *p, uint32_t size); 19 | 20 | /** 21 | * Allocates a contiguous region of memory 'size' in size. 22 | * If page_align==1, it creates that block starting on a page boundary. 23 | */ 24 | void *kmalloc_ap(uint32_t size, uint8_t page_align, uint32_t *phys); 25 | 26 | /** 27 | * Releases a block allocated with 'alloc'. 28 | */ 29 | void kfree(void *p); 30 | 31 | /** 32 | * Write kheap stats to the terminal. 33 | */ 34 | void malloc_stats(); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /kmalloc_early.c: -------------------------------------------------------------------------------- 1 | #include "kmalloc_early.h" 2 | #include "common.h" 3 | 4 | extern uint32_t end_of_kernel; 5 | uint32_t placement_address = (uint32_t)&end_of_kernel; 6 | uint8_t enabled = 1; 7 | 8 | static uint32_t kmalloc_imp(uint32_t sz, int align, uint32_t *phys) 9 | { 10 | if(!enabled) { 11 | PANIC("kmalloc is disabled!"); 12 | } 13 | if (align == 1 && (placement_address & 0xFFFFF000)) 14 | { 15 | // If the address is not already page-aligned 16 | // Align it. 17 | placement_address &= 0xFFFFF000; // Set to current page 18 | placement_address += 0x1000; // Advance one page 19 | } 20 | if (phys) 21 | { 22 | *phys = placement_address; 23 | } 24 | uint32_t tmp = placement_address; 25 | placement_address += sz; 26 | return tmp; 27 | } 28 | 29 | uint32_t e_kmalloc_a(uint32_t sz) { 30 | return kmalloc_imp(sz, 1, NULL); 31 | } 32 | 33 | uint32_t e_kmalloc_p(uint32_t sz, uint32_t *phys) { 34 | return kmalloc_imp(sz, 0, phys); 35 | } 36 | 37 | uint32_t e_kmalloc_ap(uint32_t sz, uint32_t *phys) { 38 | return kmalloc_imp(sz, 1, phys); 39 | } 40 | 41 | uint32_t e_kmalloc(uint32_t sz) { 42 | return kmalloc_imp(sz, 0, NULL); 43 | } 44 | 45 | uint32_t disable_early_kmalloc() { 46 | uint32_t end = placement_address; 47 | if(!(end & 0xFFF)) return end; 48 | 49 | end &= 0xFFFFF000; // Set to current page 50 | end += 0x1000; 51 | enabled = 0; 52 | return end; 53 | } 54 | -------------------------------------------------------------------------------- /kmalloc_early.h: -------------------------------------------------------------------------------- 1 | #ifndef KMALLOC_EARLY_H 2 | #define KMALLOC_EARLY_H 3 | 4 | #include "stdint.h" 5 | #include "stddef.h" 6 | 7 | uint32_t e_kmalloc_a(uint32_t sz); // page aligned. 8 | uint32_t e_kmalloc_p(uint32_t sz, uint32_t *phys); // returns a physical address. 9 | uint32_t e_kmalloc_ap(uint32_t sz, uint32_t *phys); // page aligned and returns a physical address. 10 | uint32_t e_kmalloc(uint32_t sz); // vanilla (normal). 11 | 12 | // Returns a pointer to the first usable page after the kernel and early-allocated data. 13 | // After this, any kmalloc calls will cause a panic. 14 | uint32_t disable_early_kmalloc(); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | /* The bootloader will look at this image and start execution at the symbol 2 | designated as the entry point. */ 3 | ENTRY(_start) 4 | 5 | /* Tell where the various sections of the object files will be put in the final 6 | kernel image. */ 7 | SECTIONS 8 | { 9 | /* Begin putting sections at 1 MiB, a conventional place for kernels to be 10 | loaded at by the bootloader. */ 11 | . = 1M; 12 | 13 | /* First put the multiboot header, as it is required to be put very early 14 | early in the image or the bootloader won't recognize the file format. 15 | Next we'll put the .text section. */ 16 | .text BLOCK(4K) : ALIGN(4K) 17 | { 18 | *(.multiboot) 19 | *(.text) 20 | } 21 | 22 | /* Read-only data. */ 23 | .rodata BLOCK(4K) : ALIGN(4K) 24 | { 25 | *(.rodata) 26 | } 27 | 28 | /* Read-write data (initialized) */ 29 | .data BLOCK(4K) : ALIGN(4K) 30 | { 31 | *(.data) 32 | } 33 | 34 | /* Read-write data (uninitialized) and stack */ 35 | .bss BLOCK(4K) : ALIGN(4K) 36 | { 37 | *(COMMON) 38 | *(.bss) 39 | *(.bootstrap_stack) 40 | } 41 | /* The compiler may produce other sections, by default it will put them in 42 | a segment with the same name. Simply add stuff here as needed. */ 43 | .kend BLOCK(4K) : ALIGN(4K) 44 | { 45 | *(.kend) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /multiboot.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTIBOOT_H 2 | #define MULTIBOOT_H 3 | 4 | //! The symbol table for a.out format. 5 | typedef struct { 6 | unsigned long tab_size; 7 | unsigned long str_size; 8 | unsigned long address; 9 | unsigned long reserved; 10 | } aout_symbol_table_t; 11 | 12 | //! The section header table for ELF format. 13 | typedef struct { 14 | unsigned long num; 15 | unsigned long size; 16 | unsigned long address; 17 | unsigned long shndx; 18 | } elf_section_header_table_t; 19 | 20 | struct multiboot_info 21 | { 22 | unsigned long flags; 23 | unsigned long mem_lower; 24 | unsigned long mem_upper; 25 | unsigned long boot_device; 26 | unsigned long cmdline; 27 | unsigned long mods_addr; 28 | union 29 | { 30 | aout_symbol_table_t aout_sym_t; 31 | elf_section_header_table_t elf_sec_t; 32 | } u; 33 | unsigned long mmap_length; 34 | unsigned long mmap_addr; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /paging.c: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "stddef.h" 3 | #include "isr.h" 4 | #include "paging.h" 5 | #include "frame.h" 6 | #include "kernio.h" 7 | #include "kmalloc_early.h" 8 | #include "common.h" 9 | #include "kheap.h" 10 | 11 | struct page_directory *kernel_directory; 12 | struct page_directory *current_directory; 13 | extern uint32_t placement_address; 14 | 15 | uint8_t initialized = 0; 16 | 17 | void identity_map(uint32_t address, uint32_t length) { 18 | uint32_t curr = address & 0xFFFFF000; 19 | while(curr < address + length) { 20 | struct page *p = get_page(curr, 1, kernel_directory); 21 | p->present = 1; 22 | p->rw = 1; 23 | p->user = 0; 24 | p->frame = curr >> 12; 25 | curr += 0x1000; 26 | } 27 | } 28 | 29 | void initialize_paging(uint32_t total_frames, uint32_t ident_addr, uint32_t ident_len) { 30 | init_frame_allocator(total_frames); 31 | 32 | // Make a page directory for the kernel. 33 | kernel_directory = (struct page_directory *)e_kmalloc_a(sizeof (struct page_directory)); 34 | memset(kernel_directory, 0, sizeof (struct page_directory)); 35 | current_directory = kernel_directory; 36 | 37 | // Go ahead and allocate all the page tables for the kernel. 38 | // This is wasteful, but a lot easier than figuring out how to build 39 | // a kernel page allocator. 40 | printf("Allocating kernel page tables... "); 41 | uint32_t i = 0; 42 | for(i = 0; i < 0xFFFFFFFF;) { 43 | get_page(i, 1, kernel_directory); 44 | i += 0x1000 * 1024; 45 | if(i == 0) { 46 | break; 47 | } 48 | } 49 | printf("Done\n"); 50 | // We need to identity map (phys addr = virt addr) from 51 | // 0x0 to the end of used memory, so we can access this 52 | // transparently, as if paging wasn't enabled. 53 | // NOTE that we use a while loop here deliberately. 54 | // inside the loop body we actually change placement_address 55 | // by calling kmalloc(). A while loop causes this to be 56 | // computed on-the-fly rather than once at the start. 57 | 58 | // This is hacky. Probably want to do this some other way. 59 | // Reaching into kmalloc_early and grabbing placement_address 60 | // is not ideal. 61 | i = 0; 62 | while(i < placement_address) 63 | { 64 | // Kernel code is readable but not writeable from userspace. 65 | alloc_frame( get_page(i, 1, kernel_directory), 0, 0); 66 | i += 0x1000; 67 | } 68 | // Before we do anything else, disable the original kmalloc so we don't 69 | // start leaking into the address space. 70 | uint32_t heap_start = disable_early_kmalloc(); 71 | 72 | printf("KHEAP_START: %x\n", heap_start); 73 | 74 | // bootstrap the kheap with INITIAL_HEAP_PAGE_COUNT pages. 75 | for(i = 0; i < INITIAL_HEAP_PAGE_COUNT; i++) { 76 | alloc_frame(get_page(heap_start + (i * 0x1000), 1, kernel_directory), 0, 0); 77 | } 78 | 79 | // Before we enable paging, we must register our page fault handler. 80 | register_interrupt_handler(14, page_fault); 81 | 82 | if(ident_addr) { 83 | identity_map(ident_addr, ident_len); 84 | } 85 | 86 | // Now, enable paging! 87 | switch_page_directory(kernel_directory); 88 | initialized = 1; 89 | 90 | 91 | printf("Initializing kheap... "); 92 | // Set up the kernel heap! 93 | initialize_kheap(heap_start); 94 | printf("Done\n"); 95 | } 96 | 97 | void switch_page_directory(struct page_directory *dir) 98 | { 99 | current_directory = dir; 100 | asm volatile("mov %0, %%cr3":: "r"(&dir->tablesPhysical)); 101 | uint32_t cr0; 102 | asm volatile("mov %%cr0, %0": "=r"(cr0)); 103 | cr0 |= 0x80000000; // Enable paging! 104 | asm volatile("mov %0, %%cr0":: "r"(cr0)); 105 | } 106 | 107 | 108 | struct page *get_page(uint32_t address, int make, struct page_directory *dir) 109 | { 110 | // Turn the address into an index. 111 | address /= 0x1000; 112 | // Find the page table containing this address. 113 | uint32_t table_idx = address / 1024; 114 | if (dir->tables[table_idx]) // If this table is already assigned 115 | { 116 | return &dir->tables[table_idx]->pages[address%1024]; 117 | } 118 | else if(make) 119 | { 120 | uint32_t tmp; 121 | if(!initialized) 122 | { 123 | dir->tables[table_idx] = (struct page_table *)e_kmalloc_ap(sizeof(struct page_table), &tmp); 124 | } 125 | else 126 | { 127 | dir->tables[table_idx] = (struct page_table *)kmalloc_ap(sizeof(struct page_table), 1, &tmp); 128 | } 129 | memset(dir->tables[table_idx], 0, 0x1000); 130 | dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US. 131 | return &dir->tables[table_idx]->pages[address%1024]; 132 | } 133 | else 134 | { 135 | return NULL; 136 | } 137 | } 138 | 139 | struct page *get_kernel_page(uint32_t address, int make) 140 | { 141 | return get_page(address, make, kernel_directory); 142 | } 143 | 144 | struct page *map_kernel_page(uint32_t address, int make) 145 | { 146 | struct page *p = get_page(address, make, kernel_directory); 147 | if(!p) return NULL; 148 | alloc_frame(p, 0, 0); 149 | return p; 150 | } 151 | 152 | void unmap_kernel_page(uint32_t address) 153 | { 154 | struct page *p = get_page(address, 0, kernel_directory); 155 | if(p) { 156 | free_frame(p); 157 | } 158 | } 159 | 160 | void page_fault(registers_t regs) 161 | { 162 | // A page fault has occurred. 163 | // The faulting address is stored in the CR2 register. 164 | uint32_t faulting_address; 165 | asm volatile("mov %%cr2, %0" : "=r" (faulting_address)); 166 | 167 | // The error code gives us details of what happened. 168 | int present = !(regs.err_code & 0x1); // Page not present 169 | int rw = regs.err_code & 0x2; // Write operation? 170 | int us = regs.err_code & 0x4; // Processor was in user-mode? 171 | int reserved = regs.err_code & 0x8; // Overwritten CPU-reserved bits of page entry? 172 | //int id = regs.err_code & 0x10; // Caused by an instruction fetch? 173 | 174 | // Output an error message. 175 | printf("Page fault! ( "); 176 | if (present) {printf("present ");} 177 | if (rw) {printf("read-only ");} 178 | if (us) {printf("user-mode ");} 179 | if (reserved) {printf("reserved ");} 180 | printf(") at %x\n", faulting_address); 181 | PANIC("Page fault"); 182 | } 183 | -------------------------------------------------------------------------------- /paging.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGING_H 2 | #define PAGING_H 3 | 4 | // An actual page descriptor 5 | struct page { 6 | uint32_t present : 1; // Page present in memory 7 | uint32_t rw : 1; // Read-only if clear, readwrite if set 8 | uint32_t user : 1; // Supervisor level only if clear 9 | uint32_t accessed : 1; // Has the page been accessed since last refresh? 10 | uint32_t dirty : 1; // Has the page been written to since last refresh? 11 | uint32_t unused : 7; // Amalgamation of unused and reserved bits 12 | uint32_t frame : 20; // Frame address (shifted right 12 bits) 13 | }; 14 | 15 | // A table of pages. Each table has 1024 descriptors. 16 | struct page_table 17 | { 18 | struct page pages[1024]; 19 | }; 20 | 21 | // A directory of page tables. Each directory has 1024 tables. 22 | struct page_directory 23 | { 24 | // Array of pointers to pagetables. 25 | struct page_table *tables[1024]; 26 | 27 | // Array of pointers to the pagetables above, but gives their *physical* 28 | // location, for loading into the CR3 register. 29 | uint32_t tablesPhysical[1024]; 30 | 31 | // The physical address of tablesPhysical. This comes into play 32 | // when we get our kernel heap allocated and the directory 33 | // may be in a different location in virtual memory. 34 | uint32_t physicalAddr; 35 | }; 36 | 37 | /** 38 | * Sets up the environment, page directories etc and 39 | * enables paging. 40 | * ident_addr (if specified) causes the initializer 41 | * to identity-map all the pages containing 42 | * ident_addr through ident_addr + ident_len. 43 | * This is currently used to map the VESA LFB in. 44 | */ 45 | void initialize_paging(uint32_t total_frames, uint32_t ident_addr, uint32_t ident_len); 46 | 47 | /** 48 | * Causes the specified page directory to be loaded into the 49 | * CR3 register. 50 | */ 51 | void switch_page_directory(struct page_directory *new); 52 | 53 | /** 54 | * Retrieves a pointer to the page required. 55 | * If make == 1, if the page-table in which this page should 56 | * reside isn't created, create it! 57 | */ 58 | struct page *get_page(uint32_t address, int make, struct page_directory *dir); 59 | 60 | /** 61 | * Calls get_page with the kernel directory. 62 | */ 63 | struct page *get_kernel_page(uint32_t address, int make); 64 | 65 | /** 66 | * Maps a kernel page. Returns NULL if it was unable to map. 67 | */ 68 | struct page *map_kernel_page(uint32_t address, int make); 69 | 70 | /** 71 | * Unmaps a page from the kernel and returns its frame to the pool. 72 | */ 73 | void unmap_kernel_page(uint32_t address); 74 | 75 | /** 76 | * Handler for page faults. 77 | */ 78 | void page_fault(registers_t regs); 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /pic.c: -------------------------------------------------------------------------------- 1 | #include "pic.h" 2 | #include "port.h" 3 | #include "kernio.h" 4 | 5 | #define MASTER_COMMAND 0x20 6 | #define MASTER_DATA 0x21 7 | #define SLAVE_COMMAND 0xA0 8 | #define SLAVE_DATA 0xA1 9 | 10 | #define ICW1_ICW4 0x01 /* ICW4 (not) needed */ 11 | #define ICW1_SINGLE 0x02 /* Single (cascade) mode */ 12 | #define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ 13 | #define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ 14 | #define ICW1_INIT 0x10 /* Initialization - required! */ 15 | 16 | #define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ 17 | #define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 18 | #define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ 19 | #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ 20 | #define ICW4_SFNM 0x10 /* Special fully nested (not) */ 21 | 22 | unsigned int io_wait() 23 | { 24 | int j = 0; 25 | for(unsigned int i = 0; i < 0xFFFFFFFF; i++) 26 | { 27 | j = i; 28 | } 29 | return j; 30 | } 31 | 32 | void remap_pic(void) 33 | { 34 | printf("Remapping the IRQ table\n"); 35 | 36 | outb(MASTER_COMMAND, ICW1_INIT | ICW1_ICW4); // Start initialization sequence 37 | outb(SLAVE_COMMAND, ICW1_INIT | ICW1_ICW4); // Start initialization sequence 38 | outb(MASTER_DATA, 0x20); // Remap to 0x20 39 | outb(SLAVE_DATA, 0x28); // Remap slave to 0x28 40 | outb(MASTER_DATA, 4); // Tell Master about slave @ IRQ 2 41 | outb(SLAVE_DATA, 2); // Tell slave PIC its cascade ID 42 | 43 | outb(MASTER_DATA, ICW4_8086); 44 | outb(SLAVE_DATA, ICW4_8086); 45 | 46 | outb(MASTER_DATA, 0x0); 47 | outb(SLAVE_DATA, 0x0); 48 | } 49 | -------------------------------------------------------------------------------- /pic.h: -------------------------------------------------------------------------------- 1 | #ifndef PIC_H 2 | #define PIC_H 3 | 4 | #define MASTER_COMMAND 0x20 5 | #define MASTER_DATA 0x21 6 | #define SLAVE_COMMAND 0xA0 7 | #define SLAVE_DATA 0xA1 8 | 9 | #define PIC_RESET 0x20 10 | 11 | void remap_pic(void); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /pit.c: -------------------------------------------------------------------------------- 1 | #include "pit.h" 2 | #include "isr.h" 3 | #include "port.h" 4 | #include "kernio.h" 5 | #include "vesa.h" 6 | 7 | #define PIT_NATURAL_FREQ 1193180 8 | 9 | #define PIT_DATA0 0x40 10 | #define PIT_DATA1 0x41 11 | #define PIT_DATA2 0x42 12 | #define PIT_COMMAND 0x43 13 | 14 | #define DRAW_MODULO 10 15 | uint8_t draw_modcount = 0; 16 | 17 | uint8_t normal_color; 18 | 19 | long long tick = 0; 20 | 21 | extern uint32_t allocated_frames; 22 | extern uint32_t total_frames; 23 | extern uint32_t heap_free; 24 | extern uint32_t allocations; 25 | 26 | #define BUFFSIZE 77 27 | 28 | static void timer_callback(registers_t regs) 29 | { 30 | draw_modcount = (draw_modcount + 1) % DRAW_MODULO; 31 | 32 | if(draw_modcount == 0) 33 | { 34 | tick++; 35 | 36 | uint32_t used = (allocated_frames * 0x1000) / 1024 / 1024; 37 | uint32_t available = (total_frames * 0x1000) / 1024 / 1024; 38 | 39 | char buffer[BUFFSIZE]; 40 | sprintf(buffer, "Frames: %d Mem: %d/%d MiB Heap-Free: %d Allocs: %d", 41 | allocated_frames, 42 | used, available, 43 | heap_free, 44 | allocations); 45 | uint32_t fg = get_vesa_color(); 46 | uint32_t bg = get_vesa_background(); 47 | set_vesa_color(make_vesa_color(0, 0, 0)); 48 | set_vesa_background(make_vesa_color(0xFF, 0xFF, 0xFF)); 49 | set_status(buffer); 50 | set_vesa_color(fg); 51 | set_vesa_background(bg); 52 | } 53 | } 54 | 55 | void init_timer(uint32_t frequency) 56 | { 57 | printf("Initializing PIT timer\n"); 58 | register_interrupt_handler(IRQ0, &timer_callback); 59 | 60 | uint32_t divisor; 61 | if(frequency) 62 | divisor = PIT_NATURAL_FREQ / frequency; 63 | else 64 | divisor = 0; 65 | /* 66 | http://wiki.osdev.org/Programmable_Interval_Timer#I.2FO_Ports 67 | 68 | Channel Access Mode Operating Mode BCD/Binary 69 | 0x36 == 0011 0110 == 00 11 (lobyte/hibyte) 011 (square Wave) 0 (16-bit bin 70 | 71 | ) 72 | */ 73 | outb(PIT_COMMAND, 0x36); 74 | 75 | //Chop freq up into bytes and send to data0 port 76 | uint8_t low = (uint8_t)(divisor & 0xFF); 77 | uint8_t high = (uint8_t)((divisor >> 8) & 0xFF); 78 | 79 | outb(PIT_DATA0, low); 80 | outb(PIT_DATA0, high); 81 | } 82 | -------------------------------------------------------------------------------- /pit.h: -------------------------------------------------------------------------------- 1 | #ifndef PIT_H 2 | #define PIT_H 3 | 4 | #include "stdint.h" 5 | 6 | void init_timer(uint32_t frequency); //frequency in Hz 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /port.h: -------------------------------------------------------------------------------- 1 | #ifndef PORT_H 2 | #define PORT_H 3 | #include "stdint.h" 4 | 5 | extern void outb(uint16_t port, uint8_t value); 6 | extern uint8_t inb(uint16_t port); 7 | extern uint16_t inw(uint16_t port); 8 | extern void insw(uint16_t port, void *addr, unsigned int count); 9 | extern void outsw(uint16_t port, void *addr, unsigned int count); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /port.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | .global outb 3 | .global inb 4 | .global inw 5 | .global insw 6 | .global outsw 7 | 8 | .type outb, @function 9 | # void outb(uint16_t port, uint8_t value) 10 | outb: 11 | 12 | movzwl 4(%esp), %edx 13 | movzbl 8(%esp), %eax 14 | 15 | outb %al, %dx 16 | ret 17 | 18 | .size outb, . - outb 19 | 20 | .type inb, @function 21 | # uint8_t inb(uint16_t port) 22 | inb: 23 | xorl %eax, %eax 24 | movzwl 4(%esp), %edx 25 | inb %dx, %al 26 | ret 27 | 28 | .size inb, . - inb 29 | 30 | .type inw, @function 31 | # uint16_t inw(uint16_t port) 32 | inw: 33 | xorl %eax, %eax 34 | movzwl 4(%esp), %edx 35 | inw %dx, %ax 36 | ret 37 | .size inw, . - inw 38 | 39 | .type insw, @function 40 | # void insw(uint16_t port, void *addr, unsigned int count) 41 | insw: 42 | pushl %edi 43 | movl 8(%esp), %edx # port 44 | movl 12(%esp), %edi # addr 45 | movl 16(%esp), %ecx # count 46 | 47 | xorl %eax, %eax 48 | .insw_startLoop: 49 | cmpl %eax, %ecx 50 | je .insw_end 51 | 52 | insw 53 | 54 | incl %eax 55 | jmp .insw_startLoop 56 | 57 | .insw_end: 58 | popl %edi 59 | ret 60 | 61 | .type outsw, @function 62 | # void outsw(uint16_t port, void *addr, unsigned int count) 63 | outsw: 64 | pushl %esi 65 | movl 8(%esp), %edx # port 66 | movl 12(%esp), %esi # addr 67 | movl 16(%esp), %ecx # count 68 | 69 | xorl %eax, %eax 70 | .outsw_startLoop: 71 | cmpl %eax, %ecx 72 | je .outsw_end 73 | 74 | outsw 75 | 76 | incl %eax 77 | jmp .outsw_startLoop 78 | 79 | .outsw_end: 80 | popl %esi 81 | ret 82 | -------------------------------------------------------------------------------- /realmode.h: -------------------------------------------------------------------------------- 1 | #ifndef REALMODE_H 2 | #define REALMODE_H 3 | 4 | // define our structure 5 | typedef struct __attribute__ ((packed)) { 6 | unsigned short di, si, bp, sp, bx, dx, cx, ax; 7 | unsigned short gs, fs, es, ds, eflags; 8 | } regs16_t; 9 | 10 | // tell compiler our int32 function is external 11 | extern void int32(unsigned char intnum, regs16_t *regs); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /realmode.s: -------------------------------------------------------------------------------- 1 | ; 2 | ; Protected Mode BIOS Call Functionailty v2.0 - by Napalm 3 | ; ------------------------------------------------------- 4 | ; 5 | ; This is code shows how its POSSIBLE to execute BIOS interrupts 6 | ; by switch out to real-mode and then back into protected mode. 7 | ; 8 | ; If you wish to use all or part of this code you must agree 9 | ; to the license at the following URL. 10 | ; 11 | ; License: http://creativecommons.org/licenses/by-sa/2.0/uk/ 12 | ; 13 | ; Notes: This file is in NASM syntax. 14 | ; Turn off paging before calling these functions. 15 | ; int32() resets all selectors. 16 | ; 17 | ; C Prototype: 18 | ; void _cdelc int32(unsigned char intnum, regs16_t *regs); 19 | ; 20 | ; Example of usage: 21 | ; regs.ax = 0x0013; 22 | ; int32(0x10, ®s); 23 | ; memset((char *)0xA0000, 1, (320*200)); 24 | ; memset((char *)0xA0000 + (100*320+80), 14, 80); 25 | ; regs.ax = 0x0000; 26 | ; int32(0x16, ®s); 27 | ; regs.ax = 0x0003; 28 | ; int32(0x10, ®s); 29 | ; 30 | ; 31 | [bits 32] 32 | 33 | global int32, _int32 34 | 35 | struc regs16_t 36 | .di resw 1 37 | .si resw 1 38 | .bp resw 1 39 | .sp resw 1 40 | .bx resw 1 41 | .dx resw 1 42 | .cx resw 1 43 | .ax resw 1 44 | .gs resw 1 45 | .fs resw 1 46 | .es resw 1 47 | .ds resw 1 48 | .ef resw 1 49 | endstruc 50 | 51 | %define INT32_BASE 0x7C00 52 | %define REBASE(x) (((x) - reloc) + INT32_BASE) 53 | %define GDTENTRY(x) ((x) << 3) 54 | %define CODE32 GDTENTRY(1) ; 0x08 55 | %define DATA32 GDTENTRY(2) ; 0x10 56 | %define CODE16 GDTENTRY(3) ; 0x18 57 | %define DATA16 GDTENTRY(4) ; 0x20 58 | %define STACK16 (INT32_BASE - regs16_t_size) 59 | 60 | 61 | section .text 62 | int32: use32 ; by Napalm 63 | _int32: 64 | cli ; disable interrupts 65 | pusha ; save register state to 32bit stack 66 | mov esi, reloc ; set source to code below 67 | mov edi, INT32_BASE ; set destination to new base address 68 | mov ecx, (int32_end - reloc) ; set copy size to our codes size 69 | cld ; clear direction flag (so we copy forward) 70 | rep movsb ; do the actual copy (relocate code to low 16bit space) 71 | jmp INT32_BASE ; jump to new code location 72 | reloc: use32 ; by Napalm 73 | mov [REBASE(stack32_ptr)], esp ; save 32bit stack pointer 74 | sidt [REBASE(idt32_ptr)] ; save 32bit idt pointer 75 | sgdt [REBASE(gdt32_ptr)] ; save 32bit gdt pointer 76 | lgdt [REBASE(gdt16_ptr)] ; load 16bit gdt pointer 77 | lea esi, [esp+0x24] ; set position of intnum on 32bit stack 78 | lodsd ; read intnum into eax 79 | mov [REBASE(ib)], al ; set intrrupt immediate byte from our arguments 80 | mov esi, [esi] ; read regs pointer in esi as source 81 | mov edi, STACK16 ; set destination to 16bit stack 82 | mov ecx, regs16_t_size ; set copy size to our struct size 83 | mov esp, edi ; save destination to as 16bit stack offset 84 | rep movsb ; do the actual copy (32bit stack to 16bit stack) 85 | jmp word CODE16:REBASE(p_mode16) ; switch to 16bit selector (16bit protected mode) 86 | p_mode16: use16 87 | mov ax, DATA16 ; get our 16bit data selector 88 | mov ds, ax ; set ds to 16bit selector 89 | mov es, ax ; set es to 16bit selector 90 | mov fs, ax ; set fs to 16bit selector 91 | mov gs, ax ; set gs to 16bit selector 92 | mov ss, ax ; set ss to 16bit selector 93 | mov eax, cr0 ; get cr0 so we can modify it 94 | and al, ~0x01 ; mask off PE bit to turn off protected mode 95 | mov cr0, eax ; set cr0 to result 96 | jmp word 0x0000:REBASE(r_mode16) ; finally set cs:ip to enter real-mode 97 | r_mode16: use16 98 | xor ax, ax ; set ax to zero 99 | mov ds, ax ; set ds so we can access idt16 100 | mov ss, ax ; set ss so they the stack is valid 101 | lidt [REBASE(idt16_ptr)] ; load 16bit idt 102 | mov bx, 0x0870 ; master 8 and slave 112 103 | call resetpic ; set pic's the to real-mode settings 104 | popa ; load general purpose registers from 16bit stack 105 | pop gs ; load gs from 16bit stack 106 | pop fs ; load fs from 16bit stack 107 | pop es ; load es from 16bit stack 108 | pop ds ; load ds from 16bit stack 109 | sti ; enable interrupts 110 | db 0xCD ; opcode of INT instruction with immediate byte 111 | ib: db 0x00 112 | cli ; disable interrupts 113 | xor sp, sp ; zero sp so we can reuse it 114 | mov ss, sp ; set ss so the stack is valid 115 | mov sp, INT32_BASE ; set correct stack position so we can copy back 116 | pushf ; save eflags to 16bit stack 117 | push ds ; save ds to 16bit stack 118 | push es ; save es to 16bit stack 119 | push fs ; save fs to 16bit stack 120 | push gs ; save gs to 16bit stack 121 | pusha ; save general purpose registers to 16bit stack 122 | mov bx, 0x2028 ; master 32 and slave 40 123 | call resetpic ; restore the pic's to protected mode settings 124 | mov eax, cr0 ; get cr0 so we can modify it 125 | inc eax ; set PE bit to turn on protected mode 126 | mov cr0, eax ; set cr0 to result 127 | jmp dword CODE32:REBASE(p_mode32) ; switch to 32bit selector (32bit protected mode) 128 | p_mode32: use32 129 | mov ax, DATA32 ; get our 32bit data selector 130 | mov ds, ax ; reset ds selector 131 | mov es, ax ; reset es selector 132 | mov fs, ax ; reset fs selector 133 | mov gs, ax ; reset gs selector 134 | mov ss, ax ; reset ss selector 135 | lgdt [REBASE(gdt32_ptr)] ; restore 32bit gdt pointer 136 | lidt [REBASE(idt32_ptr)] ; restore 32bit idt pointer 137 | mov esp, [REBASE(stack32_ptr)] ; restore 32bit stack pointer 138 | mov esi, STACK16 ; set copy source to 16bit stack 139 | lea edi, [esp+0x28] ; set position of regs pointer on 32bit stack 140 | mov edi, [edi] ; use regs pointer in edi as copy destination 141 | mov ecx, regs16_t_size ; set copy size to our struct size 142 | cld ; clear direction flag (so we copy forward) 143 | rep movsb ; do the actual copy (16bit stack to 32bit stack) 144 | popa ; restore registers 145 | sti ; enable interrupts 146 | ret ; return to caller 147 | 148 | resetpic: ; reset's 8259 master and slave pic vectors 149 | push ax ; expects bh = master vector, bl = slave vector 150 | mov al, 0x11 ; 0x11 = ICW1_INIT | ICW1_ICW4 151 | out 0x20, al ; send ICW1 to master pic 152 | out 0xA0, al ; send ICW1 to slave pic 153 | mov al, bh ; get master pic vector param 154 | out 0x21, al ; send ICW2 aka vector to master pic 155 | mov al, bl ; get slave pic vector param 156 | out 0xA1, al ; send ICW2 aka vector to slave pic 157 | mov al, 0x04 ; 0x04 = set slave to IRQ2 158 | out 0x21, al ; send ICW3 to master pic 159 | shr al, 1 ; 0x02 = tell slave its on IRQ2 of master 160 | out 0xA1, al ; send ICW3 to slave pic 161 | shr al, 1 ; 0x01 = ICW4_8086 162 | out 0x21, al ; send ICW4 to master pic 163 | out 0xA1, al ; send ICW4 to slave pic 164 | pop ax ; restore ax from stack 165 | ret ; return to caller 166 | 167 | stack32_ptr: ; address in 32bit stack after we 168 | dd 0x00000000 ; save all general purpose registers 169 | 170 | idt32_ptr: ; IDT table pointer for 32bit access 171 | dw 0x0000 ; table limit (size) 172 | dd 0x00000000 ; table base address 173 | 174 | gdt32_ptr: ; GDT table pointer for 32bit access 175 | dw 0x0000 ; table limit (size) 176 | dd 0x00000000 ; table base address 177 | 178 | idt16_ptr: ; IDT table pointer for 16bit access 179 | dw 0x03FF ; table limit (size) 180 | dd 0x00000000 ; table base address 181 | 182 | gdt16_base: ; GDT descriptor table 183 | .null: ; 0x00 - null segment descriptor 184 | dd 0x00000000 ; must be left zero'd 185 | dd 0x00000000 ; must be left zero'd 186 | 187 | .code32: ; 0x01 - 32bit code segment descriptor 0xFFFFFFFF 188 | dw 0xFFFF ; limit 0:15 189 | dw 0x0000 ; base 0:15 190 | db 0x00 ; base 16:23 191 | db 0x9A ; present, iopl/0, code, execute/read 192 | db 0xCF ; 4Kbyte granularity, 32bit selector; limit 16:19 193 | db 0x00 ; base 24:31 194 | 195 | .data32: ; 0x02 - 32bit data segment descriptor 0xFFFFFFFF 196 | dw 0xFFFF ; limit 0:15 197 | dw 0x0000 ; base 0:15 198 | db 0x00 ; base 16:23 199 | db 0x92 ; present, iopl/0, data, read/write 200 | db 0xCF ; 4Kbyte granularity, 32bit selector; limit 16:19 201 | db 0x00 ; base 24:31 202 | 203 | .code16: ; 0x03 - 16bit code segment descriptor 0x000FFFFF 204 | dw 0xFFFF ; limit 0:15 205 | dw 0x0000 ; base 0:15 206 | db 0x00 ; base 16:23 207 | db 0x9A ; present, iopl/0, code, execute/read 208 | db 0x0F ; 1Byte granularity, 16bit selector; limit 16:19 209 | db 0x00 ; base 24:31 210 | 211 | .data16: ; 0x04 - 16bit data segment descriptor 0x000FFFFF 212 | dw 0xFFFF ; limit 0:15 213 | dw 0x0000 ; base 0:15 214 | db 0x00 ; base 16:23 215 | db 0x92 ; present, iopl/0, data, read/write 216 | db 0x0F ; 1Byte granularity, 16bit selector; limit 16:19 217 | db 0x00 ; base 24:31 218 | 219 | gdt16_ptr: ; GDT table pointer for 16bit access 220 | dw gdt16_ptr - gdt16_base - 1 ; table limit (size) 221 | dd gdt16_base ; table base address 222 | 223 | int32_end: ; end marker (so we can copy the code) 224 | -------------------------------------------------------------------------------- /stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef STDDEF_H 2 | #define STDDEF_H 3 | 4 | typedef long size_t; 5 | 6 | #define NULL ((void *)0) 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef STDINT_H 2 | #define STDINT_H 3 | 4 | typedef unsigned char uint8_t; 5 | typedef unsigned short int uint16_t; 6 | typedef unsigned int uint32_t; 7 | typedef unsigned long long int uint64_t; 8 | 9 | typedef signed char int8_t; 10 | typedef signed short int int16_t; 11 | typedef signed int int32_t; 12 | typedef signed long long int int64_t; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /stdio.c: -------------------------------------------------------------------------------- 1 | #include "stddef.h" 2 | #include "stdio.h" 3 | #include "fat32.h" 4 | #include "common.h" 5 | 6 | typedef struct FILE { 7 | //struct dir_entry file_ent; 8 | uint32_t curr_cluster; 9 | uint32_t file_size; // total file size 10 | uint32_t fptr; // index into the file 11 | uint32_t buffptr; // index into currbuf 12 | uint8_t currbuf[]; // flexible member for current cluster 13 | } FILE; 14 | 15 | //static inline void dump_dir(struct directory *dir) { 16 | // for(int entryi = 0; entryi < dir->num_entries; entryi++) { 17 | // printf("\t%s\n", dir->entries[entryi]); 18 | // } 19 | //} 20 | 21 | static inline int entry_for_path(const char *path, struct dir_entry *entry) { 22 | struct directory dir; 23 | populate_root_dir(master_fs, &dir); 24 | int found_file = 0; 25 | if(path[0] != '/') { 26 | return found_file; 27 | } 28 | 29 | char *cutpath = strdup(path); 30 | char *tokstate = NULL; 31 | char *next_dir = strtok_r(cutpath, "/", &tokstate); 32 | struct dir_entry *currentry = NULL; 33 | entry->name = NULL; 34 | while (next_dir) { 35 | int found = 0; 36 | for(int entryi = 0; entryi < dir.num_entries; entryi++) { 37 | currentry = &dir.entries[entryi]; 38 | if(strcmp(currentry->name, next_dir) == 0) { 39 | if(entry->name) kfree(entry->name); 40 | *entry = *currentry; 41 | // TODO: Make sure this doesn't leak. Very dangerous: 42 | entry->name = strdup(currentry->name); 43 | 44 | uint32_t cluster = currentry->first_cluster; 45 | free_directory(master_fs, &dir); 46 | populate_dir(master_fs, &dir, cluster); 47 | found = 1; 48 | break; 49 | 50 | } 51 | } 52 | if(!found) { 53 | kfree(cutpath); 54 | free_directory(master_fs, &dir); 55 | return 0; 56 | } 57 | next_dir = strtok_r(NULL, "/", &tokstate); 58 | } 59 | free_directory(master_fs, &dir); 60 | kfree(cutpath); 61 | return 1; 62 | } 63 | 64 | FILE *fopen(const char *pathname, const char *mode) { 65 | struct dir_entry entry; 66 | if(!entry_for_path(pathname, &entry)) { 67 | kfree(entry.name); 68 | return NULL; 69 | } 70 | // printf("Got entry: %s [%d]\n", entry.name, entry.first_cluster); 71 | kfree(entry.name); 72 | 73 | FILE *f = kmalloc(sizeof (FILE) + master_fs->cluster_size); 74 | f->curr_cluster = entry.first_cluster; 75 | f->file_size = entry.file_size; 76 | f->fptr = 0; 77 | f->buffptr = 0; 78 | getCluster(master_fs, f->currbuf, f->curr_cluster); 79 | return f; 80 | } 81 | 82 | FILE *fdopen(int fd, const char *mode); 83 | FILE *freopen(const char *pathname, const char *mode, FILE *stream); 84 | 85 | int fclose(FILE *stream) { 86 | kfree(stream); 87 | } 88 | 89 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { 90 | size_t bytes_to_read = size * nmemb; 91 | size_t bytes_read = 0; 92 | 93 | if(stream->fptr + bytes_to_read > stream->file_size) { 94 | bytes_to_read = stream->file_size - stream->fptr; 95 | } 96 | //printf("Reading %d bytes.\n", bytes_to_read); 97 | while(bytes_to_read > 0) { 98 | if(stream->buffptr + bytes_to_read > master_fs->cluster_size) { 99 | // Need to read at least 1 more cluster 100 | size_t to_read_in_this_cluster = master_fs->cluster_size - stream->buffptr; 101 | memcpy(ptr + bytes_read, stream->currbuf + stream->buffptr, to_read_in_this_cluster); 102 | bytes_read += to_read_in_this_cluster; 103 | //printf("buffptr = 0\n"); 104 | stream->buffptr = 0; 105 | //printf("Getting next cluster.\n"); 106 | stream->curr_cluster = get_next_cluster_id(master_fs, stream->curr_cluster); 107 | //printf("Next cluster: %x\n", stream->curr_cluster); 108 | if(stream->curr_cluster >= EOC) { 109 | //printf("Returning.\n"); 110 | stream->fptr += bytes_read; 111 | return bytes_read; 112 | } 113 | //printf("getting next cluster.\n"); 114 | getCluster(master_fs, stream->currbuf, stream->curr_cluster); 115 | bytes_to_read -= to_read_in_this_cluster; 116 | } 117 | else { 118 | //printf("buffptr: %d\n", stream->buffptr); 119 | memcpy(ptr + bytes_read, stream->currbuf + stream->buffptr, bytes_to_read); 120 | bytes_read += bytes_to_read; 121 | stream->buffptr += bytes_to_read; 122 | bytes_to_read = 0; 123 | } 124 | } 125 | //printf("Returning.\n"); 126 | stream->fptr += bytes_read; 127 | return bytes_read; 128 | } 129 | 130 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 131 | -------------------------------------------------------------------------------- /stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef STDIO_H 2 | #define STDIO_H 3 | 4 | #include "stdint.h" 5 | #include "stddef.h" 6 | 7 | typedef struct FILE FILE; 8 | 9 | FILE *fopen(const char *pathname, const char *mode); 10 | FILE *fdopen(int fd, const char *mode); 11 | FILE *freopen(const char *pathname, const char *mode, FILE *stream); 12 | 13 | int fclose(FILE *stream); 14 | 15 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 16 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /terminal.c: -------------------------------------------------------------------------------- 1 | #include "stddef.h" 2 | #include "stdint.h" 3 | #include "terminal.h" 4 | #include "port.h" 5 | #include "common.h" 6 | 7 | #define VGA_COMMAND_PORT 0x3D4 8 | #define VGA_DATA_PORT 0x3D5 9 | #define VGA_CURSOR_HIGH 14 10 | #define VGA_CURSOR_LOW 15 11 | 12 | static size_t terminal_row; 13 | static size_t terminal_column; 14 | static uint8_t terminal_color; 15 | static uint8_t text_color; 16 | static uint16_t* terminal_buffer; 17 | static uint8_t status_color; 18 | 19 | void (*terminal_putchar)(char c); 20 | void (*terminal_set_status)(char *status); 21 | void (*terminal_set_cursor)(uint8_t x, uint8_t y); 22 | 23 | static uint16_t make_vgaentry(char c, uint8_t color); 24 | static void terminal_putentryat(char c, uint8_t color, size_t x, size_t y); 25 | static void terminal_advance(); 26 | static void terminal_newline(); 27 | 28 | uint8_t make_color(enum vga_color fg, enum vga_color bg) 29 | { 30 | return fg | bg << 4; 31 | } 32 | 33 | static uint16_t make_vgaentry(char c, uint8_t color) 34 | { 35 | uint16_t c16 = c; 36 | uint16_t color16 = color; 37 | return c16 | color16 << 8; 38 | } 39 | 40 | static void move_cursor(uint8_t xpos, uint8_t ypos) 41 | { 42 | uint16_t location = ypos * VGA_WIDTH + xpos; 43 | 44 | outb(VGA_COMMAND_PORT, VGA_CURSOR_HIGH); // We're setting the high cursor byte 45 | outb(VGA_DATA_PORT, location >> 8); 46 | outb(VGA_COMMAND_PORT, VGA_CURSOR_LOW); // We're setting the low cursor byte 47 | outb(VGA_DATA_PORT, location); 48 | } 49 | 50 | void basic_terminal_putchar(char c); 51 | void basic_terminal_set_status(char *status); 52 | void basic_terminal_set_cursor(uint8_t x, uint8_t y); 53 | 54 | void terminal_initialize(uint8_t color) 55 | { 56 | terminal_row = 1; 57 | terminal_column = 0; 58 | terminal_color = color; 59 | text_color = color; 60 | status_color = color; 61 | terminal_buffer = (uint16_t*) 0xB8000; 62 | move_cursor(0,0); 63 | terminal_putchar = basic_terminal_putchar; 64 | terminal_set_status = basic_terminal_set_status; 65 | for ( size_t y = 0; y < VGA_HEIGHT; y++ ) 66 | { 67 | for ( size_t x = 0; x < VGA_WIDTH; x++ ) 68 | { 69 | const size_t index = y * VGA_WIDTH + x; 70 | terminal_buffer[index] = make_vgaentry(' ', terminal_color); 71 | } 72 | } 73 | } 74 | 75 | void terminal_setcolor(uint8_t color) 76 | { 77 | terminal_color = color; 78 | text_color = color; 79 | for ( size_t y = 0; y < VGA_HEIGHT; y++ ) 80 | { 81 | for ( size_t x = 0; x < VGA_WIDTH; x++ ) 82 | { 83 | const size_t index = y * VGA_WIDTH + x; 84 | char c = (char)(terminal_buffer[index] & 0xFF); 85 | terminal_buffer[index] = make_vgaentry(c, terminal_color); 86 | } 87 | } 88 | } 89 | 90 | void terminal_settextcolor(uint8_t color) 91 | { 92 | text_color = color; 93 | } 94 | 95 | static void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) 96 | { 97 | const size_t index = y * VGA_WIDTH + x; 98 | terminal_buffer[index] = make_vgaentry(c, color); 99 | } 100 | 101 | static void terminal_scroll() 102 | { 103 | uint16_t blank = make_vgaentry(' ', terminal_color); 104 | //Move the lines up 105 | for(unsigned int i = VGA_WIDTH; i < VGA_WIDTH * VGA_HEIGHT; i++) 106 | { 107 | terminal_buffer[i] = terminal_buffer[i+VGA_WIDTH]; 108 | } 109 | 110 | //Clear the last line 111 | for(unsigned int i = (VGA_HEIGHT - 1) * VGA_WIDTH; i < VGA_HEIGHT * VGA_WIDTH; i++) 112 | { 113 | terminal_buffer[i] = blank; 114 | terminal_column = 0; 115 | } 116 | } 117 | 118 | static void terminal_advance() 119 | { 120 | if ( ++terminal_column == VGA_WIDTH) 121 | { 122 | terminal_newline(); 123 | } 124 | else 125 | { 126 | move_cursor(terminal_column, terminal_row); 127 | } 128 | } 129 | 130 | static void terminal_newline() 131 | { 132 | terminal_column = 0; 133 | if (++terminal_row == VGA_HEIGHT) 134 | { 135 | terminal_row--; 136 | terminal_scroll(); 137 | } 138 | move_cursor(terminal_column, terminal_row); 139 | } 140 | 141 | void basic_terminal_putchar(char c) 142 | { 143 | switch (c) 144 | { 145 | case '\n': 146 | terminal_newline(); 147 | break; 148 | case '\t': 149 | terminal_writestring(" "); 150 | break; 151 | case BS: 152 | if(terminal_column > 0) { 153 | terminal_column--; 154 | terminal_putchar(' '); 155 | terminal_column--; 156 | } 157 | break; 158 | default: 159 | terminal_putentryat(c, text_color, terminal_column, terminal_row); 160 | terminal_advance(); 161 | break; 162 | } 163 | } 164 | 165 | void terminal_writestring(const char* data) 166 | { 167 | size_t datalen = strlen(data); 168 | for ( size_t i = 0; i < datalen; i++ ) 169 | { 170 | terminal_putchar(data[i]); 171 | } 172 | } 173 | 174 | void terminal_write_dec(uint32_t d) 175 | { 176 | char buff[13]; 177 | char *x = itos(d, buff, 13); 178 | terminal_writestring(x); 179 | } 180 | 181 | void terminal_write_hex(uint32_t d) 182 | { 183 | terminal_writestring("0x"); 184 | for(int i = 28; i >= 0; i-=4) 185 | { 186 | terminal_putchar(hex_char(d>>i)); 187 | } 188 | 189 | } 190 | 191 | void terminal_set_status_color(uint8_t color) 192 | { 193 | status_color = color; 194 | } 195 | 196 | void basic_terminal_set_status(char *status) 197 | { 198 | uint16_t blank = make_vgaentry(' ', status_color); 199 | //Clear the first line 200 | for(unsigned int i = 0; i < VGA_WIDTH; i++) 201 | { 202 | terminal_buffer[i] = blank; 203 | } 204 | 205 | size_t backup_row = terminal_row; 206 | size_t backup_column = terminal_column; 207 | 208 | terminal_row = 0; 209 | terminal_column = 0; 210 | 211 | uint8_t temp_color = text_color; 212 | text_color = status_color; 213 | terminal_writestring(status); 214 | text_color = temp_color; 215 | 216 | terminal_row = backup_row; 217 | terminal_column = backup_column; 218 | } 219 | 220 | void basic_terminal_set_cursor(uint8_t x, uint8_t y) 221 | { 222 | if(x >= VGA_WIDTH) { 223 | x = terminal_column; 224 | } 225 | if(y >= VGA_HEIGHT) { 226 | y = terminal_row; 227 | } 228 | 229 | terminal_column = x; 230 | terminal_row = y; 231 | } 232 | -------------------------------------------------------------------------------- /terminal.h: -------------------------------------------------------------------------------- 1 | #ifndef TERMINAL_H 2 | #define TERMINAL_H 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | 7 | #define ESC (0x1B) 8 | #define BS (0x08) 9 | #define EOT (0x04) 10 | 11 | /* Hardware text mode color constants. */ 12 | enum vga_color 13 | { 14 | COLOR_BLACK = 0, 15 | COLOR_BLUE = 1, 16 | COLOR_GREEN = 2, 17 | COLOR_CYAN = 3, 18 | COLOR_RED = 4, 19 | COLOR_MAGENTA = 5, 20 | COLOR_BROWN = 6, 21 | COLOR_LIGHT_GREY = 7, 22 | COLOR_DARK_GREY = 8, 23 | COLOR_LIGHT_BLUE = 9, 24 | COLOR_LIGHT_GREEN = 10, 25 | COLOR_LIGHT_CYAN = 11, 26 | COLOR_LIGHT_RED = 12, 27 | COLOR_LIGHT_MAGENTA = 13, 28 | COLOR_LIGHT_BROWN = 14, 29 | COLOR_WHITE = 15, 30 | }; 31 | 32 | uint8_t make_color(enum vga_color fg, enum vga_color bg); 33 | size_t strlen(const char* str); 34 | char * itos(uint32_t myint, char buffer[], int bufflen); 35 | 36 | static const size_t VGA_WIDTH = 80; 37 | static const size_t VGA_HEIGHT = 25; 38 | 39 | void terminal_initialize(uint8_t color); 40 | //void terminal_setcolor(uint8_t color); 41 | //void terminal_settextcolor(uint8_t color); 42 | 43 | extern void (*terminal_putchar)(char c); 44 | extern void (*terminal_set_status)(char *status); 45 | extern void (*terminal_set_cursor)(uint8_t x, uint8_t y); 46 | //void terminal_putchar(char c); 47 | void terminal_writestring(const char* data); 48 | void terminal_write_dec(uint32_t d); 49 | void terminal_write_hex(uint32_t d); 50 | 51 | //void terminal_set_status_color(uint8_t color); 52 | //void (*terminal_set_status)(char *status); 53 | 54 | //void terminal_set_cursor(uint8_t x, uint8_t y); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /vesa.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "realmode.h" 3 | #include "font.h" 4 | #include "terminal.h" 5 | 6 | 7 | void vesa_putchar(char c); 8 | void vesa_set_status(char *status); 9 | void vesa_set_cursor(uint8_t x, uint8_t y); 10 | uint32_t make_vesa_color(uint8_t r, uint8_t g, uint8_t b); 11 | 12 | struct VbeInfoBlock { 13 | char VbeSignature[4]; // == "VESA" 14 | uint16_t VbeVersion; // == 0x0300 for VBE 3.0 15 | uint16_t OemStringPtr[2]; // isa vbeFarPtr 16 | uint8_t Capabilities[4]; 17 | uint16_t VideoModePtr[2]; // isa vbeFarPtr 18 | uint16_t TotalMemory; // as # of 64KB blocks 19 | } __attribute__((packed)); 20 | 21 | struct ModeInfoBlock { 22 | uint16_t attributes; 23 | uint8_t winA,winB; 24 | uint16_t granularity; 25 | uint16_t winsize; 26 | uint16_t segmentA, segmentB; 27 | uint16_t realFctPtr[2]; 28 | uint16_t pitch; // bytes per scanline 29 | 30 | uint16_t Xres, Yres; 31 | uint8_t Wchar, Ychar, planes, bpp, banks; 32 | uint8_t memory_model, bank_size, image_pages; 33 | uint8_t reserved0; 34 | 35 | uint8_t red_mask, red_position; 36 | uint8_t green_mask, green_position; 37 | uint8_t blue_mask, blue_position; 38 | uint8_t rsv_mask, rsv_position; 39 | uint8_t directcolor_attributes; 40 | 41 | uint32_t physbase; // your LFB (Linear Framebuffer) address ;) 42 | uint32_t reserved1; 43 | uint16_t reserved2; 44 | } __attribute__((packed)); 45 | 46 | #define LNG_PTR(seg, off) ((seg << 4) | off) 47 | #define REAL_PTR(arr) LNG_PTR(arr[1], arr[0]) 48 | #define SEG(addr) (((uint32_t)addr >> 4) & 0xF000) 49 | #define OFF(addr) ((uint32_t)addr & 0xFFFF) 50 | 51 | struct VbeInfoBlock vib; 52 | // VIB extends beyond size of struct. 512 is somewhat arbitrary. 53 | struct ModeInfoBlock mib; // = 0x80000 + sizeof (struct VbeInfoBlock) + 512; 54 | uint32_t *framebuffer = 0; 55 | 56 | #define CHAR_ROW 16 57 | #define CHAR_COLUMN 8 58 | #define CHARLEN (CHAR_ROW * CHAR_COLUMN) // 8 columns * 16 rows 59 | #define CHARCOUNT 94 // 94 printable characters 60 | 61 | #define CHAROFF(c) ((c - 32) * CHARLEN) 62 | 63 | uint32_t chars[CHARCOUNT * CHARLEN]; 64 | 65 | void populate_chars(uint32_t vesa_fg_color, uint32_t vesa_bg_color) { 66 | for(unsigned char c = ' '; c < '~'; c++) { 67 | unsigned short offset = (c - 31) * 16 ; 68 | 69 | for(int row = 0; row < CHAR_ROW; row++) { 70 | uint8_t mask = 1 << 7; 71 | uint32_t *abs_row = chars + CHAROFF(c) + (row * 8); 72 | for(int i = 0; i < 8; i++) { 73 | if(font.Bitmap[offset + row] & mask) { 74 | abs_row[i] = vesa_fg_color; //0xFFFFFFFF; 75 | } 76 | else { 77 | abs_row[i] = vesa_bg_color; 78 | } 79 | mask = (mask >> 1); 80 | } 81 | } 82 | } 83 | } 84 | 85 | void populate_vib() { 86 | struct VbeInfoBlock *loc_vib = 0x80000; // Safe low-memory 87 | 88 | regs16_t regs; 89 | regs.ax=0x4f00; 90 | regs.es=0x8000; 91 | regs.di=0x0; 92 | int32(0x10, ®s); 93 | vib = *loc_vib; 94 | } 95 | 96 | void set_vmode() { 97 | populate_vib(); 98 | regs16_t regs; 99 | 100 | struct ModeInfoBlock *loc_mib = 0x80000 + sizeof (struct VbeInfoBlock) + 512; 101 | 102 | uint16_t *modes = (uint16_t *)REAL_PTR(vib.VideoModePtr); 103 | 104 | int i; 105 | for(i = 0; modes[i] != 0xFFFF; i++) { 106 | regs.ax = 0x4f01; 107 | regs.cx = modes[i]; 108 | regs.es = SEG(loc_mib); 109 | regs.di = OFF(loc_mib); 110 | int32(0x10, ®s); 111 | 112 | // Currently, we only want 1280x720x32 113 | //1280 720 32 114 | if(loc_mib->Xres == 1280 && loc_mib->Yres == 720 && loc_mib->bpp == 32) { 115 | break; 116 | } 117 | } 118 | framebuffer = (uint32_t *)loc_mib->physbase; 119 | // printf("red_mask: %d\n", mib->red_mask); 120 | // printf("red_position: %d\n", mib->red_position); 121 | // printf("green_mask: %d\n", mib->green_mask); 122 | // printf("green_position: %d\n", mib->green_position); 123 | // printf("blue_mask: %d\n", mib->blue_mask); 124 | // printf("blue_position: %d\n", mib->blue_position); 125 | // printf("Bytes per scanline: %d (expecting %d)\n", mib->pitch, 1280 * 4); 126 | 127 | uint16_t mode = modes[i]; 128 | regs.ax = 0x4F02; 129 | regs.bx = mode | 0x4000; // Set Linear Frame Buffer (LFB) bit. 130 | int32(0x10, ®s); 131 | 132 | terminal_putchar = vesa_putchar; 133 | terminal_set_status = vesa_set_status; 134 | terminal_set_cursor = vesa_set_cursor; 135 | mib = *loc_mib; 136 | set_vesa_color(make_vesa_color(255, 255, 255)); 137 | set_vesa_background(make_vesa_color(0, 0, 0)); 138 | printf("\nVESA initialized.\nSet resoution to: 1280x720x32\n"); 139 | } 140 | 141 | uint32_t make_vesa_color(uint8_t r, uint8_t g, uint8_t b) { 142 | uint32_t red = ((uint32_t)r) << mib.red_position; 143 | uint32_t green = ((uint32_t)g) << mib.green_position; 144 | uint32_t blue = ((uint32_t)b) << mib.blue_position; 145 | return red | green | blue; 146 | } 147 | 148 | uint32_t current_fg, current_bg; 149 | 150 | void set_vesa_color(uint32_t color) { 151 | current_fg = color; 152 | populate_chars(current_fg, current_bg); 153 | } 154 | 155 | void set_vesa_background(uint32_t color) { 156 | current_bg = color; 157 | populate_chars(current_fg, current_bg); 158 | } 159 | 160 | uint32_t get_vesa_color() { 161 | return current_fg; 162 | } 163 | 164 | uint32_t get_vesa_background() { 165 | return current_bg; 166 | } 167 | 168 | void draw_pixel_at(int x, int y, uint32_t color) { 169 | uint32_t *row = ((unsigned char *)framebuffer) + (y * mib.pitch); 170 | row[x] = color; 171 | } 172 | 173 | static inline void draw_character_at(int x, int y, int c, uint32_t fg, uint32_t bg) { 174 | uint32_t step = mib.pitch/4; 175 | uint32_t *chardat = chars + CHAROFF(c); 176 | uint32_t *abs_row = ((unsigned char *)framebuffer) + (y * mib.pitch); 177 | abs_row += x; 178 | for(int row = 0; row < CHAR_ROW *8; row+=8) { 179 | fastcp(abs_row, chardat + row, 32); 180 | abs_row += step; 181 | } 182 | } 183 | 184 | void shift_up() { 185 | fastcp(((char *)framebuffer) + ((1280 * 16) * 4), ((char *)framebuffer) + ((1280 * 16) * 8), (1280 * 704) * 4 - ((1280 * 16) * 4)); 186 | memset(((char *)framebuffer) + (1280 * 704 * 4), 0, 1280 * 16 * 4); 187 | } 188 | 189 | int currx, curry; 190 | 191 | void vesa_newline() { 192 | currx = 0; 193 | if(curry >= 704) { 194 | curry = 704; 195 | shift_up(); 196 | } 197 | else { 198 | curry += 16; 199 | } 200 | } 201 | 202 | void vesa_set_cursor(uint8_t x, uint8_t y) { 203 | currx = x * 8; 204 | curry = y * 16; 205 | } 206 | 207 | void vesa_putchar(char c) { 208 | if(currx + 8 >= mib.Xres) { 209 | vesa_newline(); 210 | } 211 | switch (c) { 212 | case '\n': 213 | vesa_newline(); 214 | break; 215 | case '\t': 216 | vesa_putchar(' '); 217 | vesa_putchar(' '); 218 | vesa_putchar(' '); 219 | vesa_putchar(' '); 220 | break; 221 | case BS: 222 | if(currx > 0) { 223 | currx -= 8; 224 | vesa_putchar(' '); 225 | currx -= 8; 226 | } 227 | break; 228 | default: 229 | draw_character_at(currx, curry, c, 0xFFFFFFFF, 0); 230 | currx += 8; 231 | break; 232 | } 233 | 234 | } 235 | 236 | uint32_t get_framebuffer_addr() { 237 | return framebuffer; 238 | } 239 | 240 | uint32_t get_framebuffer_length() { 241 | return 1280 * 720 * 4; 242 | } 243 | 244 | void vesa_set_status(char *status) { 245 | int currx = 0; 246 | while(*status) { 247 | draw_character_at(currx, 0, *status++, 0xFFFFFFFF, 0); 248 | currx += 8; 249 | } 250 | while(currx + 8 < mib.Xres) { 251 | draw_character_at(currx, 0, ' ', 0xFFFFFFFF, 0); 252 | currx += 8; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /vesa.h: -------------------------------------------------------------------------------- 1 | #ifndef VESA_H 2 | #define VESA_H 3 | 4 | void set_vmode(); 5 | 6 | uint32_t make_vesa_color(uint8_t r, uint8_t g, uint8_t b); 7 | 8 | void draw_pixel_at(int x, int y, uint32_t color); 9 | 10 | void draw_character_at(int x, int y, int c, uint32_t fg, uint32_t bg); 11 | 12 | uint32_t get_framebuffer_addr(); 13 | uint32_t get_framebuffer_length(); 14 | 15 | void set_vesa_color(uint32_t color); 16 | void set_vesa_background(uint32_t color); 17 | 18 | uint32_t get_vesa_color(); 19 | uint32_t get_vesa_background(); 20 | 21 | #endif 22 | --------------------------------------------------------------------------------