diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..890aab7 --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +All the files of this project are under GNU GPL \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e1fd914 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +# OS Main Makefile + +OUTPUT := os.img + +Q := @ +MAKEFLAGS += --no-print-directory +MAKE := make +TARGETS := libc kernel + +# Compiler settings +PREFIX := i386-elf- +GDB := $(PREFIX)gdb + + +rebuild: + @echo "--> Rebuilding OS" + @for dir in $(TARGETS); do $(MAKE) -C $$dir rebuild; done + @echo "--> Generating $(OUTPUT)" + @cp kernel/kernel.elf isofs/boot/kernel.elf + @genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -quiet -o $(OUTPUT) isofs + @echo "--> Build finished" + +default: + @echo "--> Building OS" + @for dir in $(TARGETS); do $(MAKE) -C $$dir default; done + @echo "--> Generating $(OUTPUT)" + @cp kernel/kernel.elf isofs/boot/kernel.elf + @genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -quiet -o $(OUTPUT) isofs + @echo "--> Build finished" + +clean: + @echo "--> Cleaning OS" + @for dir in $(TARGETS); do $(MAKE) -C $$dir clean; done + @rm $(OUTPUT) + @echo "--> Build finished" + +debug: + @echo "--> Building OS (debug)" + @for dir in $(TARGETS); do $(MAKE) -C $$dir debug; done + @echo "--> Generating $(OUTPUT)" + @cp kernel/kernel.elf isofs/boot/kernel.elf + @genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -quiet -o $(OUTPUT) isofs + @echo "--> Build finished" + @echo "--> Starting emulator and debugger" + @qemu-system-i386 -s -cdrom $(OUTPUT) -d guest_errors,int & + @$(GDB) -ex "target remote localhost:1234" -ex "symbol-file kernel/kernel.elf" + +run: rebuild + @echo "--> Starting emulator" + @qemu-system-i386 -cdrom $(OUTPUT) -m 512M diff --git a/README.md b/README.md new file mode 100644 index 0000000..8274d78 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# OS +## I did not find a better name for it yet. + +It's pretty bad for now but hopefully it will be fixed sometime. +heap is currently randomly causing page faults. there must be something weird with it. + +(some info was taken from the osdev wiki, the little book about kernel development and jamesm's tutorials) \ No newline at end of file diff --git a/isofs/boot/grub/menu.lst b/isofs/boot/grub/menu.lst new file mode 100644 index 0000000..6686de9 --- /dev/null +++ b/isofs/boot/grub/menu.lst @@ -0,0 +1,6 @@ +default 0 +#timeout 1 + +title OS +kernel /boot/kernel.elf +module /boot/initrd.img \ No newline at end of file diff --git a/isofs/boot/grub/stage2_eltorito b/isofs/boot/grub/stage2_eltorito new file mode 100644 index 0000000..fc93761 Binary files /dev/null and b/isofs/boot/grub/stage2_eltorito differ diff --git a/isofs/boot/initrd.img b/isofs/boot/initrd.img new file mode 100644 index 0000000..9e54f6f Binary files /dev/null and b/isofs/boot/initrd.img differ diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000..ae7ddde --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,70 @@ +# OS Makefile + +Q := @ +MAKEFLAGS += --no-print-directory + +TARGET := kernel.elf + +# Autogenerate a list of source files +SOURCEDIR := . +C_SOURCES := $(shell find $(SOURCEDIR) -name '*.c') +A_SOURCES := $(shell find $(SOURCEDIR) -name '*.s') +C_HEADERS := $(shell find $(SOURCEDIR) -name '*.h') + +# Generate object files from source files +BUILD_DIR := build +C_OBJ := $(C_SOURCES:.c=.o) +A_OBJ := $(A_SOURCES:.s=.o) + +# Compiler settings +PREFIX := i386-elf- +CC := $(PREFIX)gcc +GDB := $(PREFIX)gdb +LD := $(PREFIX)ld +AS := nasm + +# Include kernel and libc headers +INCLUDES := -I include \ + -I ../libc/include + +# Build flags +CFLAGS := -g -m32 \ + -ffreestanding -fstack-protector -fno-exceptions \ + -Wall -Wextra -Wno-unused ${INCLUDES} + +# Rebuild by default +rebuild: clean default + +# Debug +debug: + @echo "-> Building Kernel (debug)" + @mkdir -p $(BUILD_DIR) + @$(MAKE) $(TARGET_DBG) + +# Create build directory +default: + @echo "-> Building Kernel" + @mkdir -p $(BUILD_DIR) + @$(MAKE) $(TARGET) + +# Build target +$(TARGET): $(A_OBJ) $(C_OBJ) + @echo "LD $@" + @$(LD) -T link.ld -o $@ $(addprefix $(BUILD_DIR)/, $(notdir $^)) ../libc/libk.a + +# Build target (debug) - no changes for now +$(TARGET_DBG): $(TARGET) + +# Generic rules for wildcards +# To make an object, always compile from its .c +%.o: %.c $(C_HEADERS) + @echo "CC $<" + @$(CC) $(CFLAGS) -c $< -o $(BUILD_DIR)/$(notdir $@) + +%.o: %.s + @echo "AS $<" + @$(AS) $< -f elf -o $(BUILD_DIR)/$(notdir $@) + +clean: + @echo "-> Cleaning Kernel" + @rm -rf $(TARGET) $(BUILD_DIR) diff --git a/kernel/arch/x86/fpu.s b/kernel/arch/x86/fpu.s new file mode 100644 index 0000000..7050e9e --- /dev/null +++ b/kernel/arch/x86/fpu.s @@ -0,0 +1,22 @@ +; Doesn't work right now. not really needed until starting the graphics work.... +; global fpu_init +; +; fpu_init: +; ; Search for an FPU +; mov edx, cr0 ; Start probe, get cr0 +; and edx, (-1) - (cr0_ts + cr0_em) ; clear TS and EM to force fpu access +; mov cr0, edx ; store control word +; fninit ; load defaults to FPU +; fnstsw [testword] ; store status word +; cmp word [testword], 0 ; compare the written status with the expected FPU state +; jne nofpu ; jump if the FPU hasn't written anything (i.e. it's not there) +; jmp hasfpu +; +; testword dw 0xAABB ; store garbage to be able to detect a change +; +; hasfpu: +; +; ret +; +; nofpu: +; ret \ No newline at end of file diff --git a/kernel/arch/x86/gdt.s b/kernel/arch/x86/gdt.s new file mode 100644 index 0000000..9dc8f93 --- /dev/null +++ b/kernel/arch/x86/gdt.s @@ -0,0 +1,63 @@ +; gdt.s +; +; Sets up an unprotected GDT (overlaps code and data) covering the full memory address +; Protection could easily be setup but it's not really needed when already using paging +; +; Based on os-dev.pdf +; Adapted to work on pretected mode +; + +; GDT null segment +gdt_start: + dd 0x0 ; 4 byte + dd 0x0 ; 4 byte + +; GDT code segment +gdt_code: + dw 0xffff ; segment length, bits 0-15 + dw 0x0 ; segment base, bits 0-15 + db 0x0 ; segment base, bits 16-23 + db 10011010b ; flags (8 bits) + db 11001111b ; flags (4 bits) + segment length, bits 16-19 + db 0x0 ; segment base, bits 24-31 + +; GDT data segment +gdt_data: + dw 0xffff + dw 0x0 + db 0x0 + db 10010010b + db 11001111b + db 0x0 + +gdt_end: + +; GDT descriptor +gdt_descriptor: + dw gdt_end - gdt_start - 1 ; size (16 bit) + dd gdt_start ; address (32 bit) + +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start + +; Load the GDT +global gdt_init +gdt_init: + ; Load the gdt + lgdt [gdt_descriptor] + + ; Far jump to "enable" the GDT + jmp CODE_SEG:gdt_flush + +; Update the segment registers +gdt_flush: + ; Update data segment + mov ax, DATA_SEG + mov ds, ax + mov ss, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Return + ret \ No newline at end of file diff --git a/kernel/arch/x86/idt.c b/kernel/arch/x86/idt.c new file mode 100644 index 0000000..4ae7510 --- /dev/null +++ b/kernel/arch/x86/idt.c @@ -0,0 +1,19 @@ +#include +#include + +void set_idt_gate(int n, u32 handler) +{ + idt[n].low_offset = (u16)((handler) & 0xFFFF); + idt[n].sel = KERNEL_CS; + idt[n].zero = 0; + idt[n].flags = 0x8E; + idt[n].high_offset = (u16)(((handler) >> 16) & 0xFFFF); +} + +void set_idt() +{ + idt_reg.base = (u32) &idt; + idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1; + // Don't make the mistake of loading &idt -- always load &idt_reg + asm volatile("lidtl (%0)" : : "r" (&idt_reg)); +} diff --git a/kernel/arch/x86/interrupt.s b/kernel/arch/x86/interrupt.s new file mode 100644 index 0000000..bc25068 --- /dev/null +++ b/kernel/arch/x86/interrupt.s @@ -0,0 +1,145 @@ +; Defined in isr.c +extern isr_handler +extern irq_handler + +; Define some nasm macros to avoid copypasting functions 47 times (who wants to do that?) + +%macro ISR 1 ; ISR [isr number] + global isr%1 + isr%1: + push byte 0 ; Replace error code with 0 + push byte %1 + jmp isr_common_stub +%endmacro + +%macro ISR_ERR 1 ; ISR_ERR [isr number] + global isr%1 + isr%1: + push byte %1 + jmp isr_common_stub +%endmacro + +%macro IRQ 2 ; IRQ [isr number], [irq number] + global irq%2 + irq%2: + push byte %2 + push byte %1 + jmp irq_common_stub +%endmacro + +; ISRs +ISR 0 ; Divide By Zero Exception +ISR 1 ; Debug Exception +ISR 2 ; Non Maskable Interrupt Exception +ISR 3 ; Int 3 Exception +ISR 4 ; INTO Exception +ISR 5 ; Out of Bounds Exception +ISR 6 ; Invalid Opcode Exception +ISR 7 ; Coprocessor Not Available Exception +ISR_ERR 8 ; Double Fault Exception (With Error Code!) +ISR 9 ; Coprocessor Segment Overrun Exception +ISR_ERR 10 ; Bad TSS Exception (With Error Code!) +ISR_ERR 11 ; Segment Not Present Exception (With Error Code!) +ISR_ERR 12 ; Stack Fault Exception (With Error Code!) +ISR_ERR 13 ; General Protection Fault Exception (With Error Code!) +ISR_ERR 14 ; Page Fault Exception (With Error Code!) +ISR 15 ; Reserved Exception +ISR 16 ; Floating Point Exception +ISR 17 ; Alignment Check Exception +ISR 18 ; Machine Check Exception +ISR 19 ; Reserved +ISR 20 ; Reserved +ISR 21 ; Reserved +ISR 22 ; Reserved +ISR 23 ; Reserved +ISR 24 ; Reserved +ISR 25 ; Reserved +ISR 26 ; Reserved +ISR 27 ; Reserved +ISR 28 ; Reserved +ISR 29 ; Reserved +ISR 30 ; Reserved +ISR 31 ; Reserved + +; IRQs +IRQ 32, 0 +IRQ 33, 1 +IRQ 34, 2 +IRQ 35, 3 +IRQ 36, 4 +IRQ 37, 5 +IRQ 38, 6 +IRQ 39, 7 +IRQ 40, 8 +IRQ 41, 9 +IRQ 42, 10 +IRQ 43, 11 +IRQ 44, 12 +IRQ 45, 13 +IRQ 46, 14 +IRQ 47, 15 + + +; Common ISR code +isr_common_stub: + ; Save CPU state + pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax + mov ax, ds ; Lower 16-bits of eax = ds + + ; Save the current data segment + push eax + + ; Switch to kernel data segment + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Call C handler + push esp ; registers_t *r + cld ; C code following the sysV ABI requires DF to be clear on function entry + call isr_handler + + ; Restore CPU state + pop eax + pop eax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + popa + add esp, 8 ; Cleans up the pushed error code and pushed ISR number + iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP + +; Common IRQ code. Identical to ISR code except for the 'call' and the 'pop ebx' +irq_common_stub: + ; Save CPU state + pusha + mov ax, ds + + ; Save current data segment + push eax + + ; Switch to kernel data segment + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Call C handler + push esp ; registers_t *r + cld + call irq_handler ; Common IRQ handler + + ; Restore CPU state + pop ebx ; Different than the ISR code + pop ebx + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + popa + add esp, 8 + iret \ No newline at end of file diff --git a/kernel/arch/x86/isr.c b/kernel/arch/x86/isr.c new file mode 100644 index 0000000..b009a01 --- /dev/null +++ b/kernel/arch/x86/isr.c @@ -0,0 +1,193 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +isr_t interrupt_handlers[256]; + +/* Can't do this with a loop because we need the address + * of the function names */ +void isr_init() +{ + set_idt_gate(0, (u32)isr0); + set_idt_gate(1, (u32)isr1); + set_idt_gate(2, (u32)isr2); + set_idt_gate(3, (u32)isr3); + set_idt_gate(4, (u32)isr4); + set_idt_gate(5, (u32)isr5); + set_idt_gate(6, (u32)isr6); + set_idt_gate(7, (u32)isr7); + set_idt_gate(8, (u32)isr8); + set_idt_gate(9, (u32)isr9); + set_idt_gate(10, (u32)isr10); + set_idt_gate(11, (u32)isr11); + set_idt_gate(12, (u32)isr12); + set_idt_gate(13, (u32)isr13); + set_idt_gate(14, (u32)isr14); + set_idt_gate(15, (u32)isr15); + set_idt_gate(16, (u32)isr16); + set_idt_gate(17, (u32)isr17); + set_idt_gate(18, (u32)isr18); + set_idt_gate(19, (u32)isr19); + set_idt_gate(20, (u32)isr20); + set_idt_gate(21, (u32)isr21); + set_idt_gate(22, (u32)isr22); + set_idt_gate(23, (u32)isr23); + set_idt_gate(24, (u32)isr24); + set_idt_gate(25, (u32)isr25); + set_idt_gate(26, (u32)isr26); + set_idt_gate(27, (u32)isr27); + set_idt_gate(28, (u32)isr28); + set_idt_gate(29, (u32)isr29); + set_idt_gate(30, (u32)isr30); + set_idt_gate(31, (u32)isr31); + log_printf(LOG_INFO, "IDT gates set"); + + // Remap the PIC + outb(0x20, 0x11); + outb(0xA0, 0x11); + outb(0x21, 0x20); + outb(0xA1, 0x28); + outb(0x21, 0x04); + outb(0xA1, 0x02); + outb(0x21, 0x01); + outb(0xA1, 0x01); + outb(0x21, 0x0); + outb(0xA1, 0x0); + log_printf(LOG_INFO, "PIC remapped"); + + // Install the IRQs + set_idt_gate(32, (u32)irq0); + set_idt_gate(33, (u32)irq1); + set_idt_gate(34, (u32)irq2); + set_idt_gate(35, (u32)irq3); + set_idt_gate(36, (u32)irq4); + set_idt_gate(37, (u32)irq5); + set_idt_gate(38, (u32)irq6); + set_idt_gate(39, (u32)irq7); + set_idt_gate(40, (u32)irq8); + set_idt_gate(41, (u32)irq9); + set_idt_gate(42, (u32)irq10); + set_idt_gate(43, (u32)irq11); + set_idt_gate(44, (u32)irq12); + set_idt_gate(45, (u32)irq13); + set_idt_gate(46, (u32)irq14); + set_idt_gate(47, (u32)irq15); + log_printf(LOG_INFO, "IRQ installed"); + + set_idt(); // Load with ASM + log_printf(LOG_INFO, "IDT loaded"); +} + +/* To print the message which defines every exception */ +char *exception_messages[] = +{ + "DIVISION_BY_ZERO_EXCEPTION", + "DEBUG", + "NON_MASKABLE_INTERRUPT_EXCEPTION", + "BREAKPOINT", + "INTO_DETECTED_OVERFLOW_EXCEPTION", + "OUT_OF_BOUNDS_EXCEPTION", + "INVALID_OPCODE_EXCEPTION", + "NO_COPROCESSOR_EXCEPTION", + + "DOUBLE_FAULT_EXCEPTION", + "COPROCESSOR_SEGMENT_OVERRUN_EXCEPTION", + "BAD_TSS_EXCEPTION", + "SEGMENT_NOT_PRESENT_EXCEPTION", + "STACK_FAULT_EXCEPTION", + "GENERAL_PROTECTION_FAULT_EXCEPTION", + "PAGE_FAULT_EXCEPTION", + "UNKNOWN_INTERRUPT_EXCEPTION", + + "COPROCESSOR_FAULT_EXCEPTION", + "ALIGNMENT_CHECK_EXCEPTION", + "MACHINE_CHECK_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION", + "RESERVED_EXCEPTION" +}; + +void isr_handler(registers_t *r) +{ + // BSOD ;D + + vga_color_t bsod = {VGA_COLOR_LIGHT_BLUE, VGA_COLOR_WHITE}; + tty_set_color(bsod); + + tty_clear_screen(); + + printf("OS\n\n"); + + // Informations for user + printf("A problem has been detected and OS has\nbeen shutdown to prevent further damage.\nYou'll loose all your unsaved datas.\n\n"); + printf("%s\n\n", exception_messages[r->int_no]); + + // Technical informations + printf("Technical informations:\n"); + printf("EXCEPTION: %08X\n", r->int_no); + printf("ERROR_CODE: %08X\n", r->err_code); + + // Registers dump + printf("REGISTERS:\n"); + printf("ds =%08X edi=%08X esi=%08X ebp=%08X\nebx=%08X edx=%08X ecx=%08X eax=%08X\neip=%08X cs =%08X flg=%08X esp=%08X\nss =%08X\n", + r->ds, r->edi, r->esi, r->ebp, + r->ebx, r->edx, r->ecx, r->eax, + r->eip, r->cs, r->eflags, r->esp, + r->ss); + + // Stop the cpu + for(;;); +} + +void register_interrupt_handler(u8 n, isr_t handler) +{ + interrupt_handlers[n] = handler; +} + +void irq_handler(registers_t *r) +{ + /* After every interrupt we need to send an EOI to the PICs + * or they will not send another interrupt again */ + if (r->int_no >= 40) outb(0xA0, 0x20); /* slave */ + outb(0x20, 0x20); /* master */ + + /* Handle the interrupt in a more modular way */ + if (interrupt_handlers[r->int_no] != 0) + { + isr_t handler = interrupt_handlers[r->int_no]; + handler(r); + } +} + +void irq_init() +{ + // Enable interruptions + sti(); + + // IRQ0: timer + init_timer(1000); + + // IRQ1: keyboard + init_keyboard(); + + // IRQ14/15: ide + init_ide_irq(); +} diff --git a/kernel/arch/x86/ports.c b/kernel/arch/x86/ports.c new file mode 100644 index 0000000..fd733a7 --- /dev/null +++ b/kernel/arch/x86/ports.c @@ -0,0 +1,55 @@ +#include + +//! Read a byte from the specified port +u8 inb(u16 port) +{ + u8 result; + asm("in %%dx, %%al" : "=a" (result) : "d" (port)); + return result; +} + +//! Output a byte to the specified port +void outb(u16 port, u8 data) +{ + asm volatile("out %%al, %%dx" : : "a" (data), "d" (port)); +} + +//! Read a word from the specified port +u16 inw(u16 port) +{ + u16 result; + asm("in %%dx, %%ax" : "=a" (result) : "d" (port)); + return result; +} + +//! Output a word to the specified port +void outw(u16 port, u16 data) +{ + asm volatile("out %%ax, %%dx" : : "a" (data), "d" (port)); +} + +//! Read a long from the specified port +u32 inl(u16 port) +{ + u32 result; + asm volatile("inl %%dx, %%eax" : "=a" (result) : "dN" (port)); + return result; +} + +//! Output a long to the specified port +void outl(u16 port, u32 data) +{ + asm volatile("outl %%eax, %%dx" : : "d" (port), "a" (data)); +} + +//! Read a buffer from the specified port +void insl(u16 port, void *addr, u32 cnt) +{ + asm volatile("cld; rep insl" : "=D" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "memory", "cc"); +} + +//! Output a buffer to the specified port +void outsl(u16 port, const void *addr, u32 cnt) +{ + asm volatile("cld; rep outsl" : "=S" (addr), "=c" (cnt) : "d" (port), "0" (addr), "1" (cnt) : "cc"); +} \ No newline at end of file diff --git a/kernel/arch/x86/timer.c b/kernel/arch/x86/timer.c new file mode 100644 index 0000000..e9df131 --- /dev/null +++ b/kernel/arch/x86/timer.c @@ -0,0 +1,40 @@ +#include +#include + +#include + +volatile u32 tick; + +static void timer_callback(registers_t *regs) +{ + tick++; +} + +u32 timer_get_ticks() +{ + return tick; +} + +void timer_wait(u32 ticks) +{ + sti(); // Enable interrupts + tick = 0; + while(tick < ticks); // Wait for ticks to get to the amount of time +} + +void init_timer(u32 freq) { + /* Install the function we just wrote */ + register_interrupt_handler(IRQ0, timer_callback); + + /* Get the PIT value: hardware clock at 1193180 Hz */ + u32 divisor = 1193180 / freq; + u8 low = (u8)(divisor & 0xFF); + u8 high = (u8)( (divisor >> 8) & 0xFF); + /* Send the command */ + outb(0x43, 0x36); /* Command port */ + outb(0x40, low); + outb(0x40, high); + + log_printf(LOG_INFO, "init at freq %u hz", freq); +} + diff --git a/kernel/drivers/ide.c b/kernel/drivers/ide.c new file mode 100644 index 0000000..8c536d2 --- /dev/null +++ b/kernel/drivers/ide.c @@ -0,0 +1,596 @@ +#include +#include + +#include +#include + +IDE_channel_registers channels[2]; +IDE_device ide_devices[4]; + +u8 ide_buf[2048] = {0}; +static u8 ide_irq_invoked = 0; +static u8 atapi_packet[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +void ide_write(u8 channel, u8 reg, u8 data) +{ + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); + if (reg < 0x08) + outb(channels[channel].base + reg - 0x00, data); + else if (reg < 0x0C) + outb(channels[channel].base + reg - 0x06, data); + else if (reg < 0x0E) + outb(channels[channel].ctrl + reg - 0x0A, data); + else if (reg < 0x16) + outb(channels[channel].bmide + reg - 0x0E, data); + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); +} + +u8 ide_read(u8 channel, u8 reg) +{ + u8 result; + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); + if (reg < 0x08) + result = inb(channels[channel].base + reg - 0x00); + else if (reg < 0x0C) + result = inb(channels[channel].base + reg - 0x06); + else if (reg < 0x0E) + result = inb(channels[channel].ctrl + reg - 0x0A); + else if (reg < 0x16) + result = inb(channels[channel].bmide + reg - 0x0E); + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); + return result; +} + +void ide_read_buffer(u8 channel, u8 reg, void * buffer, u32 quads) +{ + /* WARNING: This code contains a serious bug. The inline assembly trashes ES and + * ESP for all of the code the compiler generates between the inline + * assembly blocks. + */ + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN); + asm("pushw %es; movw %ds, %ax; movw %ax, %es"); + if (reg < 0x08) + insl(channels[channel].base + reg - 0x00, buffer, quads); + else if (reg < 0x0C) + insl(channels[channel].base + reg - 0x06, buffer, quads); + else if (reg < 0x0E) + insl(channels[channel].ctrl + reg - 0x0A, buffer, quads); + else if (reg < 0x16) + insl(channels[channel].bmide + reg - 0x0E, buffer, quads); + asm("popw %es;"); + if (reg > 0x07 && reg < 0x0C) + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN); +} + +u8 ide_polling(u8 channel, u32 advanced_check) +{ + // Delay 400 nanosecond for BSY to be set + for(int i = 0; i < 4; i++) // Reading the Alternate Status port wastes 100ns; loop four times + ide_read(channel, ATA_REG_ALTSTATUS); + + while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait for BSY to be cleared + + if (advanced_check) + { + // Read Status Register + u8 state = ide_read(channel, ATA_REG_STATUS); + + if (state & ATA_SR_ERR) // Check for errors + return 2; + if (state & ATA_SR_DF) // Check for device fault + return 1; + if ((state & ATA_SR_DRQ) == 0) // Check DRQ + return 3; + } + return 0; +} + +const char *ide_errors[] = { + 0, // 0 + "Device Fault", // 1 + 0, // 2 (Needs checking) + "Reads Nothing", // 3 + "Write Protected", // 4 + // Error 2 specific errors + "No Address Mark Found", + "No Media or Media Error", + "Command Aborted", + "No Media or Media Error", + "ID mark not Found", + "No Media or Media Error", + "Uncorrectable Data Error", + "Bad Sectors" +}; + +u8 ide_print_error(u32 drive, u8 err) +{ + if (err == 0) + return err; + + if (err == 2) + { + u8 st = ide_read(ide_devices[drive].channel, ATA_REG_ERROR); + if (st & ATA_ER_AMNF) err = 5; + if (st & ATA_ER_TK0NF) err = 6; + if (st & ATA_ER_ABRT) err = 7; + if (st & ATA_ER_MCR) err = 8; + if (st & ATA_ER_IDNF) err = 9; + if (st & ATA_ER_MC) err = 10; + if (st & ATA_ER_UNC) err = 11; + if (st & ATA_ER_BBK) err = 12; + } + + printf("[IDE]: %s %s %s: %s", (ide_devices[drive].channel == 0) ? "Primary" : "Secondary", + (ide_devices[drive].drive == 0) ? "Master " : "Slave ", + (ide_devices[drive].model), + (ide_errors[err])); + return err; +} + +void ide_initialize(u32 BAR0, u32 BAR1, u32 BAR2, u32 BAR3, u32 BAR4) +{ + // Detect I/O Ports which interface IDE Controller + channels[ATA_PRIMARY ].base = (BAR0 & 0xFFFFFFFC) + 0x1F0 * (!BAR0); + channels[ATA_PRIMARY ].ctrl = (BAR1 & 0xFFFFFFFC) + 0x3F6 * (!BAR1); + channels[ATA_SECONDARY].base = (BAR2 & 0xFFFFFFFC) + 0x170 * (!BAR2); + channels[ATA_SECONDARY].ctrl = (BAR3 & 0xFFFFFFFC) + 0x376 * (!BAR3); + channels[ATA_PRIMARY ].bmide = (BAR4 & 0xFFFFFFFC) + 0; // Bus Master IDE + channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFFFFFC) + 8; // Bus Master IDE + + // Disable IRQs + ide_write(ATA_PRIMARY , ATA_REG_CONTROL, 2); + ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2); + + // Detect ATA-ATAPI Devices + int count = 0; + for(int i = 0; i < 2; i++) + { + for(int j = 0; j < 2; j++) + { + + u8 err = 0, type = IDE_ATA, status; + ide_devices[count].reserved = 0; // Assume there's no drive there + + // Select Drive + ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive + usleep(1000); // Wait 1ms for drive select + + // Send ATA Identify Command + ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + usleep(1000); // Waits for 1 ms + + // Polling + if (ide_read(i, ATA_REG_STATUS) == 0) // If Status = 0, No Device + continue; + + for(;;) + { + status = ide_read(i, ATA_REG_STATUS); + + // If Err, Device is not ATA + if ((status & ATA_SR_ERR)) { + err = 1; + break; + } + + // Everything is right + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) + break; + } + + // Probe for ATAPI Devices + if (err != 0) + { + u8 cl = ide_read(i, ATA_REG_LBA1); + u8 ch = ide_read(i, ATA_REG_LBA2); + + if (cl == 0x14 && ch == 0xEB) + type = IDE_ATAPI; + else if (cl == 0x69 && ch == 0x96) + type = IDE_ATAPI; + else + continue; // Unknown Type (may not be a device) + + ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET); + usleep(1000); + } + + // Read Identification Space of the Device + ide_read_buffer(i, ATA_REG_DATA, ide_buf, 128); + + // Read Device Parameters + ide_devices[count].reserved = 1; + ide_devices[count].type = type; + ide_devices[count].channel = i; + ide_devices[count].drive = j; + ide_devices[count].signature = *((u16 *)(ide_buf + ATA_IDENT_DEVICETYPE)); + ide_devices[count].capabilities = *((u16 *)(ide_buf + ATA_IDENT_CAPABILITIES)); + ide_devices[count].command_sets = *((u32 *)(ide_buf + ATA_IDENT_COMMANDSETS)); + + // Get Size + if (ide_devices[count].command_sets & (1 << 26)) + // Device uses 48-Bit Addressing + ide_devices[count].size = *((u32 *)(ide_buf + ATA_IDENT_MAX_LBA_EXT)); + else + // Device uses CHS or 28-bit Addressing + ide_devices[count].size = *((u32 *)(ide_buf + ATA_IDENT_MAX_LBA)); + + // String indicates model of device + for(int k = 0; k < 40; k += 2) { + ide_devices[count].model[k] = ide_buf[ATA_IDENT_MODEL + k + 1]; + ide_devices[count].model[k + 1] = ide_buf[ATA_IDENT_MODEL + k]; + } + ide_devices[count].model[40] = '\0'; // Terminate String + + count++; + } + } + + // Print detected drives + for(int i = 0; i < 4; i++) + { + if (ide_devices[i].reserved == 1) + { + /* + if (ide_devices[i].size > ((1024 ^ 2) * 2)) // Gb + printf("%lu Gb", (ide_devices[i].size / 1024 / 1024 / 2)); + else if (ide_devices[i].size > ((1024) * 2)) // Mb + printf("%lu Mb", (ide_devices[i].size / 1024 / 2)); + else // Kb + printf("%lu Kb", (ide_devices[i].size / 2)); + */ + printf("Found %s drive: %s\n", (ide_devices[i].type == 0) ? "ATA" : "ATAPI", ide_devices[i].model); + } + } +} + +/* ATA/ATAPI Read/Write Modes: + * ++++++++++++++++++++++++++++++++ + * Addressing Modes: + * ================ + * - LBA28 Mode. (+) + * - LBA48 Mode. (+) + * - CHS. (+) + * Reading Modes: + * ================ + * - PIO Modes (0 : 6) (+) // Slower than DMA, but not a problem. + * - Single Word DMA Modes (0, 1, 2). + * - Double Word DMA Modes (0, 1, 2). + * - Ultra DMA Modes (0 : 6). + * Polling Modes: + * ================ + * - IRQs + * - Polling Status (+) // Suitable for Singletasking +*/ + +u8 ide_ata_access(u8 direction, u8 drive, u32 lba, u8 numsects, u16 selector, u32 edi) +{ + u8 lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd; + u8 lba_io[6]; + u32 channel = ide_devices[drive].channel; // Read the Channel + u32 slavebit = ide_devices[drive].drive; // Read the Drive [Master/Slave] + u32 bus = channels[channel].base; // Bus Base, like 0x1F0 which is also data port + u32 words = 256; // Almost every ATA drive has a sector-size of 512-byte + u16 cyl; + u8 head, sect, err; + + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02); + + // Select one from LBA28, LBA48 or CHS; + if (lba >= 0x10000000) // Sure Drive should support LBA in this case, or you are giving a wrong LBA + { + // LBA48 + lba_mode = 2; + lba_io[0] = (lba & 0x000000FF) >> 0; + lba_io[1] = (lba & 0x0000FF00) >> 8; + lba_io[2] = (lba & 0x00FF0000) >> 16; + lba_io[3] = (lba & 0xFF000000) >> 24; + lba_io[4] = 0; // LBA28 is integer, so 32-bits are enough to access 2TB + lba_io[5] = 0; // LBA28 is integer, so 32-bits are enough to access 2TB + head = 0; // Lower 4-bits of HDDEVSEL are not used here + } + else if (ide_devices[drive].capabilities & 0x200) // Drive supports LBA? + { + // LBA28 + lba_mode = 1; + lba_io[0] = (lba & 0x00000FF) >> 0; + lba_io[1] = (lba & 0x000FF00) >> 8; + lba_io[2] = (lba & 0x0FF0000) >> 16; + lba_io[3] = 0; // These Registers are not used here + lba_io[4] = 0; // These Registers are not used here + lba_io[5] = 0; // These Registers are not used here + head = (lba & 0xF000000) >> 24; + } + else + { + // CHS + lba_mode = 0; + sect = (lba % 63) + 1; + cyl = (lba + 1 - sect) / (16 * 63); + lba_io[0] = sect; + lba_io[1] = (cyl >> 0) & 0xFF; + lba_io[2] = (cyl >> 8) & 0xFF; + lba_io[3] = 0; + lba_io[4] = 0; + lba_io[5] = 0; + head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits + } + + // Check if drive supports DMA + dma = 0; // We don't support DMA + + // Wait if the drive is busy + while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); + + // Select Drive from the controller + if (lba_mode == 0) + ide_write(channel, ATA_REG_HDDEVSEL, 0xA0 | (slavebit << 4) | head); // Drive & CHS + else + ide_write(channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Drive & LBA + + // Write Parameters + if (lba_mode == 2) + { + ide_write(channel, ATA_REG_SECCOUNT1, 0); + ide_write(channel, ATA_REG_LBA3, lba_io[3]); + ide_write(channel, ATA_REG_LBA4, lba_io[4]); + ide_write(channel, ATA_REG_LBA5, lba_io[5]); + } + ide_write(channel, ATA_REG_SECCOUNT0, numsects); + ide_write(channel, ATA_REG_LBA0, lba_io[0]); + ide_write(channel, ATA_REG_LBA1, lba_io[1]); + ide_write(channel, ATA_REG_LBA2, lba_io[2]); + + // Select the command and send it; + // Routine that is followed: + // If ( DMA & LBA48) DO_DMA_EXT; + // If ( DMA & LBA28) DO_DMA_LBA; + // If ( DMA & LBA28) DO_DMA_CHS; + // If (!DMA & LBA48) DO_PIO_EXT; + // If (!DMA & LBA28) DO_PIO_LBA; + // If (!DMA & !LBA#) DO_PIO_CHS; + + if (lba_mode == 0 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; + if (lba_mode == 1 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; + if (lba_mode == 2 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO_EXT; + if (lba_mode == 0 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA; + if (lba_mode == 1 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA; + if (lba_mode == 2 && dma == 1 && direction == 0) cmd = ATA_CMD_READ_DMA_EXT; + if (lba_mode == 0 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO; + if (lba_mode == 1 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO; + if (lba_mode == 2 && dma == 0 && direction == 1) cmd = ATA_CMD_WRITE_PIO_EXT; + if (lba_mode == 0 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA; + if (lba_mode == 1 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA; + if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT; + ide_write(channel, ATA_REG_COMMAND, cmd); // Send the Command + + if (dma) + { + if (direction == 0) {} // DMA Read + else {} // DMA Write + } + else + { + if (direction == 0) // PIO Read + { + + for (u16 i = 0; i < numsects; i++) + { + err = ide_polling(channel, 1); + if (err) + return err; // Polling, set error and exit if there is. + asm("pushw %es"); + asm("mov %%ax, %%es" : : "a"(selector)); + asm("rep insw" : : "c"(words), "d"(bus), "D"(edi)); // Receive Data. + asm("popw %es"); + edi += (words*2); + } + } + else // PIO Write + { + for (u16 i = 0; i < numsects; i++) + { + ide_polling(channel, 0); // Polling + asm("pushw %ds"); + asm("mov %%ax, %%ds"::"a"(selector)); + asm("rep outsw"::"c"(words), "d"(bus), "S"(edi)); // Send Data + asm("popw %ds"); + edi += (words*2); + } + ide_write(channel, ATA_REG_COMMAND, (char []) { ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]); + ide_polling(channel, 0); // Polling + } + } + + return 0; // Successfull +} + +void ide_irq(registers_t *regs) +{ + ide_irq_invoked = 1; +} + +void ide_wait_irq() +{ + sti(); // ....Needed? + while (!ide_irq_invoked); + ide_irq_invoked = 0; +} + +void init_ide_irq() +{ + // Install IDE irq + register_interrupt_handler(IRQ14, ide_irq); + register_interrupt_handler(IRQ15, ide_irq); +} + +u8 ide_atapi_read(u8 drive, u32 lba, u8 numsects, u16 selector, u32 edi) +{ + u32 channel = ide_devices[drive].channel; + u32 slavebit = ide_devices[drive].drive; + u32 bus = channels[channel].base; + u32 words = 1024; // Sector Size: ATAPI drives have a sector size of 2048 bytes + u8 err; + + // Enable IRQs: + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = ide_irq_invoked = 0x0); + + // Setup SCSI Packet: + atapi_packet[ 0] = ATAPI_CMD_READ; + atapi_packet[ 1] = 0x0; + atapi_packet[ 2] = (lba >> 24) & 0xFF; + atapi_packet[ 3] = (lba >> 16) & 0xFF; + atapi_packet[ 4] = (lba >> 8) & 0xFF; + atapi_packet[ 5] = (lba >> 0) & 0xFF; + atapi_packet[ 6] = 0x0; + atapi_packet[ 7] = 0x0; + atapi_packet[ 8] = 0x0; + atapi_packet[ 9] = numsects; + atapi_packet[10] = 0x0; + atapi_packet[11] = 0x0; + + // Select the drive: + ide_write(channel, ATA_REG_HDDEVSEL, slavebit << 4); + + // Delay 400 nanoseconds for select to complete: + for(int i = 0; i < 4; i++) + ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns + + // Inform the Controller that we use PIO mode: + ide_write(channel, ATA_REG_FEATURES, 0); // PIO mode + + // Tell the Controller the size of buffer: + ide_write(channel, ATA_REG_LBA1, (words * 2) & 0xFF); // Lower Byte of Sector Size + ide_write(channel, ATA_REG_LBA2, (words * 2) >> 8); // Upper Byte of Sector Size + + // Send the Packet Command: + ide_write(channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command + + // Waiting for the driver to finish or return an error code: + err = ide_polling(channel, 1); + if (err) + return err; // Polling and return if error + + // Sending the packet data: + asm("rep outsw" : : "c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data + + // Receiving Data: + for (int i = 0; i < numsects; i++) + { + ide_wait_irq(); // Wait for an IRQ. + err = ide_polling(channel, 1); + if (err) + return err; // Polling and return if error. + asm("pushw %es"); + asm("mov %%ax, %%es"::"a"(selector)); + asm("rep insw"::"c"(words), "d"(bus), "D"(edi));// Receive Data. + asm("popw %es"); + edi += (words * 2); + } + + // Wait for an IRQ: + ide_wait_irq(); + + // Wait for BSY & DRQ to clear: + while (ide_read(channel, ATA_REG_STATUS) & (ATA_SR_BSY | ATA_SR_DRQ)); + + return 0; // Successfull +} + +u8 ide_read_sectors(u8 drive, u8 numsects, u32 lba, u16 es, u32 edi) +{ + + // Check if the drive presents: + if (drive > 3 || ide_devices[drive].reserved == 0) + return 0x1; // Drive Not Found! + // Check if inputs are valid: + else if (((lba + numsects) > ide_devices[drive].size) && (ide_devices[drive].type == IDE_ATA)) + return 0x2; // Seeking to invalid position + + // Read in PIO Mode through Polling & IRQs: + u8 err; + if (ide_devices[drive].type == IDE_ATA) + err = ide_ata_access(ATA_READ, drive, lba, numsects, es, edi); + else if (ide_devices[drive].type == IDE_ATAPI) + for (u8 i = 0; i < numsects; i++) + err = ide_atapi_read(drive, lba + i, 1, es, edi + (i*2048)); + return ide_print_error(drive, err); +} + +u8 ide_write_sectors(u8 drive, u8 numsects, u32 lba, u16 es, u32 edi) +{ + // Check if the drive presents: + if (drive > 3 || ide_devices[drive].reserved == 0) + return 0x1; // Drive Not Found! + // Check if inputs are valid: + else if (((lba + numsects) > ide_devices[drive].size) && (ide_devices[drive].type == IDE_ATA)) + return 0x2; // Seeking to invalid position. + + // Read in PIO Mode through Polling & IRQs: + u8 err; + if (ide_devices[drive].type == IDE_ATA) + err = ide_ata_access(ATA_WRITE, drive, lba, numsects, es, edi); + else if (ide_devices[drive].type == IDE_ATAPI) + err = 4; // Write-Protected. + return ide_print_error(drive, err); +} + +u8 ide_atapi_eject(u8 drive) { + u32 channel = ide_devices[drive].channel; + u32 slavebit = ide_devices[drive].drive; + u32 bus = channels[channel].base; + u8 err = 0; + ide_irq_invoked = 0; + + // Check if the drive presents: + if (drive > 3 || ide_devices[drive].reserved == 0) + return 0x1; // Drive Not Found! + // Check if drive isn't ATAPI: + else if (ide_devices[drive].type == IDE_ATA) + return 20; // Command Aborted. + // Eject ATAPI Driver: + else { + // Enable IRQs: + ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = ide_irq_invoked = 0x0); + + // Setup SCSI Packet: + atapi_packet[ 0] = ATAPI_CMD_EJECT; + atapi_packet[ 1] = 0x00; + atapi_packet[ 2] = 0x00; + atapi_packet[ 3] = 0x00; + atapi_packet[ 4] = 0x02; + atapi_packet[ 5] = 0x00; + atapi_packet[ 6] = 0x00; + atapi_packet[ 7] = 0x00; + atapi_packet[ 8] = 0x00; + atapi_packet[ 9] = 0x00; + atapi_packet[10] = 0x00; + atapi_packet[11] = 0x00; + + // Select the Drive: + ide_write(channel, ATA_REG_HDDEVSEL, slavebit << 4); + + // Delay 400 nanosecond for select to complete: + for(int i = 0; i < 4; i++) + ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns. + + // Send the Packet Command: + ide_write(channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command. + + // Waiting for the driver to finish or invoke an error: + err = ide_polling(channel, 1); // Polling and stop if error. + + // Sending the packet data: + asm("rep outsw"::"c"(6), "d"(bus), "S"(atapi_packet));// Send Packet Data + ide_wait_irq(); // Wait for an IRQ. + err = ide_polling(channel, 1); // Polling and get error code. + if (err == 3) err = 0; // DRQ is not needed here. + + return ide_print_error(drive, err); // Return; + } +} \ No newline at end of file diff --git a/kernel/drivers/keyboard.c b/kernel/drivers/keyboard.c new file mode 100644 index 0000000..a878063 --- /dev/null +++ b/kernel/drivers/keyboard.c @@ -0,0 +1,101 @@ +#include +#include +#include + +#include + +#include +#include +#include + + +#define BACKSPACE 0x0E +#define ENTER 0x1C + +static char key_buffer[256]; + +#define SC_MAX 57 +const char *sc_name[] = { "ERROR", "Esc", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "0", "-", "=", "Backspace", "Tab", "Q", "W", "E", + "R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Lctrl", + "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "`", + "LShift", "\\", "Z", "X", "C", "V", "B", "N", "M", ",", ".", + "/", "RShift", "Keypad *", "LAlt", "Spacebar"}; + +const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '}; + +const char sc_normal_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', '?', 'q', 'w', 'e', 'r', 't', 'y', + 'u', 'i', 'o', 'p', '[', ']', '?', '?', 'a', 's', 'd', 'f', 'g', + 'h', 'j', 'k', 'l', ';', '\'', '`', '\0', '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' '}; + +const char sc_shift_ascii[] = { '?', '?', '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', '\b', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ':', '"', '`', '\0', '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', '\0', '?', '?', ' '}; + + +const char sc_maiusc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', + 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', + 'H', 'J', 'K', 'L', ';', '\'', '`', '\0', '\\', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '}; + +int shift_state = 0; + +static void keyboard_callback(registers_t *regs) +{ + // The PIC leaves the scancode in port 0x60 + u8 scancode = inb(0x60); + + switch(scancode) + { + // Shift press + case 0x2A: + case 0x36: + shift_state = 1; + break; + + // Shift release + case 0xAA: + case 0xB6: + shift_state = 0; + break; + + // Backspace + case BACKSPACE: ; + int len = strlen(key_buffer); + if (len > 0) // Only delete if there's something to delete + { + key_buffer[len - 1] = '\0'; + putchar('\b'); + } + break; + + // Enter + case ENTER: + putchar('\n'); + user_input(key_buffer); /* kernel-controlled function. this shouldn't work like this. */ + memset(key_buffer, '\0', 256); + break; + + // Normal char + default: ; + char letter = (scancode > SC_MAX) ? '\0' : ((shift_state) ? sc_shift_ascii[(int)scancode] : sc_normal_ascii[(int)scancode]); + append(key_buffer, letter); + putchar(letter); + break; + } +} + +void init_keyboard() +{ + register_interrupt_handler(IRQ1, keyboard_callback); + log_printf(LOG_INFO, "setup keyboard irq"); +} \ No newline at end of file diff --git a/kernel/drivers/pci.c b/kernel/drivers/pci.c new file mode 100644 index 0000000..5da1968 --- /dev/null +++ b/kernel/drivers/pci.c @@ -0,0 +1,85 @@ +#include +#include + +#include +#include + +u8 pci_config_read_byte(u8 bus, u8 device, u8 function, u8 offset) +{ + /* write out the address */ + outl(0xCF8, ( ((u32)bus << 16) | // Bus Number + ((u32)device << 11) | // Device Number + ((u32)function << 8 ) | // Function Number + (offset & 0xfc) | // Register Number 11111100 (last 2 bits are not important because 32bits get readed) + ((u32)0x80000000 ) // Enable bit (= 1) + )); + + /* read in the data */ + return (u8)((inl(0xCFC) >> ((offset & 3) * 8)) & 0xFF); +} + +u16 pci_config_read_word(u8 bus, u8 device, u8 function, u8 offset) +{ + /* write out the address */outl(0xCF8, ( ((u32)bus << 16) | // Bus Number + ((u32)device << 11) | // Device Number + ((u32)function << 8 ) | // Function Number + (offset & 0xfc) | // Register Number 11111100 (last 4 bits are not important because 32bits get readed) + ((u32)0x80000000 ) // Enable bit (= 1) + )); + /* read in the data */ + return (u16)((inl(0xCFC) >> ((offset & 2) * 8)) & 0xFFFF); +} + +u32 pci_config_read_long(u8 bus, u8 device, u8 function, u8 offset) +{ + /* write out the address */ + outl(0xCF8, ( ((u32)bus << 16) | // Bus Number + ((u32)device << 11) | // Device Number + ((u32)function << 8 ) | // Function Number + (offset & 0xfc) | // Register Number 11111100 (last 4 bits are not important because 32bits get readed) + ((u32)0x80000000 ) // Enable bit (= 1) + )); + /* read in the data */ + return inl(0xCFC); +} + +void pci_check_function(u8 bus, u8 device, u8 function) +{ + u8 baseClass = pci_config_read_byte(bus, device, function, 0xB); + u8 subClass = pci_config_read_byte(bus, device, function, 0xA); + + char buffer[256]; + puts("\nPCI Device:\n"); + printf("Base Class: %x\n", baseClass); + printf("Sub Class: %x\n", subClass); +} + +void pci_check_device(u8 bus, u8 device) +{ + u8 function = 0; + + // Vendor ID is a word located at 0x0 in the PCI header + u16 vendor_id = pci_config_read_word(bus, device, function, 0); + if(vendor_id == 0xFFFF) // There are no vendors == 0xFFFF => Device doesn't exist + return; + + pci_check_function(bus, device, function); + + // Header type is a byte located at 0xE in the PCI header + u8 header_type = pci_config_read_byte(bus, device, function, 0xE); + + // Check for multi-function device + if((header_type & 0x80) != 0) + for(function = 1; function < 8; function++) + if(pci_config_read_word(bus, device, function, 0) != 0xFFFF) + pci_check_function(bus, device, function); +} + +void pci_check_all_busses() +{ + u8 device; + for(u32 bus = 0; bus < 256; bus++) + for(device = 0; device < 32; device++) + pci_check_device((u8)bus, device); +} + diff --git a/kernel/drivers/tty.c b/kernel/drivers/tty.c new file mode 100644 index 0000000..d4e90e9 --- /dev/null +++ b/kernel/drivers/tty.c @@ -0,0 +1,147 @@ +/* + * TTY text mode screen library + * Outputs text to vga text framebuffer + * For functions descriptions look at tty.h +*/ + +#include +#include +#include + +#include +#include + +// TTY definitions +#define TTY_BASE_ADDR 0xB8000 // Default adress for VGA text mode buffer +#define TTY_COLS 80 // VGA default text mode cols +#define TTY_ROWS 25 // VGA default text mode rows +#define TTY_DEFAULT_COLOR 0x0F // Default color: white on black + +// TTY I/O ports +#define TTY_CTRL_REG 0x3D4 +#define TTY_DATA_REG 0x3D5 + +// Globals +u8 tty_color; +u8 *tty_video_buffer; + +/// Private functions + +int get_offset(int col, int row) { return 2 * (row * TTY_COLS + col); } +int get_offset_row(int offset) { return offset / (2 * TTY_COLS); } +int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*TTY_COLS))/2; } + +// Get cursor position from vga port as two bytes (data 0xE/0xF) +u32 tty_get_cursor_offset() +{ + outb(TTY_CTRL_REG, 0xE); + u32 offset = inb(TTY_DATA_REG) << 8; // High byte + outb(TTY_CTRL_REG, 0xF); + offset += inb(TTY_DATA_REG); // Low byte + return offset * 2; // Each vga entry is two bytes +} + +// Set cursor position to offset +void tty_set_cursor_offset(u32 offset) +{ + offset /= 2; + outb(TTY_CTRL_REG, 0xE); + outb(TTY_DATA_REG, (u8)(offset >> 8)); + outb(TTY_CTRL_REG, 0xF); + outb(TTY_DATA_REG, (u8)(offset & 0xFF)); +} + +// Create a char entry in the video buffer +void tty_print_char(char c, char color) +{ + if (color == 0x00) // No custom color has been specified, use default + color = tty_color; + + u32 offset = tty_get_cursor_offset(); // Get current offset from the cursor + + // Choose what to do with the char + switch(c) + { + case '\n': // New line + offset = get_offset(0, get_offset_row(offset) + 1); + break; + case '\b': // Backspace + offset -= 2; + tty_video_buffer[offset] = ' '; + tty_video_buffer[offset + 1] = color; + break; + case '\0': // NULL char + return; + default: // Normal char + tty_video_buffer[offset] = c; + tty_video_buffer[offset + 1] = color; + offset += 2; + break; + } + + // Check if scrolling is needed + if (offset >= TTY_ROWS * TTY_COLS * 2) + { + memcpy(tty_video_buffer, &tty_video_buffer[TTY_COLS * 2], (TTY_ROWS - 1) * TTY_COLS * 2); + // Clear the last line + u32 last_line = 2 * ((TTY_ROWS - 1) * TTY_COLS); + for (u32 i = 0; i < (TTY_COLS * 2); i += 2) + { + tty_video_buffer[i + last_line] = 0; + tty_video_buffer[i + last_line + 1] = tty_color; + } + + offset -= 2 * TTY_COLS; + } + + tty_set_cursor_offset(offset); +} + +/// Public functions + +void tty_print(char *s) +{ + while(*s) + tty_print_char(*(s++), 0); +} + +void tty_putchar(char c) +{ + tty_print_char(c, 0); +} + +void tty_print_color(char *s, vga_color_t color) +{ + char color_c = ((u8)color.bg << 1) | (u8)color.fg; + while(*s) + tty_print_char(*(s++), color_c); +} + +void tty_putchar_color(char c, vga_color_t color) +{ + char color_c = ((u8)color.bg << 1) | (u8)color.fg; + tty_print_char(c, color_c); +} + +void tty_set_color(vga_color_t color) +{ + tty_color = ((u8)color.bg << 1) | (u8)color.fg; +} + +void tty_clear_screen() +{ + int screen_size = TTY_COLS * TTY_ROWS * 2; + for (int i = 0; i < screen_size; i += 2) + { + tty_video_buffer[i] = ' '; + tty_video_buffer[i + 1] = tty_color; + } + tty_set_cursor_offset(0); +} + +void tty_init() +{ + tty_video_buffer = (u8*)TTY_BASE_ADDR; + tty_color = TTY_DEFAULT_COLOR; + tty_clear_screen(); +} \ No newline at end of file diff --git a/kernel/fs/fs.c b/kernel/fs/fs.c new file mode 100644 index 0000000..7186fb0 --- /dev/null +++ b/kernel/fs/fs.c @@ -0,0 +1,66 @@ +#include +#include + +fs_node_t *fs_root = 0; // The root of the filesystem. + +u32 read_fs(fs_node_t * node, u32 offset, u32 size, u8 * buffer) +{ + if (!node) + return 0; + + if (node->read) + return node->read(node, offset, size, buffer); + else + return 0; +} + +u32 write_fs(fs_node_t * node, u32 offset, u32 size, u8 * buffer) +{ + if (!node) + return 0; + + if (node->write) + return node->write(node, offset, size, buffer); + else + return 0; +} + +void open_fs(fs_node_t * node, u32 flags) +{ + if (!node) + return; + + if (node->open) + return node->open(node, flags); +} + +void close_fs(fs_node_t * node) +{ + if (!node) + return; + + if(node->close) + return node->close(node); +} + +struct dirent * readdir_fs(fs_node_t * node, u32 index) +{ + if (!node) + return NULL; + + if ((node->readdir) && (node->flags & FS_DIRECTORY)) + return node->readdir(node, index); + else + return NULL; +} + +fs_node_t * finddir_fs(fs_node_t * node, char * name) +{ + if (!node) + return NULL; + + if ((node->finddir) && (node->flags & FS_DIRECTORY)) + return node->finddir(node, name); + else + return NULL; +} \ No newline at end of file diff --git a/kernel/fs/initrd.c b/kernel/fs/initrd.c new file mode 100644 index 0000000..bfee50a --- /dev/null +++ b/kernel/fs/initrd.c @@ -0,0 +1,123 @@ +/* + * initrd.c + * Based on JamesM work + * needs a rewrite too someday... +*/ + +#include + +#include +#include +#include + +#include + +#pragma pack(1) + +typedef struct +{ + char magic[8]; + uint32_t entry_count; +} initrd_header_t; + +typedef struct +{ + char name[128]; + uint32_t offset; + uint32_t length; +} initrd_file_desc_t; + +initrd_header_t * initrd_header; // Initrd header +initrd_file_desc_t * initrd_descs; // Initrd descriptors array +u8 * initrd_file_area; // Initrd file area + +fs_node_t * initrd_root; // Initrd Root directory node +fs_node_t * initrd_dev; // Directory node for /dev +fs_node_t * root_nodes; // List of file nodes +u32 nroot_nodes; // Number of file nodes + +fs_node_t * initrd_init(u8 * buff) +{ + // Let's hope the kernel didn't get hacked/messed that early and just skip checks... + initrd_header = (initrd_header_t *)buff; + initrd_descs = (initrd_file_desc_t *)((u32)buff + sizeof(initrd_header_t)); + initrd_file_area = (u8 *)((u32)buff + sizeof(initrd_header_t) + (sizeof(initrd_file_desc_t) * initrd_header->entry_count)); + + // Initialise the root directory + initrd_root = calloc(1, sizeof(fs_node_t)); + strcpy(initrd_root->name, "initrd"); + initrd_root->flags = FS_DIRECTORY; + initrd_root->readdir = &initrd_readdir; + initrd_root->finddir = &initrd_finddir; + + // Initialise the /dev directory + initrd_dev = calloc(1, sizeof(fs_node_t)); + strcpy(initrd_dev->name, "dev"); + initrd_dev->flags = FS_DIRECTORY; + initrd_dev->readdir = &initrd_readdir; + initrd_dev->finddir = &initrd_finddir; + + // Create file nodes + nroot_nodes = initrd_header->entry_count; + root_nodes = calloc(1, sizeof(fs_node_t) * nroot_nodes); + + for (u32 i = 0; i < nroot_nodes; i++) + { + // Create a new file node + strcpy(root_nodes[i].name, initrd_descs[i].name); + root_nodes[i].length = initrd_descs[i].length; + root_nodes[i].inode = i; + root_nodes[i].flags = FS_FILE; + root_nodes[i].read = &initrd_read; + } + + return initrd_root; +} + +static u32 initrd_read(fs_node_t * node, u32 offset, u32 size, u8 * buffer) +{ + initrd_file_desc_t *desc = &initrd_descs[node->inode]; + + if (offset > desc->length) + return 0; + + if ((offset + size) > desc->length) + size = desc->length - offset; + + memcpy(buffer, &initrd_file_area[desc->offset + offset], size); + return size; +} + +static struct dirent * initrd_readdir(fs_node_t * node, u32 index) +{ + struct dirent * dir = malloc(sizeof(struct dirent)); + if (node == initrd_root && index == 0) + { + strcpy(dir->d_name, "dev"); + dir->d_ino = 0; + return dir; + } + + // The first item was the /dev entry, shifting all the list after it up by one... + index--; + + if (index >= nroot_nodes) + return NULL; + + strcpy(dir->d_name, root_nodes[index].name); + dir->d_ino = root_nodes[index].inode; + return dir; +} + +static fs_node_t * initrd_finddir(fs_node_t * node, char * name) +{ + if (node == initrd_root && strcmp(name, "dev") == 0) + return initrd_dev; + + for (u32 i = 0; i < nroot_nodes; i++) + if (strcmp(name, root_nodes[i].name) == 0) + return &root_nodes[i]; + + // Not found + return NULL; +} \ No newline at end of file diff --git a/kernel/include/arch/x86/cpu.h b/kernel/include/arch/x86/cpu.h new file mode 100644 index 0000000..60c2baf --- /dev/null +++ b/kernel/include/arch/x86/cpu.h @@ -0,0 +1,15 @@ +#pragma once + +#define _CPU_X86_ + +// Include CPU headers +#include +#include +#include +#include +#include + +// Define some common asm functions +#define hlt() asm volatile("hlt") +#define sti() asm volatile("sti") +#define cli() asm volatile("cli") \ No newline at end of file diff --git a/kernel/include/arch/x86/fpu.h b/kernel/include/arch/x86/fpu.h new file mode 100644 index 0000000..674dd86 --- /dev/null +++ b/kernel/include/arch/x86/fpu.h @@ -0,0 +1,11 @@ +/* + * fpu.h + * + * Enable floating point unit on x86 CPUs + * when supported +*/ + +#pragma once + +//! Init FPU +// void fpu_init(); \ No newline at end of file diff --git a/kernel/include/arch/x86/gdt.h b/kernel/include/arch/x86/gdt.h new file mode 100644 index 0000000..7c0a4c9 --- /dev/null +++ b/kernel/include/arch/x86/gdt.h @@ -0,0 +1,10 @@ +/* + * gdt.h + * + * GDT functions +*/ + +#pragma once + +//! Initializes unprotected GDT (declared in gdt.s) +void gdt_init(); \ No newline at end of file diff --git a/kernel/include/arch/x86/idt.h b/kernel/include/arch/x86/idt.h new file mode 100644 index 0000000..007dfe2 --- /dev/null +++ b/kernel/include/arch/x86/idt.h @@ -0,0 +1,49 @@ +/* + * idt.h + * + * Interrupt descriptor table functions and definitions +*/ + +#pragma once + +#include + +#pragma pack(1) + +// Segment selectors +#define KERNEL_CS 0x08 + +// How every interrupt gate (handler) is defined +typedef struct +{ + u16 low_offset; // Lower 16 bits of handler function address + u16 sel; // Kernel segment selector + u8 zero; // Must be zero + + /* First byte + Bit 7: "Interrupt is present" + Bits 6-5: Privilege level of caller (0=kernel..3=user) + Bit 4: Set to 0 for interrupt gates + Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */ + u8 flags; + u16 high_offset; // Higher 16 bits of handler function address */ +} idt_gate_t; + +// Struct passed to the lidt assembly instruction +typedef struct +{ + u16 limit; // IDT size + u32 base; // IDT pointer +} idt_register_t; + +#define IDT_ENTRIES 256 +idt_gate_t idt[IDT_ENTRIES]; +idt_register_t idt_reg; + +#pragma pack(0) + +//! Setup an handler for the n interrupt +void set_idt_gate(int n, u32 handler); + +//! Set the idt +void set_idt(); \ No newline at end of file diff --git a/kernel/include/arch/x86/isr.h b/kernel/include/arch/x86/isr.h new file mode 100644 index 0000000..7a11e09 --- /dev/null +++ b/kernel/include/arch/x86/isr.h @@ -0,0 +1,103 @@ +/* + * isr.h + * + * ISR functions +*/ + + +#pragma once + +#include + +// TODO: Define some macro there too? +/* ISRs reserved for CPU exceptions */ +extern void isr0(); +extern void isr1(); +extern void isr2(); +extern void isr3(); +extern void isr4(); +extern void isr5(); +extern void isr6(); +extern void isr7(); +extern void isr8(); +extern void isr9(); +extern void isr10(); +extern void isr11(); +extern void isr12(); +extern void isr13(); +extern void isr14(); +extern void isr15(); +extern void isr16(); +extern void isr17(); +extern void isr18(); +extern void isr19(); +extern void isr20(); +extern void isr21(); +extern void isr22(); +extern void isr23(); +extern void isr24(); +extern void isr25(); +extern void isr26(); +extern void isr27(); +extern void isr28(); +extern void isr29(); +extern void isr30(); +extern void isr31(); + +/* IRQ definitions */ +extern void irq0(); +extern void irq1(); +extern void irq2(); +extern void irq3(); +extern void irq4(); +extern void irq5(); +extern void irq6(); +extern void irq7(); +extern void irq8(); +extern void irq9(); +extern void irq10(); +extern void irq11(); +extern void irq12(); +extern void irq13(); +extern void irq14(); +extern void irq15(); + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +/* Struct which aggregates many registers. + * It matches exactly the pushes on interrupt.asm. From the bottom: + * - Pushed by the processor automatically + * - `push byte`s on the isr-specific code: error code, then int number + * - All the registers by pusha + * - `push eax` whose lower 16-bits contain DS + */ +typedef struct +{ + u32 ds; /* Data segment selector */ + u32 edi, esi, ebp, useless, ebx, edx, ecx, eax; /* Pushed by pusha. */ + u32 int_no, err_code; /* Interrupt number and error code (if applicable) */ + u32 eip, cs, eflags, esp, ss; /* Pushed by the processor automatically */ +} registers_t; + +typedef void (*isr_t)(registers_t*); + +void isr_init(); +void isr_handler(registers_t *r); +void irq_init(); + +void register_interrupt_handler(u8 n, isr_t handler); \ No newline at end of file diff --git a/kernel/include/arch/x86/ports.h b/kernel/include/arch/x86/ports.h new file mode 100644 index 0000000..e8d9ca1 --- /dev/null +++ b/kernel/include/arch/x86/ports.h @@ -0,0 +1,34 @@ +/* + * ports.h + * + * Wrappers for assembly I/O functions +*/ + + +#pragma once + +#include + +//! Read a byte from I/O +u8 inb(u16 port); + +//! Read a word from I/O +u16 inw(u16 port); + +//! Read a long from I/O +u32 inl(u16 port); + +//! Read a buffer from I/O +void insl(u16 port, void *addr, u32 cnt); + +//! Output a byte to I/O +void outb(u16 port, u8 data); + +//! Output a word to I/O +void outw(u16 port, u16 data); + +//! Output a long to I/O +void outl(u16 port, u32 data); + +//! Outbut a buffer to I/O +void outsl(u16 port, const void *addr, u32 cnt); \ No newline at end of file diff --git a/kernel/include/arch/x86/timer.h b/kernel/include/arch/x86/timer.h new file mode 100644 index 0000000..03f4342 --- /dev/null +++ b/kernel/include/arch/x86/timer.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +//! Initialize the timer +void init_timer(u32 freq); + +//! Get the number of ticks since the start +u32 timer_get_ticks(); + +//! Wait a number of ticks +void timer_wait(u32 ticks); diff --git a/kernel/include/drivers/ide.h b/kernel/include/drivers/ide.h new file mode 100644 index 0000000..7984620 --- /dev/null +++ b/kernel/include/drivers/ide.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +/* ATA Status */ +#define ATA_SR_BSY 0x80 // Busy +#define ATA_SR_DRDY 0x40 // Drive ready +#define ATA_SR_DF 0x20 // Drive write fault +#define ATA_SR_DSC 0x10 // Drive seek complete +#define ATA_SR_DRQ 0x08 // Data request ready +#define ATA_SR_CORR 0x04 // Corrected data +#define ATA_SR_IDX 0x02 // Inlex +#define ATA_SR_ERR 0x01 // Error + +/* ATA Errors */ +#define ATA_ER_BBK 0x80 // Bad sector +#define ATA_ER_UNC 0x40 // Uncorrectable data +#define ATA_ER_MC 0x20 // No media +#define ATA_ER_IDNF 0x10 // ID mark not found +#define ATA_ER_MCR 0x08 // No media +#define ATA_ER_ABRT 0x04 // Command aborted +#define ATA_ER_TK0NF 0x02 // Track 0 not found +#define ATA_ER_AMNF 0x01 // No address mark + +/* ATA Commands */ +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + +/* ATAPI Commands */ +#define ATAPI_CMD_READ 0xA8 +#define ATAPI_CMD_EJECT 0x1B + +/* ATA Identify */ +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 +#define ATA_IDENT_CAPABILITIES 98 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 + +/* IDE Interface type */ +#define IDE_ATA 0x00 +#define IDE_ATAPI 0x01 + +#define ATA_MASTER 0x00 +#define ATA_SLAVE 0x01 + +/* ATA Registers */ +#define ATA_REG_DATA 0x00 +#define ATA_REG_ERROR 0x01 +#define ATA_REG_FEATURES 0x01 +#define ATA_REG_SECCOUNT0 0x02 +#define ATA_REG_LBA0 0x03 +#define ATA_REG_LBA1 0x04 +#define ATA_REG_LBA2 0x05 +#define ATA_REG_HDDEVSEL 0x06 +#define ATA_REG_COMMAND 0x07 +#define ATA_REG_STATUS 0x07 +#define ATA_REG_SECCOUNT1 0x08 +#define ATA_REG_LBA3 0x09 +#define ATA_REG_LBA4 0x0A +#define ATA_REG_LBA5 0x0B +#define ATA_REG_CONTROL 0x0C +#define ATA_REG_ALTSTATUS 0x0C +#define ATA_REG_DEVADDRESS 0x0D + +/* ATA Channels */ +#define ATA_PRIMARY 0x00 +#define ATA_SECONDARY 0x01 + +/* ATA Directions */ +#define ATA_READ 0x00 +#define ATA_WRITE 0x01 + + typedef struct { + u16 base; // I/O Base. + u16 ctrl; // Control Base + u16 bmide; // Bus Master IDE + u8 nIEN; // nIEN (No Interrupt); +} IDE_channel_registers; + +typedef struct { + u8 reserved; // 0 (Empty) or 1 (This Drive really exists). + u8 channel; // 0 (Primary Channel) or 1 (Secondary Channel). + u8 drive; // 0 (Master Drive) or 1 (Slave Drive). + u16 type; // 0: ATA, 1:ATAPI. + u16 signature; // Drive Signature + u16 capabilities;// Features. + u32 command_sets;// Command Sets Supported. + u32 size; // Size in Sectors. + char model[41]; // Model in string. +} IDE_device; + +void ide_initialize(u32 BAR0, u32 BAR1, u32 BAR2, u32 BAR3, u32 BAR4); +u8 ide_ata_access(u8 direction, u8 drive, u32 lba, u8 numsects, u16 selector, u32 edi); +u8 ide_read_sectors(u8 drive, u8 numsects, u32 lba, u16 es, u32 edi); +u8 ide_write_sectors(u8 drive, u8 numsects, u32 lba, u16 es, u32 edi); +u8 ide_atapi_eject(u8 drive); + +void init_ide_irq(); \ No newline at end of file diff --git a/kernel/include/drivers/keyboard.h b/kernel/include/drivers/keyboard.h new file mode 100644 index 0000000..33601ea --- /dev/null +++ b/kernel/include/drivers/keyboard.h @@ -0,0 +1,3 @@ +#pragma once + +void init_keyboard(); diff --git a/kernel/include/drivers/pci.h b/kernel/include/drivers/pci.h new file mode 100644 index 0000000..56cb7a9 --- /dev/null +++ b/kernel/include/drivers/pci.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +void pci_check_all_busses(); \ No newline at end of file diff --git a/kernel/include/drivers/tty.h b/kernel/include/drivers/tty.h new file mode 100644 index 0000000..1ddc3e9 --- /dev/null +++ b/kernel/include/drivers/tty.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#pragma pack(1) + +typedef enum +{ + VGA_COLOR_BLACK = 0x0, + VGA_COLOR_BLUE = 0x1, + VGA_COLOR_GREEN = 0x2, + VGA_COLOR_CYAN = 0x3, + VGA_COLOR_RED = 0x4, + VGA_COLOR_MAGENTA = 0x5, + VGA_COLOR_BROWN = 0x6, + VGA_COLOR_LIGHT_GREY = 0x7, + VGA_COLOR_DARK_GREY = 0x8, + VGA_COLOR_LIGHT_BLUE = 0x9, + VGA_COLOR_LIGHT_GREEN = 0xA, + VGA_COLOR_LIGHT_CYAN = 0xB, + VGA_COLOR_LIGHT_RED = 0xC, + VGA_COLOR_LIGHT_MAGENTA = 0xD, + VGA_COLOR_LIGHT_BROWN = 0xE, + VGA_COLOR_WHITE = 0xF, +} vga_color; + +typedef struct +{ + vga_color bg : 4; + vga_color fg : 4; +} vga_color_t; + +#pragma pack(0) + +//! Initialize the TTY text mode library +void tty_init(); + +//! Clear the screen to the current color +void tty_clear_screen(); + +//! Print a string to the current offset +void tty_print(char *s); + +//! Print a char to the current offset +void tty_putchar(char c); + +//! Print a custom color string +void tty_print_color(char *s, vga_color_t color); + +//! Print a custom color char +void tty_putchar_color(char c, vga_color_t color); + +//! Set the default text color +void tty_set_color(vga_color_t color); diff --git a/kernel/include/fs/fs.h b/kernel/include/fs/fs.h new file mode 100644 index 0000000..2d47d7f --- /dev/null +++ b/kernel/include/fs/fs.h @@ -0,0 +1,88 @@ +/* + * fs.h + * + * Based on JamesM's work + * It would be nice to get it rewritten sometime +*/ + +#pragma once + +#include + +// FS Flags +#define FS_FILE 0x01 +#define FS_DIRECTORY 0x02 +#define FS_CHARDEVICE 0x03 +#define FS_BLOCKDEVICE 0x04 +#define FS_PIPE 0x05 +#define FS_SYMLINK 0x06 +#define FS_MOUNTPOINT 0x08 + +// Needed for function prototypes +struct fs_node; + +// Filesystem function prototypes +typedef u32 (*read_type_t)(struct fs_node * node, u32 offset, u32 size, u8 * buffer); +typedef u32 (*write_type_t)(struct fs_node * node, u32 offset, u32 size, u8 * buffer); +typedef void (*open_type_t)(struct fs_node * node, u32 flags); +typedef void (*close_type_t)(struct fs_node * node); +typedef struct dirent * (*readdir_type_t)(struct fs_node * node, u32 index); +typedef struct fs_node * (*finddir_type_t)(struct fs_node * node, char * name); + +// Filesystem node +typedef struct fs_node +{ + char name[128]; // The filename + u32 mask; // The permissions mask + u32 uid; // The owning user + u32 gid; // The owning group + u32 flags; // The node type + u32 inode; // This is device-specific - provides a way for a filesystem to identify files. + u32 length; // Size of the file + u32 impl; // An implementation-defined number + read_type_t read; // File's read function + write_type_t write; // File's write function + open_type_t open; // File's open function + close_type_t close; // File's close function + readdir_type_t readdir; // Directory's read function + finddir_type_t finddir; // Directory's find function + struct fs_node * ptr; // Used by mountpoints and symlinks +} fs_node_t; + +// Directory entry +struct dirent +{ + u32 d_ino; // Inode number + char d_name[128]; // Filename +}; + +// Root of the filesystem +extern fs_node_t *fs_root; + +// Flags for open_fs +#define O_RDONLY 0x0000 +#define O_WRONLY 0x0001 +#define O_RDWR 0x0002 +/* +TODO: Implement the other access flags... + +#define O_ACCMODE 0x0003 +#define O_CREAT 0x0100 +#define O_EXCL 0x0200 +#define O_NOCTTY 0x0400 +#define O_TRUNC 0x0800 +#define O_APPEND 0x1000 +#define O_NONBLOCK 0x2000 +*/ + +/* + * Standard read/write/open/close functions + * Suffixed with _fs to distinguish them from the read/write/open/close which deal with file descriptors + */ +u32 read_fs(fs_node_t * node, u32 offset, u32 size, u8 * buffer); +u32 write_fs(fs_node_t * node, u32 offset, u32 size, u8 * buffer); +void open_fs(fs_node_t * node, u32 flags); +void close_fs(fs_node_t * node); +struct dirent * readdir_fs(fs_node_t * node, u32 index); +fs_node_t * finddir_fs(fs_node_t * node, char * name); + diff --git a/kernel/include/fs/initrd.h b/kernel/include/fs/initrd.h new file mode 100644 index 0000000..4b14081 --- /dev/null +++ b/kernel/include/fs/initrd.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +// Initialize initrd fs +fs_node_t * initrd_init(u8 * buff); + +// Initrd file function prototypes +static u32 initrd_read(fs_node_t * node, u32 offset, u32 size, u8 * buffer); +static struct dirent * initrd_readdir(fs_node_t * node, u32 index); +static fs_node_t * initrd_finddir(fs_node_t * node, char * name); \ No newline at end of file diff --git a/kernel/include/init/main.h b/kernel/include/init/main.h new file mode 100644 index 0000000..7f0b05b --- /dev/null +++ b/kernel/include/init/main.h @@ -0,0 +1,3 @@ +#pragma once + +void user_input(char *input); diff --git a/kernel/include/mem/heap.h b/kernel/include/mem/heap.h new file mode 100644 index 0000000..1d25882 --- /dev/null +++ b/kernel/include/mem/heap.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +typedef struct +{ + u32 heap_magic; + u32 start; + u32 end; + u32 attrib; +} heap_t; + +typedef struct +{ + u32 magic : 24; + bool free : 8; + u32 mem : 32; + u32 size : 32; +} heap_header_t; + +#define HEAP_MIN_SIZE 0x100 + +#define HEAP_MAGIC 0x48454150 // 32 bits +#define MAGIC 0xCAFE42 // 24 bits + +// Default kernel's heap +extern heap_t kernel_heap; + +// Create a new heap +void heap_create(heap_t * heap, u32 start, u32 end, u32 attrib); + +// Expand the heap +int heap_expand(heap_t * heap, u32 expansion_bytes); + +// Reduce the size of the heap +int heap_reduce(heap_t * heap, u32 reduce_bytes); + +// alloc() +void * heap_alloc(heap_t * heap, u32 size); + +// free() +void heap_free(heap_t * heap, void * addr); \ No newline at end of file diff --git a/kernel/include/mem/kmalloc.h b/kernel/include/mem/kmalloc.h new file mode 100644 index 0000000..563bd27 --- /dev/null +++ b/kernel/include/mem/kmalloc.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include + +void *kmalloc(size_t size, bool align, u32 *phys_addr); \ No newline at end of file diff --git a/kernel/include/mem/page_frame.h b/kernel/include/mem/page_frame.h new file mode 100644 index 0000000..4998296 --- /dev/null +++ b/kernel/include/mem/page_frame.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +typedef enum +{ + FRAME_SET = 1, + FRAME_CLEAR = 0 +} frame_status_t; + +// Change a memory bit status +void set_frame(u32 * aligned_addr, frame_status_t state); + +// Check a memory bit status +frame_status_t test_frame(u32 * aligned_addr); + +// Find a free frame +void* find_frame(); + +// Initialize page frame allocation +void pageframe_init(u32 mem_size); \ No newline at end of file diff --git a/kernel/include/mem/paging.h b/kernel/include/mem/paging.h new file mode 100644 index 0000000..3424ced --- /dev/null +++ b/kernel/include/mem/paging.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +// Kernel's page table +u32* kernel_pd; + +// Create a page directory +u32* create_pd(); + +// Switch to a page directory +void switch_pd(u32* pd); + +// Map a page +int map_page(u32* pd, void* physaddr, void* virtualaddr, u32 attrib); + +// Map a memory range +int map_pages(u32* pd, void* physaddr_start, void* physaddr_end, void* virtualaddr_start, void* virtualaddr_end, u32 attrib); + +// Unmap a page +int unmap_page(u32* pd, void* virtualaddr); + +// Unmap a memory range +int unmap_pages(u32* pd, void* virtualaddr_start, void* virtualaddr_end); + +// Set a page's attributes +int page_set_attrib(u32* pd, void* address, u32 attrib); + +// Get physical address from virtual address +void* get_physaddr(u32* pd, void* virtualaddr); + +// Initialize paging +void paging_init(); \ No newline at end of file diff --git a/kernel/include/multiboot/multiboot.h b/kernel/include/multiboot/multiboot.h new file mode 100644 index 0000000..699d61b --- /dev/null +++ b/kernel/include/multiboot/multiboot.h @@ -0,0 +1,216 @@ +/* multiboot.h - Multiboot header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 8192 + +/* The magic field should contain this. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* This should be in %eax. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* The bits in the required part of flags field we don't support. */ +#define MULTIBOOT_UNSUPPORTED 0x0000fffc + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000004 + +/* Flags set in the 'flags' member of the multiboot header. */ + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +/* Flags to be set in the 'flags' member of the multiboot info structure. */ + +/* is there basic lower/upper memory information? */ +#define MULTIBOOT_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MULTIBOOT_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MULTIBOOT_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MULTIBOOT_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MULTIBOOT_INFO_ELF_SHDR 0X00000020 + +/* is there a full memory map? */ +#define MULTIBOOT_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MULTIBOOT_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800 + +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* Feature flags. */ + multiboot_uint32_t flags; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; + multiboot_uint32_t entry_addr; + + /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + multiboot_uint32_t mode_type; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +/* The symbol table for a.out. */ +struct multiboot_aout_symbol_table +{ + multiboot_uint32_t tabsize; + multiboot_uint32_t strsize; + multiboot_uint32_t addr; + multiboot_uint32_t reserved; +}; +typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; + +/* The section header table for ELF. */ +struct multiboot_elf_section_header_table +{ + multiboot_uint32_t num; + multiboot_uint32_t size; + multiboot_uint32_t addr; + multiboot_uint32_t shndx; +}; +typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; + +struct multiboot_info +{ + /* Multiboot info version number */ + multiboot_uint32_t flags; + + /* Available memory from BIOS */ + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; + + /* "root" partition */ + multiboot_uint32_t boot_device; + + /* Kernel command line */ + multiboot_uint32_t cmdline; + + /* Boot-Module list */ + multiboot_uint32_t mods_count; + multiboot_uint32_t mods_addr; + + union + { + multiboot_aout_symbol_table_t aout_sym; + multiboot_elf_section_header_table_t elf_sec; + } u; + + /* Memory Mapping buffer */ + multiboot_uint32_t mmap_length; + multiboot_uint32_t mmap_addr; + + /* Drive Info buffer */ + multiboot_uint32_t drives_length; + multiboot_uint32_t drives_addr; + + /* ROM configuration table */ + multiboot_uint32_t config_table; + + /* Boot Loader Name */ + multiboot_uint32_t boot_loader_name; + + /* APM table */ + multiboot_uint32_t apm_table; + + /* Video */ + multiboot_uint32_t vbe_control_info; + multiboot_uint32_t vbe_mode_info; + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; +}; +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_mmap_entry +{ + multiboot_uint32_t size; + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 + multiboot_uint32_t type; +} __attribute__((packed)); +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_mod_list +{ + /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + + /* Module command line */ + multiboot_uint32_t cmdline; + + /* padding to take it to 16 bytes (must be zero) */ + multiboot_uint32_t pad; +}; +typedef struct multiboot_mod_list multiboot_module_t; \ No newline at end of file diff --git a/kernel/include/utils/common.h b/kernel/include/utils/common.h new file mode 100644 index 0000000..084140b --- /dev/null +++ b/kernel/include/utils/common.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +extern u32 kernel_start; + +extern u32 kernel_text_start; +extern u32 kernel_text_end; + +extern u32 kernel_rodata_start; +extern u32 kernel_rodata_end; + +extern u32 kernel_data_start; +extern u32 kernel_data_end; + +extern u32 kernel_bss_start; +extern u32 kernel_bss_end; + +extern u32 kernel_end; \ No newline at end of file diff --git a/kernel/include/utils/logger.h b/kernel/include/utils/logger.h new file mode 100644 index 0000000..1d269f8 --- /dev/null +++ b/kernel/include/utils/logger.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +typedef enum +{ + LOG_DEBUG = 0, + LOG_INFO = 1, + LOG_WARNING = 2, + LOG_ERROR = 3, + LOG_FATAL = 4 +} log_level_t; + +// Enables logging +#define LOG_ENABLED 1 + +// Minimum log level +#define LOG_LEVEL LOG_INFO + +#ifdef LOG_ENABLED + +void log_init(); + +/* MACRO for printing module name automatically + * ...unfortunately that actually includes the whole path and + * doing each call with a different string makes it quite heavy + * + * Fortunately that's not included in release builds + */ +void __log_printf(char * module_name, log_level_t level, char * fmt, ...); +#define log_printf(l, x, ...) __log_printf(__FILE__ , l, x, ##__VA_ARGS__) + +#else + +#define log_init() +#define log_printf(l, x, ...) +#define log_mprintf(m, l, x, ...) +#define log_print(l, x, ...) + +#endif \ No newline at end of file diff --git a/kernel/init/entry.s b/kernel/init/entry.s new file mode 100644 index 0000000..6640377 --- /dev/null +++ b/kernel/init/entry.s @@ -0,0 +1,40 @@ +; Declare constants for the multiboot header. +MBALIGN equ 1<<0 ; align loaded modules on page boundaries +MEMINFO equ 1<<1 ; provide memory map +FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field +MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header +CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot + +; Multiboot header +section .multiboot +align 4 + dd MAGIC + dd FLAGS + dd CHECKSUM + +section .bss + ; Reserve 16 Kib of 4 byte aligned space for the stack + align 4 + stack_bottom: + resb 16384 + stack_top: + +; The linker script specifies entrypoint as the entry point +section .text + global entrypoint:function (entrypoint.end - entrypoint) + entrypoint: + ; Setup stack + mov esp, stack_top + + ; Early kernel entrypoint + extern kernel_early + push dword ebx ; Multiboot info pointer should be in ebx + call kernel_early ; Enable GDT and paging before the real kernel + + ; Call the kernel + extern kernel_main + call kernel_main + + ; We should never reach that ...but it may happen while "debugging" + jmp $ + .end: \ No newline at end of file diff --git a/kernel/init/main.c b/kernel/init/main.c new file mode 100644 index 0000000..0cd3d21 --- /dev/null +++ b/kernel/init/main.c @@ -0,0 +1,188 @@ +/* + * kernel.c + * + * Main kernel C file + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +multiboot_info_t * mbinfo; + +/* early kernel entrypoint */ +void kernel_early(u32 mbinfo_ptr) +{ + mbinfo = (multiboot_info_t * )mbinfo_ptr; + + // Initialize tty so we get logging + tty_init(); + + // Initialize logging + log_init(); + + // Setup the gdt + gdt_init(); + + // Intialize ISR handlers + isr_init(); + + // Initialize paging + paging_init(); + + // Initialize pageframe allocation + pageframe_init(mbinfo->mem_lower + mbinfo->mem_upper); + + // Create our own heap + heap_create(&kernel_heap, 0xC0000000, 0xC0200000, 3); +} + +/* main kernel entrypoint */ +void kernel_main() +{ + // Intialize IRQ devices + irq_init(); + + // Enable IRQs + sti(); + + // Mount initial filesystem + multiboot_module_t * initrd_m = (multiboot_module_t *)mbinfo->mods_addr; + printf("initrd start: %08x, end: %08x", initrd_m->mod_start, initrd_m->mod_end); + //initrd_init(initrd_m->mod_start); + + // Kernel welcome message + printf( "\n\nOS KERNEL\n" \ + "Built on %s %s\n\n", __TIME__, __DATE__ ); + + // load_program("/dev/initramfs/shell.elf"); + puts("\n\n> "); +} + +// That's some bad temporary shell there... TODO: Remove this horrible code +#define cmd_start if(!1){ +#define cmd(str) }else if(strcmp(input,str)==0){ +#define cmd_arg(str) }else if(memcmp(input,str,strlen(str))==0){ +#define cmd_end } + +void user_input(char *input) { + cmd_start + cmd("hlt") + puts("Stopping the cpu...\n"); + hlt(); + cmd("page") + u32 phys_addr; + u32* page = kmalloc(1000, true, &phys_addr); + printf("Allocated a page\n"); + printf("Page: %08x, Physical Address: %08x\n", page, phys_addr); + cmd("memcmp") + printf("memcmp\n"); + char a[] = {1,2,3}; + char b[] = {1,2,3,1,1}; + char c[] = {1,2,3,4,5}; + int d = memcmp(a, b, sizeof(a) / sizeof(a[0])); + int e = memcmp(b, c, sizeof(b) / sizeof(b[0])); + printf("d=%d e=%d\n", d, e); + cmd("lspci") + pci_check_all_busses(); + puts("\n"); + cmd("info") + printf("\nOS KERNEL\n"); + printf("Built on %s %s\n", __TIME__, __DATE__); + printf("Released under GNU GPL\n\n"); + cmd_arg("sleep") + int n = atoi(&input[6]); + sleep(n); + cmd("ide") + ide_initialize(0x1F0, 0x3F6, 0x170, 0x376, 0x000); + //ide_read_sectors(1, 1, i, (unsigned int)buf); + cmd("float") + float a = 0.5f; + float b = 0.75f; + float c = a - b; + if (c != 0.25f) + printf("uhmmmm...\n"); + printf("%08x", c); + cmd("smash stack") + char break_me[16]; + char evil[20]; + memset(evil, 1, 20); + memcpy(break_me, evil, 256); + cmd("crash") + int ac = -1; + ac++; + int bc = 20; + int cc = bc / ac; + cmd("clear") + tty_clear_screen(); + cmd_arg("atoi") + int n = atoi(&input[5]); + printf("N = %d\n", n); + cmd("log") + log_printf(LOG_DEBUG, "Debug"); + log_printf(LOG_INFO, "Info"); + log_printf(LOG_WARNING, "Warning"); + log_printf(LOG_ERROR, "Error"); + log_printf(LOG_FATAL, "Fatal"); + cmd("heap") + u32 t1 = timer_get_ticks(); + + u32 * a = heap_alloc(&kernel_heap, 30); + u32 * b = heap_alloc(&kernel_heap, 70); + + printf("addr a1 = %08x\n", a); + printf("addr b1 = %08x\n", b); + + heap_free(&kernel_heap, a); + heap_free(&kernel_heap, b); + + a = heap_alloc(&kernel_heap, 120); + printf("addr a2 = %08x\n", a); + heap_free(&kernel_heap, a); + + u32 t2 = timer_get_ticks(); + + u32 score = t2 - t1; + printf("time: %u\n", score); + + cmd("bootinfo") + printf("Booted by: %s\n", mbinfo->boot_loader_name); + cmd_arg("color") + vga_color_t color; + + const char * hex_nms = "0123456789abcdef"; + for (int i = 0; i < 16; i++) + if (input[6] == hex_nms[i]) + color.bg = i; + for (int i = 0; i < 16; i++) + if (input[7] == hex_nms[i]) + color.fg = i; + + tty_set_color(color); + cmd_end + + puts("> "); +} diff --git a/kernel/link.ld b/kernel/link.ld new file mode 100644 index 0000000..77ec8fc --- /dev/null +++ b/kernel/link.ld @@ -0,0 +1,49 @@ +/* Define the entry point */ +ENTRY(entrypoint) + +/* Tell where the various sections of the object files will be put in the final + kernel image. */ +SECTIONS +{ + /* The Multiboot bootloader should load the kernel at 1Mib */ + . = 1M; + kernel_start = .; + + /* Align multiboot info and text to 4Kb */ + kernel_text_start = .; + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + kernel_text_end = .; + + /* Read-only data. */ + kernel_rodata_start = .; + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + kernel_rodata_end = .; + + /* Read-write data (initialized) */ + kernel_data_start = .; + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + kernel_data_end = .; + + /* Read-write data (uninitialized) and stack */ + kernel_bss_start = .; + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + kernel_bss_end = .; + + /* Other sections wil leventually be put here */ + + kernel_end = .; +} \ No newline at end of file diff --git a/kernel/mem/heap.c b/kernel/mem/heap.c new file mode 100644 index 0000000..8bc4c7d --- /dev/null +++ b/kernel/mem/heap.c @@ -0,0 +1,174 @@ +/* + * heap.c + * IT'S BAD +*/ + +#include +#include +#include +#include + +#include +#include +#include + +heap_t kernel_heap; + +void heap_create(heap_t * heap, u32 start, u32 end, u32 attrib) +{ + start &= 0xFFFFF000; // Make sure adresses are page aligned + end &= 0xFFFFF000; + + log_printf(LOG_DEBUG, "h=%08x, start=%08x, end=%08x, s=%08x", heap, start, end, end - start); + + // Setup heap attributes (executable, userspace, ...) + for (u32 i = start; i < end; i += 0x1000) + { + void * addr = find_frame(); + map_page(kernel_pd, addr, (void *)i, attrib); + set_frame(addr, FRAME_SET); + //log_printf(LOG_DEBUG, "frame phys=%08x,virt=%08x", addr, i); + } + + heap->heap_magic = HEAP_MAGIC; + heap->start = start; + heap->end = end; + heap->attrib = attrib; + + // Place an initial memory header + heap_header_t * initial_h = (void *)heap->start; + initial_h->magic = MAGIC; + initial_h->free = true; + initial_h->mem = heap->start + sizeof(heap_header_t); + initial_h->size = heap->end - initial_h->mem; +} + +int heap_expand(heap_t * heap, u32 expansion_bytes) +{ + u32 new_size = (heap->end + expansion_bytes) & 0xFFFFF000; + + // Make sure the pages after the heap are free and we can use them + for (u32 i = heap->end; i < new_size; i += 0x1000) + if(get_physaddr(kernel_pd, (void *)i)) + return -1; + + // Allocate the pages + for (u32 i = heap->end; i < new_size; i += 0x1000) + { + void * addr = find_frame(); + map_page(kernel_pd, addr, (void *)i, heap->attrib); + set_frame(addr, FRAME_SET); + } + + // Create a new heap header for the new space + heap_header_t * h; + h = (heap_header_t *)heap->end; + h->magic = MAGIC; + h->free = true; + h->mem = heap->end + sizeof(heap_header_t); + h->size = expansion_bytes - sizeof(heap_header_t); + + // Expans the heap + heap->end += expansion_bytes; + + return 0; +} + +int heap_reduce(heap_t * heap, u32 reduce_bytes) +{ + u32 new_size = (heap->end - reduce_bytes) & 0xFFFFF000; + if (new_size < HEAP_MIN_SIZE) + return -1; + + // Free up the pages + for (u32 i = new_size; i < heap->end; i += 0x1000) + { + void * addr = get_physaddr(kernel_pd, (void *)i); + unmap_page(kernel_pd, (void *)i); + set_frame(addr, FRAME_CLEAR); + } + + heap->end -= reduce_bytes; + + return 0; +} + +void * heap_alloc(heap_t * heap, u32 size) +{ + if (size <= 0) + return NULL; + + heap_header_t * h; + for (u32 i = heap->start; i < (heap->end - sizeof(heap_header_t)); i++) + { + h = (heap_header_t *)i; + if (h->magic == MAGIC) // Valid header + { + if ( (h->free == true) && // Free + (h->size >= size) ) // Enough space + { + if (h->size == size) // Easy + { + h->free = false; + return (void *)h->mem; + } + else if ( (h->size - size) > (sizeof(heap_header_t) + 1) ) + { + // Create a new header for the remaining space + heap_header_t * new = (heap_header_t *)(h->mem + size); + new->magic = MAGIC; + new->free = true; + new->mem = h->mem + size + sizeof(heap_header_t); + new->size = h->size - size - sizeof(heap_header_t); + + // Fix the old header + h->free = false; + h->size = size; + + return (void *)h->mem; + } + } + else + { + // That's a valid header, go faster + i += h->size; + } + } + } + return NULL; +} + +void heap_free(heap_t * heap, void * addr) +{ + heap_header_t * h; + h = (heap_header_t *)((u32)addr - sizeof(heap_header_t)); + if (h->magic == MAGIC) + { + h->free = true; + + // Try to join this header with another + heap_header_t * next = (heap_header_t *)(h->mem + h->size); + + if ((u32)next > (heap->end - sizeof(heap_header_t))) + { + // Going any further would trigger a page fault, return + return; + } + + if ( (next->magic == MAGIC) && + (next->free == true) ) + { + // We can join the two headers! + log_printf(LOG_DEBUG, "Joining header with the next one :D"); + + // Expand out header + h->size += next->size + sizeof(heap_header_t); + + // Clean out the old header + next->magic = 0; + + // Return + return; + } + } +} \ No newline at end of file diff --git a/kernel/mem/kmalloc.c b/kernel/mem/kmalloc.c new file mode 100644 index 0000000..c9f13e9 --- /dev/null +++ b/kernel/mem/kmalloc.c @@ -0,0 +1,28 @@ +#include + +/* + * This should be computed at link time, but an hardcoded + * value is fine for now. Remember that our kernel starts + * at 0x1000 as defined on the Makefile +*/ + +u32 placement_address = 0x10000; + +// At this early in boot we can assume that allocated pages will never need to be free'd +void *kmalloc(size_t size, bool align, u32 *phys_addr) +{ + // Pages are 4K aligned (0x1000) + if (align == true && (placement_address & 0xFFFFF000)) + { + placement_address &= 0xFFFFF000; + placement_address += 0x1000; + } + + // Save also the physical address + if (phys_addr) + *phys_addr = placement_address; + + u32 ret = placement_address; + placement_address += size; // Increment the free memory pointer + return (void *)ret; +} diff --git a/kernel/mem/page_frame.c b/kernel/mem/page_frame.c new file mode 100644 index 0000000..fdb7856 --- /dev/null +++ b/kernel/mem/page_frame.c @@ -0,0 +1,63 @@ +#include +#include + +#include +#include +#include + +u32 * frame_mem; +u32 frame_size; + +void set_frame(u32 * aligned_addr, frame_status_t state) +{ + aligned_addr = (u32*)((u32)aligned_addr / 0x1000); + + u32 idx = (u32)aligned_addr / 32; // Each block of frames is 32 bits + u32 bit = (u32)aligned_addr % 32; + + (state == FRAME_SET) ? ( frame_mem[idx] |= (1 << bit) ) : // Set the bit + ( frame_mem[idx] &= ~(1 << bit) ); // Clear the bit +} + +frame_status_t test_frame(u32 * aligned_addr) +{ + aligned_addr = (u32*)((u32)aligned_addr / 0x1000); + + u32 idx = (u32)aligned_addr / 32; // Each block of frames is 32 bits + u32 bit = (u32)aligned_addr % 32; + + return (frame_status_t)(frame_mem[idx] & (1 << bit)); // Return bit state +} + +void * find_frame() +{ + for (u32 i = 0; i < (frame_size / 32); i++) + { + if (frame_mem[i] == 0xFFFFFFFF) + continue; + + for (u32 ii = 0; ii < 32; ii++) + { + if( !( frame_mem[i] & (1 << ii) ) ) + return (void *)(((i * 32) + ii) * 0x1000); + } + } + return NULL; +} + +void pageframe_init(u32 mem_size) +{ + log_printf(LOG_INFO, "Initializing, mem size = %u Mib", mem_size / 1024); + + frame_size = mem_size / 0x1000; /* 4k blocks */ + frame_mem = kmalloc(frame_size / 8 /* blocks are 1 bit and kmalloc allocates siz3 in bytes */, false, NULL); + memset(frame_mem, 0, frame_size / 8); + + log_printf(LOG_DEBUG, "frame_size = %u, frame = 0x%08x", frame_size, frame_mem); + + // Set the kernel as "NOT FREE" + for(u32 i = 0; i < 0x100000; i += 0x1000) + set_frame((u32 *)i, FRAME_SET); + + log_printf(LOG_DEBUG, "set kernel physical memory as allocated"); +} \ No newline at end of file diff --git a/kernel/mem/paging.c b/kernel/mem/paging.c new file mode 100644 index 0000000..f7adff1 --- /dev/null +++ b/kernel/mem/paging.c @@ -0,0 +1,167 @@ +#include +#include + +#include + +#include +#include +#include + +u32* create_pd() +{ + u32* pd; + kmalloc(4096, true, (u32*)&pd); + memset(pd, 0, 4096); + return pd; +} + +void switch_pd(u32* pd) +{ + asm volatile("mov %0, %%cr3":: "r"(pd)); +} + +// Sometimes it may be needed... +void invlpg(u32 addr) +{ + asm volatile("invlpg (%0)" ::"r" (addr) : "memory"); +} + +int map_page(u32* pd, void* physaddr, void* virtualaddr, u32 attrib) +{ + u32 pdindex = (u32)virtualaddr >> 22; + u32 ptindex = (u32)virtualaddr >> 12 & 0x03FF; + + u32* pt = (u32*)((pd[pdindex] >> 12) * 0x1000); + if (!pt) + { + // Create the page table + kmalloc(4096, true, (u32*)&pt); // 4kb aligned, physical + memset(pt, 0, 4096); // init the page table to 0 + + // Add the page table to the page directory + pd[pdindex] = (u32)pt | 3; // Supervisor, rw, present + } + + if (pt[ptindex] & 0x1) // Page already present + return -1; + + pt[ptindex] = ((u32)physaddr) | (attrib & 0xFFF) | 0x01; // Present + + return 0; +} + +int map_pages(u32* pd, void* physaddr_start, void* physaddr_end, void* virtualaddr_start, void* virtualaddr_end, u32 attrib) +{ + int ret = 0; + + u32 phys = (u32)physaddr_start; + u32 virt = (u32)virtualaddr_start; + + while( phys < (u32)physaddr_end && // Map until physical address end + virt < (u32)virtualaddr_end && // Map until virtual address end + ret == 0 ) // Make sure there are no errors + { + // Map current page + ret += map_page(pd, (void *)phys, (void *)virt, attrib); + + // Each page is 4k + phys += 0x1000; + virt += 0x1000; + } + + return ret; +} + +int unmap_page(u32* pd, void* virtualaddr) +{ + u32 pdindex = (u32)virtualaddr >> 22; + u32 ptindex = (u32)virtualaddr >> 12 & 0x03FF; + + u32* pt = (u32*)((pd[pdindex] >> 12) * 0x1000); + if (!pt) // It the page table doesn't exist, the page must already be unmapped + return -1; + + if (!(pt[ptindex] & 0x1)) // The page is not present + return -1; + + pt[ptindex] = 0; // Clear the page + return 0; +} + +int unmap_pages(u32* pd, void* virtualaddr_start, void* virtualaddr_end) +{ + int ret = 0; + + u32 virt = (u32)virtualaddr_start; + + while( virt < (u32)virtualaddr_end && // Map until physical address end + ret == 0 ) // Make sure there are no errors + { + // Unmap current page + ret += unmap_page(pd, (void *)virt); + + // Pages are 4k + virt += 0x1000; + } + + return ret; +} + +int page_set_attrib(u32* pd, void* address, u32 attrib) +{ + u32 pdindex = (u32)address >> 22; + u32 ptindex = (u32)address >> 12 & 0x03FF; + + u32* pt = (u32*)((pd[pdindex] >> 12) * 0x1000); + if (!pt) + return -1; + + pt[ptindex] |= (attrib & 0xFFF); + return 0; +} + +void* get_physaddr(u32* pd, void* virtualaddr) +{ + u32 pdindex = (u32)virtualaddr >> 22; + u32 ptindex = (u32)virtualaddr >> 12 & 0x03FF; + + u32* pt = (u32*)((pd[pdindex] >> 12) * 0x1000); + if (!pt) + return 0; + + u32 page = pt[ptindex]; + if (page & 1) + return (void*)(page >> 12); + else + return 0; +} + + +void enable_paging() +{ + // Enable paging by oring cr0 with pagin enable bit + asm volatile( "mov %cr0, %eax\n\t" \ + "or $0x80000000, %eax\n\t" \ + "mov %eax, %cr0\n\t" ); +} + +void identity_map(u32* pd, void * start_addr, void * end_addr, u32 attr) +{ + for(u32 i = (u32)start_addr; i < (u32)end_addr; i += 0x1000) + map_page(pd, (void *)i, (void *)i, 3); +} + +void paging_init() +{ + // Create kernel's page directory + kernel_pd = create_pd(); + + // Identity map the kernel + identity_map(kernel_pd, 0, &kernel_end, 3); + + // Switch to kernel's page directory + switch_pd(kernel_pd); + + // Enable paging + enable_paging(); +} \ No newline at end of file diff --git a/kernel/utils/logger.c b/kernel/utils/logger.c new file mode 100644 index 0000000..e5d2988 --- /dev/null +++ b/kernel/utils/logger.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include + +// Colors contrassegnating levels +const vga_color_t level_colors[] = +{ + {VGA_COLOR_BLACK, VGA_COLOR_WHITE}, // Debug + {VGA_COLOR_BLACK, VGA_COLOR_WHITE}, // Info + {VGA_COLOR_BLACK, VGA_COLOR_LIGHT_BROWN}, // Warning + {VGA_COLOR_BLACK, VGA_COLOR_LIGHT_RED}, // Error + {VGA_COLOR_BLACK, VGA_COLOR_RED} // Fatal +}; + +// Character contrassegnating level +const char level_c[] = +{ + 'D', // Debug + 'I', // Info + 'W', // Warning + 'E', // Error + 'F' // Fatal +}; + +void log_init() +{ + // Nothing to do there ? +} + +char log_buf[255]; +void __log_printf(char * module_name, log_level_t level, char * fmt, ...) +{ + if (level < LOG_LEVEL) + return; + + // Remove file path from module name + char * p; + for(;;) + { + p = strrchr(module_name, '/'); + if (p != NULL) + { + module_name = p; + } + else + { + module_name++; + break; + } + } + + // Remove the dot + p = strrchr(module_name, '.'); + *p = 0; + + // Do actual print + printf("%c: %s: ", level_c[level], module_name); + + memset(log_buf, 0, 255); + + va_list va; + va_start(va,fmt); + vsnprintf(log_buf, 255, fmt, va); + va_end(va); + + tty_print_color(log_buf, level_colors[level]); + + putchar('\n'); + tty_set_color(level_colors[0]); +} \ No newline at end of file diff --git a/libc/Makefile b/libc/Makefile new file mode 100644 index 0000000..12c001f --- /dev/null +++ b/libc/Makefile @@ -0,0 +1,67 @@ +# OS libc/libk Makefile + +Q := @ +MAKEFLAGS += --no-print-directory + +# Build directory +BUILD_DIR := build + +# Autogenerate a list of source files +SOURCEDIR := . +CFILES := $(shell find $(SOURCEDIR) -name '*.c') + +# Generate object files from source files +OBJ := $(CFILES:.c=.o) +LIBK_OBJ := $(OBJ:.o=.libk.o) +LIBC_OBJ := $(OBJ:.o=.libc.o) + +# Compiler settings +PREFIX := i386-elf- +CC := $(PREFIX)gcc +GDB := $(PREFIX)gdb +AR := $(PREFIX)ar + +# Include kernel and libs headers +INCLUDES := -I ../kernel/include \ + -I include + +# Build flags +CFLAGS := -g -m32 \ + -ffreestanding -fno-exceptions \ + -Wall -Wextra $(INCLUDES) + +CFLAGS_LIBK := $(CFLAGS) -D__is_libk +CFLAGS_LIBC := $(CFLAGS) -D__is_libc + +rebuild: clean default + +# Create build directory +default: + @mkdir -p $(BUILD_DIR) + @echo "-> Building libk" + @$(MAKE) libk.a + @echo "-> Building libc" + @$(MAKE) libc.a + +debug: default + +libk.a: $(LIBK_OBJ) + @echo "AR $@" + @$(AR) rcs $@ $(BUILD_DIR)/*.libk.o + +libc.a: $(LIBC_OBJ) + @echo "AR $@" + @$(AR) rcs $@ $(BUILD_DIR)/*.libc.o + +# To make an object, always compile from its .c +%.libk.o: %.c + @echo "CC $<" + @$(CC) $(CFLAGS_LIBK) -c $< -o $(BUILD_DIR)/$(notdir $@) + +%.libc.o: %.c + @echo "CC $<" + @$(CC) $(CFLAGS_LIBC) -c $< -o $(BUILD_DIR)/$(notdir $@) + +clean: + @echo "-> Cleaning libk/libc" + @rm -rf $(BUILD_DIR)/*.o *.a diff --git a/libc/assert/assert.c b/libc/assert/assert.c new file mode 100644 index 0000000..8ca11ed --- /dev/null +++ b/libc/assert/assert.c @@ -0,0 +1,16 @@ +#include +#include + +void assert (int expression) +{ + if (expression == 0) + { + #if defined(__is_libk) + printf("\nFATAL: kernel assert() failed!!"); + printf("\nkernel halted."); + for(;;); + #else + // TODO: Properly terminate userspace program + #endif + } +} \ No newline at end of file diff --git a/libc/include/assert.h b/libc/include/assert.h new file mode 100644 index 0000000..68715fe --- /dev/null +++ b/libc/include/assert.h @@ -0,0 +1,3 @@ +#pragma once + +void assert (int expression); \ No newline at end of file diff --git a/libc/include/stdarg.h b/libc/include/stdarg.h new file mode 100644 index 0000000..840a393 --- /dev/null +++ b/libc/include/stdarg.h @@ -0,0 +1,10 @@ +#pragma once + +// Let's cheat and use compiler definitions... + +typedef __builtin_va_list va_list; + +#define va_start(ap,last) __builtin_va_start(ap, last) +#define va_end(ap) __builtin_va_end(ap) +#define va_arg(ap,type) __builtin_va_arg(ap,type) +#define va_copy(dest, src) __builtin_va_copy(dest,src) \ No newline at end of file diff --git a/libc/include/stdbool.h b/libc/include/stdbool.h new file mode 100644 index 0000000..85cd361 --- /dev/null +++ b/libc/include/stdbool.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum +{ + false = 0, + true = 1 +} bool; \ No newline at end of file diff --git a/libc/include/stdint.h b/libc/include/stdint.h new file mode 100644 index 0000000..9cebfaf --- /dev/null +++ b/libc/include/stdint.h @@ -0,0 +1,45 @@ +#pragma once + +// TODO: not only x86... +typedef unsigned long long u64; +typedef long long s64; +typedef unsigned int u32; +typedef int s32; +typedef unsigned short u16; +typedef short s16; +typedef unsigned char u8; +typedef char s8; + +typedef u64 uint64_t; +typedef s64 int64_t; +typedef u32 uint32_t; +typedef s32 int32_t; +typedef u16 uint16_t; +typedef s16 int16_t; +typedef u8 uint8_t; +typedef s8 int8_t; + +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +typedef int8_t int_least8_t; +typedef uint8_t uint_least8_t; +typedef int16_t int_least16_t; +typedef uint16_t uint_least16_t; +typedef int32_t int_least32_t; +typedef uint32_t uint_least32_t; +typedef int64_t int_least64_t; +typedef uint64_t uint_least64_t; + +typedef int8_t int_fast8_t; +typedef uint8_t uint_fast8_t; +typedef int16_t int_fast16_t; +typedef uint16_t uint_fast16_t; +typedef int32_t int_fast32_t; +typedef uint32_t uint_fast32_t; +typedef int64_t int_fast64_t; +typedef uint64_t uint_fast64_t; + +/* Pointer size in an x86 cpu is 32 bits */ +typedef s32 intptr_t; +typedef u32 uintptr_t; \ No newline at end of file diff --git a/libc/include/stdio.h b/libc/include/stdio.h new file mode 100644 index 0000000..a9c5903 --- /dev/null +++ b/libc/include/stdio.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +#define EOF (-1) + +#ifdef __cplusplus +extern "C" { +#endif + +int vsnprintf(char *buffer, u32 buffer_len, const char *fmt, va_list va); +int snprintf(char* buffer, u32 buffer_len, const char *fmt, ...); + +int vprintf(const char *fmt, va_list va); +void printf(const char *fmt, ...); + +int putchar (int c); +int puts (char *s); + +char * itoa(int value, char * str, int base); + + // (internal functions) +u32 int_to_ascii(int value, u32 radix, u32 uppercase, u32 unsig, char *buffer, u32 zero_pad); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h new file mode 100644 index 0000000..e07549a --- /dev/null +++ b/libc/include/stdlib.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((__noreturn__)) +void abort(); + +int atoi(const char *s); + +// Allocation functions +void * malloc(size_t size); +void * calloc(size_t num, size_t size); // Warning: Implemented badly, needs rewrite +void free(void* ptr); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libc/include/string.h b/libc/include/string.h new file mode 100644 index 0000000..2c871b4 --- /dev/null +++ b/libc/include/string.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void * memchr(const void *s, int c, size_t n); +void * memcpy(void *s1, const void *s2, size_t n); +void * memset(void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +int strcmp(const char *s1, const char *s2); +char * strcat(char *s1, const char *s2); +char * strcpy(char *s1, const char *s2); +char * strncat(char *s1, const char *s2, size_t n); +char * strncpy(char *s1, const char *s2, size_t n); +char * strrchr(char * s, int c); +size_t strlen(const char *s); + +// Utils +void reverse(char *s); +void append(char *s, char n); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libc/include/sys/cdefs.h b/libc/include/sys/cdefs.h new file mode 100644 index 0000000..ed50c66 --- /dev/null +++ b/libc/include/sys/cdefs.h @@ -0,0 +1,3 @@ +#pragma once + +#define __os_libc 1 \ No newline at end of file diff --git a/libc/include/unistd.h b/libc/include/unistd.h new file mode 100644 index 0000000..a82a930 --- /dev/null +++ b/libc/include/unistd.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +u32 sleep(u32 seconds); +u32 usleep(u32 useconds); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/libc/ssp/ssp.c b/libc/ssp/ssp.c new file mode 100644 index 0000000..54fa93b --- /dev/null +++ b/libc/ssp/ssp.c @@ -0,0 +1,20 @@ +#include +#include +#include + +#define STACK_CHK_GUARD 0x73e23789 + +uintptr_t __stack_chk_guard = STACK_CHK_GUARD; + +__attribute__((noreturn)) +void __stack_chk_fail(void) +{ + #if defined(__is_libk) + printf("\nFATAL: kernel stack smashing detected!!"); + printf("\nkernel halted."); + for(;;); + #else + // TODO: Properly terminate userspace program + for(;;); + #endif +} \ No newline at end of file diff --git a/libc/stdio/int_to_ascii.c b/libc/stdio/int_to_ascii.c new file mode 100644 index 0000000..3e8ce47 --- /dev/null +++ b/libc/stdio/int_to_ascii.c @@ -0,0 +1,44 @@ +#include +#include + +u32 int_to_ascii(int value, u32 radix, u32 uppercase, u32 unsig, char *buffer, u32 zero_pad) +{ + char *pbuffer = buffer; + int negative = 0; + u32 i, len; + + /* No support for unusual radixes. */ + if (radix > 16) + return 0; + + if (value < 0 && !unsig) { + negative = 1; + value = -value; + } + + /* This builds the string back to front ... */ + do { + int digit = value % radix; + *(pbuffer++) = (digit < 10 ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10); + value /= radix; + } while (value > 0); + + for (i = (pbuffer - buffer); i < zero_pad; i++) + *(pbuffer++) = '0'; + + if (negative) + *(pbuffer++) = '-'; + + *(pbuffer) = '\0'; + + /* ... now we reverse it (could do it recursively but will + * conserve the stack space) */ + len = (pbuffer - buffer); + for (i = 0; i < len / 2; i++) { + char j = buffer[i]; + buffer[i] = buffer[len-i-1]; + buffer[len-i-1] = j; + } + + return len; +} \ No newline at end of file diff --git a/libc/stdio/itoa.c b/libc/stdio/itoa.c new file mode 100644 index 0000000..0de6fab --- /dev/null +++ b/libc/stdio/itoa.c @@ -0,0 +1,7 @@ +#include + +char * itoa(int value, char * str, int base) +{ + int_to_ascii(value, base, 0, 0, str, 0); + return str; +} \ No newline at end of file diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c new file mode 100644 index 0000000..eebb207 --- /dev/null +++ b/libc/stdio/printf.c @@ -0,0 +1,11 @@ +#include +#include +#include + +void printf(const char *fmt, ...) +{ + va_list va; + va_start(va,fmt); + vprintf(fmt, va); + va_end(va); +} \ No newline at end of file diff --git a/libc/stdio/putchar.c b/libc/stdio/putchar.c new file mode 100644 index 0000000..8eef1ad --- /dev/null +++ b/libc/stdio/putchar.c @@ -0,0 +1,17 @@ +#include + +#if defined(__is_libk) +#include +#endif + +int putchar(int c) +{ +#if defined(__is_libk) + // Kernel implementation + tty_putchar (c); +#else + // TODO: Implement stdio and the write system call. + return 1; +#endif + return c; +} \ No newline at end of file diff --git a/libc/stdio/puts.c b/libc/stdio/puts.c new file mode 100644 index 0000000..601289e --- /dev/null +++ b/libc/stdio/puts.c @@ -0,0 +1,17 @@ +#include + +#if defined(__is_libk) +#include +#endif + +int puts(char* s) +{ + #if defined(__is_libk) + // Kernel implementation + tty_print(s); + #else + // TODO: Implement stdio and the write system call. + s[0] = *s; + #endif + return 1; +} \ No newline at end of file diff --git a/libc/stdio/snprintf.c b/libc/stdio/snprintf.c new file mode 100644 index 0000000..fc92bdd --- /dev/null +++ b/libc/stdio/snprintf.c @@ -0,0 +1,14 @@ +#include +#include +#include + +int snprintf(char* buffer, u32 buffer_len, const char *fmt, ...) +{ + int ret; + va_list va; + va_start(va, fmt); + ret = vsnprintf(buffer, buffer_len, fmt, va); + va_end(va); + + return ret; +} \ No newline at end of file diff --git a/libc/stdio/vprintf.c b/libc/stdio/vprintf.c new file mode 100644 index 0000000..d48c45f --- /dev/null +++ b/libc/stdio/vprintf.c @@ -0,0 +1,77 @@ +#include +#include +#include + +int vprintf(const char *fmt, va_list va) +{ + u32 len = 0; + + char bf[40]; + char ch; + + while ((ch = *(fmt++))) + { + if (ch != '%') + { + len++; + putchar(ch); + } + else + { + char zero_pad = 0; + char *ptr; + + ch = *(fmt++); + + if(ch == '0') // Zero padding + { + ch = *(fmt++); + if(ch == '\0') + goto end; + if(ch >= '0' && ch <= '9') + zero_pad = ch - '0'; + ch = *(fmt++); + } + + if (ch == 'l' && (*(fmt) == 'u' || *(fmt) == 'd')) + ch = *(fmt++); + + switch(ch) + { + case 0: + goto end; + + case 'u': + case 'd': + len += int_to_ascii(va_arg(va, u32), 10, 0, (ch == 'u'), bf, zero_pad); + puts(bf); + break; + + case 'p': + case 'x': + case 'X': + len += int_to_ascii(va_arg(va, u32), 16, (ch == 'X'), 1, bf, zero_pad); + puts(bf); + break; + + case 'c' : + len++; + putchar((char)(va_arg(va, int))); + break; + + case 's' : + ptr = va_arg(va, char*); + len += strlen(ptr); + puts(ptr); + break; + + default: + len++; + putchar(ch); + break; + } + } + } +end: + return len; +} \ No newline at end of file diff --git a/libc/stdio/vsnprintf.c b/libc/stdio/vsnprintf.c new file mode 100644 index 0000000..420afb3 --- /dev/null +++ b/libc/stdio/vsnprintf.c @@ -0,0 +1,210 @@ +#include +#include +#include + +/* + * Based on mini vsnprintf + * Probably needs more checks +*/ + +int vsnprintf(char *buffer, u32 buffer_len, const char *fmt, va_list va) +{ + char *pbuffer = buffer; + char bf[40]; + char ch; + + int append_char(char ch) + { + if ((u32)((pbuffer - buffer) + 1) >= buffer_len) + return 0; + *(pbuffer++) = ch; + *(pbuffer) = '\0'; + return 1; + } + + int append_string(char *s, u32 len) + { + u32 i; + + if (buffer_len - (pbuffer - buffer) - 1 < len) + len = buffer_len - (pbuffer - buffer) - 1; + + /* Copy to buffer */ + for (i = 0; i < len; i++) + *(pbuffer++) = s[i]; + *(pbuffer) = '\0'; + + return len; + } + + while ((ch = *(fmt++))) + { + if ((u32)((pbuffer - buffer) + 1) >= buffer_len) + break; + + if (ch!='%') + { + append_char(ch); + } + else + { + char zero_pad = 0; + char *ptr; + u32 len; + + ch = *(fmt++); + + if(ch == '0') // Zero padding + { + ch = *(fmt++); + if(ch == '\0') + goto end; + if(ch >= '0' && ch <= '9') + zero_pad = ch - '0'; + ch = *(fmt++); + } + + if (ch == 'l' && (*(fmt) == 'u' || *(fmt) == 'd')) + ch = *(fmt++); + + switch(ch) + { + case 0: + goto end; + + case 'u': + case 'd': + len = int_to_ascii(va_arg(va, u32), 10, 0, (ch == 'u'), bf, zero_pad); + append_string(bf, len); + break; + + case 'p': + case 'x': + case 'X': + len = int_to_ascii(va_arg(va, u32), 16, (ch == 'X'), 1, bf, zero_pad); + append_string(bf, len); + break; + + case 'c' : + append_char((char)(va_arg(va, int))); + break; + + case 's' : + ptr = va_arg(va, char*); + append_string(ptr, strlen(ptr)); + break; + + default: + append_char(ch); + break; + } + } + } +end: + return pbuffer - buffer; +} + + +/* +int vsnprintf(char *buffer, u32 buffer_len, const char *fmt, va_list va) +{ + u32 len = 0; + + char *pbuffer = buffer; + char bf[40]; + char ch; + + int append_char(char ch) + { + if ((u32)((pbuffer - buffer) + 1) >= buffer_len) + return 0; + *(pbuffer++) = ch; + *(pbuffer) = '\0'; + return 1; + } + + int append_string(char *s, u32 len) + { + u32 i; + + if (buffer_len - (pbuffer - buffer) - 1 < len) + len = buffer_len - (pbuffer - buffer) - 1; + + // Copy to buffer + for (i = 0; i < len; i++) + *(pbuffer++) = s[i]; + *(pbuffer) = '\0'; + + return len; + } + + while ((ch = *(fmt++))) + { + if (ch != '%') + { + if((len + 1) < buffer_len) + buffer[len] = ch; + len++; + } + else + { + char zero_pad = 0; + char *ptr; + u32 clen; + + ch = *(fmt++); + + if(ch == '0') // Zero padding + { + ch = *(fmt++); + if(ch == '\0') + goto end; + if(ch >= '0' && ch <= '9') + zero_pad = ch - '0'; + ch = *(fmt++); + } + + if (ch == 'l' && (*(fmt) == 'u' || *(fmt) == 'd')) + ch = *(fmt++); + + switch(ch) + { + case 0: + goto end; + + case 'u': + case 'd': + clen = int_to_ascii(va_arg(va, u32), 10, 0, (ch == 'u'), bf, zero_pad); + if((len + clen) < buffer_len) + strcpy(&buffer[len], bf); + len += clen; + break; + + case 'p': + case 'x': + case 'X': + len = int_to_ascii(va_arg(va, u32), 16, (ch == 'X'), 1, bf, zero_pad); + append_string(bf, len); + break; + + case 'c' : + if((len + 1) < buffer_len) + buffer[len] = (char)(va_arg(va, int)); + len++; + break; + + case 's' : + ptr = va_arg(va, char*); + append_string(ptr, strlen(ptr)); + break; + + default: + append_char(ch); + break; + } + } + } +end: + return pbuffer - buffer; +} +*/ \ No newline at end of file diff --git a/libc/stdlib/abort.c b/libc/stdlib/abort.c new file mode 100644 index 0000000..d550526 --- /dev/null +++ b/libc/stdlib/abort.c @@ -0,0 +1,16 @@ +#include +#include + +__attribute__((__noreturn__)) +void abort() +{ +#if defined(__is_libk) + printf("KERNEL PANIC: abort();\n"); + asm volatile("hlt"); +#else + // TODO: Abnormally terminate the process as if by SIGABRT. + printf("abort()\n"); +#endif + while (1) { } + __builtin_unreachable(); +} \ No newline at end of file diff --git a/libc/stdlib/atoi.c b/libc/stdlib/atoi.c new file mode 100644 index 0000000..b8d52c5 --- /dev/null +++ b/libc/stdlib/atoi.c @@ -0,0 +1,21 @@ +#include +#include + +int atoi (const char * s) +{ + if (s == NULL) + return 0; + + int res = 0; + int sign = 1; + + if (*s == '-') // < 0 + { + sign = -1; + s++; + } + while (*s != '\0' && *s >= '0' && *s <= '9') + res = res * 10 + (*(s++) - '0'); + + return sign * res; +} \ No newline at end of file diff --git a/libc/stdlib/calloc.c b/libc/stdlib/calloc.c new file mode 100644 index 0000000..c8ff8ff --- /dev/null +++ b/libc/stdlib/calloc.c @@ -0,0 +1,20 @@ +#include +#include + +#if defined(__is_libk) +#include +#endif + +void * calloc(size_t num, size_t size) +{ +#if defined(__is_libk) + void * buff = heap_alloc(&kernel_heap, num * size); + memset(buff, 0, num * size); + return buff; +#else + // Not implemented yet. + num = num; + size = size; + return NULL; +#endif +} \ No newline at end of file diff --git a/libc/stdlib/free.c b/libc/stdlib/free.c new file mode 100644 index 0000000..0a79658 --- /dev/null +++ b/libc/stdlib/free.c @@ -0,0 +1,16 @@ +#include + +#if defined(__is_libk) +#include +#endif + +void free(void* ptr) +{ +#if defined(__is_libk) + heap_free(&kernel_heap, ptr); + return; +#else + // Not implemented yet. + ptr = ptr; +#endif +} \ No newline at end of file diff --git a/libc/stdlib/malloc.c b/libc/stdlib/malloc.c new file mode 100644 index 0000000..29305f3 --- /dev/null +++ b/libc/stdlib/malloc.c @@ -0,0 +1,16 @@ +#include + +#if defined(__is_libk) +#include +#endif + +void * malloc(size_t size) +{ +#if defined(__is_libk) + return heap_alloc(&kernel_heap, size); +#else + // Not implemented yet. + size = size; + return NULL; +#endif +} \ No newline at end of file diff --git a/libc/string/memchr.c b/libc/string/memchr.c new file mode 100644 index 0000000..8c2c06a --- /dev/null +++ b/libc/string/memchr.c @@ -0,0 +1,11 @@ +#include + +void * memchr (const void *s, int c, size_t n) +{ + while (n--) + if (*((u8 *)s) == (u8)c) + return (void *)s; + else + ++s; + return NULL; +} \ No newline at end of file diff --git a/libc/string/memcmp.c b/libc/string/memcmp.c new file mode 100644 index 0000000..eb64c8d --- /dev/null +++ b/libc/string/memcmp.c @@ -0,0 +1,8 @@ +#include + +int memcmp (const void *s1, const void *s2, size_t n) +{ + size_t i; + for (i = 0; i < n && ((u8*)s1)[i] == ((u8*)s2)[i]; ++i); + return (i == n) ? 0 : (((u8*)s1)[i] - ((u8*)s2)[i]); +} \ No newline at end of file diff --git a/libc/string/memcpy.c b/libc/string/memcpy.c new file mode 100644 index 0000000..92a48bb --- /dev/null +++ b/libc/string/memcpy.c @@ -0,0 +1,8 @@ +#include + +void * memcpy (void *s1, const void *s2, size_t n) +{ + while (n--) + *((u8 *)s1++) = *((u8 *)s2++); + return s1; +} \ No newline at end of file diff --git a/libc/string/memset.c b/libc/string/memset.c new file mode 100644 index 0000000..4e9cb57 --- /dev/null +++ b/libc/string/memset.c @@ -0,0 +1,8 @@ +#include + +void * memset (void *s, int c, size_t n) +{ + while (n--) + *((u8 *)s++) = (u8)c; + return s; +} \ No newline at end of file diff --git a/libc/string/strcat.c b/libc/string/strcat.c new file mode 100644 index 0000000..19f57eb --- /dev/null +++ b/libc/string/strcat.c @@ -0,0 +1,10 @@ +#include + +char * strcat (char *s1, const char *s2) +{ + while (*++s1) ; + while (*s2) + *s1++ = *s2++; + *s1++ = 0; + return s1; +} \ No newline at end of file diff --git a/libc/string/strcmp.c b/libc/string/strcmp.c new file mode 100644 index 0000000..c3b52b9 --- /dev/null +++ b/libc/string/strcmp.c @@ -0,0 +1,8 @@ +#include + +int strcmp (const char *s1, const char *s2) +{ + while(*s1 == *s2) + if (*(s1++) == 0 || *(s2++) == 0) return 0; + return *s1 - *s2; +} \ No newline at end of file diff --git a/libc/string/strcpy.c b/libc/string/strcpy.c new file mode 100644 index 0000000..aa80396 --- /dev/null +++ b/libc/string/strcpy.c @@ -0,0 +1,9 @@ +#include + +char * strcpy (char *s1, const char *s2) +{ + while (*s2) + *(s1++) = *(s2++); + *(s1++) = 0; + return s1; +} diff --git a/libc/string/strlen.c b/libc/string/strlen.c new file mode 100644 index 0000000..6438738 --- /dev/null +++ b/libc/string/strlen.c @@ -0,0 +1,8 @@ +#include + +size_t strlen (const char *s) +{ + size_t n = 0; + while (*(s++)) ++n; + return n; +} \ No newline at end of file diff --git a/libc/string/strncat.c b/libc/string/strncat.c new file mode 100644 index 0000000..f7abddc --- /dev/null +++ b/libc/string/strncat.c @@ -0,0 +1,10 @@ +#include + +char * strncat (char *s1, const char *s2, size_t n) +{ + while (*++s1) ; + while (n-- && *s2) + *s1++ = *s2++; + *s1++ = 0; + return s1; +} \ No newline at end of file diff --git a/libc/string/strncpy.c b/libc/string/strncpy.c new file mode 100644 index 0000000..433fefd --- /dev/null +++ b/libc/string/strncpy.c @@ -0,0 +1,11 @@ +#include + +char * strncpy (char *s1, const char *s2, size_t n) +{ + size_t i; + for (i = 0; i < n && s2[i] != '\0'; i++) + s1[i] = s2[i]; + if (i < n) + memset(&s1[i], 0 , n - i); + return s1; +} \ No newline at end of file diff --git a/libc/string/strrchr.c b/libc/string/strrchr.c new file mode 100644 index 0000000..74f2ad8 --- /dev/null +++ b/libc/string/strrchr.c @@ -0,0 +1,9 @@ +#include + +char * strrchr(char * s, int c) +{ + for(size_t i = strlen(s); i != 0; i--) + if (s[i] == (char)c) + return &s[i]; + return NULL; +} \ No newline at end of file diff --git a/libc/string/utils.c b/libc/string/utils.c new file mode 100644 index 0000000..43e76af --- /dev/null +++ b/libc/string/utils.c @@ -0,0 +1,18 @@ +#include + +void reverse(char *s) +{ + int c, i, j; + for (i = 0, j = strlen(s)-1; i < j; i++, j--) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } +} + +void append(char *s, char n) +{ + int len = strlen(s); + s[len] = n; + s[len+1] = '\0'; +} \ No newline at end of file diff --git a/libc/unistd/sleep.c b/libc/unistd/sleep.c new file mode 100644 index 0000000..8880da9 --- /dev/null +++ b/libc/unistd/sleep.c @@ -0,0 +1,16 @@ +#include + +#if defined(__is_libk) +#include +#endif + +u32 sleep(u32 seconds) +{ +#if defined(__is_libk) + // Kernel implementation + timer_wait(1000 * seconds); +#else + // TODO: Implement stdio and the write system call. +#endif + return seconds; +} \ No newline at end of file diff --git a/libc/unistd/usleep.c b/libc/unistd/usleep.c new file mode 100644 index 0000000..7ced812 --- /dev/null +++ b/libc/unistd/usleep.c @@ -0,0 +1,16 @@ +#include + +#if defined(__is_libk) +#include +#endif + +u32 usleep(u32 useconds) +{ +#if defined(__is_libk) + // Kernel implementation + timer_wait(useconds / 1000); // Ehm.... (shitty way) +#else + // TODO: Implement stdio and the write system call. +#endif + return useconds; +} \ No newline at end of file diff --git a/utils/initrd.img b/utils/initrd.img new file mode 100644 index 0000000..9e54f6f Binary files /dev/null and b/utils/initrd.img differ diff --git a/utils/initrd/I'm a file.file b/utils/initrd/I'm a file.file new file mode 100644 index 0000000..f9de762 --- /dev/null +++ b/utils/initrd/I'm a file.file @@ -0,0 +1 @@ +File!! \ No newline at end of file diff --git a/utils/initrd/hello_world.txt b/utils/initrd/hello_world.txt new file mode 100644 index 0000000..bc7774a --- /dev/null +++ b/utils/initrd/hello_world.txt @@ -0,0 +1 @@ +hello world! \ No newline at end of file diff --git a/utils/mkinitrd b/utils/mkinitrd new file mode 100644 index 0000000..2c9ff37 Binary files /dev/null and b/utils/mkinitrd differ diff --git a/utils/mkinitrd.c b/utils/mkinitrd.c new file mode 100644 index 0000000..05ff848 --- /dev/null +++ b/utils/mkinitrd.c @@ -0,0 +1,138 @@ +/* + * mkinitrd.c + * Creates an initrd file from a directory +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma pack(1) + + +typedef struct +{ + char magic[8]; + uint32_t entry_count; +} initrd_header_t; + +typedef struct +{ + char name[128]; + uint32_t offset; + uint32_t lenght; +} initrd_file_desc_t; + +/* + * -- initrd layout -- + * header + * descriptors + * raw files +*/ + +int main(int argc, char *argv[]) +{ + printf("\nmkinitrd v0.1\n"); + if (argc < 2) + { + printf("Usage: mkinitrd [initrd directory] [initrd file]\n"); + return -1; + } + + DIR *dir = opendir(argv[1]); + assert(dir != NULL); + rewinddir(dir); + + int initrd_fd = open(argv[2], O_RDWR | O_CREAT | O_SYNC); + assert(initrd_fd > 0); + lseek(initrd_fd, 0, SEEK_SET); + + /* Create the header */ + printf("\n\nWriting the header...\n"); + + // Get file count + uint32_t file_count = 0; + struct dirent *ent; + + while((ent = readdir(dir)) != NULL) + if (ent->d_name[0] != '.') + file_count++; + + // Create an header + initrd_header_t * header = calloc(1, sizeof(initrd_header_t)); + strncpy(header->magic, "INITRD!", 8); + header->entry_count = file_count; + + printf("- magic: %s\n", header->magic); + printf("- entry count: %u\n", header->entry_count); + + // Write the header + write(initrd_fd, header, sizeof(initrd_header_t)); + free(header); + + // Rewind the directory + rewinddir(dir); + + /* Write the files */ + printf("\nWriting files to initrd...\n"); + + uint32_t current_offset = 0; + while ((ent = readdir(dir)) != NULL) + { + if (ent->d_name[0] == '.') + { + ent = readdir(dir); + continue; + } + + printf("\nWriting %s ", ent->d_name); + + // Open the file + char * f_path = malloc(strlen(argv[1]) + strlen(ent->d_name) + 30); + sprintf(f_path, "%s/%s", argv[1], ent->d_name); + int current_fd = open(f_path, O_RDONLY | O_SYNC); + free(f_path); + + // Get file's size + uint32_t f_lenght = lseek(current_fd, 0, SEEK_END); + lseek(current_fd, 0, SEEK_SET); + + // Create a file descriptor + initrd_file_desc_t * desc = malloc(sizeof(initrd_file_desc_t)); + memset(desc, 0, sizeof(initrd_file_desc_t)); + + strncpy(desc->name, ent->d_name, 128); + desc->offset = current_offset; + desc->lenght = f_lenght; + printf("(lenght: %u,", desc->lenght); + printf(" offset: %u)\n", desc->offset); + + // Write the descriptor + printf("- writing descriptor at offset %lu\n", lseek(initrd_fd, 0, SEEK_CUR)); // Write the descriptor at current offset + write(initrd_fd, desc, sizeof(initrd_file_desc_t)); + + // Write the file + uint32_t desc_pos = lseek(initrd_fd, 0, SEEK_CUR); // Save next descriptor's offset + uint32_t f_offset = sizeof(initrd_header_t) + (sizeof(initrd_file_desc_t) * file_count) + current_offset; // Calculate file's offset + lseek(initrd_fd, f_offset, SEEK_SET); // Set file's offset + + printf("- writing file at offset %lu\n", lseek(initrd_fd, 0, SEEK_CUR)); + sendfile(initrd_fd, current_fd, NULL, f_lenght); // Write the file + lseek(initrd_fd, desc_pos, SEEK_SET); // Go back to next descriptor's offset + + current_offset += f_lenght; // Prepare next file's offset + + // Cleanup + free(desc); + close(current_fd); + } + + close(initrd_fd); + closedir(dir); +} \ No newline at end of file