diff --git a/Makefile b/Makefile index 3784aeb..83998c2 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ filesystem: bootloader kernel $(C_FILES) $(FONTS) @dd if=$(BUILD)/boot.bin of=kern.iso bs=512 seek=0 conv=notrunc status=none @dd if=$(BUILD)/filetable.bin of=kern.iso bs=512 seek=15 conv=notrunc status=none @dd if=$(BUILD)/kernel.bin of=kern.iso bs=512 seek=16 conv=notrunc status=none - @dd if=$(BUILD)/term16n.bin of=kern.iso bs=512 seek=56 conv=notrunc status=none - @dd if=$(BUILD)/testfont.bin of=kern.iso bs=512 seek=60 conv=notrunc status=none + @dd if=$(BUILD)/term16n.fnt of=kern.iso bs=512 seek=56 conv=notrunc status=none + @dd if=$(BUILD)/testfont.fnt of=kern.iso bs=512 seek=60 conv=notrunc status=none @dd if=$(SRC)/fs/test.txt of=kern.iso bs=512 seek=64 conv=notrunc status=none @dd if=$(BUILD)/calc.bin of=kern.iso bs=512 seek=65 conv=notrunc status=none @dd if=$(BUILD)/editor.bin of=kern.iso bs=512 seek=71 conv=notrunc status=none @@ -63,7 +63,7 @@ $(C_FILES): $(FONTS): @echo "Assembling $(SRC)/fonts/$@.asm..." - @$(AS) -fbin -o $(BUILD)/$@.bin $(SRC)/fonts/$@.asm + @$(AS) -fbin -o $(BUILD)/$@.fnt $(SRC)/fonts/$@.asm # Run QEMU run: diff --git a/include/fs/fs.h b/include/fs/fs.h new file mode 100644 index 0000000..cee2641 --- /dev/null +++ b/include/fs/fs.h @@ -0,0 +1,91 @@ +#ifndef FS_H +#define FS_H + +#include + +#define FS_BLOCK_SIZE 4096 +#define FS_SECTOR_SIZE 512 + +#define FILETYPE_FILE 0 +#define FILETYPE_DIR 1 + +// Filesystem structures +typedef struct { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day; + uint8_t month; + uint16_t year; + uint8_t reserved; +} __attribute__ ((packed)) fsDatetime_t; + +typedef struct { + uint8_t sectors[FS_BLOCK_SIZE / FS_SECTOR_SIZE][FS_SECTOR_SIZE]; +} __attribute__ ((packed)) bootBlock_t; + +typedef struct { + uint32_t inodeCount; + + uint16_t firstInodeBitmapBlock; + uint16_t firstDataBitmapBlock; + uint16_t inodeBitmapBlockCount; + uint16_t dataBitmapBlockCount; + uint32_t firstInodeBlock; + uint32_t firstDataBlock; + uint16_t inodeBlockCount; + uint16_t dataBlockCount; + + uint32_t maxFileSize; // in bytes + uint16_t blockSize; // in bytes + uint8_t inodeSize; // in bytes + + uint32_t rootInodePtr; + uint8_t inodesPerBlock; + uint8_t directExtentsPerInode; + uint16_t extentsPerIndirectBlock; + uint32_t firstFreeInodeBit; + uint32_t firstFreeDataBit; + uint16_t deviceNumber; + uint8_t firstUnreservedInode; + + uint8_t reserved[14]; +} __attribute__ ((packed)) superBlock_t; + +typedef struct { + uint32_t firstBlock; + uint32_t length; +} __attribute__ ((packed)) extent_t; + +typedef struct { + uint32_t id; // Idetifier + uint8_t filetype; + uint32_t sizeBytes; + uint32_t sizeSectors; + fsDatetime_t timestamp; + extent_t extent[4]; + uint32_t singleIndirectBlock; + uint32_t doubleIndirectBlock; + uint8_t refCount; + uint8_t reserved[2]; +} __attribute__ ((packed)) inode_t; + +typedef struct { + uint32_t id; // Matching ID with INode + char name[60]; +} __attribute__ ((packed)) dirEntry_t; + +// Utility functions +uint32_t bytesToBlocks(uint32_t bytes) { + if (bytes == 0) return 0; + if (bytes <= FS_BLOCK_SIZE) return 1; + return (bytes / FS_BLOCK_SIZE) + ((bytes % FS_BLOCK_SIZE) > 0 ? 1 : 0); +} + +uint32_t bytesToSectors(uint32_t bytes) { + if (bytes == 0) return 0; + 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/fs_h.swo b/include/fs/fs_h.swo new file mode 100644 index 0000000..9e95f28 Binary files /dev/null and b/include/fs/fs_h.swo differ diff --git a/include/fs/fs_h.swp b/include/fs/fs_h.swp new file mode 100644 index 0000000..ff755a5 Binary files /dev/null and b/include/fs/fs_h.swp differ diff --git a/makeDisk.c b/makeDisk.c new file mode 100644 index 0000000..1136470 --- /dev/null +++ b/makeDisk.c @@ -0,0 +1,266 @@ +// A utility to build the disk +#include +#include +#include +#include +#include + +#include "include/fs/fs.h" + +#define IMAGE_NAME "kern.iso" +#define DISK_SIZE (2880 * 512) // 1.44MB + +uint8_t nullBlock[FS_BLOCK_SIZE] = { 0 }; + +typedef struct { + char name[60]; + uint32_t size; + FILE* fp; +} 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" }, + { "./src/fs/test.txt" }, + { "./build/calc.bin" }, + { "./build/editor.bin" }, +}; + +superBlock_t superblock = { 0 }; + +FILE* imagePtr; +uint32_t diskSize = DISK_SIZE; +uint32_t fileCount; +uint32_t fileBlocks = 0; +uint32_t dataBlocks; +uint32_t dataBitCount; + +void writeBootblock(); +void writeSuperblock(); +void writeINodeBitmapBlocks(); +void writeDataBitmapBlocks(); +void writeINodeBlocks(); +void writeDataBlocks(); + +uint32_t paddingBytes(uint32_t bytes, uint32_t bound) { + if (bytes % bound == 0) return 0; + return (bytes - (bytes % bound) + bound) - bytes; +} + +int main(int argc, char** argv) { + imagePtr = fopen(IMAGE_NAME, "wb"); + + fileCount = sizeof(files) / sizeof(files[0]); + + // Open files and get file info + for (int i = 0; i < fileCount; i++) { + files[i].fp = fopen(files[i].name, "rb"); + fseek(files[i].fp, 0, SEEK_END); + files[i].size = ftell(files[i].fp); + rewind(files[i].fp); + fileBlocks += bytesToBlocks(files[i].size); + } + + printf("Building disk image %s...\n", IMAGE_NAME); + printf("Block size: %d Bytes, Total disk blocks: %d\n", FS_BLOCK_SIZE, bytesToBlocks(diskSize)); + printf("Used blocks: %d, Free blocks %d\n\n", fileBlocks, bytesToBlocks(diskSize) - fileBlocks); + printf("%d files (2 hidden)\n", fileCount); // Stage 1 and 2 is hidden + + // Build disk image + writeBootblock(); + writeSuperblock(); + writeINodeBitmapBlocks(); + writeDataBitmapBlocks(); + writeINodeBlocks(); + writeDataBlocks(); + + // Pad out to the whole disk size + uint32_t writtenBlocks = 2 + superblock.inodeBitmapBlockCount + superblock.dataBitmapBlockCount + superblock.inodeBlockCount + superblock.dataBlockCount; + for (uint32_t i = 0; i < bytesToBlocks(diskSize) - writtenBlocks; i++) { + assert(fwrite(nullBlock, FS_BLOCK_SIZE, 1, imagePtr) == 1); + } + + // Cleanup + for (uint32_t i = 0; i < fileCount; i++) { + fclose(files[i].fp); + } + fclose(imagePtr); + + return 0; +} + +void writeBootblock() { + bootBlock_t bootBlock = { 0 }; + assert(fread(bootBlock.sectors[0], FS_SECTOR_SIZE, 1, files[0].fp) == 1); // File 0 - Stage 1 + + for (uint8_t i = 1; i < bytesToSectors(files[i].size); i++) { + if (i > 7) { + fprintf(stderr, "%s is too large, maximum size of bootblock is 7 sectors (%d Bytes)\n", files[1].name, 7 * FS_SECTOR_SIZE); + exit(1); + } + + assert(fread(bootBlock.sectors[i], FS_SECTOR_SIZE, 1, files[1].fp) == 1); // File 1 - Stage 2 + } + + // Write bootblock to disk + for (uint8_t i = 0; i < FS_BLOCK_SIZE / FS_SECTOR_SIZE; i++) { + assert(fwrite(bootBlock.sectors[i], FS_SECTOR_SIZE, 1, imagePtr) == 1); + } +} + +void writeSuperblock() { + superblock.inodeCount = fileCount + 2; // 2 reserved INodes (0 - Invalid, 1 - Root) + 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.firstInodeBlock + superblock.inodeBitmapBlockCount; + + dataBlocks = bytesToBlocks(diskSize); + dataBitCount = (dataBlocks - superblock.firstDataBitmapBlock); + + superblock.dataBitmapBlockCount = dataBitCount / (FS_BLOCK_SIZE * 8) + ((dataBitCount % (FS_BLOCK_SIZE * 8) > 0) ? 1 : 0); + superblock.firstInodeBlock = superblock.firstDataBitmapBlock + superblock.dataBitmapBlockCount; + superblock.inodeBlockCount = bytesToBlocks(superblock.inodeCount * sizeof(inode_t)); + 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.inodesPerBlock = FS_BLOCK_SIZE / sizeof(inode_t); + superblock.directExtentsPerInode = 4; + superblock.extentsPerIndirectBlock = FS_BLOCK_SIZE / sizeof(extent_t); + 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 + + assert(fwrite(&superblock, sizeof(superBlock_t), 1, imagePtr) == 1); + assert(fwrite(&nullBlock, paddingBytes(sizeof(superBlock_t), FS_BLOCK_SIZE), 1, imagePtr) == 1); // Padding +} + +void writeINodeBitmapBlocks() { + uint8_t temp[FS_SECTOR_SIZE] = { 0 }; + + // Set number of bits = number of inodes + *(uint32_t*)temp |= (2 << (superblock.inodeCount - 1)) - 1; + assert(fwrite(temp, FS_SECTOR_SIZE, 1, imagePtr) == 1); + + // Padd out + assert(fwrite(nullBlock, paddingBytes(FS_SECTOR_SIZE, FS_BLOCK_SIZE), 1, imagePtr) == 1); +} + +void writeDataBitmapBlocks() { + uint64_t bitChunk = 0; + uint32_t totalBlocks = superblock.dataBlockCount / 32; + uint32_t bytesWritten = 0; + + while (totalBlocks > 0) { + bitChunk = 0xffffffff; + totalBlocks--; + assert(fwrite(&bitChunk, 4, 1, imagePtr) == 1); + bytesWritten += 4; + } + + bitChunk = (2 << ((superblock.dataBlockCount % 32) - 1)) - 1; + assert(fwrite(&bitChunk, 4, 1, imagePtr) == 1); + bytesWritten += 4; + + assert(fwrite(nullBlock, paddingBytes(bytesWritten, FS_BLOCK_SIZE), 1, imagePtr) == 1); +} + +void writeINodeBlocks() { + inode_t inode = { 0 }; + fsDatetime_t defaultTimestamp = { + .second = 12, + .minute = 12, + .hour = 12, + .day = 20, + .month = 4, + .year = 2024, + }; + + // INode 0 - Invalid + assert(fwrite(&inode, sizeof(inode), 1, imagePtr) == 1); + + // INode 1 - Root directory + inode = (inode_t) { // OOP In C??? + .id = 1, + .filetype = FILETYPE_DIR, + .sizeBytes = sizeof(dirEntry_t) * fileCount, + .sizeSectors = bytesToSectors(inode.sizeBytes), + .timestamp = defaultTimestamp, + .extent[0] = (extent_t) { + .firstBlock = superblock.firstDataBlock, + .length = bytesToBlocks(inode.sizeBytes), + }, + }; + + // INode 2 - Bootloader (stage 3) followed by other INodes + uint32_t id = 2; + uint32_t block = superblock.firstDataBlock + inode.extent[0].length; + for (uint32_t i = 2; i < fileCount; i++) { + memset(&inode, 0, sizeof(inode)); + inode.id = id++; + inode.filetype = FILETYPE_FILE; + inode.sizeBytes = files[i].size; + inode.sizeSectors = bytesToSectors(inode.sizeBytes); + inode.timestamp = defaultTimestamp; + inode.extent[0] = (extent_t) { + .firstBlock = block, + .length = bytesToBlocks(inode.sizeBytes), + }; + + assert(fwrite(&inode, sizeof(inode), 1, imagePtr) == 1); + + block += inode.extent[0].length; + } + + assert(fwrite(nullBlock, paddingBytes(fileCount * sizeof(inode_t), FS_BLOCK_SIZE), 1, imagePtr) == 1); +} + +void writeDataBlocks() { + uint8_t sector[FS_SECTOR_SIZE] = { 0 }; + dirEntry_t dirEntry = { + .id = 1, + .name = ".", + }; + + assert(fwrite(&dirEntry, sizeof(dirEntry), 1, imagePtr) == 1); + strcpy(dirEntry.name, ".."); + assert(fwrite(&dirEntry, sizeof(dirEntry), 1, imagePtr) == 1); + + for (uint32_t i = 2; i < fileCount; i++) { + dirEntry.id = i; + strcpy(dirEntry.name, strrchr(files[i].name, '/') + 1); // We skip the './build/' + assert(fwrite(&dirEntry, sizeof(dirEntry), 1, imagePtr) == 1); + } + + // Padd out + assert(fwrite(nullBlock, paddingBytes(fileCount * sizeof(dirEntry_t), FS_BLOCK_SIZE), 1, imagePtr) == 1); + + // Write actual file data + uint32_t bytesWritten = 0; + for (uint32_t i = 2; i < fileCount; i++) { + uint32_t blockCount = bytesToBlocks(files[i].size); + printf("%s, %d Bytes -> %d Blocks\n", files[i].name, files[i].size, blockCount); + bytesWritten = 0; + + for (uint32_t j = 0; j < blockCount * (FS_BLOCK_SIZE / FS_SECTOR_SIZE); j++) { + uint32_t byteCount = fread(sector, 1, sizeof(sector), files[i].fp); + fwrite(sector, 1, sizeof(sector), imagePtr); + bytesWritten += byteCount; + + if (byteCount < sizeof(sector)) break; // EOF + } + + memset(sector, 0, sizeof(sector)); + + // Padd out block + fwrite(nullBlock, paddingBytes(bytesWritten, FS_BLOCK_SIZE), 1, imagePtr); + } +} + diff --git a/src/calc.c b/src/calc.c index 425bc97..0951cf9 100644 --- a/src/calc.c +++ b/src/calc.c @@ -9,14 +9,13 @@ int parseNumber(char** ptr); // TODO: Parsing doesn't account for addition/subtraction inside brackets, fix that __attribute__ ((section("entry"))) -void main() { +int main(int argc, char** argv) { printf("Welcome to calc.\n"); char input[256]; while (1) { printf("> "); read(input); - *strchr(input, '\n') = '\0'; // Remove whitespace char* old = input; @@ -39,7 +38,7 @@ void main() { } } - return; + return 0; } int evalExpr(char* expr, int* res) { diff --git a/src/kernel.c b/src/kernel.c index 4ada2a9..665fb51 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -23,6 +23,7 @@ #include #include #include +#include #define PROMPT "#> "