From e84e4719a4c8dbf8055a2fdb07d88cbfca9859a2 Mon Sep 17 00:00:00 2001 From: g1n Date: Tue, 14 Sep 2021 17:21:46 +0300 Subject: [PATCH] Kernel: add very WIP implementation of initrd --- fsgenerator.c | 57 ++++++ iso.sh | 2 + kernel/kernel/early_kernel.c | 27 ++- kernel/kernel/fs.c | 106 ++++++++++ kernel/kernel/initrd.c | 132 +++++++++++++ kernel/kernel/kernel.c | 73 ++++++- kernel/kernel/kheap.c | 365 +++++++++++++++++++++++++++++++++++ kernel/kernel/multiboot.h | 41 ++++ kernel/kernel/ordered_map.c | 84 ++++++++ kernel/kernel/paging.h | 179 +++++++++++++++++ libc/include/assert.h | 10 + libc/stdio/printf.c | 12 ++ 12 files changed, 1083 insertions(+), 5 deletions(-) create mode 100644 fsgenerator.c create mode 100644 kernel/kernel/fs.c create mode 100644 kernel/kernel/initrd.c create mode 100644 kernel/kernel/kheap.c create mode 100644 kernel/kernel/multiboot.h create mode 100644 kernel/kernel/ordered_map.c create mode 100644 libc/include/assert.h diff --git a/fsgenerator.c b/fsgenerator.c new file mode 100644 index 0000000..3a8ec34 --- /dev/null +++ b/fsgenerator.c @@ -0,0 +1,57 @@ +#include +#include +#include + +struct initrd_header +{ + unsigned char magic; // The magic number is there to check for consistency. + char name[64]; + unsigned int offset; // Offset in the initrd the file starts. + unsigned int length; // Length of the file. +}; + +int main(char argc, char **argv) +{ + int nheaders = (argc-1)/2; + struct initrd_header headers[64]; + printf("size of header: %d\n", sizeof(struct initrd_header)); + unsigned int off = sizeof(struct initrd_header) * 64 + sizeof(int); + int i; + for(i = 0; i < nheaders; i++) + { + printf("writing file %s->%s at 0x%x\n", argv[i*2+1], argv[i*2+2], off); + strcpy(headers[i].name, argv[i*2+2]); + headers[i].offset = off; + FILE *stream = fopen(argv[i*2+1], "r"); + if(stream == 0) + { + printf("Error: file not found: %s\n", argv[i*2+1]); + return 1; + } + fseek(stream, 0, SEEK_END); + headers[i].length = ftell(stream); + off += headers[i].length; + fclose(stream); + headers[i].magic = 0xBF; + } + + FILE *wstream = fopen("./initrd.img", "w"); + unsigned char *data = (unsigned char *)malloc(off); + fwrite(&nheaders, sizeof(int), 1, wstream); + fwrite(headers, sizeof(struct initrd_header), 64, wstream); + + for(i = 0; i < nheaders; i++) + { + FILE *stream = fopen(argv[i*2+1], "r"); + unsigned char *buf = (unsigned char *)malloc(headers[i].length); + fread(buf, 1, headers[i].length, stream); + fwrite(buf, 1, headers[i].length, wstream); + fclose(stream); + free(buf); + } + + fclose(wstream); + free(data); + + return 0; +} diff --git a/iso.sh b/iso.sh index 83e9be9..5dcc823 100755 --- a/iso.sh +++ b/iso.sh @@ -7,9 +7,11 @@ mkdir -p isodir/boot mkdir -p isodir/boot/grub cp sysroot/boot/orion.kernel isodir/boot/orion.kernel +cp sysroot/boot/initrd.img isodir/boot/initrd.img cat > isodir/boot/grub/grub.cfg << EOF menuentry "orion" { multiboot /boot/orion.kernel + module /boot/initrd.img } EOF grub-mkrescue -o orion.iso isodir diff --git a/kernel/kernel/early_kernel.c b/kernel/kernel/early_kernel.c index 2b3401c..6edb3c6 100644 --- a/kernel/kernel/early_kernel.c +++ b/kernel/kernel/early_kernel.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,8 +9,14 @@ //#include "timer.c" #include "keyboard.c" #include "paging.h" +//#include "fs.c" +#include "initrd.c" +#include "multiboot.h" -void kernel_early_main(void) { +extern heap_t *kheap; +extern fs_node_t *fs_root; // The root of the filesystem. + +void kernel_early_main(struct multiboot *mboot_ptr) { gdt_init(); init_serial(); serial_printf("gdt initialized!\n"); @@ -17,9 +24,25 @@ void kernel_early_main(void) { serial_printf("idt initialized!\n"); //init_timer(50); // Initialise timer to 50Hz //serial_printf("timer initialized!\n"); + + // Find the location of our initial ramdisk. + //assert(mboot_ptr->mods_count > 0); + if (!(mboot_ptr->mods_count > 0)) { + serial_printf("PANIC!!"); + } + uint32_t initrd_location = *((uint32_t*)mboot_ptr->mods_addr); + uint32_t initrd_end = *(uint32_t*)(mboot_ptr->mods_addr+4); + // Don't trample our module with placement accesses, please! + placement_address = initrd_end; + init_paging(); serial_printf("paging initialized!\n"); + //open_fs(fs_root, 1, 0); + fs_root = init_initrd(initrd_location); + serial_printf("initrd!\n"); init_keyboard(); serial_printf("keyboard initialized!\n"); - terminal_initialize(); + terminal_initialize(); // FIXME + int a = kmalloc(23); // FIXME + free(a, kheap); } diff --git a/kernel/kernel/fs.c b/kernel/kernel/fs.c new file mode 100644 index 0000000..30b91cf --- /dev/null +++ b/kernel/kernel/fs.c @@ -0,0 +1,106 @@ +typedef uint32_t (*read_type_t)(struct fs_node*,uint32_t,uint32_t,uint8_t*); +typedef uint32_t (*write_type_t)(struct fs_node*,uint32_t,uint32_t,uint8_t*); +typedef void (*open_type_t)(struct fs_node*); +typedef void (*close_type_t)(struct fs_node*); +typedef struct dirent * (*readdir_type_t)(struct fs_node*,uint32_t); +typedef struct fs_node * (*finddir_type_t)(struct fs_node*,char *name); + +typedef struct fs_node +{ + char name[128]; // The filename. + uint32_t mask; // The permissions mask. + uint32_t uid; // The owning user. + uint32_t gid; // The owning group. + uint32_t flags; // Includes the node type. See #defines above. + uint32_t inode; // This is device-specific - provides a way for a filesystem to identify files. + uint32_t length; // Size of the file, in bytes. + uint32_t impl; // An implementation-defined number. + read_type_t read; + write_type_t write; + open_type_t open; + close_type_t close; + readdir_type_t readdir; + finddir_type_t finddir; + struct fs_node *ptr; // Used by mountpoints and symlinks. +} fs_node_t; + +struct dirent // One of these is returned by the readdir call, according to POSIX. +{ + char name[128]; // Filename. + uint32_t ino; // Inode number. Required by POSIX. +}; + +#define FS_FILE 0x01 +#define FS_DIRECTORY 0x02 +#define FS_CHARDEVICE 0x03 +#define FS_BLOCKDEVICE 0x04 +#define FS_PIPE 0x05 +#define FS_SYMLINK 0x06 +#define FS_MOUNTPOINT 0x08 + +//extern fs_node_t *fs_root; // The root of the filesystem. + +// Standard read/write/open/close functions. Note that these are all suffixed with +// _fs to distinguish them from the read/write/open/close which deal with file descriptors +// not file nodes. +//uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer); +//uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer); +//void open_fs(fs_node_t *node, uint8_t read, uint8_t write); +//void close_fs(fs_node_t *node); +//struct dirent *readdir_fs(fs_node_t *node, uint32_t index); +//fs_node_t *finddir_fs(fs_node_t *node, char *name); + +fs_node_t *fs_root = 0; // The root of the filesystem. + +uint32_t read_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) +{ + // Has the node got a read callback? + if (node->read != 0) + return node->read(node, offset, size, buffer); + else + return 0; +} + +uint32_t write_fs(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) +{ + // Has the node got a write callback? + if (node->write != 0) + return node->write(node, offset, size, buffer); + else + return 0; +} + + +void open_fs(fs_node_t *node, uint8_t read, uint8_t write) +{ + // Has the node got an open callback? + if (node->open != 0) + return node->open(node); +} + +void close_fs(fs_node_t *node) +{ + // Has the node got a close callback? + if (node->close != 0) + return node->close(node); +} + +struct dirent *readdir_fs(fs_node_t *node, uint32_t index) +{ + // Is the node a directory, and does it have a callback? + if ( (node->flags&0x7) == FS_DIRECTORY && + node->readdir != 0 ) + return node->readdir(node, index); + else + return 0; +} + +fs_node_t *finddir_fs(fs_node_t *node, char *name) +{ + // Is the node a directory, and does it have a callback? + if ( (node->flags&0x7) == FS_DIRECTORY && + node->finddir != 0 ) + return node->finddir(node, name); + else + return 0; +} diff --git a/kernel/kernel/initrd.c b/kernel/kernel/initrd.c new file mode 100644 index 0000000..1819a1a --- /dev/null +++ b/kernel/kernel/initrd.c @@ -0,0 +1,132 @@ +#include +#include "fs.c" +#include "kheap.c" + +typedef struct +{ + uint32_t nfiles; // The number of files in the ramdisk. +} initrd_header_t; + +typedef struct +{ + uint8_t magic; // Magic number, for error checking. + int8_t name[64]; // Filename. + uint32_t offset; // Offset in the initrd that the file starts. + uint32_t length; // Length of the file. +} initrd_file_header_t; + +// Initialises the initial ramdisk. It gets passed the address of the multiboot module, +// and returns a completed filesystem node. +fs_node_t *initialise_initrd(uint32_t location); + +initrd_header_t *initrd_header; // The header. +initrd_file_header_t *file_headers; // The list of file headers. +fs_node_t *initrd_root; // Our root directory node. +fs_node_t *initrd_dev; // We also add a directory node for /dev, so we can mount devfs later on. +fs_node_t *root_nodes; // List of file nodes. +int nroot_nodes; // Number of file nodes. + +struct dirent dirent; + +static uint32_t initrd_read(fs_node_t *node, uint32_t offset, uint32_t size, uint8_t *buffer) +{ + initrd_file_header_t header = file_headers[node->inode]; + if (offset > header.length) + return 0; + if (offset+size > header.length) + size = header.length-offset; + memcpy(buffer, (uint8_t*) (header.offset+offset), size); + return size; +} + +static struct dirent *initrd_readdir(fs_node_t *node, uint32_t index) +{ + if (node == initrd_root && index == 0) + { + strcpy(dirent.name, "dev"); + dirent.name[3] = 0; // Make sure the string is NULL-terminated. + dirent.ino = 0; + return &dirent; + } + + if (index-1 >= nroot_nodes) + return 0; + strcpy(dirent.name, root_nodes[index-1].name); + dirent.name[strlen(root_nodes[index-1].name)] = 0; // Make sure the string is NULL-terminated. + dirent.ino = root_nodes[index-1].inode; + return &dirent; +} + +static fs_node_t *initrd_finddir(fs_node_t *node, char *name) +{ + if (node == initrd_root && + !strcmp(name, "dev") ) + return initrd_dev; + + int i; + for (i = 0; i < nroot_nodes; i++) + if (!strcmp(name, root_nodes[i].name)) + return &root_nodes[i]; + return 0; +} + +fs_node_t *init_initrd(uint32_t location) +{ + // Initialise the main and file header pointers and populate the root directory. + initrd_header = (initrd_header_t *)location; + file_headers = (initrd_file_header_t *) (location+sizeof(initrd_header_t)); + + // Initialise the root directory. + initrd_root = (fs_node_t*)kmalloc(sizeof(fs_node_t)); + strcpy(initrd_root->name, "initrd"); + initrd_root->mask = initrd_root->uid = initrd_root->gid = initrd_root->inode = initrd_root->length = 0; + initrd_root->flags = FS_DIRECTORY; + initrd_root->read = 0; + initrd_root->write = 0; + initrd_root->open = 0; + initrd_root->close = 0; + initrd_root->readdir = &initrd_readdir; + initrd_root->finddir = &initrd_finddir; + initrd_root->ptr = 0; + initrd_root->impl = 0; + + // Initialise the /dev directory (required!) + initrd_dev = (fs_node_t*)kmalloc(sizeof(fs_node_t)); + strcpy(initrd_dev->name, "dev"); + initrd_dev->mask = initrd_dev->uid = initrd_dev->gid = initrd_dev->inode = initrd_dev->length = 0; + initrd_dev->flags = FS_DIRECTORY; + initrd_dev->read = 0; + initrd_dev->write = 0; + initrd_dev->open = 0; + initrd_dev->close = 0; + initrd_dev->readdir = &initrd_readdir; + initrd_dev->finddir = &initrd_finddir; + initrd_dev->ptr = 0; + initrd_dev->impl = 0; + + root_nodes = (fs_node_t*)kmalloc(sizeof(fs_node_t) * initrd_header->nfiles); + nroot_nodes = initrd_header->nfiles; + int i; + for (i = 0; i < initrd_header->nfiles; i++) + { + // Edit the file's header - currently it holds the file offset + // relative to the start of the ramdisk. We want it relative to the start + // of memory. + file_headers[i].offset += location; + // Create a new file node. + strcpy(root_nodes[i].name, &file_headers[i].name); + root_nodes[i].mask = root_nodes[i].uid = root_nodes[i].gid = 0; + root_nodes[i].length = file_headers[i].length; + root_nodes[i].inode = i; + root_nodes[i].flags = FS_FILE; + root_nodes[i].read = &initrd_read; + root_nodes[i].write = 0; + root_nodes[i].readdir = 0; + root_nodes[i].finddir = 0; + root_nodes[i].open = 0; + root_nodes[i].close = 0; + root_nodes[i].impl = 0; + } + + return initrd_root; +} diff --git a/kernel/kernel/kernel.c b/kernel/kernel/kernel.c index 6e29b66..fd8bdb6 100644 --- a/kernel/kernel/kernel.c +++ b/kernel/kernel/kernel.c @@ -1,5 +1,44 @@ #include #include +#include +//#include "fs.c" +//#include "initrd.c" + + +typedef uint32_t (*read_type_t)(struct fs_node*,uint32_t,uint32_t,uint8_t*); +typedef uint32_t (*write_type_t)(struct fs_node*,uint32_t,uint32_t,uint8_t*); +typedef void (*open_type_t)(struct fs_node*); +typedef void (*close_type_t)(struct fs_node*); +typedef struct dirent * (*readdir_type_t)(struct fs_node*,uint32_t); +typedef struct fs_node * (*finddir_type_t)(struct fs_node*,char *name); + + +typedef struct fs_node +{ + char name[128]; // The filename. + uint32_t mask; // The permissions mask. + uint32_t uid; // The owning user. + uint32_t gid; // The owning group. + uint32_t flags; // Includes the node type. See #defines above. + uint32_t inode; // This is device-specific - provides a way for a filesystem to identify files. + uint32_t length; // Size of the file, in bytes. + uint32_t impl; // An implementation-defined number. + read_type_t read; + write_type_t write; + open_type_t open; + close_type_t close; + readdir_type_t readdir; + finddir_type_t finddir; + struct fs_node *ptr; // Used by mountpoints and symlinks. +} fs_node_t; + +struct dirent // One of these is returned by the readdir call, according to POSIX. +{ + char name[128]; // Filename. + uint32_t ino; // Inode number. Required by POSIX. +}; + +extern fs_node_t *fs_root; // The root of the filesystem. void kernel_main(void) { printf("Hello from OrionOS!\n"); @@ -10,9 +49,37 @@ void kernel_main(void) { serial_printf("This is string from variable: %s\n", test_string); printf("Test finished success!\n"); asm volatile("sti"); - uint32_t *ptr = (uint32_t*)0xA0000000; - uint32_t do_page_fault = *ptr; - printf("%s", do_page_fault); + + // list the contents of / + int i = 0; + struct dirent *node = 0; + while ( (node = readdir_fs(fs_root, i)) != 0) + { + serial_printf("Found file %s", node->name); + fs_node_t *fsnode = finddir_fs(fs_root, node->name); + + if ((fsnode->flags&0x7) == 0x02)//FS_DIRECTORY) + serial_printf("\n\t(directory)\n"); + else + { + printf("\n\t contents: \""); + char buf[256]; + uint32_t sz = read_fs(fsnode, 0, 256, buf); + uint32_t j; + for (j = 0; j < sz; j++) + serial_printf("%c", buf[j]); + + serial_printf("\"\n"); + } + i++; + } + //printf("%x\n", 0x0); + //printf("%x\n", a); + printf("%x"); + /*for (int i = 0; i < 1000; i++) { + printf("A: %c\n", i); + }*/ + close_fs(fs_root); for(;;) { asm("hlt"); } diff --git a/kernel/kernel/kheap.c b/kernel/kernel/kheap.c new file mode 100644 index 0000000..ceceb71 --- /dev/null +++ b/kernel/kernel/kheap.c @@ -0,0 +1,365 @@ +#include "ordered_map.c" + +extern uint32_t page_directory[1024]; + +#define KHEAP_START 0xC0000000 +#define KHEAP_INITIAL_SIZE 0x100000 +#define HEAP_INDEX_SIZE 0x20000 +#define HEAP_MAGIC 0x123890AB +#define HEAP_MIN_SIZE 0x70000 + +typedef struct +{ + uint32_t magic; // Magic number, used for error checking and identification. + uint8_t is_hole; // 1 if this is a hole. 0 if this is a block. + uint32_t size; // size of the block, including the end footer. +} header_t; + +typedef struct +{ + uint32_t magic; // Magic number, same as in header_t. + header_t *header; // Pointer to the block header. +} footer_t; + +typedef struct +{ + ordered_array_t index; + uint32_t start_address; // The start of our allocated space. + uint32_t end_address; // The end of our allocated space. May be expanded up to max_address. + uint32_t max_address; // The maximum address the heap can be expanded to. + uint8_t supervisor; // Should extra pages requested by us be mapped as supervisor-only? + uint8_t readonly; // Should extra pages requested by us be mapped as read-only? +} heap_t; + +heap_t *kheap; + +static int32_t find_smallest_hole(uint32_t size, uint8_t page_align, heap_t *heap) +{ + // Find the smallest hole that will fit. + uint32_t iterator = 0; + while (iterator < heap->index.size) + { + header_t *header = (header_t *)lookup_ordered_array(iterator, &heap->index); + // If the user has requested the memory be page-aligned + if (page_align > 0) + { + // Page-align the starting point of this header. + uint32_t location = (uint32_t)header; + int32_t offset = 0; + if (((location+sizeof(header_t)) & 0xFFFFF000) != 0) + offset = 0x1000 /* page size */ - (location+sizeof(header_t))%0x1000; + int32_t hole_size = (int32_t)header->size - offset; + // Can we fit now? + if (hole_size >= (int32_t)size) + break; + } + else if (header->size >= size) + break; + iterator++; + } + // Why did the loop exit? + if (iterator == heap->index.size) + return -1; // We got to the end and didn't find anything. + else + return iterator; +} + + +static int8_t header_t_less_than(void*a, void *b) +{ + return (((header_t*)a)->size < ((header_t*)b)->size)?1:0; +} + +heap_t *create_heap(uint32_t start, uint32_t end_addr, uint32_t max, uint8_t supervisor, uint8_t readonly) +{ + heap_t *heap = (heap_t*)kmalloc(sizeof(heap_t)); + + // All our assumptions are made on startAddress and endAddress being page-aligned. + assert(start%0x1000 == 0); + assert(end_addr%0x1000 == 0); + + // Initialise the index. + heap->index = place_ordered_array( (void*)start, HEAP_INDEX_SIZE, &header_t_less_than); + + // Shift the start address forward to resemble where we can start putting data. + start += sizeof(type_t)*HEAP_INDEX_SIZE; + + // Make sure the start address is page-aligned. + if ((start & 0xFFFFF000) != 0) + { + start &= 0xFFFFF000; + start += 0x1000; + } + // Write the start, end and max addresses into the heap structure. + heap->start_address = start; + heap->end_address = end_addr; + heap->max_address = max; + heap->supervisor = supervisor; + heap->readonly = readonly; + + // We start off with one large hole in the index. + header_t *hole = (header_t *)start; + hole->size = end_addr-start; + hole->magic = HEAP_MAGIC; + hole->is_hole = 1; + insert_ordered_array((void*)hole, &heap->index); + + return heap; +} + +static void expand(uint32_t new_size, heap_t *heap) +{ + // Sanity check. + assert(new_size > heap->end_address - heap->start_address); + // Get the nearest following page boundary. + if ((new_size&0xFFFFF000) != 0) + { + new_size &= 0xFFFFF000; + new_size += 0x1000; + } + // Make sure we are not overreaching ourselves. + assert(heap->start_address+new_size <= heap->max_address); + + // This should always be on a page boundary. + uint32_t old_size = heap->end_address-heap->start_address; + uint32_t i = old_size; + while (i < new_size) + { + alloc_frame( get_page(heap->start_address+i, 1, /*kernel*/page_directory), + (heap->supervisor)?1:0, (heap->readonly)?0:1); + i += 0x1000 /* page size */; + } + heap->end_address = heap->start_address+new_size; +} + +static uint32_t contract(uint32_t new_size, heap_t *heap) +{ + // Sanity check. + assert(new_size < heap->end_address-heap->start_address); + // Get the nearest following page boundary. + if (new_size&0x1000) + { + new_size &= 0x1000; + new_size += 0x1000; + } + // Don't contract too far! + if (new_size < HEAP_MIN_SIZE) + new_size = HEAP_MIN_SIZE; + uint32_t old_size = heap->end_address-heap->start_address; + uint32_t i = old_size - 0x1000; + while (new_size < i) + { + free_frame(get_page(heap->start_address+i, 0, /*kernel*/page_directory)); + i -= 0x1000; + } + heap->end_address = heap->start_address + new_size; + return new_size; +} + +void *alloc(uint32_t size, uint8_t page_align, heap_t *heap) +{ + + // Make sure we take the size of header/footer into account. + uint32_t new_size = size + sizeof(header_t) + sizeof(footer_t); + // Find the smallest hole that will fit. + int32_t iterator = find_smallest_hole(new_size, page_align, heap); + + if (iterator == -1) // If we didn't find a suitable hole + { + // Save some previous data. + uint32_t old_length = heap->end_address - heap->start_address; + uint32_t old_end_address = heap->end_address; + + // We need to allocate some more space. + expand(old_length+new_size, heap); + uint32_t new_length = heap->end_address-heap->start_address; + + // Find the endmost header. (Not endmost in size, but in location). + iterator = 0; + // Vars to hold the index of, and value of, the endmost header found so far. + uint32_t idx = -1; uint32_t value = 0x0; + while (iterator < heap->index.size) + { + uint32_t tmp = (uint32_t)lookup_ordered_array(iterator, &heap->index); + if (tmp > value) + { + value = tmp; + idx = iterator; + } + iterator++; + } + + // If we didn't find ANY headers, we need to add one. + if (idx == -1) + { + header_t *header = (header_t *)old_end_address; + header->magic = HEAP_MAGIC; + header->size = new_length - old_length; + header->is_hole = 1; + footer_t *footer = (footer_t *) (old_end_address + header->size - sizeof(footer_t)); + footer->magic = HEAP_MAGIC; + footer->header = header; + insert_ordered_array((void*)header, &heap->index); + } + else + { + // The last header needs adjusting. + header_t *header = lookup_ordered_array(idx, &heap->index); + header->size += new_length - old_length; + // Rewrite the footer. + footer_t *footer = (footer_t *) ( (uint32_t)header + header->size - sizeof(footer_t) ); + footer->header = header; + footer->magic = HEAP_MAGIC; + } + // We now have enough space. Recurse, and call the function again. + return alloc(size, page_align, heap); + } + header_t *orig_hole_header = (header_t *)lookup_ordered_array(iterator, &heap->index); + uint32_t orig_hole_pos = (uint32_t)orig_hole_header; + uint32_t orig_hole_size = orig_hole_header->size; + // Here we work out if we should split the hole we found into two parts. + // Is the original hole size - requested hole size less than the overhead for adding a new hole? + if (orig_hole_size-new_size < sizeof(header_t)+sizeof(footer_t)) + { + // Then just increase the requested size to the size of the hole we found. + size += orig_hole_size-new_size; + new_size = orig_hole_size; + } + + // If we need to page-align the data, do it now and make a new hole in front of our block. + if (page_align && orig_hole_pos & 0xFFFFF000) + { + uint32_t new_location = orig_hole_pos + 0x1000 /* page size */ - (orig_hole_pos&0xFFF) - sizeof(header_t); + header_t *hole_header = (header_t *)orig_hole_pos; + hole_header->size = 0x1000 /* page size */ - (orig_hole_pos&0xFFF) - sizeof(header_t); + hole_header->magic = HEAP_MAGIC; + hole_header->is_hole = 1; + footer_t *hole_footer = (footer_t *) ( (uint32_t)new_location - sizeof(footer_t) ); + hole_footer->magic = HEAP_MAGIC; + hole_footer->header = hole_header; + orig_hole_pos = new_location; + orig_hole_size = orig_hole_size - hole_header->size; + } + else + { + // Else we don't need this hole any more, delete it from the index. + remove_ordered_array(iterator, &heap->index); + } + + // Overwrite the original header... + header_t *block_header = (header_t *)orig_hole_pos; + block_header->magic = HEAP_MAGIC; + block_header->is_hole = 0; + block_header->size = new_size; + // ...And the footer + footer_t *block_footer = (footer_t *) (orig_hole_pos + sizeof(header_t) + size); + block_footer->magic = HEAP_MAGIC; + block_footer->header = block_header; + + // We may need to write a new hole after the allocated block. + // We do this only if the new hole would have positive size... + if (orig_hole_size - new_size > 0) + { + header_t *hole_header = (header_t *) (orig_hole_pos + sizeof(header_t) + size + sizeof(footer_t)); + hole_header->magic = HEAP_MAGIC; + hole_header->is_hole = 1; + hole_header->size = orig_hole_size - new_size; + footer_t *hole_footer = (footer_t *) ( (uint32_t)hole_header + orig_hole_size - new_size - sizeof(footer_t) ); + if ((uint32_t)hole_footer < heap->end_address) + { + hole_footer->magic = HEAP_MAGIC; + hole_footer->header = hole_header; + } + // Put the new hole in the index; + insert_ordered_array((void*)hole_header, &heap->index); + } + + return (void *) ( (uint32_t)block_header+sizeof(header_t) ); +} + +void free(void *p, heap_t *heap) +{ + // Exit gracefully for null pointers. + if (p == 0) + return; + + // Get the header and footer associated with this pointer. + header_t *header = (header_t*) ( (uint32_t)p - sizeof(header_t) ); + footer_t *footer = (footer_t*) ( (uint32_t)header + header->size - sizeof(footer_t) ); + + // Sanity checks. + assert(header->magic == HEAP_MAGIC); + assert(footer->magic == HEAP_MAGIC); + + // Make us a hole. + header->is_hole = 1; + + // Do we want to add this header into the 'free holes' index? + char do_add = 1; + + // Unify left + // If the thing immediately to the left of us is a footer... + footer_t *test_footer = (footer_t*) ( (uint32_t)header - sizeof(footer_t) ); + if (test_footer->magic == HEAP_MAGIC && + test_footer->header->is_hole == 1) + { + uint32_t cache_size = header->size; // Cache our current size. + header = test_footer->header; // Rewrite our header with the new one. + footer->header = header; // Rewrite our footer to point to the new header. + header->size += cache_size; // Change the size. + do_add = 0; // Since this header is already in the index, we don't want to add it again. + } + + // Unify right + // If the thing immediately to the right of us is a header... + header_t *test_header = (header_t*) ( (uint32_t)footer + sizeof(footer_t) ); + if (test_header->magic == HEAP_MAGIC && + test_header->is_hole) + { + header->size += test_header->size; // Increase our size. + test_footer = (footer_t*) ( (uint32_t)test_header + // Rewrite it's footer to point to our header. + test_header->size - sizeof(footer_t) ); + footer = test_footer; + // Find and remove this header from the index. + uint32_t iterator = 0; + while ( (iterator < heap->index.size) && + (lookup_ordered_array(iterator, &heap->index) != (void*)test_header) ) + iterator++; + + // Make sure we actually found the item. + assert(iterator < heap->index.size); + // Remove it. + remove_ordered_array(iterator, &heap->index); + } + + // If the footer location is the end address, we can contract. + if ( (uint32_t)footer+sizeof(footer_t) == heap->end_address) + { + uint32_t old_length = heap->end_address-heap->start_address; + uint32_t new_length = contract( (uint32_t)header - heap->start_address, heap); + // Check how big we will be after resizing. + if (header->size - (old_length-new_length) > 0) + { + // We will still exist, so resize us. + header->size -= old_length-new_length; + footer = (footer_t*) ( (uint32_t)header + header->size - sizeof(footer_t) ); + footer->magic = HEAP_MAGIC; + footer->header = header; + } + else + { + // We will no longer exist :(. Remove us from the index. + uint32_t iterator = 0; + while ( (iterator < heap->index.size) && + (lookup_ordered_array(iterator, &heap->index) != (void*)test_header) ) + iterator++; + // If we didn't find ourselves, we have nothing to remove. + if (iterator < heap->index.size) + remove_ordered_array(iterator, &heap->index); + } + } + if (do_add == 1) + insert_ordered_array((void*) header, &heap->index); +} + + diff --git a/kernel/kernel/multiboot.h b/kernel/kernel/multiboot.h new file mode 100644 index 0000000..7155e96 --- /dev/null +++ b/kernel/kernel/multiboot.h @@ -0,0 +1,41 @@ +#define MULTIBOOT_FLAG_MEM 0x001 +#define MULTIBOOT_FLAG_DEVICE 0x002 +#define MULTIBOOT_FLAG_CMDLINE 0x004 +#define MULTIBOOT_FLAG_MODS 0x008 +#define MULTIBOOT_FLAG_AOUT 0x010 +#define MULTIBOOT_FLAG_ELF 0x020 +#define MULTIBOOT_FLAG_MMAP 0x040 +#define MULTIBOOT_FLAG_CONFIG 0x080 +#define MULTIBOOT_FLAG_LOADER 0x100 +#define MULTIBOOT_FLAG_APM 0x200 +#define MULTIBOOT_FLAG_VBE 0x400 + +struct multiboot +{ + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t drives_length; + uint32_t drives_addr; + uint32_t config_table; + uint32_t boot_loader_name; + uint32_t apm_table; + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint32_t vbe_mode; + uint32_t vbe_interface_seg; + uint32_t vbe_interface_off; + uint32_t vbe_interface_len; +} __attribute__((packed)); + +typedef struct multiboot_header multiboot_header_t; diff --git a/kernel/kernel/ordered_map.c b/kernel/kernel/ordered_map.c new file mode 100644 index 0000000..222aae4 --- /dev/null +++ b/kernel/kernel/ordered_map.c @@ -0,0 +1,84 @@ +#include +typedef void* type_t; + +typedef int8_t (*lessthan_predicate_t)(type_t,type_t); +typedef struct +{ + type_t *array; + uint32_t size; + uint32_t max_size; + lessthan_predicate_t less_than; +} ordered_array_t; + +standard_lessthan_predicate(type_t a, type_t b) +{ + return (aarray); +} + +void insert_ordered_array(type_t item, ordered_array_t *array) +{ + assert(array->less_than); + uint32_t iterator = 0; + while (iterator < array->size && array->less_than(array->array[iterator], item)) + iterator++; + if (iterator == array->size) // just add at the end of the array. + array->array[array->size++] = item; + else + { + type_t tmp = array->array[iterator]; + array->array[iterator] = item; + while (iterator < array->size) + { + iterator++; + type_t tmp2 = array->array[iterator]; + array->array[iterator] = tmp; + tmp = tmp2; + } + array->size++; + } +} + +type_t lookup_ordered_array(uint32_t i, ordered_array_t *array) +{ + assert(i < array->size); + return array->array[i]; +} + +void remove_ordered_array(uint32_t i, ordered_array_t *array) +{ + while (i < array->size) + { + array->array[i] = array->array[i+1]; + i++; + } + array->size--; +} + + diff --git a/kernel/kernel/paging.h b/kernel/kernel/paging.h index 4d855b6..6cab75b 100644 --- a/kernel/kernel/paging.h +++ b/kernel/kernel/paging.h @@ -1,4 +1,10 @@ #include +#include +//#include "kheap.c" +uint32_t placement_address; //FIXME + +uint32_t *frames; +uint32_t nframes; /* Handler for page faults. @@ -26,14 +32,184 @@ void page_fault(registers_t regs){ printf("PAGE FAULT\n"); } +uint32_t kmalloc(uint32_t sz) // FIXME +{ + uint32_t tmp = placement_address; + placement_address += sz; + return tmp; +} + +typedef struct page +{ + uint32_t present : 1; // Page present in memory + uint32_t rw : 1; // Read-only if clear, readwrite if set + uint32_t user : 1; // Supervisor level only if clear + uint32_t accessed : 1; // Has the page been accessed since last refresh? + uint32_t dirty : 1; // Has the page been written to since last refresh? + uint32_t unused : 7; // Amalgamation of unused and reserved bits + uint32_t frame : 20; // Frame address (shifted right 12 bits) +} page_t; + +typedef struct page_table +{ + page_t pages[1024]; +} page_table_t; + +typedef struct page_directory +{ + /** + Array of pointers to pagetables. + **/ + page_table_t *tables[1024]; + /** + Array of pointers to the pagetables above, but gives their *physical* + location, for loading into the CR3 register. + **/ + uint32_t tablesPhysical[1024]; + /** + The physical address of tablesPhysical. This comes into play + when we get our kernel heap allocated and the directory + may be in a different location in virtual memory. + **/ + uint32_t physicalAddr; +} page_directory_t; uint32_t page_directory[1024] __attribute__((aligned(4096))); uint32_t first_page_table[1024] __attribute__((aligned(4096))); extern void loadPageDirectory(unsigned int*); extern void enable_paging(); +//extern heap_t *kheap; + +uint32_t kmalloc_ap(uint32_t sz, uint32_t *phys) +{ + if (placement_address & 0xFFFFF000) // If the address is not already page-aligned + { + // Align it. + placement_address &= 0xFFFFF000; + placement_address += 0x1000; + } + if (phys) + { + *phys = placement_address; + } + uint32_t tmp = placement_address; + placement_address += sz; + return tmp; +} + +// Macros used in the bitset algorithms. +#define INDEX_FROM_BIT(a) (a/(8*4)) +#define OFFSET_FROM_BIT(a) (a%(8*4)) + +// Static function to set a bit in the frames bitset +static void set_frame(uint32_t frame_addr) +{ + uint32_t frame = frame_addr/0x1000; + uint32_t idx = INDEX_FROM_BIT(frame); + uint32_t off = OFFSET_FROM_BIT(frame); + frames[idx] |= (0x1 << off); +} + +// Static function to clear a bit in the frames bitset +static void clear_frame(uint32_t frame_addr) +{ + uint32_t frame = frame_addr/0x1000; + uint32_t idx = INDEX_FROM_BIT(frame); + uint32_t off = OFFSET_FROM_BIT(frame); + frames[idx] &= ~(0x1 << off); +} + +// Static function to find the first free frame. +static uint32_t first_frame() +{ + uint32_t i, j; + for (i = 0; i < INDEX_FROM_BIT(nframes); i++) + { + if (frames[i] != 0xFFFFFFFF) // nothing free, exit early. + { + // at least one bit is free here. + for (j = 0; j < 32; j++) + { + uint32_t toTest = 0x1 << j; + if ( !(frames[i]&toTest) ) + { + return i*4*8+j; + } + } + } + } +} + +void alloc_frame(page_t *page, int is_kernel, int is_writeable) +{ + if (page->frame != 0) + { + return; // Frame was already allocated, return straight away. + } + else + { + uint32_t idx = first_frame(); // idx is now the index of the first free frame. + if (idx == (uint32_t)-1) + { + // PANIC is just a macro that prints a message to the screen then hits an infinite loop. + printf("No free frames!"); + } + set_frame(idx*0x1000); // this frame is now ours! + page->present = 1; // Mark it as present. + page->rw = (is_writeable)?1:0; // Should the page be writeable? + page->user = (is_kernel)?0:1; // Should the page be user-mode? + page->frame = idx; + } +} + +// Function to deallocate a frame. +void free_frame(page_t *page) +{ + uint32_t frame; + if (!(frame=page->frame)) + { + return; // The given page didn't actually have an allocated frame! + } + else + { + clear_frame(frame); // Frame is now free again. + page->frame = 0x0; // Page now doesn't have a frame. + } +} + +page_t *get_page(uint32_t address, int make, page_directory_t *dir) +{ + // Turn the address into an index. + address /= 0x1000; + // Find the page table containing this address. + uint32_t table_idx = address / 1024; + if (dir->tables[table_idx]) // If this table is already assigned + { + return &dir->tables[table_idx]->pages[address%1024]; + } + else if(make) + { + uint32_t tmp; + dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), &tmp); + memset(dir->tables[table_idx], 0, 0x1000); + dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US. + return &dir->tables[table_idx]->pages[address%1024]; + } + else + { + return 0; + } +} void init_paging() { + // The size of physical memory. For the moment we + // assume it is 16MB big. + uint32_t mem_end_page = 0x1000000; + + nframes = mem_end_page / 0x1000; + frames = (uint32_t*)kmalloc(INDEX_FROM_BIT(nframes)); + memset(frames, 0, INDEX_FROM_BIT(nframes)); //set each entry to not present int i; for(i = 0; i < 1024; i++) @@ -60,4 +236,7 @@ void init_paging() { loadPageDirectory(page_directory); enable_paging(); + + // Initialise the kernel heap. + //kheap = create_heap(KHEAP_START, KHEAP_START+KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0); } diff --git a/libc/include/assert.h b/libc/include/assert.h new file mode 100644 index 0000000..0652a64 --- /dev/null +++ b/libc/include/assert.h @@ -0,0 +1,10 @@ +#ifndef _ASSERT_H +#define _ASSERT_H 1 + +#ifdef NDEBUG +#define assert(condition) ((void)0) +#else +#define assert(condition) /*implementation defined*/ +#endif + +#endif diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index f1f95ba..63226b2 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -61,6 +61,18 @@ int printf(const char* restrict format, ...) { if (!print(str, len)) return -1; written += len; + } else if (*format == 'x') { + format++; + int c = va_arg(parameters, int); + c = c == 10 ? 17 : c; + if (!maxrem) { + // TODO: Set errno to EOVERFLOW. + return -1; + } + if (!print("0x", &c)) + return -1; + written++; + } else { format = format_begun_at; size_t len = strlen(format);