commit 6d366537ddc682573709a8d5733425a113d91678 Author: David Goeke Date: Wed May 28 14:41:02 2025 -0500 A update diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7955e0 --- /dev/null +++ b/Makefile @@ -0,0 +1,76 @@ +# === Config === +TARGET := boot/espresso.bin +ISO := boot/espresso.iso +CC := i686-elf-gcc +AS := i686-elf-as +NASM := nasm +QEMU_MKE_IMG := qemu-img create -f raw espresso.img 64M +NASMFLAGS := -f elf32 +WNOFLAGS := -Wno-discarded-qualifiers +CFLAGS := -std=gnu99 -ffreestanding -O2 -Wall -Wextra -msse $(WNOFLAGS) +LDFLAGS := -T linker.ld -ffreestanding -O2 -nostdlib +QEMUFLAGS := -device isa-debug-exit -serial stdio -kernel $(TARGET) -drive file=espresso.img,format=raw,if=ide,readonly=off,rerror=report,werror=report -cpu qemu32,sse2 #-singlestep +SRC_DIRS := kernel drivers lib +INCLUDE_DIRS := include +INCLUDES := $(addprefix -I, $(INCLUDE_DIRS)) +ISO_DIR := isodir +BOOT_DIR := $(ISO_DIR)/boot +GRUB_DIR := $(BOOT_DIR)/grub +GRUB_CFG := grub.cfg + +# === File collection === +C_SRCS := $(foreach dir, $(SRC_DIRS), $(shell find $(dir) -name '*.c')) +S_SRCS := boot.s crti.s crtn.s misc_asm.s +NASM_SRCS := idt.asm isr128.asm gdt.asm isr_stub_table.asm pit.asm +CRTI_OBJ := crti.o +CRTBEGIN_OBJ := $(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o) +CRTEND_OBJ := $(shell $(CC) $(CFLAGS) -print-file-name=crtend.o) +CRTN_OBJ := crtn.o +OBJS := $(C_SRCS:.c=.o) $(S_SRCS:.s=.o) $(NASM_SRCS:.asm=.o) + +OBJ_LINK_LIST := $(CRTI_OBJ) $(CRTBEGIN_OBJ) $(OBJS) $(CRTEND_OBJ) $(CRTN_OBJ) +INTERNAL_OBJS := $(CRTI_OBJ) $(OBJS) $(CRTN_OBJ) + +# === Build all targets === +build: all iso run + +# === Default target === +all: $(TARGET) + +# === Linking === +$(TARGET): boot.o $(filter-out boot.o, $(OBJ_LINK_LIST)) + @mkdir -p $(dir $@) + $(CC) $(LDFLAGS) -o $@ $^ -lgcc + +# === Compiling C files === +%.o: %.c + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +# === Assembling S files === +%.o: %.s + $(AS) $< -o $@ + +%.o: %.asm + $(NASM) $(NASMFLAGS) $< -o $@ + +# === ISO generation === +iso: $(TARGET) + @grub-file --is-x86-multiboot $(TARGET) && echo "multiboot confirmed" || (echo "not multiboot" && exit 1) + mkdir -p $(GRUB_DIR) + cp $(TARGET) $(BOOT_DIR)/ + cp $(GRUB_CFG) $(GRUB_DIR)/ + grub-mkrescue -o $(ISO) $(ISO_DIR) + +# === Run in QEMU === +run: iso + $(QEMU_MKE_IMG) + qemu-system-i386 $(QEMUFLAGS) + +# === Clean all build artifacts === +clean: + rm -f $(INTERNAL_OBJS) $(TARGET) $(ISO) + rm -rf $(ISO_DIR) + rm -f espresso.img + +.PHONY: all clean iso run + diff --git a/boot.s b/boot.s new file mode 100644 index 0000000..0cb7ffd --- /dev/null +++ b/boot.s @@ -0,0 +1,212 @@ +/* Declare constants for the multiboot header. */ +.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ +.set MEMINFO, 1<<1 /* provide memory map */ +.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ +.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ +.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */ + +/* +Declare a multiboot header that marks the program as a kernel. These are magic +values that are documented in the multiboot standard. The bootloader will +search for this signature in the first 8 KiB of the kernel file, aligned at a +32-bit boundary. The signature is in its own section so the header can be +forced to be within the first 8 KiB of the kernel file. +*/ +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +/* +The multiboot standard does not define the value of the stack pointer register +(esp) and it is up to the kernel to provide a stack. This allocates room for a +small stack by creating a symbol at the bottom of it, then allocating 16384 +bytes for it, and finally creating a symbol at the top. The stack grows +downwards on x86. The stack is in its own section so it can be marked nobits, +which means the kernel file is smaller because it does not contain an +uninitialized stack. The stack on x86 must be 16-byte aligned according to the +System V ABI standard and de-facto extensions. The compiler will assume the +stack is properly aligned and failure to align the stack will result in +undefined behavior. +*/ +.section .bss +.align 16 +stack_bottom: +.skip 16384 # 16 KiB +stack_top: + +/* +The linker script specifies _start as the entry point to the kernel and the +bootloader will jump to this position once the kernel has been loaded. It +doesn't make sense to return from this function as the bootloader is gone. +*/ +.section .text + +.global _start +.global _kernel_early +/*.global loadPageDirectory +.global enablePaging*/ + +.type _start, @function + +/*load_page_directory: + push %ebp + mov %esp, %ebp + mov 8(%esp), %eax + mov %eax, %cr3 + mov %ebp, %esp + pop %ebp + ret + +enable_paging: + push %ebp + mov %esp, %ebp + mov %cr0, %eax + or $0x80000000, %eax + mov %eax, %cr0 + mov %ebp, %esp + pop %ebp + ret*/ + +enable_sse_asm: + push %eax + push %ebx + push %ecx + push %edx + + # Check CPUID support + pushf + pop %eax + mov %eax, %ecx + xor $0x200000, %eax + push %eax + popf + pushf + pop %eax + xor %ecx, %eax + jz .no_cpuid + + # Check for SSE + mov $1, %eax + cpuid + test $0x02000000, %edx + jz .no_sse + + # Enable SSE + mov %cr0, %eax + and $~0x4, %eax # Clear EM (bit 2) + or $0x2, %eax # Set MP (bit 1) + mov %eax, %cr0 + + mov %cr4, %eax + or $0x600, %eax # Set OSFXSR | OSXMMEXCPT + mov %eax, %cr4 + + lea sse_initialized, %ebx + movl $1, (%ebx) + +.no_sse: +.no_cpuid: + pop %edx + pop %ecx + pop %ebx + pop %eax + ret + +_kernel_early: + call _init + + /* + TODO: add more stuff here that needs to be ran before the main kernel code. + */ + + ret + +_start: + /* + The bootloader has loaded us into 32-bit protected mode on a x86 + machine. Interrupts are disabled. Paging is disabled. The processor + state is as defined in the multiboot standard. The kernel has full + control of the CPU. The kernel can only make use of hardware features + and any code it provides as part of itself. There's no printf + function, unless the kernel provides its own header and a + printf implementation. There are no security restrictions, no + safeguards, no debugging mechanisms, only what the kernel provides + itself. It has absolute and complete power over the + machine. + */ + + /* + To set up a stack, we set the esp register to point to the top of the + stack (as it grows downwards on x86 systems). This is necessarily done + in assembly as languages such as C cannot function without a stack. + */ + movl $stack_top, %esp + andl $0xFFFFFFF0, %esp + movl %esp, %ebp + + + /* + This is a good place to initialize crucial processor state before the + high-level kernel is entered. It's best to minimize the early + environment where crucial features are offline. Note that the + processor is not fully initialized yet: Features such as floating + point instructions and instruction set extensions are not initialized + yet. The GDT should be loaded here. Paging should be enabled here. + C++ features such as global constructors and exceptions will require + runtime support to work as well. + */ + + cli /* Just in case */ + + call enable_sse_asm + + push %eax + push %ebx + + + /* + Call _kernel_early, early low-level initialization will happen there; + please note that while _kernel_early is written in assembler, + kernel_early is written in C. (kernel_early is called by _kernel_early, don't be confused. ;) ) + */ + call _kernel_early + + /* + Enter the high-level kernel. The ABI requires the stack is 16-byte + aligned at the time of the call instruction (which afterwards pushes + the return pointer of size 4 bytes). The stack was originally 16-byte + aligned above and we've pushed a multiple of 16 bytes to the + stack since (pushed 0 bytes so far), so the alignment has thus been + preserved and the call is well defined. + */ + call kernel_main + + /* + If the system has nothing more to do, put the computer into an + infinite loop. To do that: + 1) Disable interrupts with cli (clear interrupt enable in eflags). + They are already disabled by the bootloader, so this is not needed. + Mind that you might later enable interrupts and return from + kernel_main (which is sort of nonsensical to do). + 2) Wait for the next interrupt to arrive with hlt (halt instruction). + Since they are disabled, this will lock up the computer. + 3) Jump to the hlt instruction if it ever wakes up due to a + non-maskable interrupt occurring or due to system management mode. + */ + cli +1: hlt + jmp 1b + +/* +Set the size of the _start symbol to the current location '.' minus its start. +This is useful when debugging or when you implement call tracing. +*/ +.size _start, . - _start + +.section .data + +.global sse_initialized + +sse_initialized: .word 0 diff --git a/crti.s b/crti.s new file mode 100644 index 0000000..cebea3e --- /dev/null +++ b/crti.s @@ -0,0 +1,15 @@ +.section .init +.global _init +.type _init, @function +_init: + push %ebp + movl %esp, %ebp + /* gcc will nicely put the contents of crtbegin.o's .init section here. */ + +.section .fini +.global _fini +.type _fini, @function +_fini: + push %ebp + movl %esp, %ebp + /* gcc will nicely put the contents of crtbegin.o's .fini section here. */ diff --git a/crtn.s b/crtn.s new file mode 100644 index 0000000..33957bf --- /dev/null +++ b/crtn.s @@ -0,0 +1,9 @@ +.section .init + /* gcc will nicely put the contents of crtend.o's .init section here. */ + popl %ebp + ret + +.section .fini + /* gcc will nicely put the contents of crtend.o's .fini section here. */ + popl %ebp + ret diff --git a/docs_and_notes/note_about_the_PS2_keyboard.txt b/docs_and_notes/note_about_the_PS2_keyboard.txt new file mode 100644 index 0000000..43dd4b1 --- /dev/null +++ b/docs_and_notes/note_about_the_PS2_keyboard.txt @@ -0,0 +1,7 @@ +Notes about the PS/2 keyboard driver. + + +the actual key-codes are not what you might expect. +what I mean is, what does 0x3F mean? what key was pressed? +The keyset used by Espresso is pretty much the ASCII code set. +Minus a few codes, some are replaced with other functions. diff --git a/drivers/ahci.c b/drivers/ahci.c new file mode 100644 index 0000000..af8c284 --- /dev/null +++ b/drivers/ahci.c @@ -0,0 +1 @@ +#include diff --git a/drivers/ehci.c b/drivers/ehci.c new file mode 100644 index 0000000..2543e69 --- /dev/null +++ b/drivers/ehci.c @@ -0,0 +1,29 @@ +#include + + +#define EHCI_BASE_ADDR 0xF0000000 // Example, your base address will be different + +// EHCI Register Offsets (from EHCI spec) +#define EHCI_CAPLENGTH_OFFSET 0x00 +#define EHCI_HCIVERSION_OFFSET 0x02 +#define EHCI_CONTROL_OFFSET 0x04 +#define EHCI_STATUS_OFFSET 0x08 +#define EHCI_COMMAND_OFFSET 0x10 + +// Registers access +uint32_t read_ehci_register(uint32_t* base, uint32_t offset) { + return *(volatile uint32_t*)(base + offset); +} + +void write_ehci_register(uint32_t* base, uint32_t offset, uint32_t value) { + *(volatile uint32_t*)(base + offset) = value; +} + +// Initialize EHCI controller +void initialize_ehci(uint32_t* base) { + uint32_t control = read_ehci_register(base, EHCI_CONTROL_OFFSET); + control |= (1 << 1); // Enable controller + write_ehci_register(base, EHCI_CONTROL_OFFSET, control); + + // Set up frame list pointer and other initialization steps as per EHCI specs +} diff --git a/drivers/fs/duckfs.c b/drivers/fs/duckfs.c new file mode 100644 index 0000000..b201580 --- /dev/null +++ b/drivers/fs/duckfs.c @@ -0,0 +1,220 @@ +#include + +#include +#include +#include + +#include + + +static uint16_t duckfs_initialized = 0; + +static duckfs_file_header_t duckfs_root; + +const char* duckfs_versions[18] = { DFS_VERSION_0, DFS_VERSION_1, DFS_VERSION_2, DFS_VERSION_3, DFS_VERSION_4 }; + +int32_t duckfs_init(int16_t drive) +{ + char duckfs_header_block[512]; + if (ide_read48(drive, 0xA, 1, duckfs_header_block) != 0) + { + printf("[ DEBUG ] Disk read error on drive #%i\n", drive); + return -1; + } + + duckfs_superblock_t* superblock = (duckfs_superblock_t*)duckfs_header_block; + /* + if (superblock->duckfs_magic != (int32_t) DFS_MAGIC) + { + return -2; + } + + bool compliant = false; + for (int16_t i = 0; i < 5; ++i) + { + if (strcmp(superblock->duckfs_version_string, duckfs_versions[i]) == 0) + { + compliant = true; + } + } + + if (compliant == false) + { + return -3; + }*/ + + memset(&duckfs_root, 0, sizeof(duckfs_root)); + + strncpy(duckfs_root.filename, "/", DFS_MAX_FILENAME_LEN); + strncpy(duckfs_root.permissions, "RW", sizeof(duckfs_root.permissions)); + + duckfs_root.num_sectors = -1; + duckfs_root.type = DFS_FILE_DIRECTORY; + duckfs_root.next = NULL; + duckfs_root.prev = NULL; + duckfs_root.contents = NULL; + duckfs_root.lba_start = 0; + duckfs_root.lba_end = 0; + + duckfs_root.contents = malloc(sizeof(duckfs_file_header_t) * DFS_MAX_FILES); + if (!duckfs_root.contents) { + printf("Memory allocation failed for duckfs_root.contents\n"); + return -4; + } + + for (int32_t i = 0; i < DFS_MAX_FILES; i++) { + duckfs_root.contents[i].type = DFS_FILE_UNUSED; + } + + + duckfs_initialized = 1; + return 0; +} + + +void duckfs_format(int16_t drive) +{ + /* Nothing to do, DuckFS does not require formatting. */ +} + +int32_t duckfs_makedir(const char* filename, const char* perms) +{ + duckfs_file_header_t* dir = malloc(sizeof(duckfs_file_header_t)); + if (!dir) + { + return -1; + } + + if (strlen(perms) < 3) + { + return -2; + } + + char p[3] = { "." }; + p[0] = perms[0]; + p[1] = perms[1]; + p[2] = perms[2]; + + char filen[DFS_MAX_FILENAME_LEN + 1]; + char perma[3]; + + strcpy(filen, filename); + + + strcpy(perma, perms); + + strncpy(dir->filename, filen, sizeof(dir->filename) - 1); + dir->filename[sizeof(dir->filename) - 1] = '\0'; + + strncpy(dir->permissions, p, sizeof(dir->permissions)); + + dir->type = DFS_FILE_DIRECTORY; + dir->contents = malloc(sizeof(duckfs_file_header_t) * DFS_MAX_FILES); + + if (!dir->contents) { + free(dir); + return -1; + } + + for (int i = 0; i < DFS_MAX_FILES; i++) { + dir->contents[i].type = DFS_FILE_UNUSED; + } + + return 0; +} + +void duckfs_free_directory(duckfs_file_header_t* dir) +{ + if (!dir || dir->type != DFS_FILE_DIRECTORY || !dir->contents) + { + return; + } + + for (int i = 0; i < DFS_MAX_FILES; i++) { + duckfs_file_header_t* entry = &dir->contents[i]; + + if (entry->type == DFS_FILE_UNUSED) + { + continue; + } + + if (entry->type == DFS_FILE_DIRECTORY) { + duckfs_free_directory(entry); + } + } + + free(dir->contents); + dir->contents = NULL; +} + + +bool duckfs_add_entry(duckfs_file_header_t* dir, duckfs_file_header_t* entry) +{ + if (dir->type != DFS_FILE_DIRECTORY || !dir->contents) + { + return false; + } + + for (int i = 0; i < DFS_MAX_FILES; i++) { + if (dir->contents[i].type == DFS_FILE_UNUSED) { + dir->contents[i] = *entry; + return true; + } + } + return false; /* Directory full */ +} + +duckfs_file_header_t duckfs_create_entry(const char* name, const char* perms, const char* type) +{ + duckfs_file_header_t entry = {0}; + + strncpy(entry.filename, name, DFS_MAX_FILENAME_LEN); + strncpy(entry.permissions, perms, sizeof(entry.permissions) - 1); + + int16_t itype = DFS_FILE_UNUSED; + + if (strcmp(type, "text") == 0) + { + itype = DFS_FILE_TEXT; + } + else if (strcmp(type, "bin") == 0) + { + itype = DFS_FILE_BINARY; + } + else if (strcmp(type, "dir") == 0) + { + itype = DFS_FILE_BINARY; + } + else + { + duckfs_file_header_t vvvv = { .type = DFS_FILE_ERROR }; + return vvvv; + } + + entry.num_sectors = 0; + entry.type = itype; + entry.next = NULL; + entry.prev = NULL; + entry.contents = NULL; + entry.lba_start = 0; + entry.lba_end = 0; + + if (itype == DFS_FILE_DIRECTORY) + { + entry.contents = malloc(sizeof(duckfs_file_header_t) * DFS_MAX_FILES); + if (entry.contents) + { + for (int i = 0; i < DFS_MAX_FILES; i++) + { + entry.contents[i].type = DFS_FILE_UNUSED; + } + } + } + + return entry; +} + +duckfs_file_header_t duckfs_makefile(const char* filename, const char* perms, duckfs_file_header_t parent) +{ + +} diff --git a/drivers/fs/fat32.c b/drivers/fs/fat32.c new file mode 100644 index 0000000..ffa17aa --- /dev/null +++ b/drivers/fs/fat32.c @@ -0,0 +1,428 @@ +#include +#include +#include + +#include + +struct __attribute__((packed)) fat32_bpb { + uint8_t jmp[3]; + uint8_t oem[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sector_count; + uint8_t num_fats; + uint16_t root_entry_count; + uint16_t total_sectors_16; + uint8_t media; + uint16_t fat_size_16; + uint16_t sectors_per_track; + uint16_t num_heads; + uint32_t hidden_sectors; + uint32_t total_sectors_32; + uint32_t fat_size_32; + uint16_t ext_flags; + uint16_t fs_version; + uint32_t root_cluster; +}; + +static int32_t fat32_drive = 0; +static struct fat32_bpb bpb; +static uint32_t fat_start_lba; +static uint32_t cluster_heap_lba; +static uint32_t current_directory_cluster; + +static uint8_t sector[512]; + +int32_t fat32_init(int32_t drive) +{ + fat32_drive = drive; + int err = ide_read48(drive, 0, 1, sector); + if (err != 0) { + printf("ide_read48 failed on sector 0 (err = %d)\n", err); + return -1; + } + + memcpy(&bpb, sector, sizeof(bpb)); + + + fat_start_lba = bpb.reserved_sector_count; + cluster_heap_lba = fat_start_lba + bpb.num_fats * bpb.fat_size_32; + current_directory_cluster = bpb.root_cluster; + printf("Bytes per sector: %u\n", bpb.bytes_per_sector); + printf("Sectors per cluster: %u\n", bpb.sectors_per_cluster); + printf("Reserved sectors: %u\n", bpb.reserved_sector_count); + printf("Number of FATs: %u\n", bpb.num_fats); + printf("FAT size (32): %u\n", bpb.fat_size_32); + printf("Root cluster: %u\n", bpb.root_cluster); + + return 0; +} + +static uint32_t cluster_to_lba(uint32_t cluster) +{ + return cluster_heap_lba + (cluster - 2) * bpb.sectors_per_cluster; +} + +static void format_83_name(const char *input, char out[11]) { + memset(out, ' ', 11); + int i = 0, j = 0; + + // Copy name part (up to 8 chars) + while (input[i] && input[i] != '.' && j < 8) { + out[j++] = toupper((unsigned char)input[i++]); + } + + // Skip dot + if (input[i] == '.') i++; + + // Copy extension (up to 3 chars) + int k = 8; + while (input[i] && j < 11) { + out[k++] = toupper((unsigned char)input[i++]); + j++; + } +} + +static uint32_t find_free_cluster() { + // Read FAT sectors one by one + for (uint32_t cluster = 2; cluster < bpb.total_sectors_32; cluster++) { + uint32_t fat_sector = fat_start_lba + (cluster * 4) / 512; + uint32_t fat_offset = (cluster * 4) % 512; + + if (ide_read48(fat32_drive, fat_sector, 1, sector) != 0) + return 0; + + uint32_t entry = *(uint32_t *)(sector + fat_offset) & 0x0FFFFFFF; + if (entry == 0x00000000) { + return cluster; + } + } + return 0; // No free cluster found +} + + + +int32_t fat32_list_root(void) +{ + uint32_t lba = cluster_to_lba(current_directory_cluster); + if (ide_read48(fat32_drive, lba, 1, sector) != 0) + { + return -1; + } + + for (int i = 0; i < 512; i += 32) + { + char *entry = (char *)§or[i]; + if ((uint8_t)entry[0] == 0x00) + { + break; + } + + if ((uint8_t)entry[0] == 0xE5) + { + continue; + } + + char name[12]; + memcpy(name, entry, 11); + name[11] = 0; + for (int j = 0; j < 11; j++) + { + if (name[j] == ' ') + { + name[j] = 0; + } + } + + uint8_t attr = entry[11]; + if (attr & FAT32_ATTR_DIRECTORY) + { + printf(" %s\n", name); + } + else + { + printf(" %s\n", name); + } + } + return 0; +} + +int32_t fat32_read_file(const char *name, uint8_t *buffer, uint32_t max_len) +{ + uint32_t lba = cluster_to_lba(current_directory_cluster); + if (ide_read48(fat32_drive, lba, 1, sector) != 0) + { + return -1; + } + + for (int32_t i = 0; i < 512; i += 32) + { + char *entry = (char *)§or[i]; + if ((uint8_t)entry[0] == 0x00) + { + break; + } + if ((uint8_t)entry[0] == 0xE5) + { + continue; + } + + char entry_name[12]; + memcpy(entry_name, entry, 11); + entry_name[11] = 0; + for (int32_t j = 0; j < 11; j++) + { + if (entry_name[j] == ' ') + { + entry_name[j] = 0; + } + } + + if (strncmp(entry_name, name, 11) != 0) + { + continue; + } + + uint32_t cluster = (*(uint16_t *)(entry + 26)) | ((*(uint16_t *)(entry + 20)) << 16); + uint32_t size = *(uint32_t *)(entry + 28); + uint32_t bytes_read = 0; + + while (cluster < 0x0FFFFFF8 && bytes_read < max_len && bytes_read < size) + { + uint32_t lba = cluster_to_lba(cluster); + if (ide_read48(fat32_drive, lba, bpb.sectors_per_cluster, buffer + bytes_read) != 0) + { + return -1; + } + + bytes_read += bpb.sectors_per_cluster * 512; + + uint32_t fat_sector = cluster * 4 / 512; + uint32_t fat_offset = cluster * 4 % 512; + if (ide_read48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0) + { + return -1; + } + + cluster = *(uint32_t *)(sector + fat_offset) & 0x0FFFFFFF; + } + + return bytes_read; + } + return -2; +} + +int32_t fat32_write_file(const char *name, const uint8_t *buffer, uint32_t len) +{ + uint32_t lba = cluster_to_lba(current_directory_cluster); + if (ide_read48(fat32_drive, lba, 1, sector) != 0) + { + return -1; + } + + for (int32_t i = 0; i < 512; i += 32) + { + char *entry = (char *)§or[i]; + if ((uint8_t)entry[0] == 0x00) break; + if ((uint8_t)entry[0] == 0xE5) continue; + + char entry_name[12]; + memcpy(entry_name, entry, 11); + entry_name[11] = 0; + for (int j = 0; j < 11; j++) + { + if (entry_name[j] == ' ') + { + entry_name[j] = 0; + } + } + + if (strncmp(entry_name, name, 11) != 0) + { + continue; + } + + uint32_t cluster = (*(uint16_t *)(entry + 26)) | ((*(uint16_t *)(entry + 20)) << 16); + uint32_t bytes_written = 0; + + while (cluster < 0x0FFFFFF8 && bytes_written < len) + { + uint32_t lba = cluster_to_lba(cluster); + if (ide_write48(fat32_drive, lba, bpb.sectors_per_cluster, buffer + bytes_written) != 0) + { + return -1; + } + + bytes_written += bpb.sectors_per_cluster * 512; + + if (bytes_written < len) + { + uint32_t fat_sector = cluster * 4 / 512; + uint32_t fat_offset = cluster * 4 % 512; + + if (ide_read48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0) + { + return -1; + } + + uint32_t next_cluster = find_free_cluster(); + if (next_cluster == 0) + { + return -3; + } + + *(uint32_t *)(sector + fat_offset) = next_cluster & 0x0FFFFFFF; + + if (ide_write48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0) + { + return -1; + } + + cluster = next_cluster; + } + else + { + uint32_t fat_sector = cluster * 4 / 512; + uint32_t fat_offset = cluster * 4 % 512; + + if (ide_read48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0) + { + return -1; + } + + *(uint32_t *)(sector + fat_offset) = 0x0FFFFFFF; + + if (ide_write48(fat32_drive, fat_start_lba + fat_sector, 1, sector) != 0) + { + return -1; + } + + break; + } + } + + *(uint32_t *)(entry + 28) = len; + if (ide_write48(fat32_drive, lba, 1, sector) != 0) + { + return -1; + } + + return bytes_written; + } + return -2; +} + +int32_t fat32_create_file(const char *name) { + uint32_t root_lba = cluster_to_lba(bpb.root_cluster); + + // Read root directory cluster sector (assuming 1 sector here for simplicity) + if (ide_read48(fat32_drive, root_lba, 1, sector) != 0) + return -1; + + char formatted_name[11]; + format_83_name(name, formatted_name); + + for (int32_t i = 0; i < 512; i += 32) { + uint8_t *entry = §or[i]; + if (entry[0] == 0x00 || entry[0] == 0xE5) { + // Found free directory entry, clear it + memset(entry, 0, 32); + + // Copy formatted filename + memcpy(entry, formatted_name, 11); + + // Set attributes to Archive (normal file) + entry[11] = 0x20; + + // Allocate first cluster for the file + uint32_t free_cluster = find_free_cluster(); + if (free_cluster == 0) { + return -2; // No free cluster + } + + // Mark cluster as end-of-chain in FAT + uint32_t fat_sector = fat_start_lba + (free_cluster * 4) / 512; + uint32_t fat_offset = (free_cluster * 4) % 512; + if (ide_read48(fat32_drive, fat_sector, 1, sector) != 0) + return -1; + + *(uint32_t *)(sector + fat_offset) = 0x0FFFFFFF; // end of cluster chain + if (ide_write48(fat32_drive, fat_sector, 1, sector) != 0) + return -1; + + // Set first cluster in directory entry (split high and low 16 bits) + entry[20] = (free_cluster >> 16) & 0xFF; + entry[21] = (free_cluster >> 24) & 0xFF; + entry[26] = free_cluster & 0xFF; + entry[27] = (free_cluster >> 8) & 0xFF; + + // Set file size = 0 + entry[28] = 0; + entry[29] = 0; + entry[30] = 0; + entry[31] = 0; + + // Write back directory sector + if (ide_write48(fat32_drive, root_lba, 1, sector) != 0) + return -1; + + return 0; // Success + } + } + + return -3; // No free directory entry found +} + +int32_t fat32_create_directory(const char *name) { + uint32_t lba = cluster_to_lba(current_directory_cluster); + if (ide_read48(fat32_drive, lba, 1, sector) != 0) + return -1; + + for (int32_t i = 0; i < 512; i += 32) { + char *entry = (char *)§or[i]; + if ((uint8_t)entry[0] == 0x00 || (uint8_t)entry[0] == 0xE5) { + memset(entry, 0, 32); + memcpy(entry, name, strlen(name) > 11 ? 11 : strlen(name)); + entry[11] = FAT32_ATTR_DIRECTORY; + + uint32_t cluster = 6; + *(uint16_t *)(entry + 26) = cluster & 0xFFFF; + *(uint16_t *)(entry + 20) = (cluster >> 16) & 0xFFFF; + *(uint32_t *)(entry + 28) = 0; + + if (ide_write48(fat32_drive, lba, 1, sector) != 0) + return -1; + + return 0; + } + } + return -2; +} + +int32_t fat32_change_directory(const char *name) { + uint32_t lba = cluster_to_lba(current_directory_cluster); + if (ide_read48(fat32_drive, lba, 1, sector) != 0) + return -1; + + for (int32_t i = 0; i < 512; i += 32) { + char *entry = (char *)§or[i]; + if ((uint8_t)entry[0] == 0x00) break; + if ((uint8_t)entry[0] == 0xE5) continue; + + char entry_name[12]; + memcpy(entry_name, entry, 11); + entry_name[11] = 0; + for (int32_t j = 0; j < 11; j++) + if (entry_name[j] == ' ') entry_name[j] = 0; + + if (strncmp(entry_name, name, 11) != 0) continue; + + if (!(entry[11] & FAT32_ATTR_DIRECTORY)) + return -2; + + current_directory_cluster = (*(uint16_t *)(entry + 26)) | ((*(uint16_t *)(entry + 20)) << 16); + return 0; + } + return -3; +} + diff --git a/drivers/gdt.c b/drivers/gdt.c new file mode 100644 index 0000000..99e06ec --- /dev/null +++ b/drivers/gdt.c @@ -0,0 +1,34 @@ +#include + + +#define GDT_ENTRIES 3 + +struct gdt_entry gdt[GDT_ENTRIES]; +struct gdt_ptr gp; + +extern void gdt_flush(uint32_t); + +void gdt_install() +{ + gp.limit = (sizeof(struct gdt_entry) * GDT_ENTRIES) - 1; + gp.base = (uint32_t)&gdt; + + gdt_set_entry(0, 0, 0, 0, 0); /* Null segment */ + gdt_set_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); /* Code segment */ + gdt_set_entry(2, 0, 0xFFFFFFFF, 0x92, 0xCF); /* Data segment */ + + gdt_flush((uint32_t)&gp); +} + +void gdt_set_entry(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) +{ + gdt[num].base_low = (base & 0xFFFF); + gdt[num].base_middle = (base >> 16) & 0xFF; + gdt[num].base_high = (base >> 24) & 0xFF; + + gdt[num].limit_low = (limit & 0xFFFF); + gdt[num].granularity = (limit >> 16) & 0x0F; + + gdt[num].granularity |= (gran & 0xF0); + gdt[num].access = access; +} diff --git a/drivers/ide.c b/drivers/ide.c new file mode 100644 index 0000000..fe08f4b --- /dev/null +++ b/drivers/ide.c @@ -0,0 +1,173 @@ +#include + +#include + +#include + + +static void io_delay() { + for (int32_t i = 0; i < 1000; i++) { + outb(0x80, 0); + } +} + + +static int32_t ide_wait(uint16_t io, int32_t check_drq) { + for (int32_t i = 0; i < 5000; i++) { + uint8_t status = inb(io + ATA_REG_STATUS); + if (!(status & ATA_SR_BSY)) { + if (!check_drq || (status & ATA_SR_DRQ)) + return 0; + } + io_delay(1); + } + return 1; +} + +static int32_t ide_wait_ready(uint16_t io) { + for (int32_t i = 0; i < 100000; i++) { + uint8_t status = inb(io + ATA_REG_STATUS); + if (!(status & ATA_SR_BSY)) return 0; + } + return 1; +} + + + +int32_t ide_identify(uint8_t drive, uint16_t* buffer) { + uint16_t io = ATA_PRIMARY_IO; + uint8_t slave_bit = (drive & 1) << 4; + + ide_wait(io, 0); + outb(io + ATA_REG_HDDEVSEL, 0xA0 | slave_bit); + outb(io + ATA_REG_SECCOUNT0, 0); + outb(io + ATA_REG_LBA0, 0); + outb(io + ATA_REG_LBA1, 0); + outb(io + ATA_REG_LBA2, 0); + outb(io + ATA_REG_COMMAND, 0xEC); // IDENTIFY DEVICE + + uint8_t status = inb(io + ATA_REG_STATUS); + if (status == 0) return -1; // No device + + if (ide_wait(io, 1)) return -2; + + insw(io + ATA_REG_DATA, buffer, 256); // Read 512 bytes + return 0; +} + +void ide_initialize(void) { + outb(ATA_PRIMARY_CTRL, 0x02); // Disable IRQs + + uint16_t identify_buf[256]; + if (ide_identify(0, identify_buf) == 0) { + char model[41]; + for (int i = 0; i < 20; ++i) { + model[i * 2 + 0] = identify_buf[27 + i] >> 8; + model[i * 2 + 1] = identify_buf[27 + i] & 0xFF; + } + model[40] = 0; + printf("Disk model: %s\n", model); + } +} + +int32_t ide_read48(uint8_t drive, uint64_t lba, uint8_t sector_count, void* buffer) { + if (sector_count == 0) return -1; + + uint16_t io = ATA_PRIMARY_IO; + uint8_t slave_bit = (drive & 1) << 4; + + ide_wait(io, 0); // Wait for BSY to clear + + outb(io + ATA_REG_HDDEVSEL, 0xE0 | slave_bit); // Select drive + + // High bytes (use 0xC2 - 0xC6 offsets if you define them separately) + outb(io + 6, (sector_count >> 8) & 0xFF); // Sector Count High + outb(io + 7, (lba >> 40) & 0xFF); // LBA[47:40] + outb(io + 8, (lba >> 32) & 0xFF); // LBA[39:32] + outb(io + 9, (lba >> 24) & 0xFF); // LBA[31:24] + + // Low bytes + outb(io + 2, sector_count & 0xFF); // Sector Count Low + outb(io + 3, (lba >> 0) & 0xFF); // LBA[7:0] + outb(io + 4, (lba >> 8) & 0xFF); // LBA[15:8] + outb(io + 5, (lba >> 16) & 0xFF); // LBA[23:16] + + + outb(io + ATA_REG_COMMAND, ATA_CMD_READ_SECTORS_EXT); + + uint16_t* ptr = (uint16_t*)buffer; + for (int i = 0; i < sector_count; i++) { + if (ide_wait(io, 1)) { + uint8_t status = inb(io + ATA_REG_STATUS); + printf("IDE DRQ wait failed (read), status = 0x%02X\n", status); + return -2; + } + + uint8_t status = inb(io + ATA_REG_STATUS); + if (status & ATA_SR_ERR) return -3; + + insw(io + ATA_REG_DATA, ptr, 256); + ptr += 256; + } + + return 0; +} + + +int32_t ide_write48(uint8_t drive, uint64_t lba, uint8_t sector_count, const void* buffer) { + if (sector_count == 0) return -1; + + uint16_t io = ATA_PRIMARY_IO; + uint8_t slave_bit = (drive & 1) << 4; + + ide_wait(io, 0); // Wait for BSY to clear + + outb(io + ATA_REG_HDDEVSEL, 0xE0 | slave_bit); // Select drive + + // Send high bytes of sector count and LBA first + outb(io + ATA_REG_SECCOUNT0, 0); // Sector count high byte + outb(io + ATA_REG_LBA0, (lba >> 40) & 0xFF); // LBA[47:40] + outb(io + ATA_REG_LBA1, (lba >> 32) & 0xFF); // LBA[39:32] + outb(io + ATA_REG_LBA2, (lba >> 24) & 0xFF); // LBA[31:24] + + // Send low bytes + outb(io + ATA_REG_SECCOUNT0, sector_count); // Sector count low byte + outb(io + ATA_REG_LBA0, (lba >> 0) & 0xFF); // LBA[7:0] + outb(io + ATA_REG_LBA1, (lba >> 8) & 0xFF); // LBA[15:8] + outb(io + ATA_REG_LBA2, (lba >> 16) & 0xFF); // LBA[23:16] + + // Send write command + outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_SECTORS_EXT); // 0x34 + + const uint16_t* ptr = (const uint16_t*)buffer; + for (int i = 0; i < sector_count; i++) { + if (ide_wait(io, 1)) { + uint8_t status = inb(io + ATA_REG_STATUS); + printf("IDE DRQ wait failed (write), status = 0x%02X\n", status); + return -2; + } + + // Check for drive error + uint8_t status = inb(io + ATA_REG_STATUS); + if (status & ATA_SR_ERR) return -3; + + outsw(io + ATA_REG_DATA, ptr, 256); // 256 words = 512 bytes + inb(io + ATA_REG_STATUS); /* Dummy read */ + ptr += 256; + } + + // Wait for BSY=0 before issuing FLUSH CACHE + if (ide_wait_ready(io)) return -5; + outb(io + ATA_REG_COMMAND, 0xE7); // FLUSH CACHE + if (ide_wait(io, 0)) { + printf("FLUSH CACHE timeout, status=0x%02X\n", inb(io + ATA_REG_STATUS)); + return -4; + } + + + + return 0; +} + + + diff --git a/drivers/idt.c b/drivers/idt.c new file mode 100644 index 0000000..b5810de --- /dev/null +++ b/drivers/idt.c @@ -0,0 +1,131 @@ +#include +#include + +#include + +#define IDT_ENTRIES 256 + +struct idt_entry idt[IDT_ENTRIES]; +struct idt_ptr idtp; + +extern void idt_load(uint32_t); + +void idt_install(void) +{ + idtp.limit = sizeof(struct idt_entry) * IDT_ENTRIES - 1; + idtp.base = (uint32_t)&idt; + + memset(&idt, 0, sizeof(struct idt_entry) * IDT_ENTRIES); + + /* Set entries for IRQs/ISRs here using `idt_set_entry(...)` */ + /* Example: idt_set_entry(0, (uint32_t)isr0, 0x08, 0x8E); */ + + idt_load((uint32_t)&idtp); +} + +void pic_remap(void) +{ + outb(0x20, 0x11); /* Start init for master PIC */ + outb(0xA0, 0x11); /* Start init for slave PIC */ + outb(0x21, 0x20); /* Master PIC vector offset */ + outb(0xA1, 0x28); /* Slave PIC vector offset */ + outb(0x21, 0x04); /* Tell Master PIC about Slave PIC at IRQ2 */ + outb(0xA1, 0x02); /* Tell Slave PIC its cascade identity */ + outb(0x21, 0x01); /* 8086/88 mode */ + outb(0xA1, 0x01); + outb(0x21, 0x0); /* Unmask master */ + outb(0xA1, 0x0); /* Unmask slave */ +} + +void idt_set_entry(int num, uint32_t base, uint16_t sel, uint8_t flags) +{ + idt[num].base_low = base & 0xFFFF; + idt[num].base_high = (base >> 16) & 0xFFFF; + + idt[num].sel = sel; + idt[num].always0 = 0; + idt[num].flags = flags; +} + +extern void IRQ0_handler(void); +extern void isr0(void); +extern void isr1(void); +extern void isr2(void); +extern void isr3(void); +extern void isr4(void); +extern void isr5(void); +extern void isr6(void); +extern void isr7(void); +extern void isr8(void); +extern void isr9(void); +extern void isr10(void); +extern void isr11(void); +extern void isr12(void); +extern void isr13(void); +extern void isr14(void); +extern void isr15(void); +extern void isr16(void); +extern void isr17(void); +extern void isr18(void); +extern void isr19(void); +extern void isr20(void); +extern void isr21(void); +extern void isr22(void); +extern void isr23(void); +extern void isr24(void); +extern void isr25(void); +extern void isr26(void); +extern void isr27(void); +extern void isr28(void); +extern void isr29(void); +extern void isr30(void); +extern void isr31(void); + +extern void isr33(void); + +extern void isr128(void); + + +void idt_install_isrs(void) +{ + idt_set_entry(0, (uint32_t)isr0, 0x08, 0x8E); + idt_set_entry(1, (uint32_t)isr1, 0x08, 0x8E); + idt_set_entry(2, (uint32_t)isr2, 0x08, 0x8E); + idt_set_entry(3, (uint32_t)isr3, 0x08, 0x8E); + idt_set_entry(4, (uint32_t)isr4, 0x08, 0x8E); + idt_set_entry(5, (uint32_t)isr5, 0x08, 0x8E); + idt_set_entry(6, (uint32_t)isr6, 0x08, 0x8E); + idt_set_entry(7, (uint32_t)isr7, 0x08, 0x8E); + idt_set_entry(8, (uint32_t)isr8, 0x08, 0x8E); + idt_set_entry(9, (uint32_t)isr9, 0x08, 0x8E); + idt_set_entry(10, (uint32_t)isr10, 0x08, 0x8E); + idt_set_entry(11, (uint32_t)isr11, 0x08, 0x8E); + idt_set_entry(12, (uint32_t)isr12, 0x08, 0x8E); + idt_set_entry(13, (uint32_t)isr13, 0x08, 0x8E); + idt_set_entry(14, (uint32_t)isr14, 0x08, 0x8E); + idt_set_entry(15, (uint32_t)isr15, 0x08, 0x8E); + idt_set_entry(16, (uint32_t)isr16, 0x08, 0x8E); + idt_set_entry(17, (uint32_t)isr17, 0x08, 0x8E); + idt_set_entry(18, (uint32_t)isr18, 0x08, 0x8E); + idt_set_entry(19, (uint32_t)isr19, 0x08, 0x8E); + idt_set_entry(20, (uint32_t)isr20, 0x08, 0x8E); + idt_set_entry(21, (uint32_t)isr21, 0x08, 0x8E); + idt_set_entry(22, (uint32_t)isr22, 0x08, 0x8E); + idt_set_entry(23, (uint32_t)isr23, 0x08, 0x8E); + idt_set_entry(24, (uint32_t)isr24, 0x08, 0x8E); + idt_set_entry(25, (uint32_t)isr25, 0x08, 0x8E); + idt_set_entry(26, (uint32_t)isr26, 0x08, 0x8E); + idt_set_entry(27, (uint32_t)isr27, 0x08, 0x8E); + idt_set_entry(28, (uint32_t)isr28, 0x08, 0x8E); + idt_set_entry(29, (uint32_t)isr29, 0x08, 0x8E); + idt_set_entry(30, (uint32_t)isr30, 0x08, 0x8E); + idt_set_entry(31, (uint32_t)isr31, 0x08, 0x8E); + + idt_set_entry(33, (uint32_t)isr33, 0x08, 0x8E); + +} + +void idt_install_syscall(void) +{ + idt_set_entry(128, (uint32_t)isr128, 0x08, 0xEE); +} diff --git a/drivers/isr.c b/drivers/isr.c new file mode 100644 index 0000000..2b95b16 --- /dev/null +++ b/drivers/isr.c @@ -0,0 +1,42 @@ +#include +#include + +#include + + +void (*irq_handlers[256])(isr_stack_t *r) = { 0 }; + +void register_interrupt_handler(uint8_t n, void (*handler)(isr_stack_t *)) +{ + irq_handlers[n] = handler; +} + +void isr_handler(isr_stack_t *r) +{ + if (irq_handlers[r->int_no]) { + irq_handlers[r->int_no](r); + } else { + printf("Unhandled interrupt: %d\n", r->int_no); + } + + + /* Send EOI to PIC */ + if (r->int_no >= 40) + { + outb(0xA0, 0x20); + } + if (r->int_no >= 32) + { + outb(0x20, 0x20); + } +} + +void syscall_handler(regs_t *r) +{ + switch (r->eax) { + case 0: printf("Syscall: Hello world\n"); break; + case 1: printf("Syscall: value = %d\n", r->ebx); break; + default: printf("Unknown syscall %d\n", r->eax); + } +} + diff --git a/drivers/pci.c b/drivers/pci.c new file mode 100644 index 0000000..769c329 --- /dev/null +++ b/drivers/pci.c @@ -0,0 +1,59 @@ +#include +#include + +#include +#include +#include + +#define PCI_CONFIG_ADDRESS 0xCF8 +#define PCI_CONFIG_DATA 0xCFC + +uint32_t pci_config_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) +{ + uint32_t address = 0; + address |= (bus << 16); // Bus number (bits 23-16) + address |= (device << 11); // Device number (bits 15-11) + address |= (function << 8); // Function number (bits 10-8) + address |= (offset & 0xFC); // Register offset (bits 7-0) + address |= (1 << 31); // Enable configuration access + + outl(PCI_CONFIG_ADDRESS, address); // Write address to PCI config space + return inl(PCI_CONFIG_DATA); // Read data from PCI config space +} + +void pci_config_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) +{ + uint32_t address = 0x80000000 | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC); + outl(PCI_CONFIG_ADDRESS, address); // Send the address to PCI config space + outl(PCI_CONFIG_DATA, value); // Write data to PCI config space +} + +void pci_enumerate() +{ + for (uint16_t bus = 0; bus < 256; bus++) // Maximum 256 buses + { + for (uint8_t device = 0; device < 32; device++) // Maximum 32 devices per bus + { + for (uint8_t function = 0; function < 8; function++) // Maximum 8 functions per device + { + uint32_t vendor_id = pci_config_read(bus, device, function, 0x00); + if (vendor_id == 0xFFFFFFFF) continue; // No device present + + uint32_t device_id = pci_config_read(bus, device, function, 0x02); + uint32_t class_code = pci_config_read(bus, device, function, 0x08); + printf("Found PCI device: Vendor ID: 0x%X Device ID: 0x%X Class: 0x%X\n", vendor_id, device_id, class_code); + + printf("0x%X", class_code); + printf(" --- "); + printf("0x%X\n", (class_code >> 8)); + + + if ((class_code >> 8) == 0x0C) { + printf("Found USB controller (Class: 0x%02X)\n", class_code); + // Now, you can initialize and configure the USB controller. + } + + } + } + } +} diff --git a/drivers/pit.c b/drivers/pit.c new file mode 100644 index 0000000..e69de29 diff --git a/drivers/ps2_keyboard.c b/drivers/ps2_keyboard.c new file mode 100644 index 0000000..794fc44 --- /dev/null +++ b/drivers/ps2_keyboard.c @@ -0,0 +1,98 @@ +#include +#include + +#include + + +/* +PS/2 Controller IO Ports + +The PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers. + +Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and PS/2 compatibles. +IO Port Access Type Purpose +0x60 Read/Write Data Port +0x64 Read Status Register +0x64 Write Command Register +*/ + +#define PS2_DATA_PORT 0x60 +#define PS2_STATUS_PORT 0x64 + +#define KEYBOARD_IRQ 33 + +/*extern void register_interrupt_handler(int irq, void (*handler)(void));*/ + +static const char scancode_map[128] = { + 0, 27, '1','2','3','4','5','6','7','8','9','0','-','=','\b', /* Backspace */ + '\t', /* Tab */ + 'q','w','e','r','t','y','u','i','o','p','[',']','\n', /* Enter */ + 0, /* Control */ + 'a','s','d','f','g','h','j','k','l',';','\'','`', + 0, /* Left shift */ + '\\','z','x','c','v','b','n','m',',','.','/', + 0, /* Right shift */ + '*', + 0, /* Alt */ + ' ', /* Spacebar */ + 0, /* Caps lock */ + /* The rest are function and control keys */ +}; + +static bool shift_pressed = false; + +void keyboard_handler(isr_stack_t* regs) +{ + (void)regs; /* Only here to dismiss the "Unused parameter" warnings */ + printd("PS/2 Keyboard handler fired!\n"); + + uint8_t scancode = inb(PS2_DATA_PORT); + + /* Handle shift press/release */ + if (scancode == 0x2A || scancode == 0x36) + { + shift_pressed = true; + return; + } + else if (scancode == 0xAA || scancode == 0xB6) + { + shift_pressed = false; + return; + } + + if (scancode & 0x80) + { + /* Key release event, ignore for now */ + } + else + { + char c = scancode_map[scancode]; + if (shift_pressed && c >= 'a' && c <= 'z') + { + c -= 32; /* Convert to uppercase */ + } + + if (c) + { + /* Do something with the keypress (e.g., print or buffer) */ + printf("Char: %c", c); + } + } + + outb(0x20, 0x20); /* Acknowledge PIC */ +} + +void test_handler(isr_stack_t* r) { + printf("IRQ1 fired!\n"); +} + + +void keyboard_init(void) +{ + outb(0x21, inb(0x21) & ~0x02); + + register_interrupt_handler(KEYBOARD_IRQ, test_handler); + + /* Enable keyboard (controller-specific, often optional if already working) */ + //outb(0x64, 0xAE); +} diff --git a/drivers/tty.c b/drivers/tty.c new file mode 100644 index 0000000..c61e467 --- /dev/null +++ b/drivers/tty.c @@ -0,0 +1,177 @@ +#include + +#include +#include +#include +#include + +static size_t terminal_row; +static size_t terminal_column; +static uint8_t terminal_color; +static uint16_t* terminal_buffer; + +void terminal_initialize(void) +{ + terminal_initializec(0x00); +} + +void terminal_initializec(uint8_t color) +{ + terminal_row = 0; + terminal_column = 0; + + if (color == 0x00) + { + terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + } + else { + terminal_color = color; + } + + terminal_buffer = VGA_MEMORY; + for (size_t y = 0; y < VGA_HEIGHT; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + } +} + +void terminal_setcolor(uint8_t color) +{ + terminal_color = color; +} + +uint8_t terminal_getcolor(void) +{ + return terminal_color; +} + +void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y) +{ + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(c, color); +} + +void terminal_putchar(const char c) +{ + unsigned char uc = c; + + if (uc == '\n') + { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) + { + terminal_scroll(); + terminal_row = VGA_HEIGHT - 1; + } + return; + } + else if (uc == '\t') + { + terminal_column += 4; + + if (terminal_column >= VGA_WIDTH) + { + terminal_putchar('\n'); + } + + return; + } + + terminal_putentryat(uc, terminal_color, terminal_column, terminal_row); + + + if (++terminal_column == VGA_WIDTH) + { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) + { + terminal_scroll(); + terminal_row = VGA_HEIGHT - 1; + } + } +} + +void terminal_write(const char* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + terminal_putchar(data[i]); + } +} + +void terminal_writestring(const char* data) +{ + terminal_write(data, strlen(data)); +} + +void terminal_debug_writestring(const char* data) +{ + terminal_writestring("[ DEBUG ] "); + terminal_writestring(data); +} + +void terminal_writeline(const char* data) +{ + terminal_column = 0; + terminal_writestring(data); +} + +void terminal_writechar_r(const char ch) +{ + unsigned char uch = ch; + + for (size_t i = 0; i < VGA_WIDTH; i++) + { + terminal_putchar(uch); + } +} + +void terminal_clear(void) +{ + terminal_clearl(-1); +} + +void terminal_clearl(size_t num_lines) +{ + if (num_lines == (size_t)-1) + { + terminal_initializec(terminal_getcolor()); + } + else + { + /* XXX note to self: VGA_HEIGHT is 25, and VGA_WIDTH is 80 XXX */ + /* static size_t terminal_row; static size_t terminal_column; */ + + terminal_row = VGA_HEIGHT; + terminal_column = 0; + + while (terminal_row < num_lines) + { + terminal_writechar_r(' '); + terminal_row -= 1; + } + } +} + +void terminal_scroll(void) +{ + for (size_t y = 1; y < VGA_HEIGHT; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t src_index = y * VGA_WIDTH + x; + const size_t dst_index = (y - 1) * VGA_WIDTH + x; + terminal_buffer[dst_index] = terminal_buffer[src_index]; + } + } + + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = (VGA_HEIGHT - 1) * VGA_WIDTH + x; + terminal_buffer[index] = (terminal_color << 8) | ' '; + } +} diff --git a/drivers/vga/vga.c b/drivers/vga/vga.c new file mode 100644 index 0000000..b2e446e --- /dev/null +++ b/drivers/vga/vga.c @@ -0,0 +1,10 @@ +#include +#include + +uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { + return fg | bg << 4; +} + +uint16_t vga_entry(unsigned char uc, uint8_t color) { + return (uint16_t) uc | (uint16_t) color << 8; +} diff --git a/gdt.asm b/gdt.asm new file mode 100644 index 0000000..9b9fa94 --- /dev/null +++ b/gdt.asm @@ -0,0 +1,15 @@ +global gdt_flush + +gdt_flush: + mov eax, [esp + 4] + lgdt [eax] + ; Reload segment registers + mov ax, 0x10 ; Kernel data segment + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp 0x08:.flush ; Kernel code segment +.flush: + ret diff --git a/grub.cfg b/grub.cfg new file mode 100644 index 0000000..e2f6a63 --- /dev/null +++ b/grub.cfg @@ -0,0 +1,3 @@ +menuentry "Espresso" { + multiboot /boot/espresso.bin +} diff --git a/idt.asm b/idt.asm new file mode 100644 index 0000000..4bdeb86 --- /dev/null +++ b/idt.asm @@ -0,0 +1,40 @@ +[bits 32] +global idt_load +global common_isr_stub + +extern isr_handler +extern _push_regs +extern _pop_regs + +idt_load: + mov eax, [esp + 4] + lidt [eax] + ret + +common_isr_stub: + call _push_regs ; Push all general-purpose registers + push ds + push es + push fs + push gs + + mov ax, 0x10 ; Load kernel data segment + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + push esp ; Pass pointer to the register state + call isr_handler ; Call your C ISR handler + + add esp, 4 ; Clean up stack from push esp + pop gs + pop fs + pop es + pop ds + + call _pop_regs ; restore all general-purpose registers + + add esp, 8 ; Remove int_no and err_code + sti + iret diff --git a/include/drivers/ahci.h b/include/drivers/ahci.h new file mode 100644 index 0000000..95b5b35 --- /dev/null +++ b/include/drivers/ahci.h @@ -0,0 +1,6 @@ +#ifndef _AHCI_H +#define _AHCI_H + + + +#endif diff --git a/include/drivers/ide.h b/include/drivers/ide.h new file mode 100644 index 0000000..c0c009f --- /dev/null +++ b/include/drivers/ide.h @@ -0,0 +1,40 @@ +#ifndef _IDE_H +#define _IDE_H + +#include +#include + +#define ATA_PRIMARY_IO 0x1F0 +#define ATA_PRIMARY_CTRL 0x3F6 +#define ATA_MASTER 0x00 +#define ATA_SLAVE 0x10 + +#define ATA_REG_DATA 0x00 +#define ATA_REG_ERROR 0x01 +#define ATA_REG_SECCOUNT0 0x02 +#define ATA_REG_LBA0 0x03 +#define ATA_REG_LBA1 0x04 +#define ATA_REG_LBA2 0x05 +#define ATA_REG_LBA3 0x06 // LBA48 higher bits +#define ATA_REG_LBA4 0x07 // LBA48 higher bits +#define ATA_REG_LBA5 0x08 // LBA48 higher bits +#define ATA_REG_HDDEVSEL 0x06 +#define ATA_REG_COMMAND 0x07 +#define ATA_REG_STATUS 0x07 + +#define ATA_CMD_READ_SECTORS 0x20 +#define ATA_CMD_WRITE_SECTORS 0x30 +#define ATA_CMD_READ_SECTORS_EXT 0x24 +#define ATA_CMD_WRITE_SECTORS_EXT 0x34 + +#define ATA_SR_BSY 0x80 +#define ATA_SR_DRDY 0x40 +#define ATA_SR_DRQ 0x08 +#define ATA_SR_ERR 0x01 + +void ide_initialize(void); +int32_t ide_identify(uint8_t drive, uint16_t* buffer); +int32_t ide_read48(uint8_t drive, uint64_t lba, uint8_t sector_count, void* buffer); +int32_t ide_write48(uint8_t drive, uint64_t lba, uint8_t sector_count, const void* buffer); + +#endif diff --git a/include/drivers/pci.h b/include/drivers/pci.h new file mode 100644 index 0000000..8d56c5a --- /dev/null +++ b/include/drivers/pci.h @@ -0,0 +1,10 @@ +#ifndef _PCI_H +#define _PCI_H + +#include + +uint32_t pci_config_read(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); +void pci_config_write(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); +void pci_enumerate(void); + +#endif diff --git a/include/drivers/pit.h b/include/drivers/pit.h new file mode 100644 index 0000000..f8fd0d0 --- /dev/null +++ b/include/drivers/pit.h @@ -0,0 +1,7 @@ +#ifndef _PIT_H +#define _PIT_H + +#include + + +#endif diff --git a/include/drivers/ps2_keyboard.h b/include/drivers/ps2_keyboard.h new file mode 100644 index 0000000..76c5184 --- /dev/null +++ b/include/drivers/ps2_keyboard.h @@ -0,0 +1,10 @@ +#ifndef _PS2_KEYBOARD_H +#define _PS2_KEYBOARD_H + +#include +#include + +void keyboard_init(void); +void keyboard_handler(isr_stack_t* regs); + +#endif diff --git a/include/fs/duckfs.h b/include/fs/duckfs.h new file mode 100644 index 0000000..1dd97e9 --- /dev/null +++ b/include/fs/duckfs.h @@ -0,0 +1,79 @@ +#ifndef _DUCKFS_H +#define _DUCKFS_H + +#include + +#define DFS_MAGIC 0xDF1984CC + +#define DFS_BLOCK_SIZE 512 + +#define DFS_VERSION_0 "DuckFS, wheresDax?" +#define DFS_VERSION_1 "DuckFS, Terminator" +#define DFS_VERSION_2 "DuckFS-Terminator2" +#define DFS_VERSION_3 "DuckFS,StarWarsEIV" +#define DFS_VERSION_4 "DuckFS QUACK,QUACK" + + +#define DFS_FILE_ERROR -8 +#define DFS_FILE_UNUSED -1 +#define DFS_FILE_USED 0 +#define DFS_FILE_TEXT 0 +#define DFS_FILE_BINARY 1 +#define DFS_FILE_DIRECTORY 2 + +/* encryption algorithms */ +#define DFS_CAESAR_5 88 +#define DFS_CAESAR_8 84 +#define DFS_CAESAR_2 09 +#define DFS_QUACK_32 99 + +#define DFS_MAX_FILENAME_LEN 64 +#define DFS_MAX_FILES 256 + + +typedef struct duckfs_file_header { + char filename[DFS_MAX_FILENAME_LEN + 1]; /* + 1 for the \0 */ + char permissions[3]; /* Same thing here */ + + /* + 512 Bytes per sector, meaning a 2 sector file is 1024 bytes, or 1 KiB. + Only valid if this file is not a directory. + */ + int32_t num_sectors; + + int16_t type; /* Indicates the file type. -1 for null file, 0 for a text file, 1 for a binary file, and 2 for a directory. */ + + uint16_t _reserved0; /* (align to 4) */ + + struct duckfs_file_header* next; + struct duckfs_file_header* prev; + struct duckfs_file_header* contents; /* contains the directories files. only valid if this file is a directory. */ + + uint32_t lba_start; /* only is valid if this file is not a directory. */ + uint32_t lba_end; /* only is valid if this file is not a directory. */ + + uint8_t _reserved1[158]; /* padding to 256 bytes total */ +} duckfs_file_header_t; + +typedef struct duckfs_superblock { + int32_t duckfs_magic; /* must be DFS_MAGIC. */ + char duckfs_version_string[19]; + char volume_label[19]; /* 18 characters and a null zero. */ + + int16_t encryption; + + struct duckfs_file_header* duckfs_root; + + uint32_t _padding[116]; +} duckfs_superblock_t; + +int32_t duckfs_init(int16_t drive); +void duckfs_format(int16_t drive); + +int32_t duckfs_makedir(const char* filename, const char* perms); +void duckfs_free_directory(duckfs_file_header_t* dir); + +duckfs_file_header_t duckfs_create_entry(const char* name, const char* perms, const char* type); +bool duckfs_add_entry(duckfs_file_header_t* dir, duckfs_file_header_t* entry); + +#endif diff --git a/include/fs/fat32.h b/include/fs/fat32.h new file mode 100644 index 0000000..4c498be --- /dev/null +++ b/include/fs/fat32.h @@ -0,0 +1,16 @@ +#ifndef _FAT32_H +#define _FAT32_H +#include + +#define FAT32_MAX_FILENAME 11 +#define FAT32_ATTR_DIRECTORY 0x10 + +int32_t fat32_init(int32_t drive); /* 0 = primary master */ +int32_t fat32_list_root(void); +int32_t fat32_read_file(const char *name, uint8_t *buffer, uint32_t max_len); +int32_t fat32_write_file(const char *name, const uint8_t *buffer, uint32_t len); +int32_t fat32_create_file(const char *name); +int32_t fat32_create_directory(const char *name); +int32_t fat32_change_directory(const char *name); + +#endif diff --git a/include/fs/ramfs.h b/include/fs/ramfs.h new file mode 100644 index 0000000..18a6ea5 --- /dev/null +++ b/include/fs/ramfs.h @@ -0,0 +1,53 @@ +#ifndef _RAM_FS_H +#define _RAM_FS_H + +#include +#include + + +#define RAMFS_MAX_FILES 128 +#define RAMFS_BLOCK_SIZE 4096 +#define RAMFS_MAX_PATH_LEN 255 + +#define RAMFS_PERM_READ 0x01 +#define RAMFS_PERM_WRITE 0x02 +#define RAMFS_PERM_EXEC 0x04 +#define RAMFS_PERM_ALL (RAMFS_PERM_READ | RAMFS_PERM_WRITE | RAMFS_PERM_EXEC) + +typedef struct ramfs_file { + char name[RAMFS_MAX_PATH_LEN]; + uint32_t size; + uint8_t* data; + uint8_t permissions; +} ramfs_file_t; + +typedef struct ramfs_directory { + char name[RAMFS_MAX_PATH_LEN]; + ramfs_file_t* files[RAMFS_MAX_FILES]; + uint32_t file_count; +} ramfs_directory_t; + + +void ramfs_init(); + +ramfs_file_t* ramfs_create_file(const char* name, uint32_t size, uint8_t permissions); +bool ramfs_delete_file(const char* name); +ramfs_file_t* ramfs_find_file(const char* name); + +bool ramfs_create_directory(const char* name); +bool ramfs_delete_directory(const char* name); + +bool ramfs_check_permissions(const char* name, uint8_t required_permissions); + +bool ramfs_resize_file(const char* name, uint32_t new_size); +bool ramfs_append_to_file(const char* name, const uint8_t* data, uint32_t data_size); + +bool ramfs_read_file(const char* name, uint8_t* buffer, uint32_t buffer_size); +bool ramfs_write_file(const char* name, const uint8_t* data, uint32_t size); +bool ramfs_overwrite_file(const char* name, const uint8_t* data, uint32_t size); + + +void ramfs_list_files(); +void ramfs_list_directories(); + +#endif diff --git a/include/gdt.h b/include/gdt.h new file mode 100644 index 0000000..f66d62d --- /dev/null +++ b/include/gdt.h @@ -0,0 +1,23 @@ +#ifndef _GDT_H +#define _GDT_H + +#include + +struct gdt_entry { + uint16_t limit_low; /* Lower 16 bits of limit */ + uint16_t base_low; /* Lower 16 bits of base */ + uint8_t base_middle; /* Next 8 bits of base */ + uint8_t access; /* Access flags */ + uint8_t granularity; /* Granularity + high 4 bits of limit */ + uint8_t base_high; /* Last 8 bits of base */ +} __attribute__((packed)); + +struct gdt_ptr { + uint16_t limit; /* Size of GDT - 1 */ + uint32_t base; /* Base address of GDT */ +} __attribute__((packed)); + +void gdt_install(); +void gdt_set_entry(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran); + +#endif diff --git a/include/idt.h b/include/idt.h new file mode 100644 index 0000000..7fecd1c --- /dev/null +++ b/include/idt.h @@ -0,0 +1,25 @@ +#ifndef _IDT_H +#define _IDT_H + +#include + +struct idt_entry { + uint16_t base_low; + uint16_t sel; /* Kernel segment selector */ + uint8_t always0; /* Always 0 */ + uint8_t flags; /* Type and attributes */ + uint16_t base_high; +} __attribute__((packed)); + +struct idt_ptr { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +void idt_install(void); +void idt_install_isrs(void); +void idt_install_syscall(void); +void pic_remap(void); +void idt_set_entry(int num, uint32_t base, uint16_t sel, uint8_t flags); + +#endif diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..2cfa726 --- /dev/null +++ b/include/io.h @@ -0,0 +1,16 @@ +/* Defines a abstract base for I/O in the Espresso kernel. */ + +#ifndef ESPRESSO_IO_H +#define ESPRESSO_IO_H + +#include +#include + +void print(const char* str); + +char* input(uint8_t dev=0); + +void writefile(uint8_t mode, char* file, char* data); +char* readfile(char* file); + +#endif \ No newline at end of file diff --git a/include/isr.h b/include/isr.h new file mode 100644 index 0000000..d82fea5 --- /dev/null +++ b/include/isr.h @@ -0,0 +1,23 @@ +#ifndef _ISR_H +#define _ISR_H + +#include + +typedef struct { + uint32_t ds, edi, esi, ebp, esp, ebx, edx, ecx, eax; + uint32_t int_no, err_code; + uint32_t eip, cs, eflags, useresp, ss; +} regs_t; + +typedef struct { + uint32_t ds; + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; + uint32_t int_no, err_code; + uint32_t eip, cs, eflags, useresp, ss; +} isr_stack_t; + +void isr_handler(isr_stack_t *r); +void syscall_handler(regs_t *r); +void register_interrupt_handler(uint8_t n, void (*handler)(isr_stack_t *)); + +#endif diff --git a/include/kernel/boot.h b/include/kernel/boot.h new file mode 100644 index 0000000..f6982c3 --- /dev/null +++ b/include/kernel/boot.h @@ -0,0 +1,7 @@ +#ifndef _KERNEL_BOOT_H +#define _KERNEL_BOOT_H + + +uint8_t parse_boot_data(const char* data); + +#endif diff --git a/include/kernel/intro_anim.h b/include/kernel/intro_anim.h new file mode 100644 index 0000000..0fdd97e --- /dev/null +++ b/include/kernel/intro_anim.h @@ -0,0 +1,8 @@ +#ifndef _INTRO_ANIMATION_H +#define _INTRO_ANIMATION_H + +#include + +int16_t begin_anim(const char* version); + +#endif diff --git a/include/kernel/syscalls.h b/include/kernel/syscalls.h new file mode 100644 index 0000000..c29a68e --- /dev/null +++ b/include/kernel/syscalls.h @@ -0,0 +1,8 @@ +#ifndef _SYSCALLS_H +#define _SYSCALLS_H + +#include + +int16_t syscall_write(int32_t fd, const void* buffer, int32_t length); + +#endif diff --git a/include/math/software_float.h b/include/math/software_float.h new file mode 100644 index 0000000..8e75531 --- /dev/null +++ b/include/math/software_float.h @@ -0,0 +1,18 @@ +#ifndef _SOFTWARE_FLOAT_H +#define _SOFTWARE_FLOAT_H + +#include + +typedef struct { + uint8_t sign; /* 0 = positive, 1 = negative */ + uint8_t exponent; /* 8-bit biased exponent (bias = 127) */ + uint32_t mantissa; /* 23 bits stored (MSB implicit for normalized numbers) */ +} soft_float32_t; + +typedef struct { + uint8_t sign; /* 0 = positive, 1 = negative */ + uint16_t exponent; /* 11-bit biased exponent (bias = 1023) */ + uint64_t mantissa; /* 52 bits stored (MSB implicit for normalized numbers) */ +} soft_float64_t; + +#endif diff --git a/include/mm/heap.h b/include/mm/heap.h new file mode 100644 index 0000000..96f2287 --- /dev/null +++ b/include/mm/heap.h @@ -0,0 +1,19 @@ +#ifndef _HEAP_H +#define _HEAP_H + +#include + +#define HEAP_START 0xC0000000 +#define HEAP_SIZE (1024 * 4096) /* 1MB heap */ + +void heap_init(void); + +void* malloc(size_t size); +void* malloc_aligned(size_t size, size_t alignment); + +void* calloc(size_t nmemb, size_t size); +void* realloc(void* ptr, size_t size); + +void free(void* ptr); + +#endif diff --git a/include/mm/mm.h b/include/mm/mm.h new file mode 100644 index 0000000..acbdfee --- /dev/null +++ b/include/mm/mm.h @@ -0,0 +1,8 @@ +#ifndef _MM_H +#define _MM_H + +#include +#include +#include + +#endif diff --git a/include/mm/paging.h b/include/mm/paging.h new file mode 100644 index 0000000..d6152b7 --- /dev/null +++ b/include/mm/paging.h @@ -0,0 +1,9 @@ +#ifndef _PAGING_H +#define _PAGING_H + +#include + +void paging_init(void); +void map_page(void* phys, void* virt); + +#endif diff --git a/include/mm/pmm.h b/include/mm/pmm.h new file mode 100644 index 0000000..e59ea60 --- /dev/null +++ b/include/mm/pmm.h @@ -0,0 +1,11 @@ +#ifndef _PMM_H +#define _PMM_H + +#include +#include + +void pmm_init(multiboot_info_t* mb_info); +void* alloc_page(void); +void free_page(void* ptr); + +#endif diff --git a/include/multiboot.h b/include/multiboot.h new file mode 100644 index 0000000..269cf9b --- /dev/null +++ b/include/multiboot.h @@ -0,0 +1,274 @@ +/* multiboot.h - Multiboot header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 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. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_HEADER_ALIGN 4 + +/* The magic field should contain this. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* This should be in %eax. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* 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_VBE_INFO 0x00000800 +#define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +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; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + union + { + struct + { + multiboot_uint32_t framebuffer_palette_addr; + multiboot_uint16_t framebuffer_palette_num_colors; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; +typedef struct multiboot_info multiboot_info_t; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +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 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + 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; + +/* APM BIOS info. */ +struct multiboot_apm_info +{ + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; +}; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ diff --git a/include/panic.h b/include/panic.h new file mode 100644 index 0000000..f96cda6 --- /dev/null +++ b/include/panic.h @@ -0,0 +1,30 @@ +#ifndef _KERNEL_PANIC_H +#define _KERNEL_PANIC_H + +#include +#include + +void panic(const char* arr) +{ + + uint8_t color = 0x1F; + uint8_t blank = 0xFF; + + for (size_t y = 0; y < VGA_HEIGHT; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + const size_t index = y * VGA_WIDTH + x; + VGA_MEMORY[index] = (uint16_t) ' ' | (uint16_t) blank << 8; + } + } + + int i; + for (i = 0; i < 75; i += 2) + { + VGA_MEMORY[i] = arr[i]; + VGA_MEMORY[i + 1] = color; + } +} + +#endif diff --git a/include/port_io.h b/include/port_io.h new file mode 100644 index 0000000..9e3ea73 --- /dev/null +++ b/include/port_io.h @@ -0,0 +1,46 @@ +#ifndef _PORT_IO_H +#define _PORT_IO_H + +#include + +static inline uint32_t inl(uint16_t port) +{ + uint32_t rv; + asm volatile ("inl %%dx, %%eax" : "=a" (rv) : "dN" (port)); + return rv; +} + +static inline void outl(uint16_t port, uint32_t data) +{ + asm volatile ("outl %%eax, %%dx" : : "dN" (port), "a" (data)); +} + +static inline void outb(uint16_t port, uint8_t val) +{ + asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) : "memory"); +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + asm volatile ( "inb %1, %0" + : "=a"(ret) + : "Nd"(port) + : "memory"); + return ret; +} + +static inline void io_wait() +{ + outb(0x80, 0); +} + +static inline void insw(uint16_t port, void* addr, uint32_t count) { + __asm__ volatile ("rep insw" : "+D"(addr), "+c"(count) : "d"(port) : "memory"); +} + +static inline void outsw(uint16_t port, const void* addr, uint32_t count) { + __asm__ volatile ("rep outsw" : "+S"(addr), "+c"(count) : "d"(port)); +} + +#endif diff --git a/include/printf.h b/include/printf.h new file mode 100644 index 0000000..5f5928f --- /dev/null +++ b/include/printf.h @@ -0,0 +1,19 @@ +#ifndef _PRINTF_H +#define _PRINTF_H + +#include +#include + +#include /* Only for the vga_color enum */ + + +void printwc(const char*, uint8_t); +void printd(const char* str); +void printf(const char*, ...); + +void print_int(int32_t value); +void print_uint(uint32_t value); +void print_hex(uint32_t value, int width, bool uppercase); +void print_double(double value, int precision); + +#endif diff --git a/include/processes.h b/include/processes.h new file mode 100644 index 0000000..eedb8db --- /dev/null +++ b/include/processes.h @@ -0,0 +1,19 @@ +#ifndef _PROCESSES_H +#define _PROCESSES_H + +#include + + + +struct process +{ + uint16_t id; + uint8_t policy; + uint16_t priority; + + char name[16]; + + struct process* next; +}; + +#endif diff --git a/include/stdio.h b/include/stdio.h new file mode 100644 index 0000000..72306a4 --- /dev/null +++ b/include/stdio.h @@ -0,0 +1,2 @@ + +#include diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 0000000..3652b4c --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,9 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#include +#include +#include +#include + +#endif diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..12cfe04 --- /dev/null +++ b/include/string.h @@ -0,0 +1,27 @@ +#ifndef STRING_H +#define STRING_H + +#include + +/* TODO: change all applicable 'int32_t's with 'size_t's. */ + +size_t strlen(const char* str); +size_t strcmp(const char *s1, const char *s2); +int32_t strncmp(const char *s1, const char *s2, size_t n); +size_t strcpy(char *dst, const char *src); +char *strncpy(char *dest, const char *src, uint32_t n); +void strcat(char *dest, const char *src); + +int32_t ischar(int32_t c); +int32_t isspace(char c); +int32_t isalpha(char c); +char upper(char c); +char toupper(char c); +char lower(char c); + +void *memset(void *dst, char c, uint32_t n); +void *memcpy(void *dst, const void *src, uint32_t n); +int32_t memcmp(uint8_t *s1, uint8_t *s2, uint32_t n); + + +#endif diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 0000000..e3f9d1a --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,6 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + + + +#endif diff --git a/include/tty.h b/include/tty.h new file mode 100644 index 0000000..856a518 --- /dev/null +++ b/include/tty.h @@ -0,0 +1,28 @@ +#ifndef _TTY_H +#define _TTY_H + +#include +#include +#include + +static const size_t VGA_WIDTH = 80; +static const size_t VGA_HEIGHT = 25; +static uint16_t* const VGA_MEMORY = (uint16_t*) 0xB8000; + +void terminal_initialize(void); +void terminal_initializec(uint8_t color); + +void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y); +void terminal_putchar(char c); +void terminal_write(const char* data, size_t size); +void terminal_writestring(const char* data); +void terminal_debug_writestring(const char* data); + +void terminal_setcolor(uint8_t color); +uint8_t terminal_getcolor(void); + +void terminal_clear(void); +void terminal_clearl(size_t num_lines); +void terminal_scroll(void); + +#endif diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..5f54e95 --- /dev/null +++ b/include/types.h @@ -0,0 +1,8 @@ +#ifndef _TYPES_H +#define _TYPES_H + +#include +#include +#include + +#endif diff --git a/include/vector_extentions/sse.h b/include/vector_extentions/sse.h new file mode 100644 index 0000000..2351c67 --- /dev/null +++ b/include/vector_extentions/sse.h @@ -0,0 +1,27 @@ +#ifndef _SSE_H +#define _SSE_H + + +#define SSE_XMM_SIZE 128 + +void enable_sse(void); + +int32_t test_sse(void); + +void sse2_add_double_arrays(double *dst, const double *a, const double *b, size_t count); + +void sse2_add_int32_arrays(int32_t *dst, const int32_t *a, const int32_t *b, size_t count); +void sse2_add_int64_arrays(int64_t *dst, const int64_t *a, const int64_t *b, size_t count); + + +char *sse2_strncpy(char *dest, const char *src, uint32_t n); + +void *sse2_memcpy(void *dst, const void *src, uint32_t n); + +void double_vector_to_int_vector(const double *src, int32_t *dst); +void int_vector_to_double_vector(const int32_t *src, double *dst); + +void *memclr_sse2(const void *const m_start, const size_t m_count); + + +#endif diff --git a/include/vga/vga.h b/include/vga/vga.h new file mode 100644 index 0000000..a1c0fe7 --- /dev/null +++ b/include/vga/vga.h @@ -0,0 +1,30 @@ +#ifndef VGA_DRIVER_H +#define VGA_DRIVER_H + +#include + +/* Hardware text mode color constants. */ +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg); + +uint16_t vga_entry(unsigned char uc, uint8_t color); + +#endif diff --git a/isr128.asm b/isr128.asm new file mode 100644 index 0000000..8aa2976 --- /dev/null +++ b/isr128.asm @@ -0,0 +1,34 @@ +[bits 32] +global isr128 +extern isr_handler +extern _push_regs +extern _pop_regs + +isr128: + cli + call _push_regs + + push ds + push es + push fs + push gs + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Pass register state to C handler + push esp + call isr_handler + add esp, 4 + + pop gs + pop fs + pop es + pop ds + call _pop_regs + + sti + iret diff --git a/isr_stub_table.asm b/isr_stub_table.asm new file mode 100644 index 0000000..620970e --- /dev/null +++ b/isr_stub_table.asm @@ -0,0 +1,87 @@ +[bits 32] +global isr_stub_table +global isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7 +global isr8, isr9, isr10, isr11, isr12, isr13, isr14, isr15 +global isr16, isr17, isr18, isr19, isr20, isr21, isr22, isr23 +global isr24, isr25, isr26, isr27, isr28, isr29, isr30, isr31 +global isr33 + +extern isr_handler +extern _push_regs +extern _pop_regs + +%macro ISR_NOERR 1 +isr%1: + cli + push dword 0 ; Fake error code + push dword %1 ; Interrupt number + jmp isr_common_stub +%endmacro + +%macro ISR_ERR 1 +isr%1: + cli + push dword %1 ; Interrupt number + jmp isr_common_stub +%endmacro + +; ISRs 0-31 (exceptions) +ISR_NOERR 0 +ISR_NOERR 1 +ISR_NOERR 2 +ISR_NOERR 3 +ISR_NOERR 4 +ISR_NOERR 5 +ISR_NOERR 6 +ISR_NOERR 7 +ISR_ERR 8 ; Double fault +ISR_NOERR 9 +ISR_ERR 10 ; Invalid TSS +ISR_ERR 11 ; Segment not present +ISR_ERR 12 ; Stack segment fault +ISR_ERR 13 ; General protection fault +ISR_ERR 14 ; Page fault +ISR_NOERR 15 +ISR_NOERR 16 +ISR_NOERR 17 +ISR_NOERR 18 +ISR_NOERR 19 +ISR_NOERR 20 +ISR_NOERR 21 +ISR_NOERR 22 +ISR_NOERR 23 +ISR_NOERR 24 +ISR_NOERR 25 +ISR_NOERR 26 +ISR_NOERR 27 +ISR_NOERR 28 +ISR_NOERR 29 +ISR_ERR 30 ; Security exception +ISR_NOERR 31 +ISR_NOERR 33 + +isr_common_stub: + pusha + push ds + push es + push fs + push gs + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + push esp + call isr_handler + add esp, 4 + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 + ;sti + iret diff --git a/kernel/boot.c b/kernel/boot.c new file mode 100644 index 0000000..2536dde --- /dev/null +++ b/kernel/boot.c @@ -0,0 +1,15 @@ +#include + +#include + +#include + +uint8_t parse_boot_data(const char* data) +{ + if (strlen(data) == 0) + { + return 0; + } + + return 0; +} diff --git a/kernel/intro_anim.c b/kernel/intro_anim.c new file mode 100644 index 0000000..93d2b20 --- /dev/null +++ b/kernel/intro_anim.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +#include + +void delay_us(uint32_t microseconds, uint32_t cpu_mhz) { + uint32_t count = cpu_mhz * microseconds; + while (count--) { + asm volatile ("nop" ::: "memory"); + } +} + + +int16_t begin_anim(const char* version) +{ + if (strlen(version) < 5) + { + return -1; + } + + const char* e = "Espresso"; + const char* v = version; + const char* n = "Press any key to continue"; + + int32_t pos = 0; + int32_t v_pos = 0; + int16_t b = 0; + int32_t sh_pos = VGA_WIDTH / 2; + int32_t sv_pos = VGA_HEIGHT / 2; + + while (true) + { + terminal_clear(); + + for (int32_t i = 0; n[i]; ++i) + { + terminal_putentryat(n[i], terminal_getcolor(), sh_pos, sv_pos); + sh_pos++; + } + + for (int32_t i = 0; e[i]; ++i) + { + terminal_putentryat(e[i], terminal_getcolor(), pos + i, v_pos); + } + + if ((v_pos + 1) == VGA_HEIGHT) + { + v_pos = 0; + b = 1; + } + + for (int32_t i = 0; v[i]; ++i) + { + terminal_putentryat(v[i], terminal_getcolor(), pos + i + 3, (b == 0 ? (v_pos + 1) : v_pos)); + } + + pos += 2; + v_pos++; + sh_pos = VGA_WIDTH / 2; + + b = 0; + + delay_us(50000, 5000); + } + + terminal_clear(); + + return 0; +} + diff --git a/kernel/kernel.c b/kernel/kernel.c new file mode 100644 index 0000000..897f753 --- /dev/null +++ b/kernel/kernel.c @@ -0,0 +1,137 @@ +/* Check if the compiler thinks you are targeting the wrong operating system. */ +#if defined(__linux__) +#error "You are not using a cross-compiler, you will most certainly run into trouble" +#endif + +/* This tutorial will only work for the 32-bit ix86 targets. */ +#if !defined(__i386__) +#error "This kernel needs to be compiled with a ix86-elf compiler" +#endif + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +//#include +#include +#include +#include +#include +#include + +#include + +#include + +#define DEBUG + +/*extern HBA_MEM *abar; +extern HBA_PORT *ahci_port;*/ + +extern void _hang_asm(void); + + +void kernel_main(multiboot_info_t* mbd, unsigned int magic) +{ + + /* --- BEGIN INITIALIZATION SECTION --- */ + + /* We need to initialize the terminal so that any error/debuging messages show. */ + terminal_initialize(); + + terminal_setcolor(VGA_COLOR_RED); + + /* Make sure the magic number matches for memory mapping*/ + if(magic != MULTIBOOT_BOOTLOADER_MAGIC) + { + + printf("[ ERROR ] invalid magic number!\n"); + _hang_asm(); + } + + /* Check bit 6 to see if we have a valid memory map */ + if(!(mbd->flags >> 6 & 0x1)) + { + printf("[ ERROR ] invalid memory map given by GRUB bootloader\n"); + _hang_asm(); + } + + gdt_install(); + pic_remap(); + idt_install_isrs(); + idt_install(); + + terminal_setcolor(VGA_COLOR_GREEN); + + printd("Initializing physical memory manager...\n"); + + pmm_init(mbd); + + printd("Physical memory manager initialized\n"); + + printd("Initializing paging...\n"); + paging_init(); + printd("Paging initialized\n"); + + printd("Initializing heap allocator...\n"); + heap_init(); + printd("Heap allocator initialized\n"); + + printd("Testing SSE...\n"); + int32_t sse_test_result = test_sse(); + if (sse_test_result != 0) + { + printf("[ DEBUG ] SSE test failed with RV %d\n", sse_test_result); + } + else + { + printd("SSE test succeeded\n"); + } + + /* + printd("Initalizing AHCI...\n"); + ahci_init(); + printd("AHCI initialized\n"); + */ + + /*printd("Initializing RAMFS...\n"); + ramfs_init(); + printd("RAMFS initialized.\n");*/ + + printd("Initializing IDE system...\n"); + ide_initialize(); + printd("IDE initialized\n"); + + printd("Initializing DuckFS...\n"); + int32_t duckfs_init_rv = duckfs_init(0); + printf("[ DEBUG ] DuckFS initialized with RV %d\n", duckfs_init_rv); + + + /* --- END INITIALIZATION SECTION --- */ + + terminal_setcolor(VGA_COLOR_LIGHT_GREEN); + + const char* espresso_kernel_version = "0.0.0b"; + + printf("Loading Espresso %s...\n", espresso_kernel_version); + + /*begin_anim(espresso_kernel_version);*/ + + printf("Hello and welcome to Espresso\n"); + + + _hang_asm(); +} diff --git a/kernel/syscalls.c b/kernel/syscalls.c new file mode 100644 index 0000000..8c734bf --- /dev/null +++ b/kernel/syscalls.c @@ -0,0 +1,10 @@ + +#include + +#include + + +int16_t syscall_write(int32_t fd, const void* buffer, int32_t length) +{ + return -1; +} diff --git a/lib/fs/ramfs/ramfs.c b/lib/fs/ramfs/ramfs.c new file mode 100644 index 0000000..026edbd --- /dev/null +++ b/lib/fs/ramfs/ramfs.c @@ -0,0 +1,190 @@ +#include +#include + +#include +#include +#include + +#include + + +static ramfs_file_t ramfs_files[RAMFS_MAX_FILES]; +static ramfs_directory_t root_directory = { .name = "/", .file_count = 0 }; + +void ramfs_init() +{ + memset(ramfs_files, 0, sizeof(ramfs_files)); + root_directory.file_count = 0; +} + +ramfs_file_t* ramfs_create_file(const char* name, uint32_t size, uint8_t permissions) +{ + for (uint32_t i = 0; i < RAMFS_MAX_FILES; i++) { + if (ramfs_files[i].name[0] == '\0') { + // Create the file + ramfs_file_t* file = &ramfs_files[i]; + strncpy(file->name, name, RAMFS_MAX_PATH_LEN); + file->size = size; + file->data = (size > 0) ? malloc(size) : NULL; + file->permissions = permissions; + + if (root_directory.file_count < RAMFS_MAX_FILES) { + root_directory.files[root_directory.file_count++] = file; + } + + return file; + } + } + return NULL; +} + +bool ramfs_delete_file(const char* name) +{ + for (uint32_t i = 0; i < root_directory.file_count; i++) { + if (strcmp(root_directory.files[i]->name, name) == 0) { + free(root_directory.files[i]->data); + root_directory.files[i] = root_directory.files[root_directory.file_count - 1]; + root_directory.file_count--; + return true; + } + } + return false; +} + +ramfs_file_t* ramfs_find_file(const char* name) +{ + for (uint32_t i = 0; i < root_directory.file_count; i++) { + if (strcmp(root_directory.files[i]->name, name) == 0) { + return root_directory.files[i]; + } + } + return NULL; +} + +bool ramfs_resize_file(const char* name, uint32_t new_size) +{ + ramfs_file_t* file = ramfs_find_file(name); + if (file) + { + file->data = realloc(file->data, new_size); + file->size = new_size; + return true; + } + return false; +} + +bool ramfs_append_to_file(const char* name, const uint8_t* data, uint32_t data_size) +{ + ramfs_file_t* file = ramfs_find_file(name); + if (file && (file->permissions & RAMFS_PERM_WRITE)) + { + file->data = realloc(file->data, file->size + data_size + 1); + if (!file->data) return false; + + memcpy(file->data + file->size, data, data_size); + file->size += data_size; + file->data[file->size] = '\0'; + return true; + } + return false; +} + +bool ramfs_create_directory(const char* name) +{ + if (root_directory.file_count < RAMFS_MAX_FILES) { + ramfs_directory_t* dir = (ramfs_directory_t*)malloc(sizeof(ramfs_directory_t)); + strncpy(dir->name, name, RAMFS_MAX_PATH_LEN); + dir->file_count = 0; + root_directory.files[root_directory.file_count++] = (ramfs_file_t*)dir; + return true; + } + return false; +} + +bool ramfs_delete_directory(const char* name) +{ + for (uint32_t i = 0; i < root_directory.file_count; i++) { + if (strcmp(root_directory.files[i]->name, name) == 0) { + // Delete the directory (along with its files) + ramfs_directory_t* dir = (ramfs_directory_t*)root_directory.files[i]; + for (uint32_t j = 0; j < dir->file_count; j++) { + free(dir->files[j]->data); + } + free(dir); + root_directory.files[i] = root_directory.files[root_directory.file_count - 1]; + root_directory.file_count--; + return true; + } + } + return false; +} + +bool ramfs_check_permissions(const char* name, uint8_t required_permissions) +{ + ramfs_file_t* file = ramfs_find_file(name); + if (file) { + return (file->permissions & required_permissions) == required_permissions; + } + return false; +} + +void ramfs_list_files() +{ + printf("Files in RAMFS:\n"); + for (uint32_t i = 0; i < root_directory.file_count; i++) { + printf("%s (Size: %u bytes)\n", root_directory.files[i]->name, root_directory.files[i]->size); + } +} + +void ramfs_list_directories() +{ + printf("Directories in RAMFS:\n"); + for (uint32_t i = 0; i < root_directory.file_count; i++) { + if (root_directory.files[i]->data != NULL) { + printf("%s\n", root_directory.files[i]->name); + } + } +} + +bool ramfs_read_file(const char* name, uint8_t* buffer, uint32_t buffer_size) +{ + ramfs_file_t* file = ramfs_find_file(name); + if (file && (file->permissions & RAMFS_PERM_READ)) + { + uint32_t bytes_to_read = (file->size < buffer_size) ? file->size : buffer_size; + memcpy(buffer, file->data, bytes_to_read); + return true; + } + return false; +} + +bool ramfs_write_file(const char* name, const uint8_t* data, uint32_t size) +{ + return ramfs_append_to_file(name, data, size); +} + +bool ramfs_overwrite_file(const char* name, const uint8_t* data, uint32_t size) +{ + ramfs_file_t* file = ramfs_find_file(name); + if (!file || !(file->permissions & RAMFS_PERM_WRITE)) { + return false; + } + + // Allocate space for data + null terminator + uint8_t* new_data = realloc(file->data, size + 1); + if (!new_data && size > 0) { + return false; // realloc failed + } + + file->data = new_data; + + if (size > 0 && data) { + memcpy(file->data, data, size); + } + + file->data[size] = '\0'; // Ensure null-terminated + file->size = size; + + return true; +} + diff --git a/lib/math/software_float.c b/lib/math/software_float.c new file mode 100644 index 0000000..7f2c617 --- /dev/null +++ b/lib/math/software_float.c @@ -0,0 +1,26 @@ + +#include + + +/* A float = FLOAT_SIGN * FLOAT_MANTISSA * (2 ^ FLOAT_EXPONENT) */ + + + + +soft_float32_t decode_float(uint32_t raw) +{ + soft_float32_t f; + f.sign = (raw >> 31) & 1; + f.exponent = (raw >> 23) & 0xFF; + f.mantissa = raw & 0x7FFFFF; + return f; +} + +soft_float64_t decode_double(uint64_t raw) +{ + soft_float64_t d; + d.sign = (raw >> 63) & 1; + d.exponent = (raw >> 52) & 0x7FF; + d.mantissa = raw & 0xFFFFFFFFFFFFF; + return d; +} diff --git a/lib/mm/heap.c b/lib/mm/heap.c new file mode 100644 index 0000000..28fc841 --- /dev/null +++ b/lib/mm/heap.c @@ -0,0 +1,136 @@ +#include +#include + +#include +#include +#include + + +#define ALIGNMENT 8 +#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1)) + +typedef struct block_header { + size_t size; + struct block_header* next; + int free; +} block_header_t; + +#define BLOCK_SIZE sizeof(block_header_t) + +static uint8_t* heap_base = (uint8_t*)HEAP_START; +static uint8_t* heap_end = (uint8_t*)(HEAP_START + HEAP_SIZE); + +static block_header_t* free_list = NULL; + + +void heap_init(void) +{ + free_list = (block_header_t*)heap_base; + free_list->size = HEAP_SIZE - BLOCK_SIZE; + free_list->next = NULL; + free_list->free = 1; +} + + +void* malloc(size_t size) +{ + size = ALIGN(size); + block_header_t* curr = free_list; + + while (curr) + { + if (curr->free && curr->size >= size) + { + /* Split if there's space for another block */ + if (curr->size >= size + BLOCK_SIZE + ALIGNMENT) + { + block_header_t* new_block = (block_header_t*)((uint8_t*)curr + BLOCK_SIZE + size); + new_block->size = curr->size - size - BLOCK_SIZE; + new_block->next = curr->next; + new_block->free = 1; + + curr->next = new_block; + curr->size = size; + } + + curr->free = 0; + return (void*)((uint8_t*)curr + BLOCK_SIZE); + } + curr = curr->next; + } + + printd("Malloc failed due to lack of free memory\n"); + return NULL; +} + +void* calloc(size_t nmemb, size_t size) +{ + size_t total = nmemb * size; + void* ptr = malloc(total); + if (ptr) + { + memset(ptr, 0, total); + } + return ptr; +} + +void* realloc(void* ptr, size_t size) +{ + if (!ptr) + { + return malloc(size); + } + + if (size == 0) + { + free(ptr); + return NULL; + } + + block_header_t* block = (block_header_t*)((uint8_t*)ptr - BLOCK_SIZE); + if (block->size >= size) + { + return ptr; + } + + void* new_ptr = malloc(size); + if (new_ptr) { + memcpy(new_ptr, ptr, block->size); + free(ptr); + } + return new_ptr; +} + +void free(void* ptr) +{ + if (!ptr) + { + return; + } + + block_header_t* block = (block_header_t*)((uint8_t*)ptr - BLOCK_SIZE); + block->free = 1; + + /* Forward coalescing */ + if (block->next && block->next->free) + { + block->size += BLOCK_SIZE + block->next->size; + block->next = block->next->next; + } + + /* Backward coalescing */ + block_header_t* prev = NULL; + block_header_t* curr = free_list; + + while (curr && curr != block) + { + prev = curr; + curr = curr->next; + } + + if (prev && prev->free) + { + prev->size += BLOCK_SIZE + block->size; + prev->next = block->next; + } +} diff --git a/lib/mm/paging.c b/lib/mm/paging.c new file mode 100644 index 0000000..6b06e5b --- /dev/null +++ b/lib/mm/paging.c @@ -0,0 +1,82 @@ +#include +#include + +#include +#include + +#define PAGE_DIRECTORY_ENTRIES 1024 +#define PAGE_TABLE_ENTRIES 1024 +#define PAGE_SIZE 4096 + +typedef uint32_t page_directory_entry_t; +typedef uint32_t page_table_entry_t; + +static page_directory_entry_t* page_directory = (page_directory_entry_t*)0x9C000; /* Must be page-aligned */ +static page_table_entry_t* page_tables[PAGE_DIRECTORY_ENTRIES]; + +extern void _enable_paging_asm(void); + +void paging_init(void) +{ + /* Allocate and clear the page directory */ + page_directory = (uint32_t*)alloc_page(); + memset(page_directory, 0, 4096); + + /* Allocate and set up the first identity-mapped page table (0–4MB) */ + uint32_t* first_table = (uint32_t*)alloc_page(); + memset(first_table, 0, 4096); + for (uint32_t i = 0; i < 1024; i++) + { + first_table[i] = (i * 0x1000) | 3; /* Present | RW */ + } + page_directory[0] = ((uint32_t)first_table) | 3; + + /* --- Map 4MB heap at 0xC0000000 --- */ + uint32_t* heap_table = (uint32_t*)alloc_page(); + memset(heap_table, 0, 4096); + + for (uint32_t i = 0; i < 1024; i++) /* 256 pages = 1MB */ + { + uint32_t phys = (uint32_t)alloc_page(); + if (phys == 0) + { + printf("Out of physical memory during heap mapping!\n"); + while (1); + } + heap_table[i] = phys | 3; /* Present | RW */ + } + + /* 0xC0000000 >> 22 == 768 */ + page_directory[768] = ((uint32_t)heap_table) | 3; + + // Load page directory + asm volatile ("mov %0, %%cr3" : : "r"(page_directory)); + + /* Enable paging */ + _enable_paging_asm(); +} + +void map_page(void* phys_addr, void* virt_addr) +{ + uint32_t pd_index = ((uint32_t)virt_addr >> 22) & 0x3FF; + uint32_t pt_index = ((uint32_t)virt_addr >> 12) & 0x3FF; + + /* Allocate page table if necessary */ + if (!(page_directory[pd_index] & 1)) + { + void* pt_phys = alloc_page(); + page_tables[pd_index] = (page_table_entry_t*)((uint32_t)pt_phys + 0xC0000000); /* Map it higher */ + + for (int i = 0; i < PAGE_TABLE_ENTRIES; i++) + { + page_tables[pd_index][i] = 0x00000002; /* Not present */ + } + + page_directory[pd_index] = ((uint32_t)pt_phys) | 0x3; /* Present, R/W */ + } + + page_table_entry_t* page_table = (page_table_entry_t*)((page_directory[pd_index] & 0xFFFFF000) | 0xC0000000); + page_table[pt_index] = ((uint32_t)phys_addr & 0xFFFFF000) | 0x3; /* Present, R/W */ + + asm volatile ("invlpg (%0)" :: "r" (virt_addr) : "memory"); +} diff --git a/lib/mm/pmm.c b/lib/mm/pmm.c new file mode 100644 index 0000000..637780c --- /dev/null +++ b/lib/mm/pmm.c @@ -0,0 +1,79 @@ +#include + +#include + +#define PAGE_SIZE 4096 +#define BITMAP_SIZE (1024 * 1024) /* Supports up to 4GB RAM (1 bit per page) */ + +static uint8_t bitmap[BITMAP_SIZE / 8]; +static uint32_t total_pages; +static uint32_t used_pages = 0; + +static inline void set_bit(uint32_t idx) +{ + bitmap[idx / 8] |= (1 << (idx % 8)); +} + +static inline void clear_bit(uint32_t idx) +{ + bitmap[idx / 8] &= ~(1 << (idx % 8)); +} + +static inline int test_bit(uint32_t idx) +{ + return (bitmap[idx / 8] >> (idx % 8)) & 1; +} + +void pmm_init(multiboot_info_t* mb_info) +{ + total_pages = 0x100000; /* 4GB / 4KB = 1M pages */ + for (uint32_t i = 0; i < total_pages / 8; i++) + { + bitmap[i] = 0xFF; /* Mark all as used */ + } + + multiboot_memory_map_t* mmap = (multiboot_memory_map_t*) mb_info->mmap_addr; + while ((uint32_t)mmap < mb_info->mmap_addr + mb_info->mmap_length) + { + if (mmap->type == 1) /* Usable */ + { + uint64_t base = mmap->addr; + uint64_t len = mmap->len; + for (uint64_t addr = base; addr < base + len; addr += PAGE_SIZE) + { + if (addr >= 0x210000) /* Skip first 2.1MB, or ≈ 2.06MiB */ + { + uint32_t idx = addr / PAGE_SIZE; + + if (idx >= total_pages) + { + continue; /* skip entries above 4GB */ + } + + clear_bit(idx); + used_pages--; + } + } + } + mmap = (multiboot_memory_map_t*)((uint32_t)mmap + mmap->size + sizeof(mmap->size)); + } +} + +void* alloc_page(void) { + for (uint32_t i = 0; i < total_pages; i++) + { + if (!test_bit(i)) + { + set_bit(i); + used_pages++; + return (void*)(i * PAGE_SIZE); + } + } + return NULL; /* Out of memory */ +} + +void free_page(void* ptr) +{ + uint32_t idx = (uint32_t)ptr / PAGE_SIZE; + clear_bit(idx); +} diff --git a/lib/printf.c b/lib/printf.c new file mode 100644 index 0000000..4a0efcb --- /dev/null +++ b/lib/printf.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include + +#include + +void printwc(const char* str, uint8_t color) +{ + uint8_t c = terminal_getcolor(); + terminal_setcolor(color); + printf(str); + terminal_setcolor(c); +} + +void printd(const char* str) +{ + terminal_debug_writestring(str); +} + +void printdc(const char* str, uint8_t color) +{ + uint8_t c = terminal_getcolor(); + terminal_setcolor(color); + printd(str); + terminal_setcolor(c); +} + +void printf(const char* format, ...) { + va_list args; + va_start(args, format); + + for (size_t i = 0; format[i] != '\0'; ++i) { + if (format[i] == '%' && format[i + 1] != '\0') { + ++i; + + // Parse width (only supports zero-padding like %04x) + int width = 0; + if (format[i] == '0') { + ++i; + while (format[i] >= '0' && format[i] <= '9') { + width = width * 10 + (format[i] - '0'); + ++i; + } + } + + switch (format[i]) { + case 's': { + const char* str = va_arg(args, const char*); + terminal_writestring(str ? str : "(null)"); + break; + } + case 'c': { + char c = (char) va_arg(args, int); + terminal_putchar(c); + break; + } + case 'd': + case 'i': { + int32_t val = va_arg(args, int32_t); + print_int(val); + break; + } + case 'u': { + uint32_t val = va_arg(args, uint32_t); + print_uint(val); + break; + } + case 'x': { + uint32_t val = va_arg(args, uint32_t); + print_hex(val, width, false); + break; + } + case 'X': { + uint32_t val = va_arg(args, uint32_t); + print_hex(val, width, true); + break; + } + case 'p': { + void* ptr = va_arg(args, void*); + terminal_writestring("0x"); + print_hex((uint32_t)(uintptr_t)ptr, 8, true); // assumes 32-bit pointer + break; + } + case 'f': + case 'F': { + double val = va_arg(args, double); // double is promoted from float + print_double(val, 2); // 2 decimal places + break; + } + case '%': { + terminal_putchar('%'); + break; + } + default: { + terminal_putchar('%'); + terminal_putchar(format[i]); + break; + } + } + } else { + terminal_putchar(format[i]); + } + } + + va_end(args); +} + +void print_double(double value, int precision) +{ + // Handle the integer part + int32_t integer_part = (int32_t)value; + double fractional_part = value - integer_part; + + // Print the integer part + print_int(integer_part); + + // Print the decimal point + terminal_putchar('.'); + + // Print the fractional part (scaled up) + fractional_part *= 1; + for (int i = 0; i < precision; i++) { + fractional_part *= 10; + } + + int32_t frac_int = (int32_t)fractional_part; + print_int(frac_int); +} + +void print_uint(uint32_t value) { + char buffer[11]; // Enough for 32-bit unsigned int + int i = 0; + + do { + buffer[i++] = '0' + (value % 10); + value /= 10; + } while (value > 0); + + while (i--) { + terminal_putchar(buffer[i]); + } +} + +void print_int(int32_t value) { + char buffer[12]; // Enough for 32-bit signed int (-2147483648) + int i = 0; + uint32_t u; + + if (value < 0) { + terminal_putchar('-'); + u = (uint32_t)(-value); + } else { + u = (uint32_t)value; + } + + // Convert to string in reverse + do { + buffer[i++] = '0' + (u % 10); + u /= 10; + } while (u > 0); + + // Print in correct order + while (i--) { + terminal_putchar(buffer[i]); + } +} + +void print_hex(uint32_t value, int width, bool uppercase) +{ + const char* hex_chars = uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; + char buffer[9]; // 8 hex digits max for 32-bit + int i = 0; + + do { + buffer[i++] = hex_chars[value & 0xF]; + value >>= 4; + } while (value || i < width); // ensure at least 'width' digits + + while (i--) { + terminal_putchar(buffer[i]); + } +} diff --git a/lib/processes.c b/lib/processes.c new file mode 100644 index 0000000..a84eafb --- /dev/null +++ b/lib/processes.c @@ -0,0 +1,5 @@ +#include + +#include + + diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000..47660be --- /dev/null +++ b/lib/string.c @@ -0,0 +1,160 @@ +#include + +#include + +extern int16_t sse_initialized; + +size_t strlen(const char* str) +{ + size_t len = 0; + while (str[len]) len++; + return len; +} + +size_t strcmp(const char *s1, const char *s2) +{ + int32_t i = 0; + + while ((s1[i] == s2[i])) + { + if (s2[i++] == 0) + { + return 0; + } + } + return 1; +} + +int32_t strncmp(const char *s1, const char *s2, size_t n) +{ + while (n--) + { + unsigned char c1 = (unsigned char)*s1++; + unsigned char c2 = (unsigned char)*s2++; + if (c1 != c2) + { + return c1 - c2; + } + if (c1 == '\0') + { + break; + } + } + return 0; +} + + +size_t strcpy(char *dst, const char *src) +{ + int32_t i = 0; + while ((*dst++ = *src++) != 0) + i++; + return i; +} + +char *strncpy(char *dest, const char *src, uint32_t n) +{ + if (sse_initialized > 0) + { + return sse2_strncpy(dest, src, n); + } + + uint32_t i = 0; + for (; i < n && src[i]; ++i) + dest[i] = src[i]; + + for (; i < n; ++i) + dest[i] = '\0'; + + return dest; +} + +void strcat(char *dest, const char *src) +{ + char *end = (char *)dest + strlen(dest); + memcpy((void *)end, (void *)src, strlen(src)); + end = end + strlen(src); + *end = '\0'; +} + +void *memset(void *dst, char c, uint32_t n) +{ + char *temp = dst; + for (; n != 0; n--) *temp++ = c; + return dst; +} + +void *memcpy(void *dst, const void *src, uint32_t n) +{ + if (sse_initialized > 0) + { + return sse2_memcpy(dst, src, n); + } + + char *ret = dst; + char *p = dst; + const char *q = src; + while (n--) + *p++ = *q++; + return ret; +} + +int32_t memcmp(uint8_t *s1, uint8_t *s2, uint32_t n) +{ + while (n--) + { + if (*s1 != *s2) + return 0; + s1++; + s2++; + } + return 1; +} + +void* memclr(const void * const m_start, const size_t m_count) +{ + if (sse_initialized > 0) + { + return memclr_sse2(m_start, m_count); + } + + return memset(m_start, '\0', (uint32_t)m_count); +} + +int32_t isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +int32_t isalpha(char c) +{ + return (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))); +} + +int32_t ischar(int32_t c) +{ + return ((c >= 32) && (c <= 126)); +} + +char upper(char c) +{ + if ((c >= 'a') && (c <= 'z')) + return (c - 32); + return c; +} + +char toupper(char c) +{ + if ((c >= 'a') && (c <= 'z')) + { + return (c - 32); + } + return c; +} + +char lower(char c) +{ + if ((c >= 'A') && (c <= 'Z')) + return (c + 32); + return c; +} diff --git a/lib/syscall.c b/lib/syscall.c new file mode 100644 index 0000000..49f6d35 --- /dev/null +++ b/lib/syscall.c @@ -0,0 +1,4 @@ +#include + + +#include diff --git a/lib/vector_extentions/sse.c b/lib/vector_extentions/sse.c new file mode 100644 index 0000000..02ece9a --- /dev/null +++ b/lib/vector_extentions/sse.c @@ -0,0 +1,263 @@ +#include +#include +#include + +#include + +#include + + +void enable_sse(void) { + uint32_t cr0, cr4; + + __asm__ volatile ("mov %%cr0, %0" : "=r"(cr0)); + cr0 &= ~(1 << 2); // EM = 0 + cr0 |= (1 << 1); // MP = 1 + __asm__ volatile ("mov %0, %%cr0" :: "r"(cr0)); + + __asm__ volatile ("mov %%cr4, %0" : "=r"(cr4)); + cr4 |= (1 << 9); // OSFXSR = 1 + cr4 |= (1 << 10); // OSXMMEXCPT = 1 + __asm__ volatile ("mov %0, %%cr4" :: "r"(cr4)); +} + +// Basic SSE test: add two arrays of 4 floats using xmm registers +__attribute__((force_align_arg_pointer)) +int32_t test_sse(void) { + float a[4] __attribute__((aligned(16))) = {1.0f, 2.0f, 3.0f, 4.0f}; + float b[4] __attribute__((aligned(16))) = {5.0f, 6.0f, 7.0f, 8.0f}; + float result[4] __attribute__((aligned(16))); + + asm volatile ( + "movaps %1, %%xmm0\n\t" + "movaps %2, %%xmm2\n\t" + "addps %%xmm2, %%xmm0\n\t" + "movaps %%xmm0, %0\n\t" + : "=m" (result) + : "m" (a), "m" (b) + : "xmm0", "xmm2" + ); + + if (result[0] != 6.0 || result[1] != 8.0 || result[2] != 10.0 || result[3] != 12.0) + { + return -1; + } + + return 0; +} + +__attribute__((force_align_arg_pointer)) +void sse2_add_double_arrays(double *dst, const double *a, const double *b, size_t count) +{ + for (size_t i = 0; i < count; i += 2) + { + asm volatile ( + "movapd (%1), %%xmm0\n\t" + "movapd (%2), %%xmm1\n\t" + "addpd %%xmm1, %%xmm0\n\t" + "movapd %%xmm0, (%0)\n\t" + : + : "r"(dst + i), "r"(a + i), "r"(b + i) + : "xmm0", "xmm1", "memory" + ); + } +} + +__attribute__((force_align_arg_pointer)) +void sse2_add_int64_arrays(int64_t *dst, const int64_t *a, const int64_t *b, size_t count) +{ + for (size_t i = 0; i < count; i += 2) + { + asm volatile ( + "movdqa (%1), %%xmm0\n\t" + "movdqa (%2), %%xmm1\n\t" + "paddq %%xmm1, %%xmm0\n\t" + "movdqa %%xmm0, (%0)\n\t" + : + : "r"(dst + i), "r"(a + i), "r"(b + i) + : "xmm0", "xmm1", "memory" + ); + } +} + +__attribute__((force_align_arg_pointer)) +void sse2_add_int32_arrays(int32_t *dst, const int32_t *a, const int32_t *b, size_t count) +{ + for (size_t i = 0; i < count; i += 4) + { + asm volatile ( + "movdqa (%1), %%xmm0\n\t" + "movdqa (%2), %%xmm1\n\t" + "paddd %%xmm1, %%xmm0\n\t" + "movdqa %%xmm0, (%0)\n\t" + : + : "r"(dst + i), "r"(a + i), "r"(b + i) + : "xmm0", "xmm1", "memory" + ); + } +} + +__attribute__((force_align_arg_pointer)) +void *sse2_memcpy(void *dst, const void *src, uint32_t n) +{ + uintptr_t i; + + /* Align to 16 bytes if necessary */ + while (((uintptr_t)dst & 15) && n > 0) + { + *((uint8_t*)dst) = *((uint8_t*)src); + dst = (uint8_t*)dst + 1; + src = (uint8_t*)src + 1; + n--; + } + + /* Copy 16-byte chunks with SSE2 */ + for (i = 0; i + 15 < n; i += 16) + { + asm volatile ( + "movdqa (%1), %%xmm0\n\t" /* Load 16 bytes from source into xmm0 */ + "movdqa %%xmm0, (%0)\n\t" /* Store 16 bytes to destination */ + : + : "r"(dst + i), "r"(src + i) + : "xmm0", "memory" + ); + } + + /* Handle remaining bytes (less than 16) */ + while (n > 0) + { + *((uint8_t*)dst) = *((uint8_t*)src); + dst = (uint8_t*)dst + 1; + src = (uint8_t*)src + 1; + n--; + } + + return dst; /* Return pointer to destination */ +} + +__attribute__((force_align_arg_pointer)) +char *sse2_strncpy(char *dest, const char *src, uint32_t n) +{ + uint32_t i = 0; + + /* Align initial copy */ + while (((uintptr_t)(dest + i) & 15) && i < n && src[i]) { + dest[i] = src[i]; + i++; + } + + /* Bulk copy in 16-byte blocks */ + for (; i + 15 < n; i += 16) + { + asm volatile ( + "movdqu (%1), %%xmm0\n\t" + "movdqu %%xmm0, (%0)\n\t" + : + : "r"(dest + i), "r"(src + i) + : "xmm0", "memory" + ); + + /* Manually check if any nulls in the just-copied block */ + for (int j = 0; j < 16; ++j) + { + if (src[i + j] == '\0') + { + /* Null found: pad the rest with zeros */ + for (int k = j + 1; k < 16 && i + k < n; ++k) + { + dest[i + k] = '\0'; + } + i += 16; + goto tail; + } + } + } + +tail: + /* Final bytes */ + for (; i < n && src[i]; ++i) + { + dest[i] = src[i]; + } + + for (; i < n; ++i) + { + dest[i] = '\0'; + } + + return dest; +} + + +__attribute__((force_align_arg_pointer)) +void double_vector_to_int_vector(const double *src, int32_t *dst) +{ + asm volatile ( + "pxor %%xmm0, %%xmm0\n\t" /* zero xmm0 */ + "cvttpd2dq (%1), %%xmm1\n\t" /* convert src to int32s */ + "movq %%xmm1, %%xmm0\n\t" /* move low 64 bits (2 ints) to xmm0 */ + "movdqa %%xmm0, (%0)\n\t" /* store result */ + : + : "r"(dst), "r"(src) + : "xmm0", "xmm1", "memory" + ); +} + +__attribute__((force_align_arg_pointer)) +void int_vector_to_double_vector(const int32_t *src, double *dst) +{ + asm volatile ( + "movq (%1), %%xmm0\n\t" /* Load 2 int32s (64 bits) into xmm0 */ + "cvtdq2pd %%xmm0, %%xmm1\n\t" /* Convert to 2 doubles */ + "movapd %%xmm1, (%0)\n\t" /* Store to destination */ + : + : "r"(dst), "r"(src) + : "xmm0", "xmm1", "memory" + ); +} + +void * memclr_sse2(const void * const m_start, const size_t m_count) +{ + + /* "i" is our counter of how many bytes we've cleared */ + size_t i; + + /* find out if "m_start" is aligned on a SSE_XMM_SIZE boundary */ + if ((size_t)m_start & (SSE_XMM_SIZE - 1)) + { + i = 0; + + /* we need to clear byte-by-byte until "m_start" is aligned on an SSE_XMM_SIZE boundary */ + /* ... and lets make sure we don't copy 'too' many bytes (i < m_count) */ + while (((size_t)m_start + i) & (SSE_XMM_SIZE - 1) && i < m_count) + { + asm volatile ("stosb;" :: "D"((size_t)m_start + i), "a"(0)); + i++; + } + } + else + { + /* if "m_start" was aligned, set our count to 0 */ + i = 0; + } + + asm volatile ("pxor %%xmm0,%%xmm0"::); /* zero out XMM0 */ + /* clear 64-byte chunks of memory (4 16-byte operations) */ + for(; i + 64 <= m_count; i += 64) + { + asm volatile (" movdqa %%xmm0, 0(%0); " /* move 16 bytes from XMM0 to %0 + 0 */ + " movdqa %%xmm0, 16(%0); " + " movdqa %%xmm0, 32(%0); " + " movdqa %%xmm0, 48(%0); " + :: "r"((size_t)m_start + i)); + } + + /* copy the remaining bytes (if any) */ + asm volatile (" rep stosb; " :: "a"((size_t)(0)), "D"(((size_t)m_start) + i), "c"(m_count - i)); + + /* "i" will contain the total amount of bytes that were actually transfered */ + i += m_count - i; + + /* we return "m_start" + the amount of bytes that were transfered */ + return (void *)(((size_t)m_start) + i); +} diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..b6f0aed --- /dev/null +++ b/linker.ld @@ -0,0 +1,71 @@ +/* The bootloader will look at this image and start execution at the symbol + designated as the entry point. */ +ENTRY(_start) + +/* Tell where the various sections of the object files will be put in the final + kernel image. */ +SECTIONS +{ + /* It used to be universally recommended to use 1M as a start offset, + as it was effectively guaranteed to be available under BIOS systems. + However, UEFI has made things more complicated, and experimental data + strongly suggests that 2M is a safer place to load. In 2016, a new + feature was introduced to the multiboot2 spec to inform bootloaders + that a kernel can be loaded anywhere within a range of addresses and + will be able to relocate itself to run from such a loader-selected + address, in order to give the loader freedom in selecting a span of + memory which is verified to be available by the firmware, in order to + work around this issue. This does not use that feature, so 2M was + chosen as a safer option than the traditional 1M. */ + . = 2M; + + /* First put the multiboot header, as it is required to be put very early + in the image or the bootloader won't recognize the file format. + Next we'll put the .text section. */ + .text BLOCK(4K) : ALIGN(4K) + { + KEEP(*(.multiboot)) + *(.text) + } + + /* Read-only data. */ + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + /* Read-write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + /* Read-write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + + /* Include the list of initialization functions sorted. */ + .init_array : + { + crti.o(.init_array) + KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .init_array.*))) + KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .init_array)) + crtn.o(.init_array) + } + + /* Include the list of termination functions sorted. */ + .fini_array : + { + crti.o(.fini_array) + KEEP (*(SORT(EXCLUDE_FILE(crti.o crtn.o) .fini_array.*))) + KEEP (*(EXCLUDE_FILE(crti.o crtn.o) .fini_array)) + crtn.o(.fini_array) + } + + /* The compiler may produce other sections, by default it will put them in + a segment with the same name. Simply add stuff here as needed. */ +} + diff --git a/misc_asm.s b/misc_asm.s new file mode 100644 index 0000000..408b5b6 --- /dev/null +++ b/misc_asm.s @@ -0,0 +1,62 @@ +/* + A bunch of functions I could have written in C (or not), but decided not to, + Mostly so I could get more experience in assembler. +*/ + +/* + DO NOT USE "pusha" AND/OR "popa"!!! + THEY ARE DEPRECATED (or something like that) + AND SHOULD NOT BE USED!!! + INSTEAD PUSH/POP REGISTERS ON/OFF THE STACK INDIVIDUALLY!!! +*/ + + +.section .text + +.global _enable_paging_asm + +.global _push_regs +.global _pop_regs + +.global _hang_asm + +_enable_paging_asm: + push %eax + + mov %cr0, %eax + or $0x80000000, %eax + mov %eax, %cr0 + + pop %eax + + ret + +_push_regs: + push %eax + push %ebx + push %ecx + push %edx + push %esi + push %edi + push %esp + push %ebp + + ret + +_pop_regs: + pop %ebp + pop %esp + pop %edi + pop %esi + pop %edx + pop %ecx + pop %ebx + pop %eax + + ret + +_hang_asm: + hlt + jmp _hang_asm + +.section .data diff --git a/pit.asm b/pit.asm new file mode 100644 index 0000000..6db5ed6 --- /dev/null +++ b/pit.asm @@ -0,0 +1,132 @@ + +; NOTE: the code in this section is mostly copied from the OSDev wiki. (wiki.osdev.org) {though osdev.org/osdev.com works just fine.} + + + +section .bss +system_timer_fractions: resd 1 ; Fractions of 1 ms since timer initialized +system_timer_ms: resd 1 ; Number of whole ms since timer initialized +IRQ0_fractions: resd 1 ; Fractions of 1 ms between IRQs +IRQ0_ms: resd 1 ; Number of whole ms between IRQs +IRQ0_frequency: resd 1 ; Actual frequency of PIT +PIT_reload_value: resw 1 ; Current PIT reload value + +section .text + +global IRQ0_handler +global init_PIT + +IRQ0_handler: + push eax + push ebx + + mov eax, [IRQ0_fractions] + mov ebx, [IRQ0_ms] ; eax.ebx = amount of time between IRQs + add [system_timer_fractions], eax ; Update system timer tick fractions + adc [system_timer_ms], ebx ; Update system timer tick milli-seconds + + mov al, 0x20 + out 0x20, al ; Send the EOI to the PIC + + pop ebx + pop eax + iretd + + +;Input + ; ebx Desired PIT frequency in Hz + + init_PIT: + pushad + + ; Do some checking + + mov eax,0x10000 ;eax = reload value for slowest possible frequency (65536) + cmp ebx,18 ;Is the requested frequency too low? + jbe .gotReloadValue ; yes, use slowest possible frequency + + mov eax,1 ;ax = reload value for fastest possible frequency (1) + cmp ebx,1193181 ;Is the requested frequency too high? + jae .gotReloadValue ; yes, use fastest possible frequency + + ; Calculate the reload value + + mov eax,3579545 + mov edx,0 ;edx:eax = 3579545 + div ebx ;eax = 3579545 / frequency, edx = remainder + cmp edx,3579545 / 2 ;Is the remainder more than half? + jb .l1 ; no, round down + inc eax ; yes, round up + .l1: + mov ebx,3 + mov edx,0 ;edx:eax = 3579545 * 256 / frequency + div ebx ;eax = (3579545 * 256 / 3 * 256) / frequency + cmp edx,3 / 2 ;Is the remainder more than half? + jb .l2 ; no, round down + inc eax ; yes, round up + .l2: + + + ; Store the reload value and calculate the actual frequency + + .gotReloadValue: + push eax ;Store reload_value for later + mov [PIT_reload_value],ax ;Store the reload value for later + mov ebx,eax ;ebx = reload value + + mov eax,3579545 + mov edx,0 ;edx:eax = 3579545 + div ebx ;eax = 3579545 / reload_value, edx = remainder + cmp edx,3579545 / 2 ;Is the remainder more than half? + jb .l3 ; no, round down + inc eax ; yes, round up + .l3: + mov ebx,3 + mov edx,0 ;edx:eax = 3579545 / reload_value + div ebx ;eax = (3579545 / 3) / frequency + cmp edx,3 / 2 ;Is the remainder more than half? + jb .l4 ; no, round down + inc eax ; yes, round up + .l4: + mov [IRQ0_frequency],eax ;Store the actual frequency for displaying later + + + ; Calculate the amount of time between IRQs in 32.32 fixed point + ; + ; Note: The basic formula is: + ; time in ms = reload_value / (3579545 / 3) * 1000 + ; This can be rearranged in the following way: + ; time in ms = reload_value * 3000 / 3579545 + ; time in ms = reload_value * 3000 / 3579545 * (2^42)/(2^42) + ; time in ms = reload_value * 3000 * (2^42) / 3579545 / (2^42) + ; time in ms * 2^32 = reload_value * 3000 * (2^42) / 3579545 / (2^42) * (2^32) + ; time in ms * 2^32 = reload_value * 3000 * (2^42) / 3579545 / (2^10) + + pop ebx ;ebx = reload_value + mov eax,0xDBB3A062 ;eax = 3000 * (2^42) / 3579545 + mul ebx ;edx:eax = reload_value * 3000 * (2^42) / 3579545 + shrd eax,edx,10 + shr edx,10 ;edx:eax = reload_value * 3000 * (2^42) / 3579545 / (2^10) + + mov [IRQ0_ms],edx ;Set whole ms between IRQs + mov [IRQ0_fractions],eax ;Set fractions of 1 ms between IRQs + + + ; Program the PIT channel + + pushfd + cli ;Disabled interrupts (just in case) + + mov al,00110100b ;channel 0, lobyte/hibyte, rate generator + out 0x43, al + + mov ax,[PIT_reload_value] ;ax = 16 bit reload value + out 0x40,al ;Set low byte of PIT reload value + mov al,ah ;ax = high 8 bits of reload value + out 0x40,al ;Set high byte of PIT reload value + + popfd + + popad + ret +