diff --git a/src/crt/crt0.c b/src/crt/crt0.c index ccdb2eb..35c3fa9 100644 --- a/src/crt/crt0.c +++ b/src/crt/crt0.c @@ -25,12 +25,20 @@ extern int main(int, char **); static void stdio_init(void); +/** + * Entry point for userland programs. + * Sets up stdio, then enters the program's main() before exiting with main()'s + * return code. + */ void _start(void) { stdio_init(); exit(main(0, 0)); } +/** + * Initializes file handles for stdout, stdin, and stderr. + */ void stdio_init(void) { stderr = calloc(1, sizeof(FILE)); diff --git a/src/fs/initrd.c b/src/fs/initrd.c index de00a4a..efd586c 100644 --- a/src/fs/initrd.c +++ b/src/fs/initrd.c @@ -28,6 +28,7 @@ typedef struct { uint32_t size; } initrd_info; +// Defined by linker script extern uint8_t _binary_initrd_img_start[]; extern uint8_t _binary_initrd_img_size[]; @@ -39,6 +40,10 @@ uint32_t initrd_read(vfs_file_info *info, uint32_t count, uint8_t *buffer); int initrd_close(vfs_file_info *info); int initrd_seek(vfs_file_info *info, uint32_t offset, int whence); +/** + * Gets the start of the offset-th file in the initrd. + * @return Pointer to the file, NULL otherwise + */ char *initrd_getfile(uint32_t offset); static const vfs_volume_funcs initrd_funcs = { @@ -50,6 +55,9 @@ static const vfs_volume_funcs initrd_funcs = { initrd_seek }; +/** + * Custom strncmp() implementation used by this driver. + */ int initrd_strncmp(const char *a, const char *b, unsigned int n) { for (unsigned int i = 0; i < n; i++) { @@ -67,10 +75,12 @@ void initrd_init(void) void *initrd_open(const char *file) { + // Iterate through all files in the initrd char *ptr; for (uint32_t i = 0; ptr = initrd_getfile(i), ptr != 0; i++) { uint32_t len = *((uint32_t *)ptr); if (!initrd_strncmp(file, ptr + 4, len)) { + // If we found our file, create an info structure and return it initrd_info *file = (initrd_info *)malloc( sizeof(initrd_info)); file->address = ptr + len + 8; @@ -84,17 +94,18 @@ void *initrd_open(const char *file) int initrd_close(vfs_file_info *info) { - // Nothing to do free(info->fsinfo); return 0; } uint32_t initrd_read(vfs_file_info *info, uint32_t count, uint8_t *buffer) { + // Confirm this is a valid file initrd_info *iinfo = (initrd_info *)info->fsinfo; if (iinfo == 0 || iinfo->address == 0) return 0; + // Attempt to read 'count' bytes, breaking if we reach the end of the file uint32_t i; for (i = 0; i < count; i++) { if (info->pos >= iinfo->size) @@ -129,9 +140,11 @@ char *initrd_getfile(uint32_t offset) { char *ptr = (char *)initrd_start; for (uint32_t i = 0; i < offset; i++) { + // Move to the next file uint32_t len = *((uint32_t *)ptr); uint32_t datalen = *((uint32_t *)(ptr + 4 + len)); ptr += len + datalen + 8; + if (ptr >= (char *)(initrd_start + initrd_size)) return 0; } diff --git a/src/fs/initrd.h b/src/fs/initrd.h index 045b2b9..819fee3 100644 --- a/src/fs/initrd.h +++ b/src/fs/initrd.h @@ -27,3 +27,4 @@ void initrd_init(void); #endif // INITRD_H_ + diff --git a/src/fs/stdio.c b/src/fs/stdio.c index e15003d..aeb8d73 100644 --- a/src/fs/stdio.c +++ b/src/fs/stdio.c @@ -39,6 +39,9 @@ const vfs_volume_funcs stdio_funcs = { void *stdio_open(const char *path) { + /** + * All we need is the correct file handle number. + */ int *id = malloc(sizeof(uint32_t)); if (path[0] == 'o' && path[1] == 'u' && path[2] == 't') @@ -53,13 +56,13 @@ void *stdio_open(const char *path) int stdio_close(vfs_file_info *info) { - // Nothing to do free(info->fsinfo); return 0; } uint32_t stdio_read(vfs_file_info *info, uint32_t count, uint8_t *buffer) { + // TODO? (void)info; (void)count; (void)buffer; @@ -68,6 +71,7 @@ uint32_t stdio_read(vfs_file_info *info, uint32_t count, uint8_t *buffer) uint32_t stdio_write(vfs_file_info *info, uint32_t count, const uint8_t *buffer) { + // For now, output to serial (void)info; for (uint32_t i = 0; i < count; i++) serial_put(buffer[i]); diff --git a/src/fs/stdio.h b/src/fs/stdio.h index bf52fa8..e902ca8 100644 --- a/src/fs/stdio.h +++ b/src/fs/stdio.h @@ -29,3 +29,4 @@ extern const vfs_volume_funcs stdio_funcs; #endif // INITRD_H_ + diff --git a/src/kernel/clock.h b/src/kernel/clock.h index e5e46c4..325a080 100644 --- a/src/kernel/clock.h +++ b/src/kernel/clock.h @@ -47,3 +47,4 @@ void clock_udelay(uint32_t count); uint32_t clock_millis(void); #endif // CLOCK_H_ + diff --git a/src/kernel/elf.c b/src/kernel/elf.c index 77333a9..db268ce 100644 --- a/src/kernel/elf.c +++ b/src/kernel/elf.c @@ -24,6 +24,12 @@ #include +/** + * Finds the section with the given name in the ELF data, e.g. ".bss". + * @param ehdr ELF file header + * @param name Name of the section + * @return Section header of the section, NULL if not found + */ Elf32_Shdr *elf_find_section(Elf32_Ehdr *ehdr, const char *name) { Elf32_Shdr *shdr = (Elf32_Shdr *)((char *)ehdr + ehdr->e_shoff); @@ -80,6 +86,7 @@ uint32_t elf_execve(const char *file, char * const argv[], char * const envp[]) Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfData; Elf32_Phdr *phdr = (Elf32_Phdr *)(elfData + ehdr->e_phoff); + // Search for the loadable program header for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) { if (phdr->p_type == PT_LOAD) { // There should only be one PT_LOAD section... @@ -87,6 +94,7 @@ uint32_t elf_execve(const char *file, char * const argv[], char * const envp[]) while (1); ELF_OFFSET = (uint32_t)malloc(phdr->p_memsz + 8) & ~7; + // Copy the program data into RAM for execution uint8_t *src = (uint8_t *)elfData + phdr->p_offset; uint8_t *dst = (uint8_t *)(ELF_OFFSET + phdr->p_vaddr); for (uint32_t j = 0; j < phdr->p_filesz; j++) @@ -114,7 +122,7 @@ uint32_t elf_execve(const char *file, char * const argv[], char * const envp[]) gotArray[i] += ELF_OFFSET; } - // Run any constructors + // Run any initial constructors Elf32_Shdr *initArraySection = elf_find_section(ehdr, ".init_array"); if (initArraySection != 0) { Elf32_Addr *initArray = (Elf32_Addr *)(ELF_OFFSET + diff --git a/src/kernel/elf.h b/src/kernel/elf.h index 9143401..dd7ffc1 100644 --- a/src/kernel/elf.h +++ b/src/kernel/elf.h @@ -122,3 +122,4 @@ typedef struct { } __attribute__((packed)) Elf32_Phdr; #endif // ELF_H_ + diff --git a/src/kernel/fault.c b/src/kernel/fault.c index db81c1f..f43353b 100644 --- a/src/kernel/fault.c +++ b/src/kernel/fault.c @@ -24,3 +24,4 @@ void HardFault_Handler(void) // TODO have a real fault handler... while (1); } + diff --git a/src/kernel/gpio.c b/src/kernel/gpio.c index 3bd67cb..7831bf5 100644 --- a/src/kernel/gpio.c +++ b/src/kernel/gpio.c @@ -91,3 +91,4 @@ uint32_t gpio_din(GPIO_TypeDef *port, uint32_t pin) { return port->IDR & (1 << pin); } + diff --git a/src/kernel/gpio.h b/src/kernel/gpio.h index ea7e837..ed9dfa6 100644 --- a/src/kernel/gpio.h +++ b/src/kernel/gpio.h @@ -129,3 +129,4 @@ void gpio_dout(GPIO_TypeDef *port, uint32_t pin, uint32_t val); uint32_t gpio_din(GPIO_TypeDef *port, uint32_t pin); #endif // GPIO_H_ + diff --git a/src/kernel/heap.c b/src/kernel/heap.c index 123f7e6..b125d6e 100644 --- a/src/kernel/heap.c +++ b/src/kernel/heap.c @@ -20,8 +20,10 @@ #include "heap.h" +// Defines size of alignment on memory allocations #define HEAP_ALIGN 4 +// Linked list of free'd memory static alloc_t *free_blocks; static void *heap_end; @@ -34,8 +36,10 @@ void heap_init(void *buf) uint32_t heap_free(void) { uint32_t total = 0; + // Count free'd block sizes for (alloc_t *node = free_blocks; node != 0; node = node->next) total += node->size; + // Add remaining free memory to that total return total + (0x20018000 - (uint32_t)heap_end); } @@ -44,26 +48,29 @@ void *malloc(uint32_t size) if (size == 0) return 0; + // Round size to an aligned value size = (size + sizeof(alloc_t) + HEAP_ALIGN) & ~(HEAP_ALIGN - 1); + // Begin searching through free'd blocks to see if we can reuse some memory. alloc_t *node = free_blocks; alloc_t *prev = 0; while (node != 0) { if (node->size >= size) { - // get out of the free chain + // If we can use this chunk, remove it from the free_blocks chain if (prev != 0) prev->next = node->next; else free_blocks = node->next; node->next = 0; - // split alloc if too big - if (node->size > size + 64) { + // If this chunk is really big, give back the extra space + if (node->size > size + 64) { // TODO why 64..? alloc_t *leftover = (alloc_t *)((uint32_t)node + sizeof(alloc_t) + size); leftover->size = node->size - size - sizeof(alloc_t); leftover->next = 0; free((uint8_t *)leftover + sizeof(alloc_t)); + node->size = size; return (void *)((uint8_t *)node + sizeof(alloc_t)); } @@ -75,6 +82,7 @@ void *malloc(uint32_t size) node = node->next; } + // No reusable chunks, take from the end of the heap node = (alloc_t *)heap_end; node->size = size; node->next = 0; @@ -86,9 +94,10 @@ void *malloc(uint32_t size) void *calloc(uint32_t count, uint32_t size) { + // Simply malloc and zero uint8_t *buf = malloc(count * size); for (uint32_t i = 0; i < count * size; i++) - buf[i] = 0; + buf[i] = 0; // TODO safe? return buf; } @@ -97,24 +106,30 @@ void free(void *buf) if (buf == 0) return; + // Get the alloc_t structure of this chunk alloc_t *alloc = (alloc_t *)((uint8_t *)buf - sizeof(alloc_t)); if (alloc->next != 0) return; - // check for adjacent free'd blocks + // Search through the free_blocks list to see if this free chunk can merge + // into an adjacent free chunk. int merged = 0; for (alloc_t *prev = 0, *node = free_blocks; node != 0; prev = node, node = node->next) { + // If the node after the current one is ours if ((uint32_t)node + sizeof(alloc_t) + node->size == (uint32_t)alloc) { - // block before + // Merge by adding our node's size to this one merged |= 1; node->size += sizeof(alloc_t) + alloc->size; break; - //alloc = node; - } else if ((uint32_t)buf + alloc->size == (uint32_t)node) { - // block after + } + // Or, if this current node is the one after ours + else if ((uint32_t)buf + alloc->size == (uint32_t)node) { + // Merge the current node into ours merged |= 1; alloc->size += sizeof(alloc_t) + node->size; alloc->next = node->next; + + // Take the current node's place in the free_blocks chain if (prev != 0) prev->next = alloc; else @@ -123,9 +138,11 @@ void free(void *buf) } } + // If we couldn't merge, simply append to the chain if (merged == 0) { alloc->next = free_blocks; free_blocks = alloc; } } + diff --git a/src/kernel/heap.h b/src/kernel/heap.h index 82d057e..054cb8a 100644 --- a/src/kernel/heap.h +++ b/src/kernel/heap.h @@ -23,6 +23,9 @@ #include +/** + * Internal structure place before memory allocations, to track allocation size. + */ typedef struct { uint32_t size; void *next; @@ -30,13 +33,14 @@ typedef struct { /** * Initializes memory management of the given heap. - * No overflow stuff is done, so... be careful. + * Note: Heap size is not accounted for. * @param buf The heap to use for allocations */ void heap_init(void *buf); /** - * Returns the amount of free, allocatable memory, in bytes. + * Returns the amount of free, allocatable memory, in bytes, assuming that the + * heap has from its initial location to the end of SRAM. * @return Amount of free memory in bytes */ uint32_t heap_free(void); @@ -64,3 +68,4 @@ void *calloc(uint32_t count, uint32_t size); void free(void *buf); #endif // HEAP_H_ + diff --git a/src/kernel/init.c b/src/kernel/init.c index 48e1800..01bff42 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -27,6 +27,7 @@ #include #include +// Heap goes after the end of the BSS section extern uint8_t __bss_end__; extern void user_main(void); @@ -34,15 +35,17 @@ void init_idle(void); int main(void) { + // Disable interrupts while we get ready asm("cpsid i"); - // disable cached writes for precise debug info + + // Disabling cached writes can help give more precise debug info //*((uint32_t *)0xE000E008) |= 2; - // prepare flash latency for 80MHz operation + // Prepare flash latency for 80MHz operation FLASH->ACR &= ~(FLASH_ACR_LATENCY); FLASH->ACR |= FLASH_ACR_LATENCY_4WS; - // init core components + // Initialize core components clock_init(); heap_init(&__bss_end__); gpio_init(); @@ -51,17 +54,21 @@ int main(void) vfs_init(); initrd_init(); - // enable FPU + // Enable FPU (TODO why isn't it enabled?) //SCB->CPACR |= (0xF << 20); + // Begin multi-tasking (enables interrupts) task_init(init_idle, 512); while (1); } void init_idle(void) { + // Start the main userspace task with a 4kB stack task_start(user_main, 4096); + // Idle while (1) clock_delay(10); } + diff --git a/src/kernel/serial.c b/src/kernel/serial.c index 2737b7d..2b5cb2d 100644 --- a/src/kernel/serial.c +++ b/src/kernel/serial.c @@ -28,9 +28,9 @@ void serial_init(uint32_t baud) gpio_mode(GPIOA, 3, ALTERNATE); GPIOA->AFR[0] &= ~(0x0000FF00); GPIOA->AFR[0] |= 0x00007700; - RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN; - // start usart device + // Start usart device + RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN; USART2->BRR = 80000000L / baud; USART2->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } @@ -58,3 +58,4 @@ void serial_gets(char *buf, int max) } while (buf[index] != '\r' && index++ < max); buf[index] = '\0'; } + diff --git a/src/kernel/serial.h b/src/kernel/serial.h index e721d6c..9dd75e1 100644 --- a/src/kernel/serial.h +++ b/src/kernel/serial.h @@ -46,3 +46,4 @@ char serial_get(void); void serial_gets(char *buf, int max); #endif // SERIAL_H_ + diff --git a/src/kernel/svc.c b/src/kernel/svc.c index 7c81e5f..358fa3d 100644 --- a/src/kernel/svc.c +++ b/src/kernel/svc.c @@ -31,6 +31,7 @@ extern void vfs_svc(uint32_t, uint32_t *, uint32_t *args); void SVC_Handler(void) { uint32_t *stack; + // Get the appropriate stack (either kernel or process) asm("\ tst lr, #4; \ ite eq; \ @@ -44,13 +45,13 @@ void SVC_Handler(void) { uint32_t *args = (uint32_t *)stack[2]; switch (svc_number) { - case -1: + case -1: // TODO why? case 0: /* Task-related calls * 0 - _exit * 1 - fork * 2 - getpid * 3 - waitpid - * 4 - sbrk (TODO bad) + * 4 - sbrk (TODO poor implementation) * 5 - execve */ task_svc(min_number, ret, args); diff --git a/src/kernel/task.c b/src/kernel/task.c index 304e608..b6f4fc1 100644 --- a/src/kernel/task.c +++ b/src/kernel/task.c @@ -146,8 +146,9 @@ void task_purge(void) } // Free this thread's stack, and task data. - // Since we're single core, no one else can claim this memory until - // a task switch, after which we're done with this memory anyway. + // The scheduler still needs to use the data we're about to free; however, + // our single-core implementation keeps us safe from anyone else claiming + // this memory in the meantime. free(task_current->stack); free(task_current->heap); free(task_current); @@ -168,7 +169,8 @@ void task_doexit(void) /** * 'Prepares' task for running. - * Calls the task's main code, setting task_doexit() (_exit) as the return point. + * Calls the task's main code after making task_doexit() (_exit) main's return + * point. */ __attribute__ ((naked)) void task_crt0(void) @@ -191,7 +193,7 @@ task_t *task_create(void (*code)(void), uint16_t stackSize) t->heap = 0; t->stack = (uint32_t *)malloc(stackSize); - void *sp = (uint8_t *)t->stack + stackSize - 68; // excep. stack + regs + void *sp = (uint8_t *)t->stack + stackSize - 68; // exception stack + regs t->sp = sp; /* @@ -215,6 +217,7 @@ task_t *task_create(void (*code)(void), uint16_t stackSize) void task_init(void (*init)(void), uint16_t stackSize) { + // Create a dummy current task that we'll "exit" from task_current = (task_t *)malloc(sizeof(task_t)); task_current->next = 0; task_current->stack = 0; // free() is called on this @@ -222,10 +225,12 @@ void task_init(void (*init)(void), uint16_t stackSize) task_current->status.state = TASK_SLEEPING; task_current->status.value = 1000; + // Place the init task in the queue to take the dummy task's place task_queue = task_create(init, stackSize); task_disable = 0; + // Enter userspace process mode // bit 0 - priv, bit 1 - psp/msp asm("\ isb; \ @@ -237,7 +242,7 @@ void task_init(void (*init)(void), uint16_t stackSize) msr control, r0; \ "); - // exit the current (fake) task + // Exit the current (fake) task task_doexit(); } @@ -307,9 +312,9 @@ void PendSV_Handler(void) if (task_disable != 0) asm("bx lr"); - // TODO get back to c, implement task sleeping + // TODO get back to c, implement task sleeping TODO did we do this? - // Save current stack pointer + // Finish saving current state asm("\ mrs r0, psp; \ isb; \ diff --git a/src/kernel/task.h b/src/kernel/task.h index 5875e3c..b67147e 100644 --- a/src/kernel/task.h +++ b/src/kernel/task.h @@ -25,7 +25,6 @@ #define WIFEXITED(w) (w & (1 << 8)) #define WEXITSTATUS(w) (w & 0xFF) - typedef uint16_t pid_t; /** @@ -66,23 +65,44 @@ void task_init(void (*init)(void), uint16_t stackSize); void task_start(void (*task)(void), uint16_t stackSize); /** - * Allows task switching to be disabled, for low-level actions. + * Allows task switching to be disabled, for low-level/time-critical operations. * Multiple holds can be placed, and all must be removed to continue task * switching. * @param hold non-zero for hold, zero to remove hold */ void task_hold(uint8_t hold); +/** + * Puts the task to sleep for the given duration. + * @param ms Milliseconds to sleep for + */ void task_sleep(uint32_t ms); +/** + * Called by the task when it's ready to exit. + * @param code Task's exit code + */ void task_exit(int code); +/** + * Forks the task into two processes. + */ int task_fork(void); +/** + * Gets the task's PID. + */ pid_t task_getpid(void); +/** + * Waits for a task to change state (TODO confirm). + */ pid_t task_waitpid(pid_t pid, int *wstatus, int options); +/** + * TODO + */ void *task_sbrk(uint32_t bytes); #endif // TASK_H_ + diff --git a/src/kernel/vfs.c b/src/kernel/vfs.c index 8d4b975..b3af7fc 100644 --- a/src/kernel/vfs.c +++ b/src/kernel/vfs.c @@ -61,13 +61,14 @@ void vfs_svc(uint32_t n, uint32_t *ret, uint32_t *args) void vfs_init(void) { + // Mark all volumes and files as empty for (int i = 0; i < VFS_MAX_VOLS; i++) vfs_volumes[i].flags = 0; for (int i = 0; i < VFS_MAX_FILES; i++) vfs_files[i].flags = 0; vfs_mount(&stdio_funcs, 0); - // order is crucial + // Order of opening here may be important TODO confirm vfs_open(" in", VFS_FILE_READ); vfs_open(" out", VFS_FILE_WRITE); vfs_open(" err", VFS_FILE_WRITE); diff --git a/src/kernel/vfs.h b/src/kernel/vfs.h index ccad67b..be73941 100644 --- a/src/kernel/vfs.h +++ b/src/kernel/vfs.h @@ -143,3 +143,4 @@ int vfs_seek(int fd, int32_t offset, int whence); int32_t vfs_tell(int fd); #endif // VFS_H_ + diff --git a/src/libgpio/gpio.c b/src/libgpio/gpio.c index 522817f..42c8ac2 100644 --- a/src/libgpio/gpio.c +++ b/src/libgpio/gpio.c @@ -1,7 +1,28 @@ +/** + * @file gpio.c + * Userspace library for GPIO access + * + * Copyright (C) 2018 Clyne Sullivan + * + * 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 "gpio.h" #include +// SVC calls get messed up by GCC's optimization... #define NOOPTIMIZE __attribute__((optimize(0))) NOOPTIMIZE diff --git a/src/libgpio/gpio.h b/src/libgpio/gpio.h index 7d7c8f7..41156b0 100644 --- a/src/libgpio/gpio.h +++ b/src/libgpio/gpio.h @@ -1,3 +1,23 @@ +/** + * @file gpio.h + * Userspace library for GPIO access + * + * Copyright (C) 2018 Clyne Sullivan + * + * 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 STMOS_GPIO_H_ #define STMOS_GPIO_H_ @@ -55,3 +75,4 @@ void gpioWrite(gpio_pin_t pin, int value); int gpioRead(gpio_pin_t pin); #endif // STMOS_GPIO_H_ + diff --git a/src/user/user.c b/src/user/user.c index f695936..acdf85b 100644 --- a/src/user/user.c +++ b/src/user/user.c @@ -23,7 +23,8 @@ void user_main(void) { - //gpio(GPIO_MODE, 5, OUTPUT); + // Just load the init program from the initrd. + // (A:/ is stdio, B:/ is initrd) execve("B:/init", 0, 0); }