From 105076a8ce4a2d788a716cbdc00a323398ceaaf7 Mon Sep 17 00:00:00 2001 From: Tom-on64 Date: Tue, 30 Jul 2024 21:07:05 +0200 Subject: [PATCH] Commited some old changes, idk what they were --- .gitignore | 16 ++++++- Makefile | 40 +++++++++--------- docs/syscalls.txt | 29 +++++-------- include/c/syscall.h | 44 +++++++++++++------ include/disk/filesys.h | 80 ----------------------------------- include/fs/fs.h | 13 +++--- include/fs/impl.h | 52 +++++++++++++++++++++++ include/interrupt/syscalls.h | 41 +++++++++++++++--- include/screen/text.h | 10 ----- kern.iso | Bin 0 -> 1474560 bytes makedisk.c | 33 ++++++++------- src/kernel.c | 47 ++++++++++++++++---- src/stage2.asm | 6 +-- src/stage3.c | 49 +++++---------------- 14 files changed, 238 insertions(+), 222 deletions(-) delete mode 100644 include/disk/filesys.h create mode 100644 include/fs/impl.h create mode 100644 kern.iso diff --git a/.gitignore b/.gitignore index 8ba6358..8a3d186 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ -build -kern.iso +.DS_Store + +# Binaries +bin/ +*.bin makedisk + +# Object files +*.o +*.obj +*.elf + +# Vim swap files +*.swp + diff --git a/Makefile b/Makefile index e1deea1..702920c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Variables -CC = i386-elf-gcc -LD = i386-elf-ld +CC = i686-elf-gcc +LD = i686-elf-ld AS = nasm CFLAGS = -std=c17 -ffreestanding -fno-builtin -fno-stack-protector -fno-pie \ @@ -8,51 +8,51 @@ CFLAGS = -std=c17 -ffreestanding -fno-builtin -fno-stack-protector -fno-pie \ LDFLAGS = -z notext -melf_i386 SRC = ./src -BUILD = ./build +BIN = ./bin C_FILES = calc editor FONTS = testfont term16n .PHONY: os clean run # Main compilation target -os: $(BUILD) bootloader kernel $(C_FILES) $(FONTS) +os: $(BIN) bootloader kernel $(C_FILES) $(FONTS) @cc ./makedisk.c -o ./makedisk @./makedisk # TODO: List of files @echo "\nDone!\n" -$(BUILD): - @[ -d $(BUILD) ] || mkdir $(BUILD) - @rm -rf $(BUILD)/* +$(BIN): + @[ -d $(BIN) ] || mkdir $(BIN) + @rm -rf $(BIN)/* # Bootloader: This target is responsible for creating stage?.bin (1,2,3) bootloader: $(SRC)/stage1.asm $(SRC)/stage2.asm @echo "Assembling $(SRC)/stage1.asm..." - @$(AS) -fbin -o $(BUILD)/stage1.bin $(SRC)/stage1.asm + @$(AS) -fbin -o $(BIN)/stage1.bin $(SRC)/stage1.asm @echo "Assembling $(SRC)/stage2.asm..." - @$(AS) -fbin -o $(BUILD)/stage2.bin $(SRC)/stage2.asm + @$(AS) -fbin -o $(BIN)/stage2.bin $(SRC)/stage2.asm @echo "Compiling $(SRC)/stage3.c..." - @$(CC) $(CFLAGS) -Iinclude -o $(BUILD)/stage3.o -c $(SRC)/stage3.c - @$(LD) $(LDFLAGS) -T$(SRC)/stage3.ld --oformat binary -o $(BUILD)/stage3.bin $(BUILD)/stage3.o - @rm $(BUILD)/stage3.o + @$(CC) $(CFLAGS) -Iinclude -o $(BIN)/stage3.o -c $(SRC)/stage3.c + @$(LD) $(LDFLAGS) -T$(SRC)/stage3.ld --oformat binary -o $(BIN)/stage3.bin $(BIN)/stage3.o + @rm $(BIN)/stage3.o # Kernel: This target is responsible for creating kernel.bin kernel: $(SRC)/kernel.c @echo "Compiling $(SRC)/kernel.c..." - @$(CC) $(CFLAGS) -Iinclude -o $(BUILD)/kernel.o -c $(SRC)/kernel.c - @$(LD) $(LDFLAGS) -T$(SRC)/kernel.ld --oformat binary -o $(BUILD)/kernel.bin $(BUILD)/kernel.o - @rm $(BUILD)/kernel.o + @$(CC) $(CFLAGS) -Iinclude -o $(BIN)/kernel.o -c $(SRC)/kernel.c + @$(LD) $(LDFLAGS) -T$(SRC)/kernel.ld --oformat binary -o $(BIN)/kernel.bin $(BIN)/kernel.o + @rm $(BIN)/kernel.o # This target compiles *.c (except kernel.c) $(C_FILES): @echo "Compiling $(SRC)/$@.c..." - @$(CC) $(CFLAGS) -o $(BUILD)/$@.o -c $(SRC)/$@.c - @$(LD) $(LDFLAGS) -T$(SRC)/$@.ld --oformat binary -o $(BUILD)/$@.bin $(BUILD)/$@.o - @rm $(BUILD)/$@.o + @$(CC) $(CFLAGS) -o $(BIN)/$@.o -c $(SRC)/$@.c + @$(LD) $(LDFLAGS) -T$(SRC)/$@.ld --oformat binary -o $(BIN)/$@.bin $(BIN)/$@.o + @rm $(BIN)/$@.o # This target builds all fonts inside src/fonts $(FONTS): @echo "Assembling $(SRC)/fonts/$@.asm..." - @$(AS) -fbin -o $(BUILD)/$@.fnt $(SRC)/fonts/$@.asm + @$(AS) -fbin -o $(BIN)/$@.fnt $(SRC)/fonts/$@.asm # Run QEMU run: @@ -64,7 +64,7 @@ run: # Clean all build files clean: - @rm -rf $(BUILD) + @rm -rf $(BIN) @rm -f kern.iso @rm makedisk diff --git a/docs/syscalls.txt b/docs/syscalls.txt index fd3ab08..ea54056 100644 --- a/docs/syscalls.txt +++ b/docs/syscalls.txt @@ -4,23 +4,16 @@ Syscall handlers are prefixed by 'sys_' Syscalls can be called using interrupt 0x80 The EAX register should countain a syscall number on calling and it will contain a return value (if applicable) after calling -Here's a list of syscalls: (and their numbers) +--- Syscall Table --- -Syscall(0) : Sleep() - Args: EBX - Number of miliseconds to sleep for - -Syscall(1) : Write() - Args: EBX - File descriptor - ECX - Buffer to write - EDX - Length - Returns: Status - -Syscall(2) : Read() #TBD - -Syscall(3) : Malloc() - Args: EBX - Amount of bytes to malloc - Returns: Address of allocated memory - -Syscall(4) : Free() - Args: EBX - Pointer to free +| NR | SYSC NAME | RETURN (EAX) | ARG0 (EBX) | ARG1 (ECX) | ARG2 (EDX) | +| 00 | reserved | - | - | - | - | +| 01 | sleep | - | uint ms | - | - | +| 02 | read | int count | int fd | char* buf | uint count | +| 03 | write | int count | int fd | char* buf | uint count | +| 04 | open | int fd | char* path | int oflag | - | +| 05 | close | int status | int fd | - | - | +| 06 | malloc | void* buf | int count | - | - | +| 07 | free | - | void* buf | - | - | +| 08 | seek | #TBD | | | | diff --git a/include/c/syscall.h b/include/c/syscall.h index 7a5067f..5b0389a 100644 --- a/include/c/syscall.h +++ b/include/c/syscall.h @@ -1,23 +1,41 @@ #ifndef SYSCALLNUMS_H #define SYSCALLNUMS_H -#include - -#define MAX_SYSCALLS 5 - -typedef enum { - SYS_SLEEP, - SYS_WRITE, - SYS_GETS, - SYS_MALLOC, - SYS_FREE, -} SYSCALL_NUMBERS; +#define MAX_SYSCALLS 9 // TODO: Add malloc() and free() syscall wrappers?? -int32_t write(int32_t fd, const void* buf, uint32_t len) { - int32_t result = -1; + +typedef enum { + SYS_RESERVED, + SYS_SLEEP, + SYS_GETS, + SYS_WRITE, + SYS_OPEN, + SYS_CLOSE, + SYS_MALLOC, + SYS_FREE, + SYS_SEEK, +} SYSCALL_NUMBERS; + +typedef enum { + O_CREAT = 0x1, // Create if does not exist + O_RDONLY = 0x2, // Read only + O_WRONLY = 0x4, // Write only + O_RDWR = 0x8, // Read and write + O_APPEND = 0x10, // Always writes at the end (appends) + O_TRUNC = 0x20, // Truncate file size and position to 0 on Open +} OFLAGS; + +int write(int fd, const void* buf, unsigned int len) { + int result = -1; __asm__ volatile ("int $0x80" : "=a"(result) : "a"(SYS_WRITE), "b"(fd), "c"(buf), "d"(len)); return result; } +int open(const char* path, int oflag) { + int result = -1; + __asm__ volatile ("int $0x80" : "=a"(result) : "a"(SYS_OPEN), "b"(path), "c"(oflag)); + return result; +} + #endif diff --git a/include/disk/filesys.h b/include/disk/filesys.h deleted file mode 100644 index 7a7748a..0000000 --- a/include/disk/filesys.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef FILESYS_H -#define FILESYS_H - -#include -#include -#include - -typedef struct { - char filename[10]; - char filetype[3]; - uint8_t reserved; - uint8_t startAddress; - uint8_t length; -} __attribute__ ((packed)) fileEntry_t; - -char* filetable = (char*)FILETABLE_LOC; // Pretty sure we don't have to care about the bootloader at 0x7c00 - -fileEntry_t* findFile(const char* filename) { - if (strlen(filename) > 14) { return NULL; } // Filename too long - static char filenameBuf[16]; - strcpy(filenameBuf, filename); - - char* filetype = strchr(filenameBuf, '.'); - *filetype++ = '\0'; // Split filename and filetype by a NULL - - char* ft = filetable; - while (*ft != '\0') { - if (strcmp(filenameBuf, ft) != 0) { // Filename doesn't match - ft += 16; // Goto next entry - continue; - } - ft += 10; // Goto filetype - - if (strcmp(filetype, ft) != 0) { - ft += 6; // Goto next entry - continue; - } - ft -= 10; // Go back to start of entry - - return (fileEntry_t*)ft; - } - - return NULL; // No such file -} - -void updateFiletable() { - // TODO: Update the filetable by writing data from memory to disk! -} - -uint8_t readFile(char* filename, void* buffer) { // Hopefully the buffer is large enought :/ - fileEntry_t* file = findFile(filename); - if (file == NULL) { return 1; } // File doesn't exist! - - diskRead(file->startAddress, file->length, buffer); - return 0; // Success -} - -uint8_t writeFile(char* filename, void* buffer) { // TODO: Add length so we don't write too much/too little - fileEntry_t* file = findFile(filetable); - if (file == NULL) { // File exists -> override - // File doesn't exist -> create new file - file = (fileEntry_t*)filetable; - while (*(char*)file != '\0') { file++; } // Find end of the filetable - - *strchr(filename, '.') = '\0'; - - char* filetype = filename + strlen(filename); // + 1? - fileEntry_t* previous = file - 1; - - strcpy(file->filename, filename); - strcpy(file->filetype, filetype); - file->startAddress = previous->startAddress + previous->length; - file->length = 1; // TODO: Add lenght! Currently will just assume 1 sector :/ - } - - diskWrite(file->startAddress, file->length, buffer); // Write the file - return 0; // Success -} - -#endif diff --git a/include/fs/fs.h b/include/fs/fs.h index cee2641..9cca27d 100644 --- a/include/fs/fs.h +++ b/include/fs/fs.h @@ -27,11 +27,11 @@ typedef struct { typedef struct { uint32_t inodeCount; - uint16_t firstInodeBitmapBlock; + uint16_t firstINodeBitmapBlock; uint16_t firstDataBitmapBlock; uint16_t inodeBitmapBlockCount; uint16_t dataBitmapBlockCount; - uint32_t firstInodeBlock; + uint32_t firstINodeBlock; uint32_t firstDataBlock; uint16_t inodeBlockCount; uint16_t dataBlockCount; @@ -40,14 +40,14 @@ typedef struct { uint16_t blockSize; // in bytes uint8_t inodeSize; // in bytes - uint32_t rootInodePtr; + uint32_t rootINodePtr; uint8_t inodesPerBlock; - uint8_t directExtentsPerInode; + uint8_t directExtentsPerINode; uint16_t extentsPerIndirectBlock; - uint32_t firstFreeInodeBit; + uint32_t firstFreeINodeBit; uint32_t firstFreeDataBit; uint16_t deviceNumber; - uint8_t firstUnreservedInode; + uint8_t firstUnreservedINode; uint8_t reserved[14]; } __attribute__ ((packed)) superBlock_t; @@ -87,5 +87,4 @@ uint32_t bytesToSectors(uint32_t bytes) { if (bytes <= FS_SECTOR_SIZE) return 1; return (bytes / FS_SECTOR_SIZE) + ((bytes % FS_SECTOR_SIZE) > 0 ? 1 : 0); } - #endif diff --git a/include/fs/impl.h b/include/fs/impl.h new file mode 100644 index 0000000..8af4cd6 --- /dev/null +++ b/include/fs/impl.h @@ -0,0 +1,52 @@ +#ifndef IMPL_H +#define IMPL_H + +#include +#include +#include +#include +#include + +char* currentDir; +inode_t currentDirINode; + +char tempSector[FS_SECTOR_SIZE]; +char tempBlock[FS_BLOCK_SIZE]; + +inode_t rootINode; + +inode_t* getINode(char* path) { + dirEntry_t* dirEntry = (dirEntry_t*)SCRATCH_BLOCK_LOC; + while (dirEntry->name[0] != '\0') { + if (strcmp(dirEntry->name, path) == 0) break; + dirEntry++; + } + + if (dirEntry->name[0] == '\0') return NULL; + + inode_t* inode = (inode_t*)BOOT_FIRST_INODE_LOC; + while (inode->id != dirEntry->id) { + inode++; + } + + return inode; +} + +uint8_t loadFile(inode_t* inode, void* dest) { + uint32_t totalBlocks = bytesToBlocks(inode->sizeBytes); + uint32_t addressOffset = 0; + + for (uint8_t i = 0; i < 4 && totalBlocks > 0; i++) { + diskRead(inode->extent[i].firstBlock * 8, inode->extent[i].length * 8, dest + addressOffset); + addressOffset += inode->extent[i].length * FS_BLOCK_SIZE; + totalBlocks -= inode->extent[i].length; + } + + if (totalBlocks > 0) { + // TODO: Indirect extents + } + + return 0; +} + +#endif diff --git a/include/interrupt/syscalls.h b/include/interrupt/syscalls.h index 6634424..470d6a2 100644 --- a/include/interrupt/syscalls.h +++ b/include/interrupt/syscalls.h @@ -9,8 +9,12 @@ #include #include #include +#include + +/* + * TODO WARNING - Update MAX_SYSCALLS in the handler TODO + */ -// Syscall(0) - Sleep() // Args: ebx - number of miliseconds void sys_sleep() { __asm__ volatile ("movl %%ebx, %0" : "=r"(*sleepTimerTicks)); @@ -20,7 +24,6 @@ void sys_sleep() { } } -// Syscall(1) - Write() // Args: ebx - int fd // ecx - void* buf // edx - uint32_t len @@ -48,7 +51,6 @@ void sys_gets() { read(s); } -// Syscall(3) - Malloc() // Args: ebx - amount of bytes to malloc // Returns: eax - Pointer to allocated memory void sys_malloc() { @@ -67,7 +69,6 @@ void sys_malloc() { __asm__ volatile ("movl %0, %%eax" : : "r"(ptr)); } -// Syscall(4) - Free() // Args: ebx - allocated pointer void sys_free() { void* ptr = NULL; @@ -75,13 +76,39 @@ void sys_free() { mallocFree(ptr); } +// Args: ebx - path, ecx - oflag +void sys_open() { + int32_t fd = -1; + char* path = NULL; + uint32_t oflag = 0; + + // From kernel.c + //extern openFileTable_t* openFileTable; + //extern inode_t* openINodeTable; + //extern uint32_t maxOpenFiles; + //extern uint32_t openFileCount; + //extern uint32_t maxOpenInodes; + //extern uint32_t openInodeCount; + + __asm__ volatile ("nop" : "=b"(path), "=c"(oflag)); + + __asm__ volatile ("movl %0, %%eax" : : "a"(fd)); +} + +void sys_close() {} + +void sys_seek() {} + // System call table void (*syscalls[MAX_SYSCALLS])(void) = { [SYS_SLEEP] = sys_sleep, [SYS_WRITE] = sys_write, [SYS_GETS] = sys_gets, [SYS_MALLOC] = sys_malloc, - [SYS_FREE] = sys_free, + [SYS_FREE] = sys_free, + [SYS_OPEN] = sys_open, + [SYS_CLOSE] = sys_close, + [SYS_SEEK] = sys_seek, }; // int 0x80 - Syscall interrupt, handled by this function @@ -92,7 +119,7 @@ void syscallHandler(intFrame_t* iframe) { // We should also push these regs: ax, gs, fs, es, ds, bp, di, si, dx, cx, bx __asm__ volatile ( ".intel_syntax noprefix\n" // We use intel syntax - ".equ MAX_SYSCALLS, 5\n" // Define MAX_SYSCALLS again TODO: I don't want to have to do this + ".equ MAX_SYSCALLS, 9\n" // Define MAX_SYSCALLS again TODO: I don't want to have to do this // Check if syscall exists "cmp eax, MAX_SYSCALLS - 1\n" @@ -127,7 +154,7 @@ void syscallHandler(intFrame_t* iframe) { "iretd\n" // Return from interrupt (32 bit) // TODO: Send an error or something here - "invalidSyscall:\n" + "invalidSyscall:\n" "mov eax, -1\n" // Current error thing "iretd\n" diff --git a/include/screen/text.h b/include/screen/text.h index 9aa76a4..4a30c28 100644 --- a/include/screen/text.h +++ b/include/screen/text.h @@ -4,19 +4,9 @@ #include #include #include -#include #include #include -// TODO: Make this check if it's a valid font -// TODO: Make it take a fileEntry_t? -uint8_t loadFont(char* filename) { - if (readFile(filename, (void*)FONT_LOC) != 0) { return 1; } // File not found - if (*(uint8_t*)FONT_WIDTH == 0 || *(uint8_t*)FONT_HEIGHT == 0) { return 1; } // Not a font - - return 0; // Success -} - // TODO: Reimplement larger text void drawChar(char c, uint32_t x, uint32_t y, uint32_t fgColor, uint32_t bgColor) { // Font info diff --git a/kern.iso b/kern.iso new file mode 100644 index 0000000000000000000000000000000000000000..cdf5b2596baf507c8e544443f43520e8f27b3ee4 GIT binary patch literal 1474560 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 zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U pAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0{_Vc{y*&nAS(a> literal 0 HcmV?d00001 diff --git a/makedisk.c b/makedisk.c index 70bfede..e84780e 100644 --- a/makedisk.c +++ b/makedisk.c @@ -8,6 +8,7 @@ #include "include/fs/fs.h" #define IMAGE_NAME "kern.iso" +#define BIN_DIR "./bin/" #define DISK_SIZE (2880 * 512) // 1.44MB uint8_t nullBlock[FS_BLOCK_SIZE] = { 0 }; @@ -19,15 +20,15 @@ typedef struct { } fileInfo_t; fileInfo_t files[] = { // TODO: Not have to list all of the files, loop through a directory or smth - { "./build/stage1.bin" }, - { "./build/stage2.bin" }, - { "./build/stage3.bin" }, - { "./build/kernel.bin" }, - { "./build/term16n.fnt" }, - { "./build/testfont.fnt" }, + { BIN_DIR"stage1.bin" }, + { BIN_DIR"stage2.bin" }, + { BIN_DIR"stage3.bin" }, + { BIN_DIR"kernel.bin" }, + { BIN_DIR"term16n.fnt" }, + { BIN_DIR"testfont.fnt" }, { "./src/fs/test.txt" }, - { "./build/calc.bin" }, - { "./build/editor.bin" }, + { BIN_DIR"calc.bin" }, + { BIN_DIR"editor.bin" }, }; superBlock_t superblock = { 0 }; @@ -117,29 +118,29 @@ void writeBootblock() { void writeSuperblock() { superblock.inodeCount = fileCount; // 2 reserved INodes (0 - Invalid, 1 - Root) - superblock.firstInodeBitmapBlock = 2; // Block 0 - Boot, Block 1 - Superblock + superblock.firstINodeBitmapBlock = 2; // Block 0 - Boot, Block 1 - Superblock superblock.inodeBitmapBlockCount = superblock.inodeCount / (FS_BLOCK_SIZE * 8) + ((superblock.inodeCount % (FS_BLOCK_SIZE * 8) > 0) ? 1 : 0); - superblock.firstDataBitmapBlock = superblock.firstInodeBitmapBlock + superblock.inodeBitmapBlockCount; + superblock.firstDataBitmapBlock = superblock.firstINodeBitmapBlock + superblock.inodeBitmapBlockCount; superblock.inodeBlockCount = bytesToBlocks(superblock.inodeCount * sizeof(inode_t)); dataBlocks = bytesToBlocks(diskSize); dataBitCount = (dataBlocks - superblock.firstDataBitmapBlock - superblock.inodeBlockCount); superblock.dataBitmapBlockCount = dataBitCount / (FS_BLOCK_SIZE * 8) + ((dataBitCount % (FS_BLOCK_SIZE * 8) > 0) ? 1 : 0); - superblock.firstInodeBlock = superblock.firstDataBitmapBlock + superblock.dataBitmapBlockCount; - superblock.firstDataBlock = superblock.firstInodeBlock + superblock.inodeBlockCount; + superblock.firstINodeBlock = superblock.firstDataBitmapBlock + superblock.dataBitmapBlockCount; + superblock.firstDataBlock = superblock.firstINodeBlock + superblock.inodeBlockCount; superblock.dataBlockCount = (fileBlocks - 2) + 1; // 1 Block for root dir (-1 for boot block) superblock.maxFileSize = 0xffffffff; superblock.blockSize = FS_BLOCK_SIZE; superblock.inodeSize = sizeof(inode_t); - superblock.rootInodePtr = 0; // Gets setup at runtime + superblock.rootINodePtr = 0; // Gets setup at runtime superblock.inodesPerBlock = FS_BLOCK_SIZE / sizeof(inode_t); - superblock.directExtentsPerInode = 4; + superblock.directExtentsPerINode = 4; superblock.extentsPerIndirectBlock = FS_BLOCK_SIZE / sizeof(extent_t); - superblock.firstFreeInodeBit = superblock.inodeCount; + superblock.firstFreeINodeBit = superblock.inodeCount; superblock.firstFreeDataBit = (fileBlocks - 2) + 1; // +1 for root dir superblock.deviceNumber = 0x01; - superblock.firstUnreservedInode = 3; // 0 - Invalid; 1 - Root dir; 2 - Bootloader INode + superblock.firstUnreservedINode = 3; // 0 - Invalid; 1 - Root dir; 2 - Bootloader INode assert(fwrite(&superblock, sizeof(superBlock_t), 1, imagePtr) == 1); assert(fwrite(nullBlock, paddingBytes(sizeof(superBlock_t), FS_BLOCK_SIZE), 1, imagePtr) == 1); // Padding diff --git a/src/kernel.c b/src/kernel.c index 0c0356f..bf9afc4 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -10,8 +10,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -23,14 +23,22 @@ #include #include #include -#include -#define PROMPT "/ #> " +#define PROMPT "#>" + +//openFileTable_t* openFileTable; +inode_t* openINodeTable; +uint32_t maxOpenFiles = 256; +uint32_t openFileCount = 3; // First 3 reserved for stdin, stdout, stderr +uint32_t maxOpenINodes = 256; +uint32_t openINodeCount = 0; // Function declarations void listFiles(); void printRegs(); void printPhysicalMemmap(); +void initOpenFileTable(); +void initOpenINodeTable(); __attribute__ ((section("entry"))) void main() { @@ -73,6 +81,18 @@ void main() { mallocPhysicalAddr = kernelMallocPhysicalAddr; mallocPages = kernelMallocPages; + // Setup filesystem + currentDir = malloc(1024); + strcpy(currentDir, "/"); // Start at root + + + // Setup kernel open file table and open inode table + initOpenFileTable(); + initOpenINodeTable(); + currentDir = malloc(1024); + strcpy(currentDir, "/"); + + // Terminal setup terminal->fg = FG_COLOR; terminal->bg = BG_COLOR; clear(); @@ -81,7 +101,7 @@ void main() { // Run Interactive Shell Program // TODO: Make it in another file while (1) { - printf(PROMPT); + printf("%s %s ", currentDir, PROMPT); char input[256]; char* inputPtr = input; @@ -223,12 +243,13 @@ void main() { } else if (strcmp(argv[0], "test") == 0) { printf("\x1b[1MError: 'test' is not available.\x1b[8M\n"); } else { // TODO: Use new filesystem! + /* fileEntry_t* file = findFile(argv[0]); - if (file == NULL) { + if (file == NULL) {*/ printf("Command not found: %s\n", argv[0]); continue; - } + /*} uint32_t neededPages = (file->length * 512) / PAGE_SIZE; if ((file->length * 512) % PAGE_SIZE > 0) { neededPages++; } @@ -291,7 +312,7 @@ void main() { unmapPage((void*)virtualAddr); flushTlbEntry(virtualAddr); // Invalidate unused page } - } + }*/ } } @@ -375,3 +396,13 @@ void printPhysicalMemmap() { printf("Free blocks: %d\n\n", maxBlocks - usedBlocks); } +void initOpenFileTable() { + //openFileTable = malloc(sizeof(openFileTable_t) * maxOpenFiles); + //*openFileTable = (openFileTable_t) { 0 }; +} + +void initOpenINodeTable() { + openINodeTable = malloc(sizeof(inode_t) * maxOpenINodes); + *openINodeTable = (inode_t) { 0 }; +} + diff --git a/src/stage2.asm b/src/stage2.asm index 8b70dc0..73b9130 100644 --- a/src/stage2.asm +++ b/src/stage2.asm @@ -369,9 +369,9 @@ DATA_SEG equ GDT.dataDescriptor - GDT.start ;; VBE Stuff ;; !! IF YOU WANT TO SELECT ANY VALUE IN THE SETUP, SET THESE TO ZERO !! -width: dw 0 -height: dw 0 -bpp: db 0 +width: dw 1920 +height: dw 1080 +bpp: db 32 offset: dw 0 _segment: dw 0 ; segment is a keyword diff --git a/src/stage3.c b/src/stage3.c index b10eabc..644a13f 100644 --- a/src/stage3.c +++ b/src/stage3.c @@ -2,12 +2,9 @@ #include #include #include -//#include -#include - +#include #include - -uint8_t loadFile(char* filename, void* dest); +#include __attribute__ ((section("entry"))) void main() { @@ -31,11 +28,17 @@ void main() { // Load root dir, find kernel INode and parse it superBlock_t* superblock = (superBlock_t*)SUPERBLOCK_LOC; - superblock->rootInodePtr = BOOT_FIRST_INODE_LOC + sizeof(inode_t); + superblock->rootINodePtr = BOOT_FIRST_INODE_LOC + sizeof(inode_t); diskRead(superblock->firstDataBlock * 8, 8, (void*)SCRATCH_BLOCK_LOC); - if (loadFile("kernel.bin", (void*)KERNEL_LOC) != 0) __asm__ volatile ("cli; hlt" : : "a"(0xDeadBeef)); - if (loadFile("term16n.fnt", (void*)FONT_LOC) != 0) __asm__ volatile ("cli; hlt" : : "a"(0xDeadBeef)); + inode_t* inode; + if ((inode = getINode("kernel.bin")) == NULL) { + __asm__ volatile ("cli; hlt" : : "a"(0xDeadBeef)); + } else { loadFile(inode, (void*)KERNEL_LOC); } + + if ((inode = getINode("term16n.fnt")) == NULL) { + __asm__ volatile ("cli; hlt" : : "a"(0xDeadBeef)); + } else { loadFile(inode, (void*)FONT_LOC); } // Virtual memory manager setup if (setupVirtualMemoryManager() != 0) { @@ -66,33 +69,3 @@ void main() { ((void(*)(void))0xC0000000)(); } -uint8_t loadFile(char* filename, void* dest) { - dirEntry_t* dirEntry = (dirEntry_t*)SCRATCH_BLOCK_LOC; - while (dirEntry->name[0] != '\0') { - if (strcmp(dirEntry->name, filename) == 0) break; - dirEntry++; - } - - if (dirEntry->name[0] == '\0') return 1; - - inode_t* inode = (inode_t*)BOOT_FIRST_INODE_LOC; - while (inode->id != dirEntry->id) { - inode++; - } - - uint32_t totalBlocks = bytesToBlocks(inode->sizeBytes); - uint32_t addressOffset = 0; - - for (uint8_t i = 0; i < 4 && totalBlocks > 0; i++) { - diskRead(inode->extent[i].firstBlock * 8, inode->extent[i].length * 8, dest + addressOffset); - addressOffset += inode->extent[i].length * FS_BLOCK_SIZE; - totalBlocks -= inode->extent[i].length; - } - - if (totalBlocks > 0) { - // TODO: Indirect extents - } - - return 0; -} -