/* generic write for an fd */ ssize_t sys_write(int fd, char *buf, size_t n) { struct file_s *flip = get_file(fd); if (flip == NULL) return ERROR; if (flip->f_op == NULL) debug_panic("sys_read: f_op is NULL"); if (flip->f_op->write == NULL) debug_panic("sys_read: write func pointer is NULL"); return flip->f_op->write(flip, buf, n); }
/* generic lseek */ size_t sys_lseek(int fd, off_t offset, int whence) { struct file_s *flip = get_file(fd); if (flip == NULL) return ERROR; if (flip->f_op == NULL) debug_panic("sys_read: f_op is NULL"); if (flip->f_op->lseek == NULL) debug_panic("sys_lseek: lseek func pointer is NULL"); return flip->f_op->lseek(flip, offset, whence); }
/* generic flush */ int sys_flush(int fd) { struct file_s *flip = get_file(fd); if (flip == NULL) return ERROR; if (flip->f_op == NULL) debug_panic("sys_flush: f_op is NULL"); if (flip->f_op->flush == NULL) debug_panic("sys_flush: flush func pointer is NULL"); return flip->f_op->flush(flip); }
struct thread *fault_page(struct thread *image) { uint32_t cr2; /* Get faulting address from register CR2 */ cr2 = cpu_get_cr2(); /* If in kernelspace, panic */ if ((image->cs & 0x3) == 0) { /* i.e. if it was kernelmode */ debug_printf("page fault at %x, ip = %x, frame %x, esp = %x\n", cr2, image->eip, page_get(cr2), image->esp); debug_panic("page fault exception"); } if (cr2 >= image->stack && cr2 < image->stack + SEGSZ) { /* allocate stack */ mem_alloc(cr2 & ~0xFFF, PAGESZ, PF_PRES | PF_RW | PF_USER); return image; } else { /* fault */ debug_printf("%d: %s: page fault at %x, ip = %x, frame %x\n", image->proc->pid, image->proc->name, cr2, image->eip, page_get(cr2)); debug_printf("user stack: %x - %x\n", image->stack, image->stack + SEGSZ); debug_printf("user stack dump: (ebp = %x)\n", image->ebp); debug_dumpi((void*) image->useresp, 30); process_freeze(image->proc); return thread_send(image, image->proc->pid, PORT_PAGE, NULL); } }
struct thread *fault_double(struct thread *image) { /* Can only come from kernel problems */ debug_printf("DS:%x CS:%x\n", image->ds, image->cs); debug_panic("double fault exception"); return NULL; }
/* make new device file in /dev folder, with type, major and minor numbers */ int fs_make_dev(const char *name, int type, dev_t major, dev_t minor) { struct inode_s *ino, *dev; ino = dev = NULL; if ( (dev = find_inode(NULL, "/dev", FS_SEARCH_CREAT)) == NULL) debug_panic("fs_make_dev: error searching/creating /dev"); if ( (ino = find_inode(dev, name, FS_SEARCH_CREAT)) == NULL) goto err; ino->i_zone[0] = major; ino->i_zone[1] = minor; ino->i_dirty = 1; release_inode(dev); release_inode(ino); return OK; err: release_inode(dev); release_inode(ino); return ERROR; }
void schedule_remove(struct thread *thread) { struct thread *temp; if (!schedule_queue.out) { debug_printf("scheduler empty, but tried to remove %x\n", thread); debug_panic(""); return; } if (schedule_queue.out == thread) { schedule_queue.out = thread->next; return; } for (temp = schedule_queue.out; temp->next; temp = temp->next) { if (temp->next == thread) { temp->next = thread->next; if (schedule_queue.in == thread) { schedule_queue.in = temp; } thread->next = NULL; break; } } }
struct thread *syscall_exit(struct thread *image) { if (image->proc->pid == 1) { debug_panic("init died"); } process_switch(process_get(1)); process_kill(image->proc); return thread_switch(image, schedule_next()); }
struct thread *fault_generic(struct thread *image) { /* If in kernelspace, panic */ if ((image->cs & 0x3) == 0) { debug_printf("EIP:%x NUM:%d ERR:%x\n", image->eip, image->num, image->err); debug_panic("unknown exception"); } process_freeze(image->proc); return thread_send(image, image->proc->pid, PORT_ILL, NULL); }
void fault_generic(struct thread *image) { /* If in kernelspace, panic */ if (image->cs & 0x3) { debug_printf("EIP:%x NUM:%d ERR:%x\n", image->eip, image->num, image->err); debug_panic("unknown exception"); } // pause thread thread_save(image); image->state = TS_PAUSED; image->fault = FV_ACCS; // add to fault queue fault_push(image); }
void schedule_insert(struct thread *thread) { if (!schedule_queue.out && schedule_queue.in) { debug_printf("scheduler inconsistency\n"); debug_panic(""); } if (!schedule_queue.in) { schedule_queue.out = thread; schedule_queue.in = thread; thread->next = NULL; } else { schedule_queue.in->next = thread; schedule_queue.in = thread; thread->next = NULL; } }
/* main The portable side of a multiboot-loaded kernel starts here. It is called by some bootstrap code, initialises the system and begins running programs. We'll assume the following: 1. The kernel is mapped into 0xC0400000 (4MB into top 1GB) logical, and 0x00400000 physical. 2. The lowest 2x4MB pages of physical RAM are mapped from 0xC0000000 logical. 3. We're running on an architecture with 4KB page sizes, and the multiboot data will be stored below the lowest 1MB boundary. It's 4.30am, and I'll try to worry about portability later. A multiboot bootloader is also essential. => mbd = ptr to multiboot data about the system we're running in magic = special word passed by the bootloader to prove it is multiboot capable */ void _main(multiboot_info_t *mbd, unsigned int magic) { if(DEBUG) debug_initialise(); dprintf("[core] %s rev %s" " " __TIME__ " " __DATE__ " (built with GCC " __VERSION__ ")\n", KERNEL_IDENTIFIER, SVN_REV); if(magic != MULTIBOOT_MAGIC) /* as defined in the multiboot spec */ dprintf("*** warning: bootloader magic was %x (expecting %x).\n", magic, MULTIBOOT_MAGIC); /* initialise critical infrastructure of the kernel */ locks_initialise(KernelPhysLockPage); /* ---- multiboot + SMP data must be preserved during these calls ------- */ /* discover processor(s), get exception handling running */ if(mp_initialise()) goto goforhalt; /* parse modules payloaded by the boot loader, best halt if there are none? */ if(payload_preinit(mbd)) goto goforhalt; /* initialise the memory manager or halt if it fails */ if(vmm_initialise(mbd)) goto goforhalt; /* initialise the interrupt and hardware driver subsystem */ if(int_initialise()) goto goforhalt; /* bring up the remaining processor(s) cores */ if(mp_post_initialise()) goto goforhalt; /* initialise process and thread management, prepare first process */ if(proc_initialise()) goto goforhalt; /* ---- multiboot + SMP data is no longer required by this point ------- */ /* hocus pocus, mumbo jumbo, black magic */ sched_initialise(); /* enable interrupts and start the execution of userspace processes */ /* shouldn't fall through... but halt if so */ goforhalt: /* halt here - there is nothing to return to */ debug_panic("unexpected end-of-boot"); }
struct thread *syscall_exit(struct thread *image) { struct thread *new_image; uint16_t pid; uint32_t parent; pid = image->proc->pid; if (pid == 1) { debug_panic("init died"); } parent = image->proc->parent->pid; process_switch(process_get(1)); new_image = thread_send(image, parent, PORT_CHILD, NULL); image->proc->status = image->eax; process_kill(image->proc); return new_image; }
static void *heap_valloc(uintptr_t size) { static uintptr_t brk = KERNEL_HEAP; int i; if (brk + size >= KERNEL_HEAP_END) { /* out of memory */ /* print analysis */ for (i = 0; i < 16; i++) { debug_printf("%d: %d\n", i, analysis[i]); } debug_panic("out of virtual memory"); return NULL; } else { page_set(brk, page_fmt(frame_new(), PF_PRES | PF_RW)); mem_alloc(brk, size, PF_PRES | PF_RW); brk += size; return (void*) (brk - size); } }
void fault_page(struct thread *image) { uint32_t cr2; /* Get faulting address from register CR2 */ cr2 = cpu_get_cr2(); /* If in kernelspace, panic */ if ((image->cs & 0x3) == 0) { /* i.e. if it was kernelmode */ debug_printf("page fault at %x, ip = %x, frame %x, esp = %x\n", cr2, image->eip, page_get(cr2), image->esp); debug_panic("page fault exception"); } /* fault */ thread_save(image); image->fault_addr = cr2; image->fault = FV_PAGE; image->state = TS_PAUSED; // add to fault queue fault_push(image); }
struct thread *init(struct multiboot *mboot, uint32_t mboot_magic) { struct process *idle, *init; struct module *module; struct memory_map *mem_map; size_t mem_map_count, i, addr; uintptr_t boot_image_size; void *boot_image; struct elf32_ehdr *init_image; struct elf32_ehdr *dl_image; /* initialize debugging output */ debug_init(); debug_printf("Rhombus Operating System Kernel v0.8a\n"); /* check multiboot header */ if (mboot_magic != 0x2BADB002) { debug_panic("bootloader is not multiboot compliant"); } /* touch pages for the kernel heap */ for (i = KSPACE; i < KERNEL_HEAP_END; i += SEGSZ) { page_touch(i); } /* identity map kernel boot frames */ for (i = KSPACE + KERNEL_BOOT; i < KSPACE + KERNEL_BOOT_END; i += PAGESZ) { page_set(i, page_fmt(i - KSPACE, PF_PRES | PF_RW)); } /* parse the multiboot memory map to find the size of memory */ mem_map = (void*) (mboot->mmap_addr + KSPACE); mem_map_count = mboot->mmap_length / sizeof(struct memory_map); for (i = 0; i < mem_map_count; i++) { if (mem_map[i].type == 1 && mem_map[i].base_addr_low <= 0x100000) { for (addr = 0; addr < mem_map[i].length_low; addr += PAGESZ) { frame_add(mem_map[i].base_addr_low + addr); } } } /* bootstrap process 0 (idle) */ idle = process_alloc(); idle->space = cpu_get_cr3(); idle->user = 0; /* fork process 1 (init) and switch */ init = process_clone(idle, NULL); process_switch(init); /* get multiboot module information */ if (mboot->mods_count < 3) { if (mboot->mods_count < 2) { if (mboot->mods_count < 1) { debug_panic("no boot or init or dl modules found"); } else { debug_panic("no boot or dl modules found"); } } else { debug_panic("no dl module found"); } } module = (void*) (mboot->mods_addr + KSPACE); init_image = (void*) (module[0].mod_start + KSPACE); boot_image = (void*) (module[1].mod_start + KSPACE); dl_image = (void*) (module[2].mod_start + KSPACE); boot_image_size = module[1].mod_end - module[1].mod_start; /* move boot image to BOOT_IMAGE in userspace */ mem_alloc(BOOT_IMAGE, boot_image_size, PF_PRES | PF_USER | PF_RW); memcpy((void*) BOOT_IMAGE, boot_image, boot_image_size); /* bootstrap thread 0 in init */ thread_bind(init->thread[0], init); init->thread[0]->useresp = init->thread[0]->stack + SEGSZ; init->thread[0]->esp = (uintptr_t) &init->thread[0]->num; init->thread[0]->ss = 0x23; init->thread[0]->ds = 0x23; init->thread[0]->cs = 0x1B; init->thread[0]->eflags = cpu_get_eflags() | 0x3200; /* IF, IOPL = 3 */ /* bootstrap idle thread */ idle->thread[0] = &__idle_thread; __idle_thread.proc = idle; /* load dl */ if (elf_check_file(dl_image)) { debug_panic("dl.so is not a valid ELF executable"); } elf_load_file(dl_image); /* execute init */ if (elf_check_file(init_image)) { debug_panic("init is not a valid ELF executable"); } elf_load_file(init_image); init->thread[0]->eip = init_image->e_entry; /* register system calls */ int_set_handler(SYSCALL_SEND, syscall_send); int_set_handler(SYSCALL_DONE, syscall_done); int_set_handler(SYSCALL_WHEN, syscall_when); int_set_handler(SYSCALL_RIRQ, syscall_rirq); int_set_handler(SYSCALL_ALSO, syscall_also); int_set_handler(SYSCALL_STAT, syscall_stat); int_set_handler(SYSCALL_PAGE, syscall_page); int_set_handler(SYSCALL_PHYS, syscall_phys); int_set_handler(SYSCALL_FORK, syscall_fork); int_set_handler(SYSCALL_EXIT, syscall_exit); int_set_handler(SYSCALL_STOP, syscall_stop); int_set_handler(SYSCALL_WAKE, syscall_wake); int_set_handler(SYSCALL_GPID, syscall_gpid); int_set_handler(SYSCALL_TIME, syscall_time); int_set_handler(SYSCALL_USER, syscall_user); int_set_handler(SYSCALL_AUTH, syscall_auth); int_set_handler(SYSCALL_PROC, syscall_proc); int_set_handler(SYSCALL_KILL, syscall_kill); int_set_handler(SYSCALL_VM86, syscall_vm86); int_set_handler(SYSCALL_NAME, syscall_name); int_set_handler(SYSCALL_REAP, syscall_reap); /* register fault handlers */ int_set_handler(FAULT_DE, fault_float); int_set_handler(FAULT_DB, fault_generic); int_set_handler(FAULT_NI, fault_generic); int_set_handler(FAULT_BP, fault_generic); int_set_handler(FAULT_OF, fault_generic); int_set_handler(FAULT_BR, fault_generic); int_set_handler(FAULT_UD, fault_generic); int_set_handler(FAULT_NM, fault_nomath); int_set_handler(FAULT_DF, fault_double); int_set_handler(FAULT_CO, fault_float); int_set_handler(FAULT_TS, fault_generic); int_set_handler(FAULT_NP, fault_generic); int_set_handler(FAULT_SS, fault_generic); int_set_handler(FAULT_GP, fault_gpf); int_set_handler(FAULT_PF, fault_page); int_set_handler(FAULT_MF, fault_float); int_set_handler(FAULT_AC, fault_generic); int_set_handler(FAULT_MC, fault_generic); int_set_handler(FAULT_XM, fault_nomath); /* start timer (for preemption) */ timer_set_freq(64); /* initialize FPU/MMX/SSE */ cpu_init_fpu(); /* drop to usermode, scheduling the next thread */ debug_printf("dropping to usermode\n"); return thread_switch(NULL, schedule_next()); }
/* lock_gate Allow multiple threads to read during a critical section, but only allow one writer. To avoid deadlocks, it keeps track of the exclusive owner - so that a thread can enter a gate as a reader and later upgrade to a writer, or start off a writer and call a function that requires a read or write lock. This function will block and spin if write access is requested and other threads are reading/writing, or read access is requested and another thread is writing. => gate = lock structure to modify flags = set either LOCK_READ or LOCK_WRITE, also set LOCK_SELFDESTRUCT to mark a gate as defunct, causing other threads to fail if they try to access it <= success or a failure code */ kresult lock_gate(rw_gate *gate, unsigned int flags) { #ifndef UNIPROC kresult err = success; unsigned int caller; #ifdef LOCK_TIME_CHECK unsigned long long ticks = x86_read_cyclecount(); #endif /* sanity checks */ if(!gate) return e_failure; if(!cpu_table) return success; /* only one processor running */ #ifdef LOCK_DEBUG if(cpu_table) { LOCK_DEBUG("[lock:%i] -> lock_gate(%p, %x) by thread %p\n", CPU_ID, gate, flags, cpu_table[CPU_ID].current); } else { LOCK_DEBUG("[lock:%i] -> lock_gate(%p, %x) during boot\n", CPU_ID, gate, flags); } #endif /* cpu_table[CPU_ID].current cannot be lower than the kernel virtual base so it won't collide with the processor's CPU_ID, which is used to identify the owner if no thread is running */ if(cpu_table[CPU_ID].current) caller = (unsigned int)cpu_table[CPU_ID].current; else caller = (CPU_ID) + 1; /* zero means no owner, CPU_IDs start at zero... */ while(1) { lock_spin(&(gate->spinlock)); /* we're in - is the gate claimed? */ if(gate->owner) { /* it's in use - but is it another thread? */ if(gate->owner != caller) { /* another thread has it :( perform checks */ /* this lock is defunct? */ if(gate->flags & LOCK_SELFDESTRUCT) { err = e_failure; goto exit_lock_gate; } /* if we're not trying to write and the owner isn't writing and a writer isn't waiting, then it's safe to progress */ if(!(flags & LOCK_WRITE) && !(gate->flags & LOCK_WRITE) && !(gate->flags & LOCK_WRITEWAITING)) goto exit_lock_gate; /* if we're trying to write then set a write-wait flag. when this flag is set, stop allowing new reading threads. this should prevent writer starvation */ if(flags & LOCK_WRITE) gate->flags |= LOCK_WRITEWAITING; } else { /* if the gate's owned by this thread, then carry on */ gate->refcount++; /* keep track of the number of times we're entering */ goto exit_lock_gate; } } else { /* no one owns this gate, so make our mark */ gate->owner = caller; gate->flags = flags; gate->refcount = 1; /* first in */ goto exit_lock_gate; } unlock_spin(&(gate->spinlock)); /* small window of opportunity for the other thread to release the gate :-/ */ /* hint to newer processors that this is a spin-wait loop or NOP for older processors */ __asm__ __volatile__("pause"); #ifdef LOCK_TIME_CHECK if((x86_read_cyclecount() - ticks) > LOCK_TIMEOUT) { /* prevent other cores from trashing the output debug while we dump this info */ lock_spin(&lock_time_check_lock); KOOPS_DEBUG("[lock:%i] OMGWTF waited too long for gate %p to become available (flags %x)\n" " lock is owned by %p", CPU_ID, gate, flags, gate->owner); if(gate->owner > KERNEL_SPACE_BASE) { thread *t = (thread *)(gate->owner); KOOPS_DEBUG(" (thread %i process %i on cpu %i)", t->tid, t->proc->pid, t->cpu); } KOOPS_DEBUG("\n"); debug_stacktrace(); unlock_spin(&lock_time_check_lock); debug_panic("deadlock in kernel: we can't go on together with suspicious minds"); } #endif } exit_lock_gate: /* release the gate so others can inspect it */ unlock_spin(&(gate->spinlock)); LOCK_DEBUG("[lock:%i] locked %p with %x\n", CPU_ID, gate, flags); return err; #endif }