diff --git a/Makefile b/Makefile index 3a4a4f4..9b7f98c 100644 --- a/Makefile +++ b/Makefile @@ -103,9 +103,12 @@ index: cscope.files ctags --C-kinds=+pxzL -L $< cscope -b -q -i $< +.PHONY: clean-full +clean-full: clean + -rm -rdf userland/mlibc/ + .PHONY: clean clean: - $(MAKE) -C userland clean -rm -rd $(OBJ_DIR)/ tags cscope.* .PHONY: test-all @@ -133,6 +136,7 @@ $(USERLAND_TARGETS): userland $(TEST_TARGETS): test +.PHONY: $(TEST_EXEC) $(TEST_EXEC): %: %.a $(OBJ_DIR)/boot.a $(OBJ_DIR)/kernel.a $(OBJ_DIR)/drivers.a $(CC) -g -O0 -std=c23 $(CWARN) -fuse-ld=lld -o $@ $^ diff --git a/boot/multiboot2/init.c b/boot/multiboot2/init.c index b53a244..9aae077 100644 --- a/boot/multiboot2/init.c +++ b/boot/multiboot2/init.c @@ -72,7 +72,7 @@ struct mb2_tag_t { struct { struct acpi_rsdp_t rsdp; - } __attribute__((packed)) rsdpv2; + } rsdpv2; #ifdef GRAPHICSBASE struct { @@ -91,8 +91,8 @@ struct mb2_tag_t { uint8_t framebuffer_green_mask_size; uint8_t framebuffer_blue_field_position; uint8_t framebuffer_blue_mask_size; - } __attribute__((packed)) rgb; - } __attribute__((packed)) color_info; + } rgb; + } color_info; } __attribute__((packed)) framebuffer; #endif /* GRAPHICSBASE */ } tag; @@ -101,11 +101,11 @@ struct mb2_tag_t { struct mb2_info_t { const uint32_t total_size; const uint32_t reserved; -} __attribute__((packed)) header; +} __attribute__((packed)); -extern volatile struct gdt_t gdt[GDT_NUM_ENTRIES]; +extern struct gdt_t gdt[GDT_NUM_ENTRIES]; -static volatile struct mb2_tag_t* memmap_tag; +static struct mb2_tag_t* memmap_tag; extern uint64_t init_stack_paddr; extern uint64_t init_stack_vaddr; diff --git a/drivers/ahci/ahci.c b/drivers/ahci/ahci.c index 12b7880..bfaf088 100644 --- a/drivers/ahci/ahci.c +++ b/drivers/ahci/ahci.c @@ -255,13 +255,13 @@ static enum disk_error_t ahci_read_lba(void* cntx, void* buffer, uint64_t lba, u while (hba_read(ahci, PXTFD_OFF(port)) & TFD_STS_CON_MASK) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); } hba_write(ahci, PXCI_OFF(port), 1u << slot); while (hba_read(ahci, PXCI_OFF(port)) & (1u << slot)) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); if (hba_read(ahci, PXIS_OFF(port)) & IS_TFES) { logging_log_error("AHCI error while reading"); @@ -381,13 +381,13 @@ static enum disk_error_t ahci_write_lba(void* cntx, void* buffer, uint64_t lba, while (hba_read(ahci, PXTFD_OFF(port)) & TFD_STS_CON_MASK) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); } hba_write(ahci, PXCI_OFF(port), 1u << slot); while (hba_read(ahci, PXCI_OFF(port)) & (1u << slot)) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); if (hba_read(ahci, PXIS_OFF(port)) & IS_TFES) { logging_log_error("AHCI error while reading"); @@ -462,13 +462,13 @@ static enum disk_error_t ahci_flush_cache(void* cntx) { ahci->ports[port]->com_list[slot].ctba_u0 = 0; while (hba_read(ahci, PXTFD_OFF(port)) & TFD_STS_CON_MASK) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); } hba_write(ahci, PXCI_OFF(port), 1u << slot); while (hba_read(ahci, PXCI_OFF(port)) & (1u << slot)) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); if (hba_read(ahci, PXIS_OFF(port)) & IS_TFES) { logging_log_error("AHCI flush cache error"); @@ -494,7 +494,7 @@ static enum disk_error_t ahci_flush_cache(void* cntx) { static void port_identify(struct ahci_t* ahci, uint32_t port) { uint8_t slot; uint32_t paddr_identity; - volatile uint16_t* identity; + uint16_t* identity; uint8_t model[41]; paddr_identity = (uint32_t)mm_alloc_pmax(PAGE_SIZE_4K, 0, ~0u); @@ -515,12 +515,18 @@ static void port_identify(struct ahci_t* ahci, uint32_t port) { lock_acquire(&ahci->lock); slot = find_slot(ahci, port); + + if (slot == SLOT_NO_SLOT) { + logging_log_error("No available slots for identification"); + panic(PANIC_STATE); + } + ahci->used_com |= 1u << slot; lock_release(&ahci->lock); kmemset(&ahci->ports[port]->com_list[slot], 0, sizeof(struct ahci_command_header_t)); kmemset(ahci->com_tables_v[slot], 0, PAGE_SIZE_4K); - kmemset((void*)identity, 0, PAGE_SIZE_4K); + kmemset(identity, 0, PAGE_SIZE_4K); ahci->com_tables_v[slot]->prdt[0].dba = paddr_identity; ahci->com_tables_v[slot]->prdt[0].dbau = 0; @@ -542,13 +548,13 @@ static void port_identify(struct ahci_t* ahci, uint32_t port) { ahci->ports[port]->com_list[slot].ctba_u0 = 0; while (hba_read(ahci, PXTFD_OFF(port)) & TFD_STS_CON_MASK) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); } hba_write(ahci, PXCI_OFF(port), 1u << slot); while (hba_read(ahci, PXCI_OFF(port)) & (1u << slot)) { - time_busy_wait(10 * TIME_CONV_MS_TO_NS); + time_busy_wait(100); } lock_release(&ahci->ports[port]->lock); diff --git a/drivers/disk/disk.c b/drivers/disk/disk.c index b438e65..1f241c2 100644 --- a/drivers/disk/disk.c +++ b/drivers/disk/disk.c @@ -36,7 +36,7 @@ struct disk_t { struct disk_t* next; }; -uint8_t disk_lock; +static uint8_t disk_lock; static struct disk_t* disk_list; static uint64_t disk_id; diff --git a/drivers/ext2/ext2.c b/drivers/ext2/ext2.c index dd71869..24a790a 100644 --- a/drivers/ext2/ext2.c +++ b/drivers/ext2/ext2.c @@ -27,12 +27,14 @@ #include #include #include -#include #include +#include #include #include #include +#include +#include #define SUPERBLOCK_LBA 2 #define SUPERBLOCK_SECTORS 2 @@ -42,13 +44,6 @@ #define INDIR_2 13 #define INDIR_3 14 -#define BLOCK_OK 0 -#define BLOCK_RETRY 1 -#define BLOCK_LAST 2 -#define BLOCK_ERROR 3 - -#define EXT2_FT_DIR 2 - #define EXT2_SUPER_MAGIC 0xEF53 #define EXT2_ROOT_INO 2 @@ -56,7 +51,10 @@ #define EXT2_S_IFREG 0x8000 #define EXT2_S_IFDIR 0x4000 -#define DIRLS_SET 0x8000000000000000 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 + +#define MODE_DIR 0x1 struct ext2_superblock_t { uint32_t s_inodes_count; @@ -160,6 +158,7 @@ struct ext2_ll_dir_entry_t { } __attribute__((packed)); _Static_assert(sizeof(struct ext2_superblock_t) == 1024, "Bad ext2 superblock size"); +_Static_assert(sizeof(struct ext2_superblock_t) == SUPERBLOCK_SECTORS * SECTOR_SIZE, "Bad ext2 superblock size"); _Static_assert(sizeof(struct ext2_bg_desc_t) == 32, "Bad ext2 bg descriptor size"); _Static_assert(sizeof(struct ext2_inode_t) == 128, "Bad exte2 inode size"); @@ -169,6 +168,8 @@ struct ext2_t { struct ext2_superblock_t* superblock; struct ext2_bg_desc_t* bgdt; struct disk_t* disk; + uint64_t block_size; + uint8_t lock; }; struct ext2_inode_handle_t { @@ -176,26 +177,27 @@ struct ext2_inode_handle_t { uint64_t inode_index; uint64_t seek; uint64_t seek_block; + uint8_t ext2_mode; }; -struct ext2_block_track_t { - struct ext2_inode_handle_t* handle; - struct ext2_inode_t* inode; - uint64_t off; - uint64_t block; +enum ext2_block_state_t { + BLOCK_OK, + BLOCK_SPARSE, + BLOCK_ERROR }; static uint8_t label_rootfs[16] = {'r', 'o', 'o', 't', 'f', 's', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static void* read_block(uint64_t block, struct ext2_t* ext2) { - const uint64_t block_size = 1024u << ext2->superblock->s_log_block_size; + const uint64_t block_size = ext2->block_size; void* buffer = kmalloc(block_size); const uint64_t lba = ext2->start_lba + block * block_size / SECTOR_SIZE; if (lba > ext2->end_lba) { - logging_log_error("Attempt to read beyond ext2 end lba (0x%x > 0x%x)", lba, ext2->end_lba); + logging_log_error("Attempt to read beyond ext2 end lba (0x%llx > 0x%llx)", lba, ext2->end_lba); + return 0; } if (disk_read(ext2->disk, buffer, lba, (uint16_t)(block_size / SECTOR_SIZE)) != DISK_OK) { @@ -207,11 +209,39 @@ static void* read_block(uint64_t block, struct ext2_t* ext2) { return buffer; } +static void* write_block_free(uint64_t block, struct ext2_t* ext2, void* buffer, uint8_t free) { + const uint64_t block_size = ext2->block_size; + + const uint64_t lba = ext2->start_lba + block * block_size / SECTOR_SIZE; + + if (lba > ext2->end_lba) { + logging_log_error("Attempt to write beyond ext2 end lba (0x%x > 0x%x)", lba, ext2->end_lba); + if (free) { + kfree(buffer); + } + return 0; + } + + if (disk_write(ext2->disk, buffer, lba, (uint16_t)(block_size / SECTOR_SIZE)) != DISK_OK) { + logging_log_error("Failed to write"); + if (free) { + kfree(buffer); + } + return 0; + } + + return buffer; +} + +static inline void* write_block(uint64_t block, struct ext2_t* ext2, void* buffer) { + return write_block_free(block, ext2, buffer, 1); +} + static uint8_t get_inode(const struct ext2_inode_handle_t* inode_handle, struct ext2_inode_t* inode) { const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; const struct ext2_bg_desc_t* bgdt = inode_handle->ext2->bgdt; - const uint64_t block_size = 1024u << superblock->s_log_block_size; + const uint64_t block_size = inode_handle->ext2->block_size; const uint64_t block_group = (inode_handle->inode_index - 1) / superblock->s_inodes_per_group; const uint64_t lcl_inode_idx = (inode_handle->inode_index - 1) % superblock->s_inodes_per_group; const uint64_t lcl_inode_off = lcl_inode_idx * superblock->s_inode_size; @@ -232,160 +262,554 @@ static uint8_t get_inode(const struct ext2_inode_handle_t* inode_handle, struct return 0; } -static uint8_t get_block_index(struct ext2_block_track_t* bt, uint64_t* index) { - uint64_t block = bt->block; - struct ext2_inode_t* inode = bt->inode; - struct ext2_t* ext2 = bt->handle->ext2; +static uint8_t set_inode(const struct ext2_inode_handle_t* inode_handle, struct ext2_inode_t* inode) { + const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; + const struct ext2_bg_desc_t* bgdt = inode_handle->ext2->bgdt; + + const uint64_t block_size = inode_handle->ext2->block_size; + const uint64_t block_group = (inode_handle->inode_index - 1) / superblock->s_inodes_per_group; + const uint64_t lcl_inode_idx = (inode_handle->inode_index - 1) % superblock->s_inodes_per_group; + const uint64_t lcl_inode_off = lcl_inode_idx * superblock->s_inode_size; + const uint64_t lcl_inode_blk = lcl_inode_off / block_size; + const uint64_t inode_off = lcl_inode_off % block_size; + + const uint64_t inode_table_block = bgdt[block_group].bg_inode_table + lcl_inode_blk; + void* buffer = read_block(inode_table_block, inode_handle->ext2); + + if (!buffer) { + logging_log_error("Failed to read inode %lu", inode_handle->inode_index); + return 1; + } + + *(struct ext2_inode_t*)((uint64_t)buffer + inode_off) = *inode; + + if (!(buffer = write_block(inode_table_block, inode_handle->ext2, buffer))) { + logging_log_error("Failed to write inode %lu", inode_handle->inode_index); + return 1; + } + + kfree(buffer); + return 0; +} + +static void sync_meta(const struct ext2_t* ext2) { + // superblock + disk_write(ext2->disk, ext2->superblock, ext2->start_lba + SUPERBLOCK_LBA, SUPERBLOCK_SECTORS); + + // bgdt + const uint64_t bgdt_start_lba = ext2->start_lba + (1u << (1 + ext2->superblock->s_log_block_size)); + uint64_t bgdt_size = (1 + ext2->superblock->s_blocks_count / ext2->superblock->s_blocks_per_group) + * sizeof(struct ext2_bg_desc_t); + const uint64_t adj = bgdt_size % SECTOR_SIZE; + if (adj) { + bgdt_size += SECTOR_SIZE - adj; + } + + disk_write(ext2->disk, ext2->bgdt, bgdt_start_lba, (uint16_t)(bgdt_size / SECTOR_SIZE)); +} + +static uint32_t alloc_block(struct ext2_t* ext2, uint64_t group) { + const uint64_t start_group = group; + const uint64_t num_groups = ext2->superblock->s_inodes_count / ext2->superblock->s_inodes_per_group; + const uint64_t bitmap_bytes = ext2->superblock->s_blocks_per_group / 8; + + do { + if (ext2->bgdt[group].bg_free_blocks_count) { + void* bitmap = 0; + for (uint64_t off = 0; off < bitmap_bytes; off += sizeof(uint64_t)) { + if (off % ext2->block_size == 0) { + kfree(bitmap); + + bitmap = read_block(ext2->bgdt[group].bg_block_bitmap + (off / ext2->block_size), ext2); + + if (!bitmap) { + // try next block on failure + off += ext2->block_size - sizeof(uint64_t); + continue; + } + } + + uint64_t word = ~*(uint64_t*)((uint64_t)bitmap + (off % ext2->block_size)); + if (word) { + for (uint8_t bit = 0; bit < 64; bit++) { + if (word & (1uLL << bit)) { + *(uint64_t*)((uint64_t)bitmap + (off % ext2->block_size)) = ~word | (1uLL << bit); + ext2->bgdt[group].bg_free_blocks_count--; + ext2->superblock->s_free_blocks_count--; + bitmap = write_block(ext2->bgdt[group].bg_block_bitmap + (off / ext2->block_size), ext2, bitmap); - if (block < DIRECT_BLOCKS) { - *index = inode->i_block[block]; + sync_meta(ext2); - if (!*index) { - bt->block += 1; - return BLOCK_RETRY; + kfree(bitmap); + return (uint32_t)((group * ext2->superblock->s_blocks_per_group) + off * 8 + bit); + } + } + } + } } - return BLOCK_OK; + if (++group == num_groups - 1) { // TODO: support allocating from last group + group = 0; + } + } while (start_group != group); + + return 0; // out of blocks +} + +static uint64_t alloc_inode(struct ext2_t* ext2, uint64_t group) { + const uint64_t start_group = group; + const uint64_t num_groups = ext2->superblock->s_inodes_count / ext2->superblock->s_inodes_per_group; + const uint64_t bitmap_bytes = ext2->superblock->s_inodes_per_group / 8; + + do { + if (ext2->bgdt[group].bg_free_inodes_count) { + void* bitmap = 0; + for (uint64_t off = 0; off < bitmap_bytes; off += sizeof(uint64_t)) { + if (off % ext2->block_size == 0) { + kfree(bitmap); + + bitmap = read_block(ext2->bgdt[group].bg_inode_bitmap + (off / ext2->block_size), ext2); + + if (!bitmap) { + // try next block on failure + off += ext2->block_size - sizeof(uint64_t); + continue; + } + } + + uint64_t word = ~*(uint64_t*)((uint64_t)bitmap + (off % ext2->block_size)); + if (word) { + for (uint8_t bit = 0; bit < 64; bit++) { + if (word & (1uLL << bit)) { + *(uint64_t*)((uint64_t)bitmap + (off % ext2->block_size)) = ~word | (1uLL << bit); + ext2->bgdt[group].bg_free_inodes_count--; + ext2->superblock->s_free_inodes_count--; + + bitmap = write_block(ext2->bgdt[group].bg_inode_bitmap + (off / ext2->block_size), ext2, bitmap); + + sync_meta(ext2); + + kfree(bitmap); + return (uint32_t)((group * ext2->superblock->s_inodes_per_group) + off * 8 + bit + 1); + } + } + } + } + } + + if (++group == num_groups - 1) { // TODO: support allocating from last group + group = 0; + } + } while (start_group != group); + + return 0; // out of inodes +} + +static uint32_t alloc_and_zero_block(struct ext2_t* ext2, void* zeros, uint64_t group) { + uint32_t block = alloc_block(ext2, group); + + if (block) { + write_block_free(block, ext2, zeros, 0); + } + + return block; +} + +static uint64_t assign_block(struct ext2_inode_handle_t* handle, uint64_t index, struct ext2_inode_t* inode, uint8_t lock) { + const uint64_t group = (handle->inode_index - 1) / handle->ext2->superblock->s_inodes_per_group; + const uint64_t blocks_usage = handle->ext2->block_size / 512; + + void* zeros = kmalloc(handle->ext2->block_size); + kmemset(zeros, 0, handle->ext2->block_size); + + if (!lock) { + lock_acquire(&handle->ext2->lock); + } + + if (get_inode(handle, inode)) { + return 0; } - block -= DIRECT_BLOCKS; + uint32_t block = 0; - const uint64_t block_size = 1024u << ext2->superblock->s_log_block_size; + if (index < DIRECT_BLOCKS) { + block = inode->i_block[index]; + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + inode->i_block[index] = block; + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + goto cleanup; + } + + index -= DIRECT_BLOCKS; + + uint32_t* buffer; + const uint64_t block_size = handle->ext2->block_size; const uint64_t indir1 = block_size / sizeof(uint32_t); + uint64_t old_block; + + if (index < indir1) { + block = inode->i_block[INDIR_1]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + + if (!block) { + goto cleanup; + } + + inode->i_blocks += blocks_usage; + + inode->i_block[INDIR_1] = block; + } + + + old_block = block; + buffer = read_block(block, handle->ext2); + + if (!buffer) { + block = 0; + goto cleanup; + } + + block = buffer[index]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + buffer[index] = block; + buffer = write_block(old_block, handle->ext2, buffer); + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + kfree(buffer); + + goto cleanup; + } + + index -= indir1; + + const uint64_t indir2 = indir1 * indir1; + + if (index < indir2) { + block = inode->i_block[INDIR_2]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + + if (!block) { + goto cleanup; + } + + inode->i_blocks += blocks_usage; + + inode->i_block[INDIR_2] = block; + } + + old_block = block; + buffer = read_block(block, handle->ext2); + + if (!buffer) { + block = 0; + goto cleanup; + } + + block = buffer[index / indir1]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + buffer[index / indir1] = block; + buffer = write_block(old_block, handle->ext2, buffer); + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + kfree(buffer); + + if (!block) { + goto cleanup; + } + + old_block = block; + buffer = read_block(block, handle->ext2); + + if (!buffer) { + block = 0; + goto cleanup; + } + + block = buffer[index % indir1]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + buffer[index % indir1] = block; + buffer = write_block(old_block, handle->ext2, buffer); + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + kfree(buffer); + + goto cleanup; + } + + index -= indir2; + + const uint64_t indir3 = indir2 * indir1; + + if (index < indir3) { + block = inode->i_block[INDIR_3]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + + if (!block) { + goto cleanup; + } + + inode->i_blocks += blocks_usage; + + inode->i_block[INDIR_3] = block; + } + + old_block = block; + buffer = read_block(block, handle->ext2); + + if (!buffer) { + block = 0; + goto cleanup; + } + + block = buffer[index / indir2]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + buffer[index / indir2] = block; + buffer = write_block(old_block, handle->ext2, buffer); + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + kfree(buffer); + + if (!block) { + goto cleanup; + } + + old_block = block; + buffer = read_block(block, handle->ext2); + + if (!buffer) { + block = 0; + goto cleanup; + } + + block = buffer[(index % indir2) / indir1]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + buffer[(index % indir2) / indir1] = block; + buffer = write_block(old_block, handle->ext2, buffer); + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + kfree(buffer); + + if (!block) { + goto cleanup; + } + + old_block = block; + buffer = read_block(block, handle->ext2); + + if (!buffer) { + block = 0; + goto cleanup; + } + + block = buffer[index % indir1]; + + if (!block) { + block = alloc_and_zero_block(handle->ext2, zeros, group); + buffer[index % indir1] = block; + buffer = write_block(old_block, handle->ext2, buffer); + + if (block) { + inode->i_blocks += blocks_usage; + } + } + + kfree(buffer); + + goto cleanup; + } + +cleanup: + set_inode(handle, inode); + + if (!lock) { + lock_release(&handle->ext2->lock); + } + kfree(zeros); + + return block; +} + +static enum ext2_block_state_t get_block(struct ext2_t* ext2, + struct ext2_inode_t* inode, + uint64_t index, + uint64_t* block) { + if (index < DIRECT_BLOCKS) { + *block = inode->i_block[index]; + if (!*block) { + return BLOCK_SPARSE; + } + return BLOCK_OK; + } + + index -= DIRECT_BLOCKS; uint32_t* buffer; + const uint64_t block_size = ext2->block_size; + const uint64_t indir1 = block_size / sizeof(uint32_t); - if (block < indir1) { - *index = inode->i_block[INDIR_1]; + if (index < indir1) { + *block = inode->i_block[INDIR_1]; - if (!*index) { - bt->block += indir1; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } - buffer = read_block(*index, ext2); + buffer = read_block(*block, ext2); if (!buffer) { return BLOCK_ERROR; } - *index = buffer[block]; + *block = buffer[index]; kfree(buffer); - if (!*index) { - bt->block += 1; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } return BLOCK_OK; } - block -= indir1; + index -= indir1; const uint64_t indir2 = indir1 * indir1; + if (index < indir2) { + *block = inode->i_block[INDIR_2]; - if (block < indir2) { - *index = inode->i_block[INDIR_2]; - - if (!*index) { - bt->block += indir2; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } - buffer = read_block(*index, ext2); + buffer = read_block(*block, ext2); if (!buffer) { return BLOCK_ERROR; } - *index = buffer[block / indir1]; + *block = buffer[index / indir1]; kfree(buffer); - if (!*index) { - bt->block += indir1; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } - buffer = read_block(*index, ext2); + buffer = read_block(*block, ext2); if (!buffer) { return BLOCK_ERROR; } - *index = buffer[block % indir1]; + *block = buffer[index % indir1]; kfree(buffer); - if (!*index) { - bt->block += 1; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } return BLOCK_OK; } - block -= indir2; + index -= indir2; const uint64_t indir3 = indir2 * indir1; - if (block < indir3) { - *index = inode->i_block[INDIR_3]; + if (index < indir3) { + *block = inode->i_block[INDIR_3]; - if (!*index) { - bt->block += indir3; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } - buffer = read_block(*index, ext2); + buffer = read_block(*block, ext2); if (!buffer) { return BLOCK_ERROR; } - *index = buffer[block / indir2]; + *block = buffer[index / indir2]; kfree(buffer); - if (!*index) { - bt->block += indir2; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } - buffer = read_block(*index, ext2); + buffer = read_block(*block, ext2); if (!buffer) { return BLOCK_ERROR; } - *index = buffer[(block % indir2) / indir1]; + *block = buffer[(index % indir2) / indir1]; kfree(buffer); - if (!*index) { - bt->block += indir1; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } - buffer = read_block(*index, ext2); + buffer = read_block(*block, ext2); if (!buffer) { return BLOCK_ERROR; } - *index = buffer[block % indir1]; + *block = buffer[index % indir1]; kfree(buffer); - if (!*index) { - bt->block += 1; - return BLOCK_RETRY; + if (!*block) { + return BLOCK_SPARSE; } return BLOCK_OK; } - return BLOCK_LAST; + return BLOCK_ERROR; } -static inline size_t path_entry_len(char* path) { +static inline size_t path_entry_len(const char* path) { size_t len = 0; for (; *path && *path != '/'; path++) { @@ -395,221 +819,426 @@ static inline size_t path_entry_len(char* path) { return len; } +static uint64_t ext2_get_seek(struct file_handle_t* handle) { + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; + const uint64_t block_size = inode_handle->ext2->block_size; + + return inode_handle->seek_block * block_size + inode_handle->seek; +} -static struct file_handle_t* ext2_open(struct mount_cntx_t* cntx, char* path) { +static enum file_status_t ext2_stat(struct file_handle_t* handle, struct file_info_t* info) { struct ext2_inode_t inode; - size_t path_len; - struct ext2_block_track_t track; - uint64_t block_index; - uint8_t cntrl = 0; + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; + + if (!inode_handle || get_inode(inode_handle, &inode)) { + return FILE_ERROR; + } + + if (inode.i_mode & EXT2_S_IFREG) { + info->type = FILE_TYPE_REG; + } + else if (inode.i_mode & EXT2_S_IFDIR) { + info->type = FILE_TYPE_DIR; + } + else { + return FILE_NO_SUPPORT; + } + + info->size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + return FILE_OK; +} - track.handle = kmalloc(sizeof(struct ext2_inode_handle_t)); +static enum file_status_t ext2_open_dir(struct file_handle_t* handle) { + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; - if (!track.handle) { - return 0; + struct file_info_t info; + if (ext2_stat(handle, &info) != FILE_OK) { + return FILE_ERROR; } - track.handle->ext2 = (struct ext2_t*)cntx; - track.handle->inode_index = EXT2_ROOT_INO; - track.handle->seek = 0; - track.handle->seek_block = 0; + if (info.type != FILE_TYPE_DIR) { + return FILE_NOT_DIR; + } - const struct ext2_superblock_t* superblock = track.handle->ext2->superblock; - const uint64_t block_size = 1024u << superblock->s_log_block_size; - const uint64_t size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); - void* buffer; + inode_handle->seek = 0; + inode_handle->seek_block = 0; + inode_handle->ext2_mode |= MODE_DIR; + return FILE_OK; +} - while (*path && cntrl != 2) { - path_len = path_entry_len(path); - if (get_inode(track.handle, &inode)) { - return 0; - } +static void ext2_reset_dir(struct file_handle_t* handle) { + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; - track.inode = &inode; - track.block = 0; - track.off = 0; + inode_handle->seek = 0; + inode_handle->seek_block = 0; + inode_handle->ext2_mode &= ~MODE_DIR; +} - cntrl = 1; - while (cntrl == 1) { - if (track.block * block_size > size) { - cntrl = 2; - break; - } +static enum file_status_t ext2_read_dir(struct file_handle_t* handle, struct dir_info_t* info) { + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; + struct ext2_inode_t inode; - if (track.off >= block_size) { - track.block++; - track.off = 0; - } + if (!(inode_handle->ext2_mode & MODE_DIR)) { + return FILE_BAD_FLAGS; + } - switch (get_block_index(&track, &block_index)) { - case BLOCK_OK: - buffer = read_block(block_index, track.handle->ext2); - if (!buffer) { - kfree(track.handle); - return 0; - } + if (get_inode(inode_handle, &inode)) { + return FILE_ERROR; + } - struct ext2_ll_dir_entry_t* entry = (struct ext2_ll_dir_entry_t*)((uint64_t)buffer + track.off); + const uint64_t size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + uint64_t block; + + while (1) { + info->seek_pos = ext2_get_seek(handle); + + if (info->seek_pos >= size) { + return FILE_DNE; + } - if (entry->inode != 0) { - if (entry->name_len == path_len && !kmemcmp(entry->name, path, path_len)) { - path += path_len; + switch (get_block(inode_handle->ext2, &inode, inode_handle->seek_block, &block)) { + case BLOCK_OK: + void* buffer = read_block(block, inode_handle->ext2); + + if (!buffer) { + return FILE_ERROR; + } - // handle non EOS - if (*path == '/') { - path++; - } + struct ext2_ll_dir_entry_t* entry = (struct ext2_ll_dir_entry_t*)((uint64_t)buffer + inode_handle->seek); - track.handle->inode_index = entry->inode; + inode_handle->seek += entry->rec_len; + if (inode_handle->seek >= inode_handle->ext2->block_size) { + inode_handle->seek_block++; + inode_handle->seek = 0; + } - if (*path && entry->file_type != EXT2_FT_DIR) { - cntrl = 2; // file not found - } - else { - cntrl = 0; - } + if (entry->inode != 0) { + info->inode_num = entry->inode; + info->rec_len = entry->rec_len; - kfree(buffer); + switch (entry->file_type) { + case EXT2_FT_REG_FILE: + info->type = FILE_INFO_REG; + break; + case EXT2_FT_DIR: + info->type = FILE_INFO_DIR; + break; + default: + info->type = FILE_INFO_UNK; break; - } } - track.off += entry->rec_len; + kmemcpy(info->name, entry->name, entry->name_len); + info->name[entry->name_len] = 0; + kfree(buffer); - __attribute__((fallthrough)); - case BLOCK_RETRY: - continue; - case BLOCK_LAST: - cntrl = 2; // file not found - break; - case BLOCK_ERROR: - default: - kfree(track.handle); - return 0; - } + return FILE_OK; + } + + kfree(buffer); + continue; + case BLOCK_SPARSE: + inode_handle->seek_block++; + inode_handle->seek = 0; + continue; + case BLOCK_ERROR: + return FILE_ERROR; } } +} - if (*path || cntrl == 2) { - kfree(track.handle); // file not found - return 0; - } +static struct ext2_inode_handle_t* ext2_duplicate(struct ext2_inode_handle_t* handle) { + struct ext2_inode_handle_t* dup = kmalloc(sizeof(struct ext2_inode_handle_t)); - return (struct file_handle_t*)track.handle; + *dup = *handle; + return dup; } static void ext2_close(struct file_handle_t* handle) { kfree(handle); } -static enum file_status_t ext2_stat(struct file_handle_t* handle, struct file_info_t* info) { - struct ext2_inode_t inode; - struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; +static const char* reduce_path(struct ext2_t* ext2, const char* path, struct ext2_inode_handle_t** handle_ret) { + size_t path_len; + uint8_t cntrl = 0; - if (!inode_handle || get_inode(inode_handle, &inode)) { - return FILE_ERROR; + struct ext2_inode_handle_t* handle = kmalloc(sizeof(struct ext2_inode_handle_t)); + handle->ext2 = ext2; + handle->inode_index = EXT2_ROOT_INO; + handle->ext2_mode = 0; + + struct dir_info_t info; + + while (*path) { + if (ext2_open_dir((struct file_handle_t*)handle) != FILE_OK) { + break; + } + + path_len = path_entry_len(path); + + cntrl = 1; + while (ext2_read_dir((struct file_handle_t*)handle, &info) == FILE_OK) { + if (path_len == kstrlen(info.name) && kmemcmp(path, info.name, path_len) == 0) { + cntrl = 0; + ext2_reset_dir((struct file_handle_t*)handle); + handle->inode_index = info.inode_num; + break; + } + } + + if (cntrl) { + break; // file not found + } + + path += path_len; + + // handle non EOS + if (*path == '/') { + path++; + } } - if (inode.i_mode & EXT2_S_IFREG) { - info->type = FILE_TYPE_REG; + ext2_reset_dir((struct file_handle_t*)handle); + *handle_ret = handle; + + return path; +} + +static enum file_status_t ext2_create(struct ext2_t* ext2, const char* path, uint32_t mode) { + (void)mode; + + enum file_status_t sts; + struct ext2_inode_handle_t* handle; + + path = reduce_path(ext2, path, &handle); + + if (!*path) { + return FILE_BUSY; } - else if (inode.i_mode & EXT2_S_IFDIR) { - info->type = FILE_TYPE_DIR; + + const char* name = path; + + while (*path && *path != '/') { + path++; } - else { + + if (*path == '/') { + return FILE_DNE; + } + + struct file_info_t info; + if ((sts = ext2_stat((struct file_handle_t*)handle, &info)) != FILE_OK) { + return sts; + } + + if (info.type != FILE_TYPE_DIR) { return FILE_NO_SUPPORT; } - info->size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + struct dir_info_t dir_info; + struct ext2_inode_handle_t* inode_handle = ext2_duplicate((struct ext2_inode_handle_t*)handle); - return FILE_OK; + lock_acquire(&ext2->lock); + + ext2_open_dir((struct file_handle_t*)inode_handle); + + while (ext2_read_dir((struct file_handle_t*)inode_handle, &dir_info) == FILE_OK) { + if (kstrcmp(dir_info.name, name) == 0) { + ext2_reset_dir((struct file_handle_t*)inode_handle); + + lock_release(&inode_handle->ext2->lock); + + ext2_close((struct file_handle_t*)inode_handle); + + return FILE_BUSY; + } + } + + const uint64_t group = (inode_handle->inode_index - 1) / inode_handle->ext2->superblock->s_inodes_per_group; + uint64_t inode_index = alloc_inode(inode_handle->ext2, group); + struct ext2_inode_handle_t* parent_handle = ext2_duplicate((struct ext2_inode_handle_t*)handle); + + sts = FILE_OK; + if (inode_index == 0) { + sts = FILE_ERROR; + goto cleanup; + } + + struct ext2_inode_t parent_inode; + + if (get_inode(parent_handle, &parent_inode)) { + sts = FILE_ERROR; + goto cleanup; + } + + struct ext2_inode_t inode = { + .i_mode = EXT2_S_IFREG, + .i_uid = 0, + .i_size = 0, + .i_atime = 0, + .i_ctime = 0, + .i_mtime = 0, + .i_dtime = 0, + .i_gid = 0, + .i_links_count = 1, + .i_blocks = 0, + .i_flags = 0, + .i_osd1 = 0, + .i_block = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + .i_generation = 0, + .i_file_acl = 0, + .i_dir_acl = 0, + .i_faddr = 0, + .i_osd2 = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + } + }; + + inode_handle->inode_index = inode_index; + + set_inode(inode_handle, &inode); + + uint64_t block; + ext2_open_dir((struct file_handle_t*)parent_handle); + //TODO: allocate on an already used block + while (1) { + switch (get_block(parent_handle->ext2, &parent_inode, parent_handle->seek_block, &block)) { + case BLOCK_SPARSE: + block = assign_block(parent_handle, parent_handle->seek_block, &parent_inode, 1); + + if (!block) { + sts = FILE_ERROR; + goto cleanup; + } + + struct ext2_ll_dir_entry_t* dir_entry = read_block(block, parent_handle->ext2); + *dir_entry = (struct ext2_ll_dir_entry_t){ + .inode = (uint32_t)inode_index, + .rec_len = (uint16_t)parent_handle->ext2->block_size, + .name_len = (uint8_t)kstrlen(name), + .file_type = EXT2_FT_REG_FILE + }; + + kmemcpy(&dir_entry->name, name, dir_entry->name_len); + + dir_entry = write_block(block, parent_handle->ext2, dir_entry); + kfree(dir_entry); + + parent_inode.i_size += parent_handle->ext2->block_size; + set_inode(parent_handle, &parent_inode); + goto cleanup; + case BLOCK_OK: + parent_handle->seek_block++; + continue; + case BLOCK_ERROR: + sts = FILE_ERROR; + goto cleanup; + } + } + +cleanup: + ext2_reset_dir((struct file_handle_t*)inode_handle); + + lock_release(&inode_handle->ext2->lock); + + ext2_close((struct file_handle_t*)inode_handle); + ext2_close((struct file_handle_t*)parent_handle); + + return sts; } -static uint64_t ext2_get_seek(struct file_handle_t* handle) { - struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; - const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; - const uint64_t block_size = 1024u << superblock->s_log_block_size; +static struct file_handle_t* ext2_open(struct mount_cntx_t* cntx, const char* path, uint32_t flags, uint32_t mode) { + struct ext2_t* ext2 = (struct ext2_t*)cntx; + struct ext2_inode_handle_t* handle; - return inode_handle->seek_block * block_size + inode_handle->seek; + path = reduce_path(ext2, path, &handle); + + if (*path) { + // file not found + kfree(handle); + + if (flags & FILE_FLAGS_CREATE) { + enum file_status_t create_sts = ext2_create(ext2, path, mode); + if (create_sts == FILE_OK) { + return ext2_open(cntx, path, flags & FILE_FLAGS_CREATE, mode); + } + } + return 0; + } + + return (struct file_handle_t*)handle; } static size_t ext2_read(struct file_handle_t* handle, void* buffer, size_t count) { struct ext2_inode_t inode; struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; - struct ext2_block_track_t track; uint8_t* block_buffer = 0; + uint64_t block; if (!inode_handle || get_inode(inode_handle, &inode)) { return 0; } const uint64_t size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); - const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; - const uint64_t block_size = 1024u << superblock->s_log_block_size; + const uint64_t block_size = inode_handle->ext2->block_size; size_t read = 0; - uint64_t index; uint64_t write_seek = 0; - size_t write_len; + size_t read_len; uint64_t full_seek = ext2_get_seek(handle); - if (full_seek > size) { - return 0; - } while (count) { - track.block = inode_handle->seek_block; - track.inode = &inode; - track.handle = inode_handle; - - if (track.block * block_size > size) { + if (full_seek >= size) { break; } - write_len = block_size; // default full block + read_len = block_size; // default full block if (count < block_size) { // read partial, only requested - write_len = count; + read_len = count; } - if (write_len + inode_handle->seek > block_size) { + if (read_len + inode_handle->seek > block_size) { // read partial, not past block - write_len = block_size - inode_handle->seek; + read_len = block_size - inode_handle->seek; } - switch (get_block_index(&track, &index)) { + switch (get_block(inode_handle->ext2, &inode, inode_handle->seek_block, &block)) { case BLOCK_OK: - block_buffer = read_block(index, inode_handle->ext2); + block_buffer = read_block(block, inode_handle->ext2); if (!block_buffer) { return read; } - if (write_len + full_seek > size) { + if (read_len + full_seek >= size) { // full block read, but only copy up to limit of the file - write_len = size - full_seek; + read_len = size - full_seek; count = 0; } - kmemcpy((uint8_t*)buffer + write_seek, block_buffer + inode_handle->seek, write_len); + kmemcpy((uint8_t*)buffer + write_seek, block_buffer + inode_handle->seek, read_len); kfree(block_buffer); break; - case BLOCK_RETRY: - kmemset((uint8_t*)buffer + write_seek, 0, write_len); + case BLOCK_SPARSE: + kmemset((uint8_t*)buffer + write_seek, 0, read_len); break; - default: + case BLOCK_ERROR: return read; } - write_seek += write_len; - full_seek += write_len; - inode_handle->seek += write_len; - count -= write_len; - read += write_len; + write_seek += read_len; + full_seek += read_len; + inode_handle->seek += read_len; + count -= read_len; + read += read_len; if (inode_handle->seek == block_size) { inode_handle->seek = 0; @@ -622,8 +1251,7 @@ static size_t ext2_read(struct file_handle_t* handle, void* buffer, size_t count static enum file_status_t ext2_seek(struct file_handle_t* handle, uint64_t seek) { struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; - const struct ext2_superblock_t* superblock = inode_handle->ext2->superblock; - const uint64_t block_size = 1024u << superblock->s_log_block_size; + const uint64_t block_size = inode_handle->ext2->block_size; inode_handle->seek_block = seek / block_size; inode_handle->seek = seek % block_size; @@ -631,11 +1259,125 @@ static enum file_status_t ext2_seek(struct file_handle_t* handle, uint64_t seek) return FILE_OK; } -static size_t ext2_write(struct file_handle_t* handle, void* buffer, size_t count) { +static size_t ext2_write(struct file_handle_t* handle, const void* buffer, size_t count) { + struct ext2_inode_t inode; + struct ext2_inode_handle_t* inode_handle = (struct ext2_inode_handle_t*)handle; + uint8_t* block_buffer = 0; + uint64_t block; + + if (!inode_handle || get_inode(inode_handle, &inode)) { + return 0; + } + + const uint64_t size = (uint64_t)inode.i_size | ((uint64_t)inode.i_dir_acl << 32); + const uint64_t block_size = inode_handle->ext2->block_size; + + size_t written = 0; + uint64_t read_seek = 0; + size_t write_len; + uint64_t full_seek = ext2_get_seek(handle); + + + while (count) { + write_len = block_size; // default full block + + if (count < block_size) { + // write partial, only requested + write_len = count; + } + + if (write_len + inode_handle->seek > block_size) { + // write partial, not past block + write_len = block_size - inode_handle->seek; + } + + switch (get_block(inode_handle->ext2, &inode, inode_handle->seek_block, &block)) { + case BLOCK_SPARSE: + block = assign_block(inode_handle, inode_handle->seek_block, &inode, 0); + + if (!block) { + goto update_inode; + } + + __attribute__((fallthrough)); + case BLOCK_OK: + block_buffer = read_block(block, inode_handle->ext2); + + if (!block_buffer) { + goto update_inode; + } + + kmemcpy(block_buffer + inode_handle->seek, (const uint8_t*)buffer + read_seek, write_len); + + block_buffer = write_block(block, inode_handle->ext2, block_buffer); + + if (!block_buffer) { + goto update_inode; + } + + kfree(block_buffer); + break; + case BLOCK_ERROR: + goto update_inode; + } + + read_seek += write_len; + full_seek += write_len; + inode_handle->seek += write_len; + count -= write_len; + written += write_len; + + if (inode_handle->seek == block_size) { + inode_handle->seek = 0; + inode_handle->seek_block++; + } + } + +update_inode: + if (full_seek > size) { + inode.i_size = (uint32_t)full_seek; + inode.i_dir_acl = (uint32_t)(full_seek >> 32); + + set_inode(inode_handle, &inode); + } + + return written; +} + +static void ext2_delete_final(struct file_handle_t* handle) { (void)handle; - (void)buffer; - (void)count; - return 0; +} + +static enum file_status_t ext2_create_dir(struct file_handle_t* handle) { + (void)handle; + + return FILE_NO_SUPPORT; +} + +static enum file_status_t ext2_delete_dir(struct file_handle_t* handle) { + (void)handle; + + return FILE_NO_SUPPORT; +} + +static enum file_status_t ext2_truncate(struct file_handle_t* handle, size_t size) { + (void)handle; + (void)size; + + return FILE_NO_SUPPORT; +} + +static enum file_status_t ext2_link(struct file_handle_t* handle, struct file_handle_t* replace) { + (void)handle; + (void)replace; + + return FILE_NO_SUPPORT; +} + +static enum file_status_t ext2_unlink(struct file_handle_t* handle) { + (void)handle; + + return FILE_NO_SUPPORT; } uint8_t ext2_attempt_init(struct disk_t* disk, uint64_t start_lba, uint64_t end_lba) { @@ -678,6 +1420,8 @@ uint8_t ext2_attempt_init(struct disk_t* disk, uint64_t start_lba, uint64_t end_ ext2->superblock = superblock; ext2->bgdt = bgdt; ext2->disk = disk; + ext2->block_size = 1024u << superblock->s_log_block_size; + lock_init(&ext2->lock); logging_log_debug("ext2 blocks: 0x%x x 0x%x (0x%lX)", 1024u << superblock->s_log_block_size, superblock->s_blocks_count, @@ -693,44 +1437,21 @@ uint8_t ext2_attempt_init(struct disk_t* disk, uint64_t start_lba, uint64_t end_ ext2_read, ext2_get_seek, ext2_seek, - ext2_write + ext2_write, + ext2_delete_final, + ext2_open_dir, + ext2_read_dir, + ext2_create_dir, + ext2_delete_dir, + ext2_truncate, + ext2_link, + ext2_unlink ) != FILE_OK) { logging_log_error("Failed to mount rootfs"); panic(PANIC_STATE); } - struct file_info_t stat_buf; - enum file_status_t sts; - - struct fs_handle_t* root_handle = fs_open("/"); - if (!root_handle) { - logging_log_error("Failed to open root directory"); - } - else { - if ((sts = fs_stat(root_handle, &stat_buf)) != FILE_OK) { - logging_log_error("Failed to stat root directory %u", (uint32_t)sts); - } - else { - logging_log_debug("Root directory %u (%u)", stat_buf.size, stat_buf.type); - } - - fs_close(root_handle); - } - - - struct fs_handle_t* shell = fs_open("/bin/shell"); - if (!shell) { - logging_log_error("Failed to open shell file"); - } - else { - struct pcb_t* shell_pcb = elf_load(shell, process_assign_pid(), "/bin/shell ModulOS", "USER=root PWD=/"); - if (!shell_pcb) { - logging_log_error("Failed to load shell file"); - } - fs_close(shell); - - scheduler_schedule(shell_pcb); - } + scheduler_schedule(process_from_func(prepare_userland, 0)); } //TODO: mount other volumes diff --git a/drivers/hpet/hpet_init.c b/drivers/hpet/hpet_init.c index d28bf02..e813c6f 100644 --- a/drivers/hpet/hpet_init.c +++ b/drivers/hpet/hpet_init.c @@ -54,13 +54,13 @@ static volatile struct hpet_group_t { struct hpet_timer_t timers[32]; } __attribute__((packed))* hpet_reg_bases[8]; -static uint64_t hpet_get_counter(void* meta) { +static uint64_t hpet_get_counter(volatile void* meta) { volatile struct hpet_group_t* hpet = meta; const uint64_t ret = hpet->counter; return ret; } -static void hpet_set_counter(void* meta, uint64_t counter) { +static void hpet_set_counter(volatile void* meta, uint64_t counter) { volatile struct hpet_group_t* hpet = meta; hpet->gen_conf &= ~(uint64_t)HPET_GROUP_ENA; hpet->counter = counter; @@ -104,7 +104,7 @@ void hpet_init(void) { clock = kmalloc(sizeof(struct clock_src_t)); clock->counter = hpet_get_counter; clock->reset = hpet_set_counter; - clock->meta = (void*)hpet_reg_bases[i]; + clock->meta = hpet_reg_bases[i]; clock->period_fs = (hpet_reg_bases[i]->cap & HPET_PER) >> HPET_PER_SHF; clock_src_register(clock); diff --git a/drivers/include/pcie/pcie.h b/drivers/include/pcie/pcie.h index 6ee320f..cc0f0bd 100644 --- a/drivers/include/pcie/pcie.h +++ b/drivers/include/pcie/pcie.h @@ -38,6 +38,8 @@ #define PCI_BAR_BA_MAKS 0xFFFFE000 +extern uint64_t**** ecam; + extern uint32_t pcie_read(uint16_t segment, uint8_t bus, uint8_t dev, uint8_t fun, uint16_t off); extern void pcie_write(uint16_t segment, uint8_t bus, uint8_t dev, uint8_t fun, uint16_t off, uint32_t val); diff --git a/drivers/pcie/pcie_init.c b/drivers/pcie/pcie_init.c index 738c20b..8c9cb2c 100644 --- a/drivers/pcie/pcie_init.c +++ b/drivers/pcie/pcie_init.c @@ -46,7 +46,7 @@ #define PCI_BRIDGE_SEC_NUM(seg, bus, dev, func) (0xFF & (pcie_read(seg, bus, dev, func, 0x18) >> 8)) -#define DISABLE_INT(seg, bus, dev, func) pcie_write(seg, bus, dev, func, 0x4, pcie_read(seg, bus, dev, 0, 0x4) | 0x400); +#define DISABLE_INT(seg, bus, dev, func) pcie_write(seg, bus, dev, func, 0x4, pcie_read(seg, bus, dev, 0, 0x4) | 0x400) extern uint64_t**** ecam; diff --git a/drivers/serial/interrupts.c b/drivers/serial/interrupts.c index 03d5dcb..fae9540 100644 --- a/drivers/serial/interrupts.c +++ b/drivers/serial/interrupts.c @@ -42,9 +42,6 @@ #define DR_MASK 1 -struct signal_wait_t* com1_signal; -struct signal_wait_t* com2_signal; - void serial_init_interrupts(void) { uint8_t com1_v = idt_get_vector(); uint8_t com2_v = idt_get_vector(); diff --git a/drivers/serial/serial_print.c b/drivers/serial/serial_print.c index e96452a..23f390c 100644 --- a/drivers/serial/serial_print.c +++ b/drivers/serial/serial_print.c @@ -241,7 +241,7 @@ void serial_log(enum log_severity_t severity, const char* s, va_list args) { serial_printf(s, serial_com12, args); serial_print("\r\n", serial_com12); break; - default: + case SEVERITY_NON: serial_printf(s, serial_com12, args); break; } diff --git a/kernel/acpi/tables.c b/kernel/acpi/tables.c index 6689a8e..4c530f8 100644 --- a/kernel/acpi/tables.c +++ b/kernel/acpi/tables.c @@ -233,24 +233,24 @@ static uint8_t hpet_count; #define CHECK_AND_COPY(sig, tbl, store, fnd, post) \ do { \ - if (!kmemcmp((uint8_t*)gen->Signature, sig, 4)) { \ + if (!kmemcmp(gen->Signature, sig, 4)) { \ logging_log_info("Copying ACPI " tbl " @ 0x%lX", (uint64_t)gen); \ store = kmalloc(gen->Length); \ - kmemcpy((void*)store, (void*)gen, gen->Length); \ + kmemcpy(store, gen, gen->Length); \ found |= fnd; \ post; \ } \ } \ while (0) -static inline uint8_t verify_checksum(const volatile struct acpi_gen_header_t* table) { - const volatile struct acpi_gen_header_t* gen = (struct acpi_gen_header_t*)table; - return hash_byte_sum((void*)gen, gen->Length); +static inline uint8_t verify_checksum(const struct acpi_gen_header_t* table) { + const struct acpi_gen_header_t* gen = (const struct acpi_gen_header_t*)table; + return hash_byte_sum(gen, gen->Length); } -static volatile void* map_table(const volatile void* table) { +static void* map_table(const void* table) { uint64_t base = (uint64_t)table & PAGE_BASE_MASK, vaddr, len, off; - volatile struct acpi_gen_header_t* v_table; + struct acpi_gen_header_t* v_table; vaddr = mm_alloc_v(PAGE_SIZE_4K * 2); if (!vaddr) { @@ -261,7 +261,7 @@ static volatile void* map_table(const volatile void* table) { paging_map(vaddr, base, PAGE_PRESENT | PAGE_RW | PAT_MMIO_4K, PAGE_4K); paging_map(vaddr + PAGE_SIZE_4K, base + PAGE_SIZE_4K, PAGE_PRESENT | PAGE_RW | PAT_MMIO_4K, PAGE_4K); - v_table = (volatile struct acpi_gen_header_t*)(vaddr + (uint64_t)table - base); + v_table = (struct acpi_gen_header_t*)(vaddr + (uint64_t)table - base); if (!vaddr) { logging_log_error("Failed to allocate memory for ACPI table"); panic(PANIC_NO_MEM); @@ -293,8 +293,8 @@ static volatile void* map_table(const volatile void* table) { return (void*)(vaddr + (uint64_t)table - base); } -static void unmap_table(const volatile void* table) { - volatile struct acpi_gen_header_t* v_table = (volatile struct acpi_gen_header_t*)table; +static void unmap_table(const void* table) { + const struct acpi_gen_header_t* v_table = (const struct acpi_gen_header_t*)table; uint64_t base, len, off; base = (uint64_t)table & PAGE_BASE_MASK; @@ -312,7 +312,7 @@ void acpi_copy_tables(void) { hpet_count = 0; #endif /* HPET */ - const volatile struct acpi_gen_header_t* gen; + const struct acpi_gen_header_t* gen; uint8_t found; if (kmemcmp(boot_context.rsdp.Signature, "RSD PTR ", 8)) { @@ -329,7 +329,7 @@ void acpi_copy_tables(void) { case RSDPV1: fallback: found = 0; - volatile struct acpi_rsdt_t* rsdt = (volatile struct acpi_rsdt_t*)(uint64_t)boot_context.rsdp.RsdtAddress; + struct acpi_rsdt_t* rsdt = (struct acpi_rsdt_t*)(uint64_t)boot_context.rsdp.RsdtAddress; rsdt = map_table(rsdt); if (!rsdt) { logging_log_error("Bad RSDT checksum"); @@ -343,9 +343,9 @@ void acpi_copy_tables(void) { logging_log_info("Parsing ACPI RSDT entries @ 0x%lX", (uint64_t)&rsdt->Entry[0]); - for (const volatile uint32_t* entry = &rsdt->Entry[0]; + for (const uint32_t* entry = &rsdt->Entry[0]; (uint64_t)entry < (uint64_t)rsdt + rsdt->Length; entry++) { - gen = (const volatile struct acpi_gen_header_t*)(uint64_t)*entry; + gen = (const struct acpi_gen_header_t*)(uint64_t)*entry; gen = map_table(gen); if (!gen) { @@ -389,7 +389,7 @@ void acpi_copy_tables(void) { } found = 0; - volatile struct acpi_xsdt_t* xsdt = (volatile struct acpi_xsdt_t*)boot_context.rsdp.XsdtAddress; + struct acpi_xsdt_t* xsdt = (struct acpi_xsdt_t*)boot_context.rsdp.XsdtAddress; xsdt = map_table(xsdt); if (!xsdt) { @@ -404,9 +404,9 @@ void acpi_copy_tables(void) { logging_log_info("Parsing ACPI XSDT entries @ 0x%lX", (uint64_t)&xsdt->Entry[0]); - for (const volatile uint64_t* entry = &xsdt->Entry[0]; + for (const uint64_t* entry = &xsdt->Entry[0]; (uint64_t)entry < (uint64_t)xsdt + xsdt->Length; entry++) { - gen = (const volatile struct acpi_gen_header_t*)*entry; + gen = (const struct acpi_gen_header_t*)*entry; gen = map_table(gen); if (!gen) { diff --git a/kernel/apic/apic_init.c b/kernel/apic/apic_init.c index 85817b9..1f72f2a 100644 --- a/kernel/apic/apic_init.c +++ b/kernel/apic/apic_init.c @@ -99,8 +99,8 @@ extern uint8_t kernel_pml4; uint64_t* init_stacks_paddr; uint64_t* init_stacks_vaddr; -volatile struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; -volatile struct gdt_ptr_64_t** ap_gdt_ptr_64; +struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; +struct gdt_ptr_64_t** ap_gdt_ptr_64; uint8_t* ap_init_locks; void apic_init(void) { diff --git a/kernel/apic/ipi.c b/kernel/apic/ipi.c index 04b69b5..26cebe7 100644 --- a/kernel/apic/ipi.c +++ b/kernel/apic/ipi.c @@ -40,7 +40,7 @@ #define ICR_PID_SHFT 24 static uint8_t tlb_shootdown_vector; -uint8_t shootdown_enable = 0; +static uint8_t shootdown_enable = 0; struct shootdown_node_t { struct shootdown_node_t* next; diff --git a/kernel/core/alloc.c b/kernel/core/alloc.c index 78110bf..35e9d16 100644 --- a/kernel/core/alloc.c +++ b/kernel/core/alloc.c @@ -206,7 +206,7 @@ void* kmalloc(size_t size) { lock_acquire(&alloc_lock); i = i->next; lock_release(&alloc_lock); - }; + } // out of heap space, new arena arena_base = mm_alloc_p(ARENA_SIZE); diff --git a/kernel/core/elf.c b/kernel/core/elf.c index ba01228..2df7fbb 100644 --- a/kernel/core/elf.c +++ b/kernel/core/elf.c @@ -35,6 +35,7 @@ #include #include #include +#include #define EI_MAG0 0 #define EI_CLASS 4 @@ -109,7 +110,10 @@ #define AT_EXECFN 31 #define AT_SYSINFO_EHDR 33 -enum { +#define FD_INIT_SIZE 4 +#define FD_GROWTH 8 + +enum at_index_t { AT_INDEX_PHDR, AT_INDEX_PHENT, AT_INDEX_PHNUM, @@ -123,7 +127,7 @@ enum { AT_INDEX_CLKTCK, AT_INDEX_NULL -} at_index_t; +}; typedef struct { int a_type; @@ -537,11 +541,13 @@ struct pcb_t* elf_load(struct fs_handle_t* file, uint64_t pid, const char* invok } } - kmemset(pcb->fd_table, 0, sizeof(struct fs_handle_t*) * MAX_FD); - - pcb->fd_table[0] = fs_open("/dev/ttyS0"); - pcb->fd_table[1] = fs_open("/dev/ttyS0"); - pcb->fd_table[2] = fs_open("/dev/ttyS0"); + pcb->fd_table = array_list_alloc(FD_INIT_SIZE, FD_GROWTH, 0); + pcb->wd = fs_open("/", FILE_FLAGS_READ | FILE_FLAGS_WRITE); +#ifdef SERIAL + array_list_push(pcb->fd_table, fs_open("/dev/ttyS0", FILE_FLAGS_READ)); + array_list_push(pcb->fd_table, fs_open("/dev/ttyS0", FILE_FLAGS_WRITE)); + array_list_push(pcb->fd_table, fs_open("/dev/ttyS0", FILE_FLAGS_WRITE)); +#endif /* SERIAL */ restore_cr3: diff --git a/kernel/core/exception_dispatch.c b/kernel/core/exception_dispatch.c index c7bde50..2fb0bbe 100644 --- a/kernel/core/exception_dispatch.c +++ b/kernel/core/exception_dispatch.c @@ -101,7 +101,6 @@ static inline const char* get_exception_name(uint64_t vector) { } void exception_dispatch(struct exception_context_t* context) { - //TODO: remove reduntant rsp push logging_log_error("Unrecoverable exception 0x%lX %s (0x%lX) @ 0x%lX", context->vector, get_exception_name(context->vector), context->code, context->rip); diff --git a/kernel/core/fs.c b/kernel/core/fs.c index 975e0eb..071d5d0 100644 --- a/kernel/core/fs.c +++ b/kernel/core/fs.c @@ -22,18 +22,37 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include -//TODO: implement per file blocking - -static uint8_t fs_lock; +#define OPEN_TABLE_BUCKETS 100 + +/* + * Locking conventions: + * + * The vfs layer guarantees non concurrent access to the same file, up to absolute paths. + * The vfs layer does not guarnatee non concurrent access to directory creation, file creation, + * multiple references to the same file via hard links, or atomic operations for multiple step + * calls (e.g. directory listing). + * + * In other words, the vfs is only responsible for ensuring that no two access are made to the + * inode at the same time. It is the actual fs driver's responsibility to ensure consistent access + * to filesystem metadata such as inode tables and journals + * + * Internally, the vfs must guarantee locked access to the open_table via the fs_lock. The vfs must + * also guarantee locked access to the vfs tree. + */ + +static struct semaphore_t* fs_sem; struct vfs_mount_t { struct mount_cntx_t* cntx; @@ -45,11 +64,30 @@ struct vfs_mount_t { fs_get_seek_t get_seek; fs_seek_t seek; fs_write_t write; + fs_delete_final_t delete_final; + fs_open_dir_t open_dir; + fs_read_dir_t read_dir; + fs_create_dir_t create_dir; + fs_delete_dir_t delete_dir; + fs_is_interactive_t is_interactive; + fs_truncate_t truncate; + fs_link_t link; + fs_unlink_t unlink; +}; + +struct vfs_open_file_t { + char* path; + uint64_t refs; + uint64_t key; + uint8_t lock; + uint8_t pending_delete; }; struct fs_handle_t { struct vfs_mount_t* mount; struct file_handle_t* handle; + struct vfs_open_file_t* shared; + uint32_t flags; }; struct vfs_tree_node_t { @@ -64,6 +102,8 @@ struct vfs_tree_node_t { static struct vfs_tree_node_t vfs_root; static struct vfs_tree_node_t dev_root; +static struct hash_table_t* open_table; + static struct vfs_mount_t dev_mount = { .cntx = 0, .open = devfs_open, @@ -72,9 +112,21 @@ static struct vfs_mount_t dev_mount = { .read = devfs_read, .get_seek = devfs_get_seek, .seek = devfs_seek, - .write = devfs_write + .write = devfs_write, + .delete_final = devfs_delete_final, + .open_dir = devfs_open_dir, + .read_dir = devfs_read_dir, + .is_interactive = devfs_is_interactive, + .truncate = devfs_truncate, + .link = devfs_link, + .unlink = devfs_unlink }; +static uint8_t fs_not_interactive(struct file_handle_t* handle) { + (void)handle; + return 0; +} + static inline char* path_next(char* path, size_t* len) { *len = 0; @@ -90,8 +142,58 @@ static inline char* path_next(char* path, size_t* len) { return path; } +static struct vfs_open_file_t* lookup_register(char* path) { + size_t path_len = kstrlen(path); + uint64_t key = fnv64_1a(path, path_len); + struct vfs_open_file_t* file; + + while (hash_table_get(open_table, key, (void**)&file)) { + if (kstrcmp(path, file->path + 1) == 0) { + break; + } + + key++; + } + + if (!file) { + file = kmalloc(sizeof(struct vfs_open_file_t)); + + file->path = kmalloc(path_len + 2); + file->path[0] = '/'; + kstrcpy(file->path + 1, path); + + file->refs = 0; + file->key = key; + file->pending_delete = 0; + lock_init(&file->lock); + + hash_table_insert(open_table, key, file); + } + + file->refs++; + + return file; +} + +static uint8_t lookup_close(struct vfs_open_file_t* file) { + file->refs--; + + if (file->refs) { + return 0; + } + + kfree(file->path); + void* ign; + hash_table_remove(open_table, file->key, &ign); + + uint8_t pending_delete = file->pending_delete; + + kfree(file); + return pending_delete; +} + void fs_init(void) { - lock_init(&fs_lock); + fs_sem = semaphore_alloc(SEMAPHORE_CAP_UNLIM); vfs_root.co = 0; vfs_root.sub = &dev_root; @@ -102,6 +204,8 @@ void fs_init(void) { dev_root.sub = 0; dev_root.name = "dev"; dev_root.mount = &dev_mount; + + open_table = hash_table_alloc(OPEN_TABLE_BUCKETS); } enum file_status_t fs_mount( @@ -113,7 +217,15 @@ enum file_status_t fs_mount( fs_read_t read, fs_get_seek_t get_seek, fs_seek_t seek, - fs_write_t write + fs_write_t write, + fs_delete_final_t delete_final, + fs_open_dir_t open_dir, + fs_read_dir_t read_dir, + fs_create_dir_t create_dir, + fs_delete_dir_t delete_dir, + fs_truncate_t truncate, + fs_link_t link, + fs_unlink_t unlink ) { if (kstrcmp(mountpoint, "") && !vfs_root.mount) { @@ -132,6 +244,16 @@ enum file_status_t fs_mount( vfs_root.mount->get_seek = get_seek; vfs_root.mount->seek = seek; vfs_root.mount->write = write; + vfs_root.mount->delete_final = delete_final; + vfs_root.mount->open_dir = open_dir; + vfs_root.mount->read_dir = read_dir; + vfs_root.mount->create_dir = create_dir; + vfs_root.mount->delete_dir = delete_dir; + vfs_root.mount->truncate = truncate; + vfs_root.mount->link = link; + vfs_root.mount->unlink = unlink; + + vfs_root.mount->is_interactive = fs_not_interactive; return FILE_OK; } @@ -141,13 +263,13 @@ enum file_status_t fs_mount( return FILE_ERROR; } -struct fs_handle_t* fs_open(const char* path) { +static const char* find_mount(const char* path, struct vfs_mount_t** mount_out, void** clean_path_out, char** path_write_out) { struct vfs_tree_node_t* node = &vfs_root, * walk = 0; - size_t len = kstrlen(path); - char* clean_path = kmalloc(len + 1); - char* mount_path = (char*)""; + const char* mount_path = ""; struct vfs_mount_t* mount = 0; + size_t len = kstrlen(path); + char* clean_path = kmalloc(len + 1); uint64_t num_chars = 0; uint64_t skip = 0; @@ -199,8 +321,9 @@ struct fs_handle_t* fs_open(const char* path) { path_write++; // one to skip / } + *path_write_out = path_write; - //lock_acquire(&fs_lock); + semaphore_wait(fs_sem); do { if (node->mount) { @@ -220,46 +343,101 @@ struct fs_handle_t* fs_open(const char* path) { } while (walk && node == walk); - //lock_release(&fs_lock); + semaphore_signal(fs_sem); + + *mount_out = mount; + *clean_path_out = clean_path; + + return mount_path; +} + +struct fs_handle_t* fs_open_mode(const char* path, uint32_t flags, uint32_t mode) { + struct vfs_mount_t* mount; + void* clean_path; + char* path_write; + const char* mount_path = find_mount(path, &mount, &clean_path, &path_write); if (!mount) { kfree(clean_path); return 0; } - struct file_handle_t* handle = mount->open(mount->cntx, mount_path); + struct file_handle_t* handle = mount->open(mount->cntx, mount_path, flags, mode); + + if (!handle) { + return 0; + } + + semaphore_wait(fs_sem); + struct vfs_open_file_t* open_file = lookup_register(path_write); + semaphore_signal(fs_sem); kfree(clean_path); struct fs_handle_t* fs_handle = kmalloc(sizeof(struct fs_handle_t)); fs_handle->handle = handle; fs_handle->mount = mount; + fs_handle->shared = open_file; + fs_handle->flags = flags; return fs_handle; } +struct fs_handle_t* fs_open(const char* path, uint32_t flags) { + return fs_open_mode(path, flags, 0); +} + +struct fs_handle_t* fs_openat(const char* path, uint32_t flags, struct fs_handle_t* at, uint32_t mode) { + + if (*path == '/') { + // absolute, no at + return fs_open_mode(path, flags, mode); + } + else { + size_t prefix_len = kstrlen(at->shared->path); + size_t suffix_len = kstrlen(path); + char* full_path = kmalloc(prefix_len + suffix_len + 1); + kmemcpy(full_path, at->shared->path, prefix_len); + kmemcpy(full_path + prefix_len, path, suffix_len); + full_path[prefix_len + suffix_len] = 0; + struct fs_handle_t* ret = fs_open_mode(full_path, flags, mode); + kfree(full_path); + return ret; + } +} + void fs_close(struct fs_handle_t* handle) { - //lock_acquire(&fs_lock); + semaphore_wait_full(fs_sem); + if (lookup_close(handle->shared)) { + handle->mount->delete_final(handle->handle); + } + semaphore_signal_full(fs_sem); + lock_acquire(&handle->shared->lock); handle->mount->close(handle->handle); - //lock_release(&fs_lock); + lock_release(&handle->shared->lock); kfree(handle); } enum file_status_t fs_stat(struct fs_handle_t* handle, struct file_info_t* info) { - //lock_acquire(&fs_lock); + lock_acquire(&handle->shared->lock); enum file_status_t ret = handle->mount->stat(handle->handle, info); - //lock_release(&fs_lock); + lock_release(&handle->shared->lock); + return ret; } size_t fs_read(struct fs_handle_t* handle, void* buffer, size_t count) { size_t ret; - //lock_acquire(&fs_lock); + if (!(handle->flags & FILE_FLAGS_READ)) { + return 0; + } + + lock_acquire(&handle->shared->lock); ret = handle->mount->read(handle->handle, buffer, count); - //lock_release(&fs_lock); + lock_release(&handle->shared->lock); return ret; } @@ -267,9 +445,9 @@ size_t fs_read(struct fs_handle_t* handle, void* buffer, size_t count) { uint64_t fs_get_seek(struct fs_handle_t* handle) { uint64_t seek; - //lock_acquire(&fs_lock); + lock_acquire(&handle->shared->lock); seek = handle->mount->get_seek(handle->handle); - //lock_release(&fs_lock); + lock_release(&handle->shared->lock); return seek; } @@ -277,19 +455,111 @@ uint64_t fs_get_seek(struct fs_handle_t* handle) { enum file_status_t fs_seek(struct fs_handle_t* handle, uint64_t seek) { enum file_status_t sts; - //lock_acquire(&fs_lock); + lock_acquire(&handle->shared->lock); sts = handle->mount->seek(handle->handle, seek); - //lock_release(&fs_lock); + lock_release(&handle->shared->lock); return sts; } -size_t fs_write(struct fs_handle_t* handle, void* buffer, size_t count) { +size_t fs_write(struct fs_handle_t* handle, const void* buffer, size_t count) { size_t ret; - //lock_acquire(&fs_lock); + if (!(handle->flags & FILE_FLAGS_WRITE)) { + return 0; + } + + lock_acquire(&handle->shared->lock); ret = handle->mount->write(handle->handle, buffer, count); - //lock_release(&fs_lock); + lock_release(&handle->shared->lock); return ret; } + +struct fs_handle_t* fs_open_dir(struct fs_handle_t* handle) { + enum file_status_t sts; + + lock_acquire(&handle->shared->lock); + sts = handle->mount->open_dir(handle->handle); + lock_release(&handle->shared->lock); + + if (sts != FILE_OK) { + return 0; + } + + return handle; +} + +enum file_status_t fs_create_dir(struct fs_handle_t* handle) { + enum file_status_t sts; + + lock_acquire(&handle->shared->lock); + sts = handle->mount->create_dir(handle->handle); + lock_release(&handle->shared->lock); + + return sts; +} + +enum file_status_t fs_delete_dir(struct fs_handle_t* handle) { + enum file_status_t sts; + + lock_acquire(&handle->shared->lock); + sts = handle->mount->delete_dir(handle->handle); + lock_release(&handle->shared->lock); + + return sts; +} + +enum file_status_t fs_read_dir(struct fs_handle_t* handle, struct dir_info_t* info) { + enum file_status_t sts; + + lock_acquire(&handle->shared->lock); + sts = handle->mount->read_dir(handle->handle, info); + lock_release(&handle->shared->lock); + + return sts; +} + +enum file_status_t fs_truncate(struct fs_handle_t* handle, size_t size) { + enum file_status_t sts; + + lock_acquire(&handle->shared->lock); + sts = handle->mount->truncate(handle->handle, size); + lock_release(&handle->shared->lock); + + return sts; + +} + +enum file_status_t fs_link(struct fs_handle_t* handle, struct fs_handle_t* replace) { + enum file_status_t sts; + + if (handle->mount != replace->mount) { + return FILE_BAD_FLAGS; // cannot create hardlink between filesystems + } + + lock_acquire(&handle->shared->lock); + sts = handle->mount->link(handle->handle, replace->handle); + lock_release(&handle->shared->lock); + + return sts; +} + +enum file_status_t fs_unlink(struct fs_handle_t* handle) { + enum file_status_t sts; + + lock_acquire(&handle->shared->lock); + sts = handle->mount->unlink(handle->handle); + lock_release(&handle->shared->lock); + + return sts; + +} + +void fs_path(struct fs_handle_t* handle, size_t max_len, char* buf) { + kstrncpy(buf, handle->shared->path, max_len); +} + +uint8_t fs_is_interactive(struct fs_handle_t* handle) { + return handle->mount->is_interactive(handle->handle); +} diff --git a/kernel/core/idt.c b/kernel/core/idt.c index 8785133..f85ead8 100644 --- a/kernel/core/idt.c +++ b/kernel/core/idt.c @@ -40,12 +40,12 @@ struct idt_ptr_t { uint64_t base; } __attribute__((packed)); -static volatile struct idt_entry_t idt[IDT_MAX_ENTRY] __attribute__((aligned(64))); -static volatile struct idt_ptr_t idt_ptr; +static struct idt_entry_t idt[IDT_MAX_ENTRY] __attribute__((aligned(64))); +static struct idt_ptr_t idt_ptr; static uint8_t next_vector; void idt_init(void) { - kmemset((void*)&idt[0], 0, sizeof(idt)); + kmemset(&idt[0], 0, sizeof(idt)); idt_install(0x00, (uint64_t)isr_00, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); idt_install(0x01, (uint64_t)isr_01, GDT_CODE_SEL, 0, IDT_GATE_TRP, 0); diff --git a/kernel/core/kentry.c b/kernel/core/kentry.c index 2a567bf..92b6065 100644 --- a/kernel/core/kentry.c +++ b/kernel/core/kentry.c @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include #include @@ -64,7 +67,7 @@ struct boot_context_t boot_context; extern uint8_t ap_bootstrap_start; extern uint64_t* init_stacks; extern uint8_t ap_bootstrap_end; -extern volatile struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; +extern struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; extern uint8_t* ap_init_locks; extern uint64_t init_stack_vaddr; @@ -73,6 +76,9 @@ extern uint64_t init_stack_paddr; extern uint64_t* init_stacks_paddr; extern uint64_t* init_stacks_vaddr; +static uint8_t prepare_userland_lock; +static uint8_t init_done; + static inline void write_syscall_msr(void) { msr_write(MSR_STAR, ((GDT_USER_CS - 0x10) << 48) | (GDT_KERNEL_CS << 32)); msr_write(MSR_LSTAR, (uint64_t)syscall_entry); @@ -95,7 +101,8 @@ void kentry(void) { paging_ensure_mapped(); scheduler_init(); - mm_transaction_init(); + init_done = 0; + lock_init(&prepare_userland_lock); write_syscall_msr(); cpu_set_cr4(CR4_FSGSBASE); @@ -128,6 +135,7 @@ void kentry(void) { logging_log_debug("Early PCIE init"); disk_init(); fs_init(); + mm_transaction_init(); tty_init(); pcie_init(); pcie_enumerate(); @@ -182,3 +190,38 @@ void kapentry(uint64_t arb_id) { process_kill_current(); } + +void prepare_userland(void* cntx) { + (void)cntx; + + struct fs_handle_t* f = fs_open("/test.txt", FILE_FLAGS_READ | FILE_FLAGS_WRITE | FILE_FLAGS_CREATE); + if (f == 0) { + logging_log_error("Failed to create /test.txt"); + } + fs_write(f, "Hi\n", 3); + fs_close(f); + + lock_acquire(&prepare_userland_lock); + if (init_done) { + logging_log_error("Multiple calls to prepare userland"); + panic(PANIC_STATE); + } + + init_done = 1; + lock_release(&prepare_userland_lock); + + struct fs_handle_t* shell = fs_open("/bin/shell", FILE_FLAGS_READ); + if (!shell) { + logging_log_error("Failed to open shell file"); + } + + else { + struct pcb_t* shell_pcb = elf_load(shell, process_assign_pid(), "/bin/shell ModulOS", "USER=root PWD=/"); + if (!shell_pcb) { + logging_log_error("Failed to load shell file"); + } + fs_close(shell); + + scheduler_schedule(shell_pcb); + } +} diff --git a/kernel/core/logging.c b/kernel/core/logging.c index 6ee875d..a109b6d 100644 --- a/kernel/core/logging.c +++ b/kernel/core/logging.c @@ -26,7 +26,7 @@ static void (*loggers[MAX_LOGGER_OUT])(enum log_severity_t, const char* format, static uint64_t last_logger; static void log(enum log_severity_t severity, const char* format, va_list args) { - for (uint64_t i = 0 ; i < last_logger; i++) { + for (uint64_t i = 0 ; i < last_logger && i < MAX_LOGGER_OUT; i++) { loggers[i](severity, format, args); } } @@ -36,7 +36,7 @@ void logging_init(void) { } void logging_register(void (*logger)(enum log_severity_t, const char* format, va_list args)) { - loggers[last_logger++] = logger; + loggers[(last_logger++) % MAX_LOGGER_OUT] = logger; } void _logging_log_debug(const char* format, ...) { diff --git a/kernel/core/mm.c b/kernel/core/mm.c index 6f7a6ba..35d7cd8 100644 --- a/kernel/core/mm.c +++ b/kernel/core/mm.c @@ -30,8 +30,6 @@ #include #include -#include - #include #define PAGE_4K_MASK 0xFFFFFFFFFFFFF000 @@ -52,7 +50,7 @@ struct mm_tree_node_t { struct disarm_list_t { struct disarm_list_t* next; uint8_t id; - volatile uint8_t state; + uint8_t state; }; static struct mm_tree_node_t* p_tree; @@ -69,9 +67,9 @@ static uint8_t n_lock; static struct mm_tree_node_t node_pool[MAX_INIT_NODES]; static struct mm_tree_node_t* free_nodes; -static struct free_transaction_list_t* volatile pending_free; +static struct free_transaction_list_t* pending_free; static struct free_transaction_list_t* transaction_list; -uint8_t pending_free_lock; +static uint8_t pending_free_lock; static struct disarm_list_t* disarm_list; static struct signal_wait_t* free_pending_wait; @@ -431,12 +429,12 @@ void mm_free_p(uint64_t base, size_t size) { } void mm_free_v(uint64_t base, size_t size) { - volatile struct free_transaction_list_t* pending = kmalloc(sizeof(struct free_transaction_list_t)); + struct free_transaction_list_t* pending = kmalloc(sizeof(struct free_transaction_list_t)); pending->base = base; pending->size = size; lock_acquire(&pending_free_lock); pending->next = pending_free; - pending_free = (struct free_transaction_list_t* volatile)pending; + pending_free = (struct free_transaction_list_t*)pending; lock_release(&pending_free_lock); if (free_pending_wait) { @@ -444,7 +442,7 @@ void mm_free_v(uint64_t base, size_t size) { } } -static void free_all_pending(void* _ign) { +__attribute((noreturn)) static void free_all_pending(void* _ign) { (void)_ign; struct free_transaction_list_t* next; diff --git a/kernel/core/paging.c b/kernel/core/paging.c index 3320f7c..4113246 100644 --- a/kernel/core/paging.c +++ b/kernel/core/paging.c @@ -103,9 +103,12 @@ static uint64_t* increase_granularity(uint64_t vaddr, uint64_t* access, enum pag case PAGE_1G: access = &(access)[GET_PD_INDEX(vaddr)]; break; - default: + case PAGE_2M: access = &(access)[GET_PT_INDEX(vaddr)]; break; + case PAGE_4K: + logging_log_error("Invalid call to increase granularity"); + panic(PANIC_STATE); } } diff --git a/kernel/core/process.c b/kernel/core/process.c index 0717354..bd40de1 100644 --- a/kernel/core/process.c +++ b/kernel/core/process.c @@ -32,6 +32,7 @@ #include #include +#include #include @@ -70,7 +71,8 @@ void process_init_ap(uint64_t init_rsp_vaddr, uint64_t init_rsp_paddr) { pcb->init_k_rsp_vaddr = init_rsp_vaddr; pcb->init_k_rsp_paddr = init_rsp_paddr; pcb->sched_cntr = SCHED_SKIP; - kmemset(pcb->fd_table, 0, sizeof(struct fs_handle_t*) * MAX_FD); + pcb->fd_table = array_list_alloc(1, 1, 0); + pcb->wd = 0; proc_data_get()->current_process = pcb; proc_data_get()->current_process->pid = process_assign_pid(); proc_data_get()->current_process->cr3 = 0; @@ -126,11 +128,8 @@ struct pcb_t* process_from_vaddr(uint64_t vaddr) { pcb->pid = process_assign_pid(); - kmemset(pcb->fd_table, 0, sizeof(struct fs_handle_t*) * MAX_FD); - - pcb->fd_table[0] = fs_open("/dev/ttyS0"); - pcb->fd_table[1] = fs_open("/dev/ttyS0"); - pcb->fd_table[2] = fs_open("/dev/ttyS0"); + pcb->fd_table = array_list_alloc(1, 1, 0); + pcb->wd = fs_open("/", FILE_FLAGS_READ | FILE_FLAGS_WRITE); return pcb; } @@ -157,6 +156,10 @@ void process_kill_current(void) { cpu_wait_loop(); } +static void close_fd(void* handle) { + fs_close(handle); +} + void process_discard(struct pcb_t* pcb) { _Static_assert(INIT_STACK_SIZE == 4 * PAGE_SIZE_4K, "stack size must be page size multiple of four"); paging_unmap(pcb->init_k_rsp_vaddr + 1 * PAGE_SIZE_4K, PAGE_4K); @@ -172,13 +175,12 @@ void process_discard(struct pcb_t* pcb) { paging_free_userspace((uint64_t*)pcb->cr3); } - for (uint64_t i = 0; i < MAX_FD; i++) { - if (pcb->fd_table[i]) { - fs_close(pcb->fd_table[i]); - } + array_list_clear(pcb->fd_table, close_fd); + if (pcb->wd) { + fs_close(pcb->wd); } - logging_log_debug("Killed %ld", pcb->pid); + logging_log_debug("Killed %lu (%ld)", pcb->pid, pcb->exit_code); kfree(pcb); } diff --git a/kernel/core/scheduler.c b/kernel/core/scheduler.c index 3aa044e..28c5af0 100644 --- a/kernel/core/scheduler.c +++ b/kernel/core/scheduler.c @@ -30,8 +30,8 @@ #include static uint8_t lock_sched; -static volatile struct pcb_t* active_queue; -static volatile struct pcb_t* active_queue_tail; +static struct pcb_t* active_queue; +static struct pcb_t* active_queue_tail; static struct pcb_t* sleep_queue; @@ -71,7 +71,6 @@ void scheduler_run(void) { cpu_cli(); apic_write_reg(APIC_REG_EOI, APIC_EOI); process_resume(current_pcb); - break; case SCHED_KILL: process_discard(current_pcb); break; @@ -93,9 +92,6 @@ void scheduler_run(void) { case SCHED_CALLBACK: current_pcb->sleep_state.callback(current_pcb); break; - default: - logging_log_error("Invalid scheduler code %u", current_pcb->sched_cntr); - __attribute__((fallthrough)); case SCHED_READY: case SCHED_SIGNAL_READY: scheduler_schedule(current_pcb); diff --git a/kernel/core/semaphore.c b/kernel/core/semaphore.c new file mode 100644 index 0000000..1b069fc --- /dev/null +++ b/kernel/core/semaphore.c @@ -0,0 +1,75 @@ +/* semaphore.c - semaphore implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include +#include + +#include +#include +#include +#include + +struct semaphore_t { + size_t rem; + size_t cap; + uint8_t lock; +}; + +struct semaphore_t* semaphore_alloc(size_t cap) { + struct semaphore_t* sem = kmalloc(sizeof(struct semaphore_t)); + + sem->rem = sem->cap = cap; + lock_init(&sem->lock); + + return sem; +} + +void semaphore_free(struct semaphore_t* sem) { + kfree(sem); +} + +void semaphore_wait(struct semaphore_t* sem) { + lock_acquire(&sem->lock); + + while (!sem->rem) { + cpu_pause(); + } + + sem->rem--; + + lock_release(&sem->lock); +} + +void semaphore_signal(struct semaphore_t* sem) { + sem->rem++; +} + +void semaphore_wait_full(struct semaphore_t* sem) { + lock_acquire(&sem->lock); + + while (sem->rem != sem->cap) { + cpu_pause(); + } + + sem->rem = 0; + + lock_release(&sem->lock); +} + +void semaphore_signal_full(struct semaphore_t* sem) { + sem->rem = sem->cap; +} diff --git a/kernel/core/signal.c b/kernel/core/signal.c index e8a91cc..6266ee9 100644 --- a/kernel/core/signal.c +++ b/kernel/core/signal.c @@ -49,13 +49,13 @@ struct signal_wait_t* signal_wait_alloc(void) { } void signal_wait(struct signal_wait_t* wait) { - volatile struct pcb_t* current = proc_data_get()->current_process; + struct pcb_t* current = proc_data_get()->current_process; current->meta[0] = wait; process_set_callback(signal_wait_callback); while (current->sched_cntr != SCHED_SIGNAL_READY) { - cpu_wait_loop(); + cpu_hlt(); } current->sched_cntr = SCHED_READY; diff --git a/kernel/core/syscall.S b/kernel/core/syscall.S index d4738ff..3b80822 100644 --- a/kernel/core/syscall.S +++ b/kernel/core/syscall.S @@ -60,19 +60,33 @@ sysretq .section .rodata -.extern syscall_dispatch_exit -.extern syscall_dispatch_open -.extern syscall_disaptch_close -.extern syscall_dispatch_read -.extern syscall_dispatch_write -.extern syscall_dispatch_alloc - .global syscall_handlers syscall_handlers: .quad syscall_dispatch_exit -.quad syscall_dispatch_open +.quad syscall_dispatch_openat .quad syscall_dispatch_close .quad syscall_dispatch_read .quad syscall_dispatch_write .quad syscall_dispatch_alloc +.quad 0 +.quad 0 +.quad syscall_dispatch_open_dir +.quad syscall_dispatch_read_dir +.quad syscall_dispatch_truncate +.quad syscall_dispatch_seek +.quad syscall_dispatch_tell +.quad syscall_dispatch_create_dir +.quad syscall_dispatch_delete_dir +.quad syscall_dispatch_epoch_time +.quad syscall_dispatch_is_a_tty +.quad syscall_dispatch_gcwd +.quad syscall_dispatch_ccwd +.quad syscall_dispatch_link +.quad syscall_dispatch_unlink +.quad syscall_dispatch_stat + +.set num_entries, . - syscall_handlers +.if num_entries != SYSCALL_MAX * 8 +.error "Inconsistent number of registered system cals in call table" +.endif diff --git a/kernel/core/syscall_dispatch.c b/kernel/core/syscall_dispatch.c index 1f6481c..02472c6 100644 --- a/kernel/core/syscall_dispatch.c +++ b/kernel/core/syscall_dispatch.c @@ -27,8 +27,10 @@ #include #include #include +#include #include +#include #define ARGC_0 \ (void)arg1; \ @@ -65,46 +67,54 @@ #define ARGC_6 +#define USERLAND_AT_FDCWD -100 + DECLARE_SYSCALL(exit) { ARGC_1; - //TODO: handle exit code - (void)arg1; + struct pcb_t* pcb = proc_data_get()->current_process; + pcb->exit_code = arg1; process_kill_current(); } -DECLARE_SYSCALL(open) { - ARGC_0; +DECLARE_SYSCALL(openat) { + ARGC_4; - int fd; struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* at; - for (fd = 0; fd < MAX_FD; fd++) { - if (!pcb->fd_table[fd]) { - pcb->fd_table[fd] = fs_open((const char*)arg1); - if (!pcb->fd_table[fd]) { - return SYSCALL_STS_FAIL; - } + if ((int32_t)arg3 == USERLAND_AT_FDCWD) { + at = pcb->wd; + } + else { + at = array_list_get(pcb->fd_table, arg1); + } - return (uint64_t)fd; - } + if (!at) { + return SYSCALL_STS_FAIL; } - return SYSCALL_STS_FAIL; + struct fs_handle_t* handle = fs_openat((const char*)arg1, (uint32_t)arg2, at, (uint32_t)arg4); + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return array_list_push(pcb->fd_table, handle); } DECLARE_SYSCALL(close) { - ARGC_0; + ARGC_1; struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); - if (!pcb->fd_table[arg1]) { + if (!handle) { return SYSCALL_STS_FAIL; } - fs_close(pcb->fd_table[arg1]); - pcb->fd_table[arg1] = 0; + fs_close(handle); + array_list_remove(pcb->fd_table, arg1); return SYSCALL_STS_OK; } @@ -113,14 +123,26 @@ DECLARE_SYSCALL(read) { ARGC_3; struct pcb_t* pcb = proc_data_get()->current_process; - return (uint64_t)fs_read(pcb->fd_table[arg1], (void*)arg2, (size_t)arg3); + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return fs_read(handle, (void*)arg2, (size_t)arg3); } DECLARE_SYSCALL(write) { ARGC_3; struct pcb_t* pcb = proc_data_get()->current_process; - return (uint64_t)fs_write(pcb->fd_table[arg1], (void*)arg2, (size_t)arg3); + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return fs_write(handle, (void*)arg2, (size_t)arg3); } DECLARE_SYSCALL(alloc) { @@ -134,7 +156,7 @@ DECLARE_SYSCALL(alloc) { uint64_t paddr = mm_alloc_p(arg1); if (!paddr) { - return 0; + return SYSCALL_STS_FAIL; } uint64_t vaddr = pcb->mem_top; @@ -150,3 +172,194 @@ DECLARE_SYSCALL(alloc) { return vaddr; } + +DECLARE_SYSCALL(open_dir) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return (uint64_t)fs_open_dir(handle); +} + +DECLARE_SYSCALL(read_dir) { + ARGC_3; + + (void)arg1; + (void)arg2; + (void)arg3; + + //TODO + return SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(truncate) { + ARGC_2; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return (fs_truncate(handle, (size_t)arg2) == FILE_OK) ? SYSCALL_STS_OK : SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(seek) { + ARGC_2; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + fs_seek(handle, (uint64_t)arg2); + return fs_get_seek(handle); +} + +DECLARE_SYSCALL(tell) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return fs_get_seek(handle); +} + +DECLARE_SYSCALL(create_dir) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return (fs_create_dir(handle) == FILE_OK) ? SYSCALL_STS_OK : SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(delete_dir) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return (fs_delete_dir(handle) == FILE_OK) ? SYSCALL_STS_OK : SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(epoch_time) { + ARGC_0; + + return time_since_init_ns(); +} + +DECLARE_SYSCALL(is_a_tty) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return fs_is_interactive(handle); +} + +DECLARE_SYSCALL(gcwd) { + ARGC_2; + + struct pcb_t* pcb = proc_data_get()->current_process; + + fs_path(pcb->wd, (size_t)arg2, (char*)arg1); + + return SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(ccwd) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + pcb->wd = handle; + + return SYSCALL_STS_OK; +} + +DECLARE_SYSCALL(link) { + ARGC_2; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle1 = array_list_get(pcb->fd_table, arg1); + + if (!handle1) { + return SYSCALL_STS_FAIL; + } + + struct fs_handle_t* handle2 = array_list_get(pcb->fd_table, arg2); + + if (!handle2) { + return SYSCALL_STS_FAIL; + } + + return (fs_link(handle1, handle2) == FILE_OK) ? SYSCALL_STS_OK : SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(unlink) { + ARGC_1; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + return (fs_unlink(handle) == FILE_OK) ? SYSCALL_STS_OK : SYSCALL_STS_FAIL; +} + +DECLARE_SYSCALL(stat) { + ARGC_2; + + struct pcb_t* pcb = proc_data_get()->current_process; + struct fs_handle_t* handle = array_list_get(pcb->fd_table, arg1); + + if (!handle) { + return SYSCALL_STS_FAIL; + } + + struct userland_stat_t { + size_t st_size; + }; + + struct file_info_t info; + if (fs_stat(handle, &info) != FILE_OK) { + return SYSCALL_STS_FAIL; + } + + struct userland_stat_t* u_stat = (struct userland_stat_t*)arg2; + u_stat->st_size = info.size; + + return SYSCALL_STS_OK; +} diff --git a/kernel/core/tss.c b/kernel/core/tss.c index fdc1408..a446420 100644 --- a/kernel/core/tss.c +++ b/kernel/core/tss.c @@ -34,8 +34,8 @@ #define IST_LO_MASK 0xFFFFFFFF #define IST_HI_SHFT 32 -void tss_init(volatile struct gdt_t(* gdt)[GDT_NUM_ENTRIES]) { - volatile struct tss_t* tss = kmalloc(sizeof(struct tss_t)); +void tss_init(struct gdt_t(* gdt)[GDT_NUM_ENTRIES]) { + struct tss_t* tss = kmalloc(sizeof(struct tss_t)); kmemset((void*)tss, 0, sizeof(struct tss_t)); diff --git a/kernel/devfs/devfs.c b/kernel/devfs/devfs.c index e0c9103..9d1ad01 100644 --- a/kernel/devfs/devfs.c +++ b/kernel/devfs/devfs.c @@ -27,16 +27,18 @@ #include struct dev_handle_t { - enum { - DEV_TYPE_TTY - } type; union { struct tty_handle_t* tty; } dev_handle; + enum { + DEV_TYPE_TTY + } type; }; -struct file_handle_t* devfs_open(struct mount_cntx_t* cntx, char* path) { +struct file_handle_t* devfs_open(struct mount_cntx_t* cntx, const char* path, uint32_t flags, uint32_t mode) { (void)cntx; + (void)flags; + (void)mode; struct dev_handle_t* dev_handle; // tty devices @@ -129,7 +131,7 @@ enum file_status_t devfs_seek(struct file_handle_t* handle, uint64_t seek) { } } -size_t devfs_write(struct file_handle_t* handle, void* buffer, size_t count) { +size_t devfs_write(struct file_handle_t* handle, const void* buffer, size_t count) { struct dev_handle_t* dev_handle = (struct dev_handle_t*)handle; if (!dev_handle) { @@ -142,3 +144,64 @@ size_t devfs_write(struct file_handle_t* handle, void* buffer, size_t count) { return count; } } + +void devfs_delete_final(struct file_handle_t* handle) { + (void)handle; +} + +enum file_status_t devfs_open_dir(struct file_handle_t* handle) { + (void)handle; + return FILE_NO_SUPPORT; +} + +enum file_status_t devfs_read_dir(struct file_handle_t* handle, struct dir_info_t* info) { + (void)handle; + (void)info; + return FILE_NO_SUPPORT; +} + +enum file_status_t devfs_create_dir(struct file_handle_t* handle, const char* name) { + (void)handle; + (void)name; + + return FILE_NO_SUPPORT; +} + +enum file_status_t devfs_delete_dir(struct file_handle_t* handle) { + (void)handle; + + return FILE_NO_SUPPORT; +} + +enum file_status_t devfs_truncate(struct file_handle_t* handle, size_t size) { + (void)handle; + (void)size; + + return FILE_NO_SUPPORT; +} + +enum file_status_t devfs_link(struct file_handle_t* handle, struct file_handle_t* replace) { + (void)handle; + (void)replace; + + return FILE_NO_SUPPORT; +} + +enum file_status_t devfs_unlink(struct file_handle_t* handle) { + (void)handle; + + return FILE_NO_SUPPORT; +} + +uint8_t devfs_is_interactive(struct file_handle_t* handle) { + struct dev_handle_t* dev_handle = (struct dev_handle_t*)handle; + + if (!dev_handle) { + return FILE_ERROR; + } + + switch (dev_handle->type) { + case DEV_TYPE_TTY: + return 1; + } +} diff --git a/kernel/devfs/tty.c b/kernel/devfs/tty.c index 8ba73b4..687bf30 100644 --- a/kernel/devfs/tty.c +++ b/kernel/devfs/tty.c @@ -50,8 +50,8 @@ typedef void (*tty_write_t)(uint8_t byte); struct tty_handle_t { tty_write_t writer; uint8_t* read_buffer; - volatile uint16_t write_index; - volatile uint16_t read_index; + uint16_t write_index; + uint16_t read_index; struct signal_wait_t* signal; enum { TTY_MODE_COOKED, @@ -97,7 +97,7 @@ struct tty_handle_t* tty_com2(void) { } #endif /* SERIAL */ -struct tty_handle_t* tty_open(char* name) { +struct tty_handle_t* tty_open(const char* name) { #ifdef SERIAL if (!kstrcmp(name, "S0")) { // COM1 @@ -146,22 +146,22 @@ size_t tty_read(struct tty_handle_t* tty, void* buffer, size_t count) { return read; } -void tty_write(struct tty_handle_t* tty, void* buffer, size_t count) { +void tty_write(struct tty_handle_t* tty, const void* buffer, size_t count) { for (size_t i = 0; i < count; i++) { - uint8_t val = ((uint8_t*)buffer)[i]; + uint8_t val = ((const uint8_t*)buffer)[i]; switch (val) { case '\n': tty->writer('\r'); __attribute__((fallthrough)); default: - tty->writer(((uint8_t*)buffer)[i]); + tty->writer(((const uint8_t*)buffer)[i]); break; } } } uint8_t tty_queue_read(struct tty_handle_t* tty, uint8_t byte) { - if (FULL(tty->read_index, tty->write_index)) { + if (byte == 0 || FULL(tty->read_index, tty->write_index)) { return 0; } diff --git a/kernel/graphicsbase/framebuffer.c b/kernel/graphicsbase/framebuffer.c index 4268a15..8a5e720 100644 --- a/kernel/graphicsbase/framebuffer.c +++ b/kernel/graphicsbase/framebuffer.c @@ -25,7 +25,7 @@ static uint32_t convert_color(struct framebuffer_t* framebuffer, uint8_t r, uint switch (framebuffer->mode) { case VIDEO_RGB_XRGB8888: return ((uint32_t)r << 16) + ((uint32_t)g << 8) + (uint32_t)b; - default: + case VIDEO_NONE: return 0; } } @@ -34,8 +34,8 @@ void framebuffer_fillrect( struct framebuffer_t* framebuffer, uint32_t startx, uint32_t starty, uint32_t width, uint32_t height, uint8_t r, uint8_t g, uint8_t b) { - uint8_t* row = - (uint8_t*)(framebuffer->addr + starty * framebuffer->pitch + startx * BYTES_PER_PIXEL); + volatile uint8_t* row = + (volatile uint8_t*)(framebuffer->addr + starty * framebuffer->pitch + startx * BYTES_PER_PIXEL); volatile uint32_t* loc; uint32_t color = convert_color(framebuffer, r, g, b); diff --git a/kernel/include/acpi/tables.h b/kernel/include/acpi/tables.h index a1e4449..835458f 100644 --- a/kernel/include/acpi/tables.h +++ b/kernel/include/acpi/tables.h @@ -53,12 +53,12 @@ struct acpi_madt_ics_gen_t { uint8_t Type; uint8_t Length; uint8_t _[]; -} __attribute__((packed)); +}; struct acpi_madt_ics_mps_t { uint8_t PolarityAndTriggerMode; uint8_t Reserved; -} __attribute__((packed)); +}; struct acpi_madt_ics_local_apic_t { uint8_t Type; @@ -66,7 +66,7 @@ struct acpi_madt_ics_local_apic_t { uint8_t ACPIProcessorUID; uint8_t APICID; uint8_t Flags; -} __attribute__((packed)); +}; struct acpi_madt_ics_io_apic_t { uint8_t Type; @@ -99,7 +99,7 @@ struct acpi_madt_ics_local_apic_nmi_t { uint8_t ACPIProcessorUID; struct acpi_madt_ics_mps_t Flags; uint8_t LocalAPICLINTNum; -} __attribute__((packed)); +}; struct acpi_mcfg_conf_t { uint64_t base; diff --git a/kernel/include/apic/apic_init.h b/kernel/include/apic/apic_init.h index 2146ba7..15661bc 100644 --- a/kernel/include/apic/apic_init.h +++ b/kernel/include/apic/apic_init.h @@ -20,6 +20,20 @@ #include +#include + +extern uint64_t apic_base; +extern uint8_t bsp_apic_id; + +extern struct gdt_ptr_64_t** ap_gdt_ptr_64; + +extern struct gdt_t(** ap_gdts)[GDT_NUM_ENTRIES]; + +extern uint64_t* init_stacks_paddr; +extern uint64_t* init_stacks_vaddr; + +extern uint8_t* ap_init_locks; + extern void apic_init(void); extern void apic_init_ap(void); diff --git a/kernel/include/core/clock_src.h b/kernel/include/core/clock_src.h index 6b95b47..afecf75 100644 --- a/kernel/include/core/clock_src.h +++ b/kernel/include/core/clock_src.h @@ -22,9 +22,9 @@ struct clock_src_t { uint64_t period_fs; - uint64_t (*counter)(void*); - void (*reset)(void*, uint64_t); - void* meta; + uint64_t (*counter)(volatile void*); + void (*reset)(volatile void*, uint64_t); + volatile void* meta; }; extern void clock_src_init(void); diff --git a/kernel/include/core/fs.h b/kernel/include/core/fs.h index 8c2e754..11456d6 100644 --- a/kernel/include/core/fs.h +++ b/kernel/include/core/fs.h @@ -21,6 +21,15 @@ #include #include +#define FILE_FLAGS_READ 0x1 +#define FILE_FLAGS_WRITE 0x2 + +#define FILE_FLAGS_CREATE 0100 + +#define FILE_INFO_UNK 0 +#define FILE_INFO_REG 1 +#define FILE_INFO_DIR 2 + struct mount_cntx_t; struct file_handle_t; @@ -32,7 +41,9 @@ enum file_status_t { FILE_ERROR, FILE_DNE, FILE_BUSY, - FILE_NO_SUPPORT + FILE_NO_SUPPORT, + FILE_BAD_FLAGS, + FILE_NOT_DIR, }; struct file_info_t { @@ -44,14 +55,36 @@ struct file_info_t { uint64_t size; }; -typedef struct file_handle_t* (*fs_open_t)(struct mount_cntx_t*, char*); +struct dir_info_t { + uint64_t inode_num; + uint64_t seek_pos; + uint16_t rec_len; + uint8_t type; + char name[256]; +}; + +typedef struct file_handle_t* (*fs_open_t)(struct mount_cntx_t*, const char*, uint32_t, uint32_t); typedef void (*fs_close_t)(struct file_handle_t*); typedef enum file_status_t (*fs_stat_t)(struct file_handle_t*, struct file_info_t*); typedef size_t (*fs_read_t)(struct file_handle_t*, void*, size_t); typedef uint64_t (*fs_get_seek_t)(struct file_handle_t*); typedef enum file_status_t (*fs_seek_t)(struct file_handle_t*, uint64_t); -typedef size_t (*fs_write_t)(struct file_handle_t*, void*, size_t); +typedef size_t (*fs_write_t)(struct file_handle_t*, const void*, size_t); +typedef void (*fs_delete_final_t)(struct file_handle_t*); + +typedef enum file_status_t (*fs_open_dir_t)(struct file_handle_t*); + +typedef enum file_status_t (*fs_read_dir_t)(struct file_handle_t*, struct dir_info_t*); + +typedef enum file_status_t (*fs_create_dir_t)(struct file_handle_t*); +typedef enum file_status_t (*fs_delete_dir_t)(struct file_handle_t*); + +typedef enum file_status_t (*fs_truncate_t)(struct file_handle_t*, size_t); +typedef enum file_status_t (*fs_link_t)(struct file_handle_t*, struct file_handle_t*); +typedef enum file_status_t (*fs_unlink_t)(struct file_handle_t*); + +typedef uint8_t (*fs_is_interactive_t)(struct file_handle_t*); void fs_init(void); @@ -64,16 +97,42 @@ enum file_status_t fs_mount( fs_read_t read, fs_get_seek_t get_seek, fs_seek_t seek, - fs_write_t write + fs_write_t write, + fs_delete_final_t delete_final, + fs_open_dir_t open_dir, + fs_read_dir_t read_dir, + fs_create_dir_t create_dir, + fs_delete_dir_t delete_dir, + fs_truncate_t truncate, + fs_link_t link, + fs_unlink_t unlink ); -extern struct fs_handle_t* fs_open(const char* path); +extern struct fs_handle_t* fs_open_mode(const char* path, uint32_t flags, uint32_t mode); +extern struct fs_handle_t* fs_open(const char* path, uint32_t flags); extern void fs_close(struct fs_handle_t* handle); +extern struct fs_handle_t* fs_openat(const char* path, uint32_t flags, struct fs_handle_t* at, uint32_t mode); + extern enum file_status_t fs_stat(struct fs_handle_t* handle, struct file_info_t* info); extern size_t fs_read(struct fs_handle_t* handle, void* buffer, size_t count); extern uint64_t fs_get_seek(struct fs_handle_t* handle); extern enum file_status_t fs_seek(struct fs_handle_t* handle, uint64_t seek); -extern size_t fs_write(struct fs_handle_t* handle, void* buffer, size_t count); +extern size_t fs_write(struct fs_handle_t* handle, const void* buffer, size_t count); + +extern struct fs_handle_t* fs_open_dir(struct fs_handle_t* handle); // invalidates old handle + +extern enum file_status_t fs_read_dir(struct fs_handle_t* handle, struct dir_info_t* info); + +extern enum file_status_t fs_create_dir(struct fs_handle_t* handle); +extern enum file_status_t fs_delete_dir(struct fs_handle_t* handle); + +extern enum file_status_t fs_truncate(struct fs_handle_t* handle, size_t size); +extern enum file_status_t fs_link(struct fs_handle_t* handle, struct fs_handle_t* replace); +extern enum file_status_t fs_unlink(struct fs_handle_t* handle); + +extern uint8_t fs_is_interactive(struct fs_handle_t* handle); + +extern void fs_path(struct fs_handle_t* handle, size_t max_len, char* buf); #endif /* KERNEL_CORE_FS_H */ diff --git a/kernel/include/core/kentry.h b/kernel/include/core/kentry.h index cd53561..bc5c2ff 100644 --- a/kernel/include/core/kentry.h +++ b/kernel/include/core/kentry.h @@ -31,7 +31,7 @@ struct boot_context_t { struct acpi_rsdp_t rsdp; - volatile struct gdt_t(* gdt)[GDT_NUM_ENTRIES]; + struct gdt_t(* gdt)[GDT_NUM_ENTRIES]; #ifdef GRAPHICSBASE struct framebuffer_t framebuffer; #endif /* GRAPHICSBASE */ @@ -43,4 +43,6 @@ extern void kentry(void) __attribute__((noreturn)); extern void kapentry(uint64_t arb_id) __attribute__((noreturn)); +extern void prepare_userland(void* cntx); + #endif /* KERNEL_CORE_KENTRY_H */ diff --git a/kernel/include/core/lock.h b/kernel/include/core/lock.h index 0ba48ef..cc2c79d 100644 --- a/kernel/include/core/lock.h +++ b/kernel/include/core/lock.h @@ -17,11 +17,11 @@ #include -#ifndef KERNEL_INCLUDE_CORE_LOCK_H -#define KERNEL_INCLUDE_CORE_LOCK_H +#ifndef KERNEL_CORE_LOCK_H +#define KERNEL_CORE_LOCK_H extern void lock_init(uint8_t* lock); extern void lock_acquire(uint8_t* lock); extern void lock_release(uint8_t* lock); -#endif /* KERNEL_INCLUDE_CORE_LOCK_H */ +#endif /* KERNEL_CORE_LOCK_H */ diff --git a/kernel/include/core/proc_data.h b/kernel/include/core/proc_data.h index 20890f0..53fe3d6 100644 --- a/kernel/include/core/proc_data.h +++ b/kernel/include/core/proc_data.h @@ -28,7 +28,7 @@ struct proc_data_t { uint64_t kernel_rsp; // order matters uint8_t arb_id; - volatile struct tss_t* tss; + struct tss_t* tss; struct pcb_t* current_process; uint64_t sts; }; diff --git a/kernel/include/core/process.h b/kernel/include/core/process.h index 25e5d74..eab2c86 100644 --- a/kernel/include/core/process.h +++ b/kernel/include/core/process.h @@ -25,8 +25,9 @@ #include #include -#define MAX_FD 256 -#define MAX_META 1 +#include + +#define MAX_META 1 struct pcb_t; @@ -68,7 +69,10 @@ struct pcb_t { struct pcb_t* next; - struct fs_handle_t* fd_table[MAX_FD]; + uint64_t exit_code; + + struct fs_handle_t* wd; + struct array_list_t* fd_table; uint8_t fxdata[512] __attribute__((aligned(16))); diff --git a/kernel/include/core/semaphore.h b/kernel/include/core/semaphore.h new file mode 100644 index 0000000..6c670c8 --- /dev/null +++ b/kernel/include/core/semaphore.h @@ -0,0 +1,38 @@ +/* semaphore.h - semaphore interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_CORE_SEMAPHORE_H +#define KERNEL_CORE_SEMAPHORE_H + +#include +#include + +#define SEMAPHORE_CAP_UNLIM (uint64_t)(-1) + +struct semaphore_t; + +extern struct semaphore_t* semaphore_alloc(size_t cap); +extern void semaphore_free(struct semaphore_t* sem); + +extern void semaphore_wait(struct semaphore_t* sem); +extern void semaphore_signal(struct semaphore_t* sem); + +extern void semaphore_wait_full(struct semaphore_t* sem); +extern void semaphore_signal_full(struct semaphore_t* sem); + +#endif /* KERNEL_CORE_SEMAPHORE_H */ + diff --git a/kernel/include/core/syscall_dispatch.h b/kernel/include/core/syscall_dispatch.h index bd82d6f..fef9737 100644 --- a/kernel/include/core/syscall_dispatch.h +++ b/kernel/include/core/syscall_dispatch.h @@ -43,10 +43,26 @@ typedef uint64_t (*syscall_dispatch_t)( extern syscall_dispatch_t syscall_handlers[SYSCALL_MAX]; extern DECLARE_SYSCALL(exit); -extern DECLARE_SYSCALL(open); +extern DECLARE_SYSCALL(openat); extern DECLARE_SYSCALL(close); extern DECLARE_SYSCALL(read); extern DECLARE_SYSCALL(write); extern DECLARE_SYSCALL(alloc); +// reserved +// reserved +extern DECLARE_SYSCALL(open_dir); +extern DECLARE_SYSCALL(read_dir); +extern DECLARE_SYSCALL(truncate); +extern DECLARE_SYSCALL(seek); +extern DECLARE_SYSCALL(tell); +extern DECLARE_SYSCALL(create_dir); +extern DECLARE_SYSCALL(delete_dir); +extern DECLARE_SYSCALL(epoch_time); +extern DECLARE_SYSCALL(is_a_tty); +extern DECLARE_SYSCALL(gcwd); +extern DECLARE_SYSCALL(ccwd); +extern DECLARE_SYSCALL(link); +extern DECLARE_SYSCALL(unlink); +extern DECLARE_SYSCALL(stat); #endif /* KERNEL_CORE_SYSCALL_DISPATCH_H */ diff --git a/kernel/include/core/syscall_vectors.h b/kernel/include/core/syscall_vectors.h index 92d0a40..86dfd20 100644 --- a/kernel/include/core/syscall_vectors.h +++ b/kernel/include/core/syscall_vectors.h @@ -43,9 +43,12 @@ /* * rdi: path (const char*) + * rsi: flags (uint8_t) + * rdx: at (int) + * r8 : mode (int) * ret: handle (int) */ -#define SYSCALL_OPEN 1 +#define SYSCALL_OPENAT 1 /* * rdi: handle (int) @@ -75,6 +78,106 @@ */ #define SYSCALL_ALLOC 5 -#define SYSCALL_MAX 6 +/* + * reserved + */ +#define SYSCALL_RESV1 6 + +/* + * reserved + */ +#define SYSCALL_RESV2 7 + +/* + * rdi: handle (int) + * ret handle (int) + */ +#define SYSCALL_OPEN_DIR 8 + +/* + * rdi: handle (int) + * rsi: info buffer (void*) + * rdx: max_size (size_t) + * ret: size (size_t) + */ +#define SYSCALL_READ_DIR 9 + +/* + * rdi: handle (int) + * rsi: size (size_t) + * ret: success (int) + */ +#define SYSCALL_TRUNCATE 10 + +/* + * rdi: handle (int) + * rsi: seek (long int) + * ret: new seek (long int) + */ +#define SYSCALL_SEEK 11 + +/* + * rdi: handle + * ret: seek (long int) + */ +#define SYSCALL_TELL 12 + +/* + * rdi: handle (int) + * ret: success (int) + */ +#define SYSCALL_CREATE_DIR 13 + +/* + * rdi: handle (int) + * ret: success (int) + */ +#define SYSCALL_DELETE_DIR 14 + +/* + * ret: time since epoch in nanos (long int) + */ +#define SYSCALL_EPOCH_TIME 15 + +/* + * rdi: handle (int) + * ret: 1 on tty. 0 otherwise (int) + */ +#define SYSCALL_IS_A_TTY 16 + +/* + * rdi: buffer (char*) + * rsi: max len (size_t) + * ret: success (int) + */ +#define SYSCALL_GCWD 17 + +/* + * rdi: handle (int) + * ret: success (int) + */ +#define SYSCALL_CCWD 18 + +/* + * rdi: handle + * rsi: replace handle + * ret: success (int) + */ +#define SYSCALL_LINK 19 + +/* + * rdi: handle (int) + * ret: success (int) + */ +#define SYSCALL_UNLINK 20 + +/* + * rdi: handle (int) + * rsi: statbuf (struct file_info_t*) + * ret: success (int) + */ +#define SYSCALL_STAT 21 + +#define SYSCALL_MAX 22 diff --git a/kernel/include/core/tss.h b/kernel/include/core/tss.h index 9913cf9..cfd9bc5 100644 --- a/kernel/include/core/tss.h +++ b/kernel/include/core/tss.h @@ -54,6 +54,6 @@ struct tss_t { uint16_t iopb; } __attribute__((packed)); -extern void tss_init(volatile struct gdt_t(* gdt)[GDT_NUM_ENTRIES]); +extern void tss_init(struct gdt_t(* gdt)[GDT_NUM_ENTRIES]); #endif /* KERNEL_CORE_TSS_H */ diff --git a/kernel/include/devfs/devfs.h b/kernel/include/devfs/devfs.h index 4a2996d..5143e7c 100644 --- a/kernel/include/devfs/devfs.h +++ b/kernel/include/devfs/devfs.h @@ -21,15 +21,29 @@ #include #include -#include +#include -extern struct file_handle_t* devfs_open(struct mount_cntx_t* cntx, char* path); +extern struct file_handle_t* devfs_open(struct mount_cntx_t* cntx, const char* path, uint32_t flags, uint32_t mode); extern void devfs_close(struct file_handle_t* handle); extern enum file_status_t devfs_stat(struct file_handle_t* handle, struct file_info_t* info); extern size_t devfs_read(struct file_handle_t* handle, void* buffer, size_t count); extern uint64_t devfs_get_seek(struct file_handle_t* handle); extern enum file_status_t devfs_seek(struct file_handle_t* handle, uint64_t seek); -extern size_t devfs_write(struct file_handle_t* handle, void* buffer, size_t count); +extern size_t devfs_write(struct file_handle_t* handle, const void* buffer, size_t count); +extern void devfs_delete_final(struct file_handle_t* handle); + +extern enum file_status_t devfs_open_dir(struct file_handle_t* handle); + +extern enum file_status_t devfs_read_dir(struct file_handle_t* handle, struct dir_info_t* info); + +extern enum file_status_t devfs_create_dir(struct file_handle_t* handle, const char* name); +extern enum file_status_t devfs_delete_dir(struct file_handle_t* handle); + +extern enum file_status_t devfs_truncate(struct file_handle_t* handle, size_t size); +extern enum file_status_t devfs_link(struct file_handle_t* handle, struct file_handle_t* replace); +extern enum file_status_t devfs_unlink(struct file_handle_t* handle); + +extern uint8_t devfs_is_interactive(struct file_handle_t* handle); #endif /* KERNEL_DEVFS_DEVFS_H */ diff --git a/kernel/include/devfs/tty.h b/kernel/include/devfs/tty.h index 0b8a157..8dfc672 100644 --- a/kernel/include/devfs/tty.h +++ b/kernel/include/devfs/tty.h @@ -32,10 +32,10 @@ extern struct tty_handle_t* tty_com1(void); extern struct tty_handle_t* tty_com2(void); #endif /* SERIAL */ -extern struct tty_handle_t* tty_open(char* name); +extern struct tty_handle_t* tty_open(const char* name); extern void tty_close(struct tty_handle_t* tty); extern size_t tty_read(struct tty_handle_t* tty, void* buffer, size_t count); extern uint8_t tty_queue_read(struct tty_handle_t* tty, uint8_t byte); -extern void tty_write(struct tty_handle_t* tty, void* buffer, size_t count); +extern void tty_write(struct tty_handle_t* tty, const void* buffer, size_t count); #endif /* KERNEL_DEVFS_TTY_H */ diff --git a/kernel/include/lib/array_list.h b/kernel/include/lib/array_list.h new file mode 100644 index 0000000..a0a6954 --- /dev/null +++ b/kernel/include/lib/array_list.h @@ -0,0 +1,43 @@ +/* array_list.h - array list interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_LIB_ARRAY_LIST_H +#define KERNEL_LIB_ARRAY_LIST_H + +#include +#include + +struct array_list_t; + +extern struct array_list_t* array_list_alloc(size_t init_cap, size_t growth, void* null); + +extern uint64_t array_list_push(struct array_list_t* list, void* value); + +extern void* array_list_get(struct array_list_t* list, uint64_t index); + +extern void* array_list_remove(struct array_list_t* list, uint64_t index); + +extern void array_list_clear(struct array_list_t* list, void (*free_func)(void*)); + +extern void array_list_free(struct array_list_t* list, void (*free_func)(void*)); + +extern struct array_list_t* array_list_dup(struct array_list_t* list, void* (*dup_func)(void*)); + +extern uint64_t array_list_count(struct array_list_t* list); + +#endif /* KERNEL_LIB_ARRAY_LIST_H */ + diff --git a/kernel/include/lib/hash.h b/kernel/include/lib/hash.h index 763a90d..6e28cbc 100644 --- a/kernel/include/lib/hash.h +++ b/kernel/include/lib/hash.h @@ -25,4 +25,6 @@ extern uint8_t hash_byte_sum(const void* ptr, size_t c); extern uint32_t crc32_ansi(const void* data, size_t length); +extern uint64_t fnv64_1a(const void* data, size_t length); + #endif /* KERNEL_LIB_HASH_H */ diff --git a/kernel/include/lib/hash_table.h b/kernel/include/lib/hash_table.h new file mode 100644 index 0000000..e30d61f --- /dev/null +++ b/kernel/include/lib/hash_table.h @@ -0,0 +1,45 @@ +/* hash_table.h - hash table interface */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef KERNEL_LIB_HASH_TABLE_H +#define KERNEL_LIB_HASH_TABLE_H + +#include +#include + +struct hash_table_t; + +extern struct hash_table_t* hash_table_alloc(size_t buckets); + +extern void* hash_table_insert(struct hash_table_t* table, uint64_t key, void* value); + +extern uint8_t hash_table_get(struct hash_table_t* table, uint64_t key, void** out); + +extern uint8_t hash_table_remove(struct hash_table_t* table, uint64_t key, void** out); + +extern void hash_table_clear(struct hash_table_t* table, void (*free_func)(void*)); + +extern void hash_table_free(struct hash_table_t* table, void (*free_func)(void*)); + +extern void hash_table_copy(struct hash_table_t* table, struct hash_table_t* copy, void* (*dup_func)(void*)); + +extern void hash_table_resize(struct hash_table_t* table, size_t buckets); + +extern size_t hash_table_count(struct hash_table_t* table); + +#endif /* KERNEL_LIB_HASH_TABLE_H */ + diff --git a/kernel/include/lib/kstrcpy.h b/kernel/include/lib/kstrcpy.h index b591a4c..5a2a6b0 100644 --- a/kernel/include/lib/kstrcpy.h +++ b/kernel/include/lib/kstrcpy.h @@ -18,9 +18,13 @@ #ifndef KERNEL_LIB_KSTRCPY_H #define KERNEL_LIB_KSTRCPY_H +#include + extern char* kstrcpy(char* dest, const char* src); extern char* kstrcpy_no_null(char* dest, const char* src); +extern char* kstrncpy(char* dest, const char* src, size_t len); + #endif /* KERNEL_LIB_KSTRCPY_H */ diff --git a/kernel/lib/array_list.c b/kernel/lib/array_list.c new file mode 100644 index 0000000..7eb568b --- /dev/null +++ b/kernel/lib/array_list.c @@ -0,0 +1,154 @@ +/* array_list.c - array list implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include +#include + +#include +#include + +#include + +struct array_list_t { + void** buffer; + void* null; + size_t cap; + size_t growth; + size_t count; + size_t hint; +}; + +static void* get_increase(struct array_list_t* list, size_t index) { + if (index < list->cap) { + return list->buffer[index]; + } + + void** buffer = kmalloc(sizeof(void*) * list->cap + list->growth); + kmemcpy(buffer, list->buffer, sizeof(void*) * list->cap); + + for (size_t i = list->cap; i < list->cap + list->growth; i++) { + buffer[i] = list->null; + } + + kfree(list->buffer); + list->buffer = buffer; + list->cap += list->growth; + + return list->null; +} + +struct array_list_t* array_list_alloc(size_t init_cap, size_t growth, void* null) { + struct array_list_t* list = kmalloc(sizeof(struct array_list_t)); + + list->cap = init_cap; + list->null = null; + list->growth = growth; + list->count = list->hint = 0; + + list->buffer = kmalloc(sizeof(void*) * init_cap); + + for (size_t i = 0; i < init_cap; i++) { + list->buffer[i] = null; + } + + return list; +} + +uint64_t array_list_push(struct array_list_t* list, void* value) { + while (get_increase(list, list->hint) != list->null) { + list->hint++; + } + + list->buffer[list->hint] = value; + list->count++; + return list->hint++; +} + +void* array_list_get(struct array_list_t* list, uint64_t index) { + if (index >= list->cap) { + return list->null; + } + + return list->buffer[index]; +} + +void* array_list_remove(struct array_list_t* list, uint64_t index) { + if (index >= list->cap) { + return list->null; + } + + void* ret = list->buffer[index]; + list->buffer[index] = list->null; + + list->count--; + + if (index < list->hint) { + list->hint = index; + } + + return ret; +} + +void array_list_clear(struct array_list_t* list, void (*free_func)(void*)) { + for (size_t i = 0; i < list->cap; i++) { + if (list->buffer[i] != list->null) { + if (free_func) { + free_func(list->buffer[i]); + } + list->buffer[i] = list->null; + } + } + + list->count = 0; + list->hint = 0; +} + +void array_list_free(struct array_list_t* list, void (*free_func)(void*)) { + array_list_clear(list, free_func); + + kfree(list->buffer); + kfree(list); +} + +struct array_list_t* array_list_dup(struct array_list_t* list, void* (*dup_func)(void*)) { + struct array_list_t* copy = kmalloc(sizeof(struct array_list_t)); + + copy->cap = list->cap; + copy->null = list->null; + copy->growth = list->growth; + copy->count = list->count; + copy->hint = list->hint; + + copy->buffer = kmalloc(sizeof(void*) * list->cap); + + for (size_t i = 0; i < list->cap; i++) { + if (list->buffer[i] == list->null) { + copy->buffer[i] = list->null; + } + else { + copy->buffer[i] = dup_func(list->buffer[i]); + } + } + + kmemcpy(copy->buffer, list->buffer, sizeof(void*) * list->cap); + + return copy; +} + +uint64_t array_list_count(struct array_list_t* list) { + return list->count; +} diff --git a/kernel/lib/hash.c b/kernel/lib/hash.c index 501dcf0..923b136 100644 --- a/kernel/lib/hash.c +++ b/kernel/lib/hash.c @@ -20,6 +20,13 @@ #include +#define CRC32_ANSI_C0 0xFFFFFFFF +#define CRC32_ANSI_C1 0xEDB88320 + + +#define FNV64_1A_C0 0xcbf29ce484222325 +#define FNV64_1A_C1 0x100000001b3 + uint8_t hash_byte_sum(const void* ptr, size_t c) { const uint8_t* _ptr = ptr; uint8_t sum = 0; @@ -31,7 +38,7 @@ uint8_t hash_byte_sum(const void* ptr, size_t c) { } uint32_t crc32_ansi(const void* data, size_t length) { - uint32_t crc = 0xFFFFFFFF; + uint32_t crc = CRC32_ANSI_C0; const uint8_t *p = (const uint8_t*)data; uint8_t i; @@ -40,7 +47,7 @@ uint32_t crc32_ansi(const void* data, size_t length) { for (i = 0; i < 8; i++) { if (crc & 1) { - crc = (crc >> 1) ^ 0xEDB88320; + crc = (crc >> 1) ^ CRC32_ANSI_C1; } else { crc >>= 1; @@ -48,5 +55,18 @@ uint32_t crc32_ansi(const void* data, size_t length) { } } - return crc ^ 0xFFFFFFFF; + return crc ^ CRC32_ANSI_C0; +} + +uint64_t fnv64_1a(const void* data, size_t length) { + const uint8_t* bytes = data; + uint64_t fnv64 = FNV64_1A_C0; + + for (size_t i = 0; i < length; i++) { + fnv64 ^= *bytes; + fnv64 *= FNV64_1A_C1; + bytes++; + } + + return fnv64; } diff --git a/kernel/lib/hash_table.c b/kernel/lib/hash_table.c new file mode 100644 index 0000000..acf32f3 --- /dev/null +++ b/kernel/lib/hash_table.c @@ -0,0 +1,172 @@ +/* hash_table.c - hash table implementation */ +/* Copyright (C) 2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#include +#include + +#include +#include + +#include + +struct node_t { + uint64_t key; + void* value; + struct node_t* next; +}; + +struct hash_table_t { + size_t num_buckets; + size_t num_elems; + struct node_t** buckets; +}; + +static void* dup(void* value) { + return value; +} + +static inline uint64_t bucket_index(struct hash_table_t* table, uint64_t key) { + return key % table->num_buckets; +} + +static struct node_t** find_node(struct node_t** bucket, uint64_t key) { + for (; *bucket; bucket = &(*bucket)->next) { + if ((*bucket)->key == key) { + break; + } + } + + return bucket; +} + +struct hash_table_t* hash_table_alloc(size_t buckets) { + struct hash_table_t* table = kmalloc(sizeof(struct hash_table_t)); + + if (!table) { + return 0; + } + + table->num_buckets = buckets; + table->num_elems = 0; + table->buckets = kmalloc(sizeof(struct node_t*) * buckets); + + kmemset(table->buckets, 0, sizeof(struct node_t*) * buckets); + + return table; +} + +void* hash_table_insert(struct hash_table_t* table, uint64_t key, void* value) { + uint64_t index = bucket_index(table, key); + + struct node_t* node = *find_node(&table->buckets[index], key); + + if (!node) { + node = kmalloc(sizeof(struct node_t)); + node->next = table->buckets[index]; + node->key = key; + node->value = value; + table->buckets[index] = node; + table->num_elems++; + } + + void* old = node->value; + node->value = value; + + return old; +} + +uint8_t hash_table_get(struct hash_table_t* table, uint64_t key, void** out) { + uint64_t index = bucket_index(table, key); + + struct node_t* node = *find_node(&table->buckets[index], key); + + if (!node) { + return 0; + } + + *out = node->value; + return 1; +} + +uint8_t hash_table_remove(struct hash_table_t* table, uint64_t key, void** out) { + uint64_t index = bucket_index(table, key); + + struct node_t** node = find_node(&table->buckets[index], key); + + if (!*node) { + return 0; + } + + *out = (*node)->value; + struct node_t* to_free = *node; + *node = (*node)->next; + + kfree(to_free); + + table->num_elems--; + + return 1; +} + +void hash_table_clear(struct hash_table_t* table, void (*free_func)(void*)) { + for (size_t i = 0; i < table->num_buckets; i++) { + struct node_t* next; + for (struct node_t* node = table->buckets[i]; node; node = next) { + next = node->next; + if (free_func) { + free_func(node->value); + } + kfree(node); + } + + table->buckets[i] = 0; + } + + table->num_elems = 0; +} + +void hash_table_free(struct hash_table_t* table, void (*free_func)(void*)) { + hash_table_clear(table, free_func); + + kfree(table->buckets); + kfree(table); +} + +void hash_table_copy(struct hash_table_t* table, struct hash_table_t* copy, void* (*dup_func)(void*)) { + for (size_t i = 0; i < table->num_buckets; i++) { + for (struct node_t* node = table->buckets[i]; node; node = node->next) { + hash_table_insert(copy, node->key, dup_func(node->value)); + } + } +} + +void hash_table_resize(struct hash_table_t* table, size_t buckets) { + struct hash_table_t temp = *table; + + table->num_buckets = buckets; + table->num_elems = 0; + table->buckets = kmalloc(sizeof(struct node_t*) * buckets); + kmemset(table->buckets, 0, sizeof(struct node_t*) * buckets); + + hash_table_copy(&temp, table, dup); + + kfree(temp.buckets); +} + +size_t hash_table_count(struct hash_table_t* table) { + return table->num_elems; +} diff --git a/kernel/lib/kmemcpy.c b/kernel/lib/kmemcpy.c index 8c65b43..79efcfd 100644 --- a/kernel/lib/kmemcpy.c +++ b/kernel/lib/kmemcpy.c @@ -23,13 +23,24 @@ void* memcpy(void* dest, const void* src, size_t c) __attribute__((weak)); void* memcpy(void* dest, const void* src, size_t c) { - const uint8_t* p1 = src; - uint8_t* p2 = dest; - - for (size_t i = 0; i < c; i++) { - *p2 = *p1; - p1++; - p2++; + const uint64_t* src64 = (const uint64_t*)src; + uint64_t* dest64 = (uint64_t*)dest; + + while (c >= sizeof(uint64_t)) { + *dest64 = *src64; + dest64++; + src64++; + c -= sizeof(uint64_t); + } + + const uint8_t* src8 = (const uint8_t*)src64; + uint8_t* dest8 = (uint8_t*)dest64; + + while (c >= sizeof(uint8_t)) { + *dest8 = *src8; + dest8++; + src8++; + c -= sizeof(uint8_t); } return dest; diff --git a/kernel/lib/kmemset.c b/kernel/lib/kmemset.c index c3c6cbb..2297b5f 100644 --- a/kernel/lib/kmemset.c +++ b/kernel/lib/kmemset.c @@ -20,13 +20,27 @@ #include +#define PAT(s) (((uint64_t)v8) << (8*s)) + void* memset(void* ptr, int32_t v, size_t c) __attribute__((weak)); void* memset(void* ptr, int32_t v, size_t c) { - uint8_t* p = ptr; + uint8_t v8 = (uint8_t)v; + uint64_t v64 = PAT(0) | PAT(1) | PAT(2) | PAT(3) | PAT(4) | PAT(5) | PAT(6) | PAT(7); + uint64_t* ptr64 = (uint64_t*)ptr; + + while (c >= sizeof(uint64_t)) { + *ptr64 = v64; + ptr64++; + c -= sizeof(uint64_t); + } + + uint8_t* ptr8 = (uint8_t*)ptr64; - for (size_t i = 0; i < c; i++) { - p[i] = (uint8_t)v; + while (c >= sizeof(uint8_t)) { + *ptr8 = v8; + ptr8++; + c -= sizeof(uint8_t); } return ptr; diff --git a/kernel/lib/kstrcpy.c b/kernel/lib/kstrcpy.c index a24e571..843f4ae 100644 --- a/kernel/lib/kstrcpy.c +++ b/kernel/lib/kstrcpy.c @@ -37,3 +37,18 @@ char* kstrcpy_no_null(char* dest, const char* src) { return dest; } + +char* kstrncpy(char* dest, const char* src, size_t len) { + if (!len) { + return dest; + } + + while (*src && --len) { + *dest = *src; + src++; + dest++; + } + + *dest = 0; + return dest + 1; +} diff --git a/kernel/lib/mergesort.c b/kernel/lib/mergesort.c deleted file mode 100644 index b46aaf9..0000000 --- a/kernel/lib/mergesort.c +++ /dev/null @@ -1,72 +0,0 @@ -/* mergesort.h - mergesort implementation */ -/* Copyright (C) 2025-2026 Ebrahim Aleem -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see -*/ - -#include - -void* mergesort_ll_inplace_ul(void* ll, void** (*next)(void*), unsigned long (*value)(void*)) { - if (!ll || !*next(ll)) { - return ll; - } - - void *mid, *right, *left; - void *fast; - void *head, *tail; - - // find mid - mid = ll; - fast = *next(ll); - - while (fast && *next(fast)) { - mid = *next(mid); - fast = *next(*next(fast)); - } - - right = *next(mid); - *next(mid) = 0; - - left = mergesort_ll_inplace_ul(ll, next, value); - right = mergesort_ll_inplace_ul(right, next, value); - - // merge - if (!left) return right; - if (!right) return left; - - if (value(left) <= value(right)) { - head = left; - left = *next(left); - } else { - head = right; - right = *next(right); - } - - tail = head; - - while (left && right) { - if (value(left) <= value(right)) { - *next(tail) = left; - left = *next(left); - } else { - *next(tail) = right; - right = *next(right); - } - tail = *next(tail); - } - - *next(tail) = left ? left : right; - - return head; -} diff --git a/scripts/Makefile.kcflags b/scripts/Makefile.kcflags index 2e5cf8d..408f551 100644 --- a/scripts/Makefile.kcflags +++ b/scripts/Makefile.kcflags @@ -16,7 +16,9 @@ CWARN := -Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wwrite-strings -Wmissing-prototypes \ -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wconversion \ - -Wstrict-prototypes + -Wstrict-prototypes -Wundef -Wcast-qual -Wstrict-overflow=5 -Wswitch-enum -Winit-self -Wdouble-promotion \ + -Wformat=2 -Wnull-dereference -Wpacked -Wvolatile-register-var -Walloca -Wvla -Wframe-larger-than=1024 \ + -Wstrict-aliasing=2 -Wmissing-noreturn -Wmissing-format-attribute ifndef DEBUG CWARN := $(CWARN) -Werror diff --git a/scripts/gen_compile_commands b/scripts/gen_compile_commands new file mode 100755 index 0000000..329d224 --- /dev/null +++ b/scripts/gen_compile_commands @@ -0,0 +1,3 @@ +#!/bin/bash + +bear -- make all diff --git a/scripts/scan_build b/scripts/scan_build new file mode 100755 index 0000000..910ca76 --- /dev/null +++ b/scripts/scan_build @@ -0,0 +1,5 @@ +#!/bin/bash + +scan-build --use-cc=clang --use-c++=clang++ \ + -analyze-headers -enable-checker core,deadcode,nullability,security,unix \ + make all diff --git a/test/helpers/main.c b/test/helpers/main.c index 84b699a..cf8eaeb 100644 --- a/test/helpers/main.c +++ b/test/helpers/main.c @@ -101,7 +101,7 @@ int main(void) { } else { test->test(); - exit(0); + exit(EXIT_SUCCESS); } } diff --git a/kernel/include/lib/mergesort.h b/test/helpers/memport.c similarity index 74% rename from kernel/include/lib/mergesort.h rename to test/helpers/memport.c index 0877ffd..6befcb1 100644 --- a/kernel/include/lib/mergesort.h +++ b/test/helpers/memport.c @@ -1,4 +1,4 @@ -/* mergesort.h - mergesort interface */ +/* memport.c - kernel allocator port to userland */ /* Copyright (C) 2025-2026 Ebrahim Aleem * * This program is free software: you can redistribute it and/or modify @@ -15,9 +15,15 @@ * along with this program. If not, see */ -#ifndef KERNEL_LIB_MERGESORT_H -#define KERNEL_LIB_MERGESORT_H +#include +#include -extern void* mergesort_ll_inplace_ul(void* ll, void** (*next)(void*), unsigned long (*value)(void*)); +#include -#endif /* KERNEL_LIB_MERGESORT_H */ +void* kmalloc(size_t size) { + return malloc(size); +} + +void kfree(void* ptr) { + free(ptr); +} diff --git a/test/include/macros.h b/test/include/macros.h index 5d4c0b1..f7ce5d2 100644 --- a/test/include/macros.h +++ b/test/include/macros.h @@ -24,6 +24,8 @@ #include #include +#pragma clang diagnostic ignored "-Wmissing-noreturn" + enum _test_result_t { _TEST_PASS, _TEST_FAIL, @@ -40,7 +42,7 @@ extern const char* _test_name; extern void _test_report(const char* report); -#define TEST_NAME(name) const char* _test_name = name; +#define TEST_NAME(name) const char* _test_name = name #define TEST_EXP_PASS(nm) _TEST_EXPAND(__COUNTER__, nm, _TEST_PASS) #define TEST_EXP_FAIL(nm) _TEST_EXPAND(__COUNTER__, nm, _TEST_FAIL) @@ -60,19 +62,23 @@ extern void _test_report(const char* report); static void _test_##id(void) #define TEST_PASS() exit(EXIT_SUCCESS) -#define TEST_FAIL(code) exit(EXIT_FAILURE) +#define TEST_FAIL() exit(EXIT_FAILURE) #define ASSERT_TRUE(condition, report) \ - if (!(condition)) { \ - _test_report(report); \ - TEST_FAIL(); \ - } + do { \ + if (!(condition)) { \ + _test_report(report); \ + TEST_FAIL(); \ + } \ + } while (0) #define ASSERT_FALSE(condition, report) \ - if ((condition)) { \ - _test_report(report); \ - TEST_FAIL(); \ - } + do { \ + if ((condition)) { \ + _test_report(report); \ + TEST_FAIL(); \ + } \ + } while (0) #define ASSERT_EXCEPTION(callback, report, ...) \ do { \ @@ -86,9 +92,9 @@ extern void _test_report(const char* report); } \ else { \ callback(__VA_OPT__(__VA_ARGS__)); \ - exit(0); \ + exit(EXIT_SUCCESS); \ } \ - } while (0); + } while (0) #define ASSERT_NO_EXCEPTION(callback, report, ...) \ do { \ @@ -102,8 +108,8 @@ extern void _test_report(const char* report); } \ else { \ callback(__VA_ARGS__); \ - exit(0); \ + exit(EXIT_SUCCESS); \ } \ - } while (0); + } while (0) #endif /* TEST_HELPERS_MACROS_H */ diff --git a/test/include/memport.h b/test/include/memport.h new file mode 100644 index 0000000..3bcafbb --- /dev/null +++ b/test/include/memport.h @@ -0,0 +1,26 @@ +/* memport.h - kernel allocator port to userland interface */ +/* Copyright (C) 2025-2026 Ebrahim Aleem +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ + +#ifndef TEST_HELPERS_MEMPORT_H +#define TEST_HELPERS_MEMPORT_H + +#include + +void* kmalloc(size_t size); +void kfree(void* ptr); + +#endif /* TEST_HELPERS_MEMPORT_H */ diff --git a/test/lib/test.c b/test/lib/test.c index 983890a..d10f004 100644 --- a/test/lib/test.c +++ b/test/lib/test.c @@ -15,7 +15,7 @@ * along with this program. If not, see */ -#include +#include #include #include #include @@ -29,10 +29,12 @@ #include #include #include +#include +#include #define MEM_TEST_SIZE 256 -TEST_NAME("libary test suite") +TEST_NAME("libary test suite"); TEST("kmemset") { char* actual = malloc(MEM_TEST_SIZE); @@ -119,11 +121,143 @@ TEST("kstrcmp") { TEST("hash_byte_sum") { ASSERT_TRUE(hash_byte_sum((uint8_t*)0xdeadbeef, 0) == 0, "fails invalid ptr"); - ASSERT_TRUE(hash_byte_sum((uint8_t*)"\0\1\2\3\0\0\1\2", 8) == 9, "fails basic sum"); - ASSERT_TRUE(hash_byte_sum((uint8_t*)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 248, "fails overflow"); + ASSERT_TRUE(hash_byte_sum((const uint8_t*)"\0\1\2\3\0\0\1\2", 8) == 9, "fails basic sum"); + ASSERT_TRUE(hash_byte_sum((const uint8_t*)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) == 248, "fails overflow"); } TEST("crc32_ansi") { ASSERT_TRUE(crc32_ansi("Hello, World!", 13) == 0xec4ac3d0, "fails expected checksum"); ASSERT_FALSE(crc32_ansi("Hello, World!", 12) == 0xec4ac3d0, "fails unexpected checksum"); } + +TEST("fnc64_1a") { + ASSERT_TRUE(fnv64_1a("Hello, World!", 13) == 0x6ef05bd7cc857c54, "fails expected checksum"); + ASSERT_FALSE(fnv64_1a("Hello, World!", 12) == 0x6ef05bd7cc857c54, "fails expected checksum"); +} + +static void* hash_table_dup_fun(void* value) { + return (void*)((uint64_t)value + 1); +} + +TEST("hash_table") { + struct hash_table_t* table1 = hash_table_alloc(10); + struct hash_table_t* table2 = hash_table_alloc(10); + void* tmp; + + ASSERT_TRUE(hash_table_insert(table1, 1, (void*)1) == (void*)1, "fails hash_table_insert"); + ASSERT_TRUE(hash_table_insert(table1, 2, (void*)2) == (void*)2, "fails hash_table_insert"); + ASSERT_TRUE(hash_table_insert(table1, 328642, (void*)3) == (void*)3, "fails hash_table_insert"); + ASSERT_TRUE(hash_table_insert(table1, ~834ULL, (void*)4) == (void*)4, "fails hash_table_insert"); + ASSERT_TRUE(hash_table_insert(table1, 1, (void*)5) == (void*)1, "fails hash_table_insert"); + ASSERT_TRUE(hash_table_insert(table1, 11, (void*)6) == (void*)6, "fails hash_table_insert"); + + ASSERT_TRUE(hash_table_get(table1, 1, &tmp), "fails hash_table_get"); + ASSERT_TRUE(tmp == (void*)5, "fails hash_table_get"); + + ASSERT_TRUE(hash_table_get(table1, 2, &tmp), "fails hash_table_get"); + ASSERT_TRUE(tmp == (void*)2, "fails hash_table_get"); + + ASSERT_TRUE(hash_table_get(table1, 328642, &tmp), "fails hash_table_get"); + ASSERT_TRUE(tmp == (void*)3, "fails hash_table_get"); + + ASSERT_TRUE(hash_table_get(table1, ~834ULL, &tmp), "fails hash_table_get"); + ASSERT_TRUE(tmp == (void*)4, "fails hash_table_get"); + + ASSERT_TRUE(hash_table_get(table1, 11, &tmp), "fails hash_table_get"); + ASSERT_TRUE(tmp == (void*)6, "fails hash_table_get"); + + ASSERT_FALSE(hash_table_get(table1, 3, &tmp), "fails hash_table_get"); + ASSERT_FALSE(hash_table_get(table1, 0, &tmp), "fails hash_table_get"); + ASSERT_FALSE(hash_table_get(table2, 0, &tmp), "fails hash_table_get"); + + ASSERT_TRUE(hash_table_remove(table1, 1, &tmp), "fails hash_table_remove"); + ASSERT_TRUE(tmp == (void*)5, "fails hash_table_remove"); + tmp = (void*)0; + ASSERT_FALSE(hash_table_remove(table1, 1, &tmp), "fails hash_table_remove"); + ASSERT_TRUE(tmp == (void*)0, "fails hash_table_remove"); + + ASSERT_TRUE(hash_table_get(table1, 11, &tmp), "fails hash_table_get after remove"); + ASSERT_FALSE(hash_table_remove(table2, 1, &tmp), "fails hash_table_remove"); + + hash_table_copy(table1, table2, hash_table_dup_fun); + hash_table_clear(table1, 0); + + ASSERT_TRUE(hash_table_count(table1) == 0, "fails hash_table_count"); + ASSERT_TRUE(hash_table_count(table2) == 4, "fails hash_table_count"); + + ASSERT_FALSE(hash_table_get(table1, 11, &tmp), "fails hash_table_get after clear"); + ASSERT_TRUE(hash_table_get(table2, 11, &tmp), "fails hash_table_get after copy"); + ASSERT_TRUE(tmp == (void*)7, "fails hash_table_get after copy"); + + hash_table_insert(table2, 1, (void*)8); + hash_table_insert(table2, 101, (void*)9); + hash_table_resize(table2, 100); + + ASSERT_TRUE(hash_table_get(table2, 1, &tmp), "fails hash_table_get after resize"); + ASSERT_TRUE(tmp == (void*)8, "fails hash_table_get after resize"); + + ASSERT_TRUE(hash_table_get(table2, 2, &tmp), "fails hash_table_get after resize"); + ASSERT_TRUE(tmp == (void*)3, "fails hash_table_get after resize"); + + ASSERT_TRUE(hash_table_get(table2, 328642, &tmp), "fails hash_table_get after resize"); + ASSERT_TRUE(tmp == (void*)4, "fails hash_table_get after resize"); + + ASSERT_TRUE(hash_table_get(table2, ~834ULL, &tmp), "fails hash_table_get after resize"); + ASSERT_TRUE(tmp == (void*)5, "fails hash_table_get after resize"); + + ASSERT_TRUE(hash_table_get(table2, 11, &tmp), "fails hash_table_get after resize"); + ASSERT_TRUE(tmp == (void*)7, "fails hash_table_get after resize"); + + ASSERT_TRUE(hash_table_get(table2, 101, &tmp), "fails hash_table_get after resize"); + ASSERT_TRUE(tmp == (void*)9, "fails hash_table_get after resize"); + + hash_table_free(table1, 0); + hash_table_free(table2, 0); +} + +static void* array_list_dup_fun(void* value) { + return (void*)((uint64_t)value + 1); +} + +TEST("array_list") { + struct array_list_t* list1 = array_list_alloc(1, 1, (void*)2); + struct array_list_t* list2; + + ASSERT_TRUE(array_list_count(list1) == 0, "fails array_list_count"); + + ASSERT_TRUE(array_list_get(list1, 0) == (void*)2, "fails array_list_get"); + ASSERT_TRUE(array_list_get(list1, 1) == (void*)2, "fails array_list_get"); + + ASSERT_TRUE(array_list_push(list1, (void*)101) == 0, "fails array_list_push"); + ASSERT_TRUE(array_list_push(list1, (void*)201) == 1, "fails array_list_push"); + ASSERT_TRUE(array_list_push(list1, (void*)301) == 2, "fails array_list_push"); + + ASSERT_TRUE(array_list_get(list1, 0) == (void*)101, "fails array_list_get"); + ASSERT_TRUE(array_list_get(list1, 1) == (void*)201, "fails array_list_get"); + ASSERT_TRUE(array_list_get(list1, 100) == (void*)2, "fails array_list_get"); + + ASSERT_TRUE(array_list_remove(list1, 1) == (void*)201, "fails array_list_remove"); + ASSERT_TRUE(array_list_remove(list1, 3) == (void*)2, "fails array_list_remove"); + ASSERT_TRUE(array_list_remove(list1, 100) == (void*)2, "fails array_list_remove"); + + ASSERT_TRUE(array_list_push(list1, (void*)401) == 1, "fails array_list_push"); + + list2 = array_list_dup(list1, array_list_dup_fun); + array_list_clear(list1, 0); + + ASSERT_TRUE(array_list_count(list1) == 0, "fails array_list_count after clear"); + ASSERT_TRUE(array_list_count(list2) == 3, "fails array_list_count after copy"); + + ASSERT_TRUE(array_list_push(list1, (void*)501) == 0, "fails array_list_push after clear"); + + ASSERT_TRUE(array_list_get(list1, 0) == (void*)501, "fails array_list_get after clear"); + ASSERT_TRUE(array_list_get(list1, 1) == (void*)2, "fails array_list_get after clear"); + ASSERT_TRUE(array_list_get(list1, 100) == (void*)2, "fails array_list_get after clear"); + + ASSERT_TRUE(array_list_get(list2, 0) == (void*)101, "fails array_list_get after copy"); + ASSERT_TRUE(array_list_get(list2, 1) == (void*)401, "fails array_list_get after copy"); + ASSERT_TRUE(array_list_get(list2, 100) == (void*)2, "fails array_list_get after copy"); + + array_list_free(list1, 0); + array_list_free(list2, 0); +} diff --git a/test/testsuite/test.c b/test/testsuite/test.c index 49b23a8..9bd260d 100644 --- a/test/testsuite/test.c +++ b/test/testsuite/test.c @@ -29,7 +29,7 @@ static void nested_exception(void) { ASSERT_NO_EXCEPTION(will_throw, "nested exception caught"); } -TEST_NAME("testsuite validation") +TEST_NAME("testsuite validation"); TEST("Implicit pass") {} diff --git a/userland/Makefile b/userland/Makefile index e9fb829..75329af 100644 --- a/userland/Makefile +++ b/userland/Makefile @@ -35,17 +35,13 @@ all: build .PHONY: build build: libc $(OBJ_DIR)/../userland_files/bin/shell -.PHONY: clean -clean: - -rm -rdf $(OBJ_DIR)/mlibc-meson-setup mlibc/ - - -$(OBJ_DIR)/mlibc-meson-setup: | $(OBJ_DIR)/libc-build/ $(OBJ_DIR)/../userland_files/usr/ - -rm -rdf mlibc/ - git clone https://github.com/managarm/mlibc.git mlibc - git -C mlibc/ checkout v6.3.1 - git -C mlibc/ apply ../mlibc-patch/mlibc-v6-3-1-port.patch +mlibc/: + git clone https://github.com/managarm/mlibc.git $@ + git -C $@ checkout v6.3.1 + git -C $@ apply ../mlibc-patch/mlibc-v6-3-1-port.patch cp -r mlibc-patch/* mlibc/ + +$(OBJ_DIR)/mlibc-meson-setup: | mlibc/ $(OBJ_DIR)/libc-build/ $(OBJ_DIR)/../userland_files/usr/ meson setup \ --native-file mlibc/ci/linux-x86_64-clang.cross-file \ --cross-file mlibc/ci/modulos.cross-file \ @@ -58,6 +54,7 @@ $(OBJ_DIR)/mlibc-meson-setup: | $(OBJ_DIR)/libc-build/ $(OBJ_DIR)/../userland_fi .PHONY: libc libc: $(OBJ_DIR)/mlibc-meson-setup + cp -u -r mlibc-patch/* mlibc/ ninja -C $(OBJ_DIR)/libc-build/ meson install --only-changed -C $(OBJ_DIR)/libc-build/ diff --git a/userland/mlibc-patch/abis/modulos/dev_t.h b/userland/mlibc-patch/abis/modulos/dev_t.h new file mode 100644 index 0000000..e814cf9 --- /dev/null +++ b/userland/mlibc-patch/abis/modulos/dev_t.h @@ -0,0 +1,9 @@ +#ifndef _ABIBITS_DEV_T_H +#define _ABIBITS_DEV_T_H + +#include + +typedef uint32_t dev_t; + +#endif /* _ABIBITS_DEV_T_H */ + diff --git a/userland/mlibc-patch/abis/modulos/fcntl.h b/userland/mlibc-patch/abis/modulos/fcntl.h index 4e7eb2e..058957f 100644 --- a/userland/mlibc-patch/abis/modulos/fcntl.h +++ b/userland/mlibc-patch/abis/modulos/fcntl.h @@ -6,10 +6,10 @@ #define O_PATH 010000000 -#define O_ACCMODE (03 | O_PATH) -#define O_RDONLY 00 -#define O_WRONLY 01 -#define O_RDWR 02 +#define O_ACCMODE (0x03) +#define O_RDONLY 0x1 +#define O_WRONLY 0x2 +#define O_RDWR 0x3 #define O_CREAT 0100 #define O_EXCL 0200 diff --git a/userland/mlibc-patch/abis/modulos/ino_t.h b/userland/mlibc-patch/abis/modulos/ino_t.h new file mode 100644 index 0000000..c7976dc --- /dev/null +++ b/userland/mlibc-patch/abis/modulos/ino_t.h @@ -0,0 +1,11 @@ +#ifndef _ABIBITS_INO_T_H +#define _ABIBITS_INO_T_H + +#include + +typedef uint32_t ino_t; +typedef ino_t ino64_t; + +#endif /* _ABIBITS_INO_T_H */ + + diff --git a/userland/mlibc-patch/abis/modulos/mode_t.h b/userland/mlibc-patch/abis/modulos/mode_t.h index b00bb97..2e00d6f 100644 --- a/userland/mlibc-patch/abis/modulos/mode_t.h +++ b/userland/mlibc-patch/abis/modulos/mode_t.h @@ -1,6 +1,8 @@ #ifndef _ABIBITS_MODE_T_H #define _ABIBITS_MODE_T_H +#include + typedef unsigned int mode_t; #endif /* _ABIBITS_MODE_T_H */ diff --git a/userland/mlibc-patch/abis/modulos/seek-whence.h b/userland/mlibc-patch/abis/modulos/seek-whence.h index b8864db..29360c7 100644 --- a/userland/mlibc-patch/abis/modulos/seek-whence.h +++ b/userland/mlibc-patch/abis/modulos/seek-whence.h @@ -4,8 +4,6 @@ #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 -#define SEEK_DATA 3 -#define SEEK_HOLE 4 #endif /* _ABIBITS_SEEK_WHENCE_H */ diff --git a/userland/mlibc-patch/abis/modulos/stat.h b/userland/mlibc-patch/abis/modulos/stat.h index 9bc4686..bf26331 100644 --- a/userland/mlibc-patch/abis/modulos/stat.h +++ b/userland/mlibc-patch/abis/modulos/stat.h @@ -2,7 +2,11 @@ #define _ABIBITS_STAT_H #include +#include + #include +#include +#include #define S_IFMT 0x0F000 #define S_IFBLK 0x06000 @@ -37,5 +41,7 @@ struct stat { size_t st_size; }; +#define stat64 stat + #endif /* _ABIBITS_STAT_H */ diff --git a/userland/mlibc-patch/sysdeps/modulos/sysdeps.cpp b/userland/mlibc-patch/sysdeps/modulos/sysdeps.cpp index a6a16cf..0f22575 100644 --- a/userland/mlibc-patch/sysdeps/modulos/sysdeps.cpp +++ b/userland/mlibc-patch/sysdeps/modulos/sysdeps.cpp @@ -19,17 +19,34 @@ #include #include +#include + +#include +#include +#include -#include #include +#define stdout 0 +#define stdin 1 #define stderr 2 -#define TRAP asm volatile ("int3" : : : "memory") +#define SECONDS_PER_NANO 0x10000000000 + +extern "C" { +struct file_info_t { + enum { + FILE_TYPE_REG, + FILE_TYPE_DIR, + FILE_TYPE_CHAR + } type; + uint64_t size; +}; +} namespace mlibc { -// internal +// misc void sys_libc_log(const char *message) { ssize_t _ign; @@ -42,11 +59,20 @@ void sys_libc_log(const char *message) { sys_exit(-1); } +// proccess + int sys_tcb_set(void *pointer) { asm volatile ("wrfsbaseq %0" : : "r"(pointer) : "memory"); return 0; } +[[noreturn]] void sys_exit(int status) { + syscall_1(status, 0, 0, SYSCALL_EXIT); + __builtin_unreachable(); +} + +// locking + int sys_futex_wait(int *pointer, int expected, const struct timespec *time) { (void)pointer; (void)expected; @@ -59,10 +85,12 @@ int sys_futex_wake(int *pointer) { return 0; } +// memory + int sys_anon_allocate(size_t size, void **pointer) { uint64_t addr = syscall_1(size, 0, 0, SYSCALL_ALLOC); if (!addr) { - return 1; + return ENOMEM; } memset((void*)addr, 0, size); @@ -77,89 +105,313 @@ int sys_anon_free(void *pointer, size_t size) { return 0; } -int sys_open(const char *pathname, int flags, mode_t mode, int *fd) { +// mlibc assumes that anonymous memory returned by sys_vm_map() is zeroed by the kernel / whatever is behind the sysdeps +int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window) { + (void)hint; + (void)size; + (void)prot; (void)flags; - (void)mode; + (void)fd; + (void)offset; + (void)window; - int f = syscall_1((uint64_t)pathname, 0, 0, SYSCALL_OPEN); - if (!f) { - return 1; - } + return ENOSYS; +} + +int sys_vm_unmap(void *pointer, size_t size) { + (void)pointer; + (void)size; + return 0; +} +// files + +int sys_openat(int dirfd, const char *path, int flags, mode_t mode, int *fd) { + uint64_t f = syscall_4((uint64_t)path, flags, dirfd, SYSCALL_OPENAT, mode); + + if (f == SYSCALL_STS_FAIL) { + return EACCES; + } + *fd = f; return 0; } -int sys_read(int fd, void *buf, size_t count, ssize_t *bytes_read) { - *bytes_read = (ssize_t)syscall_3((uint64_t)fd, (uint64_t)buf, (uint64_t)count, SYSCALL_READ); +int sys_open(const char *pathname, int flags, mode_t mode, int *fd) { + return sys_openat(AT_FDCWD, pathname, flags, mode, fd); +} +int sys_close(int fd) { + syscall_1(fd, 0, 0, SYSCALL_CLOSE); return 0; } int sys_seek(int fd, off_t offset, int whence, off_t *new_offset) { + off_t new_off; + switch (whence) { + case SEEK_END: + struct stat statbuf; + if (sys_stat(fsfd_target::fd, fd, nullptr, 0, &statbuf)) { + return EACCES; + } + offset += statbuf.st_size; + goto set; + case SEEK_CUR: + offset += syscall_1(fd, 0, 0, SYSCALL_TELL); + goto set; + case SEEK_SET: +set: + new_off = syscall_2(fd, (uint64_t)offset, 0, SYSCALL_SEEK); + if ((uint64_t)*new_offset == SYSCALL_STS_FAIL) { + return EACCES; + } + + *new_offset = new_off; + return 0; + + default: + return ENOSYS; + } +} + +int sys_ftruncate(int fd, size_t size) { (void)fd; - (void)offset; - (void)whence; - (void)new_offset; + (void)size; + if (syscall_2(fd, size, 0, SYSCALL_TRUNCATE) == SYSCALL_STS_FAIL) { + return EACCES; + } return 0; } -int sys_close(int fd) { - syscall_1(fd, 0, 0, SYSCALL_CLOSE); +int sys_fallocate(int fd, off_t offset, size_t size) { + return sys_ftruncate(fd, offset + size); +} + +int sys_read(int fd, void *buf, size_t count, ssize_t *bytes_read) { + *bytes_read = (ssize_t)syscall_3((uint64_t)fd, (uint64_t)buf, (uint64_t)count, SYSCALL_READ); + return 0; } -// mlibc assumes that anonymous memory returned by sys_vm_map() is zeroed by the kernel / whatever is behind the sysdeps -int sys_vm_map(void *hint, size_t size, int prot, int flags, int fd, off_t offset, void **window) { - (void)hint; - (void)size; - (void)prot; - (void)flags; - (void)fd; - (void)offset; - (void)window; +int sys_write(int fd, const void *buf, size_t count, ssize_t *bytes_written) { + *bytes_written = syscall_3((uint64_t)fd, (uint64_t)buf, (uint64_t)count, SYSCALL_WRITE); - TRAP; return 0; } -int sys_vm_unmap(void *pointer, size_t size) { - (void)pointer; - (void)size; +int sys_open_dir(const char *path, int *handle) { + int fd; + int sts; + if ((sts = sys_open(path, O_RDWR, 0, &fd))) { + return sts; + } + + uint64_t dfd = syscall_1(fd, 0, 0, SYSCALL_OPEN_DIR); + + if (dfd == SYSCALL_STS_FAIL) { + sys_close(fd); + return ENOTDIR; + } + + *handle = (int)dfd; + return 0; } -// ansi +int sys_read_entries(int handle, void *buffer, size_t max_size, + size_t *bytes_read) { + (void)handle; + (void)buffer; + (void)max_size; + (void)bytes_read; -[[noreturn]] void sys_exit(int status) { - syscall_1(status, 0, 0, SYSCALL_EXIT); - __builtin_unreachable(); + //TODO + return ENOSYS; } -int sys_write(int fd, const void *buf, size_t count, ssize_t *bytes_written) { - *bytes_written = syscall_3((uint64_t)fd, (uint64_t)buf , (uint64_t)count, SYSCALL_WRITE); +int sys_isatty(int fd) { + if (syscall_1(fd, 0, 0, SYSCALL_IS_A_TTY)) { + return 0; //mlibc expects 0 for tty + } + + return ENOTTY; +} + +int sys_faccessat(int dirfd, const char *pathname, int mode, int flags) { + (void)dirfd; + (void)pathname; + (void)mode; + (void)flags; return 0; } -int sys_isatty(int fd) { - switch (fd) { - case 0: - case 1: - case 2: - return 0; //mlibc expects 0 for tty +int sys_access(const char *path, int mode) { + return sys_faccessat(AT_FDCWD, path, mode, 0); +} + +int sys_stat(fsfd_target fsfdt, int fd, const char *path, int flags, + struct stat *statbuf) { + + int f; + int sts; + + switch (fsfdt) { + case fsfd_target::fd: + f = fd; + break; + case fsfd_target::path: + if ((sts = sys_open(path, flags, 0, &f))) { + return sts; + } + case fsfd_target::fd_path: + if ((sts = sys_openat(fd, path, flags, 0, &f))) { + return sts; + } default: - return ENOTTY; + return ENOSYS; + + } + + struct file_info_t buf; + uint64_t res = syscall_2(f, (uint64_t)&buf, 0, SYSCALL_STAT); + + if (res == SYSCALL_STS_FAIL) { + return EACCES; } + + statbuf->st_size = buf.size; + + return 0; } -int sys_clock_get(int clock, time_t *secs, long *nanos) { - (void)clock; - (void)secs; - (void)nanos; - TRAP; +int sys_linkat(int olddirfd, const char *old_path, int newdirfd, const char *new_path, int flags) { + int sts; + int old_f; + int new_f; + + if ((sts = sys_openat(olddirfd, old_path, flags, 0, &old_f))) { + return sts; + } + + if ((sts = sys_openat(newdirfd, new_path, flags, 0, &new_f))) { + return sts; + } + + uint64_t res = syscall_2(old_f, new_f, 0, SYSCALL_LINK); + + if (res == SYSCALL_STS_FAIL) { + return EACCES; + } + + return 0; +} + +int sys_link(const char *old_path, const char *new_path) { + return sys_linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0); +} + +int sys_unlinkat(int fd, const char *path, int flags) { + int sts; + int f; + + if ((sts = sys_openat(fd, path, O_RDWR, 0, &f))) { + return sts; + } + + uint64_t res = syscall_1(f, 0, 0, SYSCALL_UNLINK); + + if (res == SYSCALL_STS_FAIL) { + return EACCES; + } + return 0; } +int sys_rmdir(const char *path) { + return sys_unlinkat(AT_FDCWD, path, AT_REMOVEDIR); +} + +int sys_mkdirat(int dirfd, const char *path, mode_t mode) { + (void)dirfd; + (void)path; + (void)mode; + + return ENOSYS; + //TODO +} + +int sys_mkdir(const char *path, mode_t mode) { + return sys_mkdirat(AT_FDCWD, path, mode); +} + +int sys_renameat(int olddirfd, const char *old_path, int newdirfd, const char *new_path) { + int sts; + if ((sts = sys_linkat(olddirfd, old_path, newdirfd, new_path, 0))) { + return sts; + } + + return sys_unlinkat(olddirfd, old_path, 0); +} + +int sys_rename(const char *path, const char *new_path) { + return sys_renameat(AT_FDCWD, path, AT_FDCWD, new_path); +} + +// working directory + +int sys_getcwd(char *buffer, size_t size) { + if (syscall_2((uint64_t)buffer, size, 0, SYSCALL_GCWD) == SYSCALL_STS_FAIL) { + return EACCES; + } + + return 0; +} + +int sys_fchdir(int fd) { + if (syscall_1(fd, 0, 0, SYSCALL_CCWD) SYSCALL_STS_FAIL) { + return ENOTDIR; + } + + return 0; +} + +int sys_chdir(const char *path) { + int fd; + int sts; + if ((sts = sys_open(path, O_RDWR, 0, &fd))) { + return sts; + } + + sts = sys_fchdir(fd); + sys_close(fd); + + return sts; +} + +// time + +int sys_clock_get(int clock, time_t *secs, long *nanos) { + uint64_t epoch_nanos = syscall_0(0, 0, 0, SYSCALL_EPOCH_TIME); + + switch (clock) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_RAW: + case CLOCK_REALTIME_COARSE: + case CLOCK_MONOTONIC_COARSE: + case CLOCK_BOOTTIME: + case CLOCK_REALTIME_ALARM: + case CLOCK_BOOTTIME_ALARM: + case CLOCK_TAI: + *secs = epoch_nanos / SECONDS_PER_NANO; + *nanos = epoch_nanos % SECONDS_PER_NANO; + return 0; + default: + return ENOSYS; + } +} + + } //namespace mlibc diff --git a/userland/shell/main.c b/userland/shell/main.c index 113cfeb..239954a 100644 --- a/userland/shell/main.c +++ b/userland/shell/main.c @@ -19,29 +19,48 @@ #include int main(int argc, char** argv) { - printf("%s\n", argv[0]); - if (argc > 1) { - printf("%s ", argv[1]); - } + (void)argc; + (void)argv; + + static char buffer[8]; + printf("Shell\n"); - FILE* f = fopen(argv[0], "r"); + FILE* f = fopen("/test.txt", "r"); + if (f == 0) { + perror("Failed to open file"); + return EXIT_FAILURE; + } - if (!f) { - perror("Failed top open file"); + ssize_t bytes_read = fread(buffer, 1, 8, f); + if (bytes_read == -1) { + perror("Failed to read file"); + return EXIT_FAILURE; } - static char buffer[4]; + fclose(f); - ssize_t read = fread(buffer, 1, sizeof(buffer), f); - if (read != 4) { - fprintf(stderr, "Failed to read file\n"); + printf("Bytes read: %lu\nContent: %s", bytes_read, buffer); + + static char input_buf[64]; + printf("Enter file name:\n"); + scanf("%s", input_buf); + + f = fopen(input_buf, "a"); + + if (f == 0) { + perror("Failed to open file"); return EXIT_FAILURE; } - printf("%3s\n", &buffer[1]); + if (fwrite(buffer, 1, bytes_read, f) != bytes_read) { + perror("Failed to write to file"); + return EXIT_FAILURE; + } fclose(f); + printf("Done\n"); + return EXIT_SUCCESS; }