Example #1
0
/** Handles an interrupt (exception code 0). All interrupt handlers
 * that are registered for any of the occured interrupts (hardware
 * 0-5, software 0-1) are called. The scheduler is called if a timer
 * interrupt (hardware 5) or a context switch request (software
 * interrupt 0) occured, or if the currently running thread for the
 * processor is the idle thread.
 *
 * @param cause The Cause register from CP0
 */
void interrupt_handle(uint32_t cause) {
    int this_cpu, i;
    
    if(cause & INTERRUPT_CAUSE_SOFTWARE_0) {
        _interrupt_clear_sw0();
    }

    this_cpu = _interrupt_getcpu();

    /* Exceptions should be handled elsewhere: */
    if((cause  & 0x0000007c) != 0) {
	kprintf("Caught exception, cause %.8x, CPU %i\n", cause, this_cpu);
	KERNEL_PANIC("Exception in interrupt_handle");
    }


    /* Call appropiate interrupt handlers.  Handlers cannot be
     * unregistered, so after the first empty * entry all others are
     * also empty.
     */
    for (i=0; i<CONFIG_MAX_DEVICES; i++) {
	if (interrupt_handlers[i].device == NULL)
	    break;
	
	/* If this handler is registered for any of the interrupts
	 * that occured, call it.
	 */
	if ((cause & interrupt_handlers[i].irq) != 0)
	    interrupt_handlers[i].handler(interrupt_handlers[i].device);
    }


    /* Timer interrupt (HW5) or requested context switch (SW0)
     * Also call scheduler if we're running the idle thread.
     */
    if((cause & (INTERRUPT_CAUSE_SOFTWARE_0 |
		 INTERRUPT_CAUSE_HARDWARE_5)) ||
       scheduler_current_thread[this_cpu] == IDLE_THREAD_TID) {
	scheduler_schedule();
	
	/* Until we have proper VM we must manually fill
	   the TLB with pagetable entries before running code using
	   given pagetable. Note that this method limits pagetable
	   rows (possible mapping pairs) to 16 and can't be used
	   with proper pagetables and VM.

           Note that if you remove this call (which you probably do when
           you implement proper VM), you must manually call _tlb_set_asid
           here. See the implementation of tlb_fill on details how to do that.
        */
    _tlb_set_asid(thread_get_current_thread());
    }
}
Example #2
0
File: tlb.c Project: Amr116/Buenos
void tlb_fill(pagetable_t *pagetable)
{
    if(pagetable == NULL)
	return;

    /* Check that the pagetable can fit into TLB. This is needed until
     we have proper VM system, because the whole pagetable must fit
     into TLB. */
    KERNEL_ASSERT(pagetable->valid_count <= (_tlb_get_maxindex()+1));

    _tlb_write(pagetable->entries, 0, pagetable->valid_count);

    /* Set ASID field in Co-Processor 0 to match thread ID so that
       only entries with the ASID of the current thread will match in
       the TLB hardware. */
    _tlb_set_asid(pagetable->ASID);
}
Example #3
0
/** Handles an interrupt (exception code 0). All interrupt handlers
 * that are registered for any of the occured interrupts (hardware
 * 0-5, software 0-1) are called. The scheduler is called if a timer
 * interrupt (hardware 5) or a context switch request (software
 * interrupt 0) occured, or if the currently running thread for the
 * processor is the idle thread.
 *
 * @param cause The Cause register from CP0
 */
void interrupt_handle(virtaddr_t cause) {
    int this_cpu, i;

    if(cause & INTERRUPT_CAUSE_SOFTWARE_0) {
        _interrupt_clear_sw0();
    }

    this_cpu = _interrupt_getcpu();

    /* Exceptions should be handled elsewhere: */
    if((cause  & 0x0000007c) != 0) {
        kprintf("Caught exception, cause %.8x, CPU %i\n", cause, this_cpu);
        KERNEL_PANIC("Exception in interrupt_handle");
    }


    /* Call appropiate interrupt handlers.  Handlers cannot be
     * unregistered, so after the first empty * entry all others are
     * also empty.
     */
    for (i=0; i<CONFIG_MAX_DEVICES; i++) {
        if (interrupt_handlers[i].device == NULL)
            break;

        /* If this handler is registered for any of the interrupts
         * that occured, call it.
         */
        if ((cause & interrupt_handlers[i].irq) != 0)
            interrupt_handlers[i].handler(interrupt_handlers[i].device);
    }


    /* Timer interrupt (HW5) or requested context switch (SW0)
     * Also call scheduler if we're running the idle thread.
     */
    if((cause & (INTERRUPT_CAUSE_SOFTWARE_0 |
                 INTERRUPT_CAUSE_HARDWARE_5)) ||
            scheduler_current_thread[this_cpu] == IDLE_THREAD_TID) {
        scheduler_schedule();

        /* Until we have proper VM we must manually fill
           the TLB with pagetable entries before running code using
           given pagetable. Note that this method limits pagetable
           rows (possible mapping pairs) to 16 and can't be used
           with proper pagetables and VM.

           Note that if you remove this call (which you probably do when
           you implement proper VM), you must manually call _tlb_set_asid
           here. See the implementation of tlb_fill on details how to do that.
        */
        pagetable_t* pagetable = thread_get_current_thread_entry()->pagetable;

        if(pagetable == NULL)
            return;

        /* Check that the pagetable can fit into TLB. This is needed until
           we have proper VM system, because the whole pagetable must fit
           into TLB. */
        KERNEL_ASSERT(pagetable->valid_count <= (_tlb_get_maxindex()+1));

        _tlb_write(pagetable->entries, 0, pagetable->valid_count);

        /* Set ASID field in Co-Processor 0 to match thread ID so that
           only entries with the ASID of the current thread will match in
           the TLB hardware. */
        _tlb_set_asid(pagetable->ASID);
    }
}
Example #4
0
/**
 * Starts one userland process. The thread calling this function will
 * be used to run the process and will therefore never return from
 * this function. This function asserts that no errors occur in
 * process startup (the executable file exists and is a valid ecoff
 * file, enough memory is available, file operations succeed...).
 * Therefore this function is not suitable to allow startup of
 * arbitrary processes.
 *
 * @executable The name of the executable to be run in the userland
 * process
 */
void process_start(process_id_t pid) {
    thread_table_t *my_entry;
    pagetable_t *pagetable;
    uint32_t phys_page;
    context_t user_context;
    uint32_t stack_bottom;
    elf_info_t elf;
    openfile_t file;
    char *executable;

    int i;

    interrupt_status_t intr_status;

    my_entry = thread_get_current_thread_entry();
    my_entry->process_id = pid;
    executable = process_table[pid].executable;

    /* If the pagetable of this thread is not NULL, we are trying to
       run a userland process for a second time in the same thread.
       This is not possible. */
    KERNEL_ASSERT(my_entry->pagetable == NULL);

    pagetable = vm_create_pagetable(thread_get_current_thread());
    KERNEL_ASSERT(pagetable != NULL);

    intr_status = _interrupt_disable();
    my_entry->pagetable = pagetable;

    /* Update TLB to allow access to the virtual addresses of the
       segments. */
    _tlb_set_asid(thread_get_current_thread());

    _interrupt_set_state(intr_status);

    file = vfs_open((char *) executable);
    /* Make sure the file existed and was a valid ELF file */
    KERNEL_ASSERT(file >= 0);
    KERNEL_ASSERT(elf_parse_header(&elf, file));

    /* Trivial and naive sanity check for entry point: */
    KERNEL_ASSERT(elf.entry_point >= PAGE_SIZE);

    /* Calculate the number of pages needed by the whole process
       (including userland stack). Since we don't have proper tlb
       handling code, all these pages must fit into TLB. */
    KERNEL_ASSERT(elf.ro_pages + elf.rw_pages + CONFIG_USERLAND_STACK_SIZE
            <= _tlb_get_maxindex() + 1);

    /* Allocate and map stack */
    for (i = 0; i < CONFIG_USERLAND_STACK_SIZE; i++) {
        phys_page = pagepool_get_phys_page();
        KERNEL_ASSERT(phys_page != 0);
        vm_map(my_entry->pagetable, phys_page,
                (USERLAND_STACK_TOP & PAGE_SIZE_MASK) - i*PAGE_SIZE, 1);
    }

    /* Put the mapped pages into TLB. Here we again assume that the
       pages fit into the TLB. After writing proper TLB exception
       handling this call should be skipped. */
    intr_status = _interrupt_disable();
    tlb_fill(my_entry->pagetable);
    _interrupt_set_state(intr_status);

    /* Now we may use the virtual addresses of the segments. */

    /* Allocate and map pages for the segments. We assume that
       segments begin at page boundary. (The linker script in tests
       directory creates this kind of segments) */
    for (i = 0; i < (int) elf.ro_pages; i++) {
        phys_page = pagepool_get_phys_page();
        KERNEL_ASSERT(phys_page != 0);
        vm_map(my_entry->pagetable, phys_page,
                elf.ro_vaddr + i*PAGE_SIZE, 1);
    }

    for (i = 0; i < (int) elf.rw_pages; i++) {
        phys_page = pagepool_get_phys_page();
        KERNEL_ASSERT(phys_page != 0);
        vm_map(my_entry->pagetable, phys_page,
                elf.rw_vaddr + i*PAGE_SIZE, 1);
    }

    /* Initialize heap pointer */
    uint32_t heap_end = elf.rw_vaddr + elf.rw_size;
    process_table[pid].heap_end = heap_end;
    if (heap_end % PAGE_SIZE == 0) {
        /* In the unlikely event that the heap should start on the 
           first address of a page we must allocate that page. */
        uint32_t phys_page = pagepool_get_phys_page();
        KERNEL_ASSERT(phys_page != 0);
        vm_map(pagetable, phys_page, heap_end, 1);
    }

    /* Zero the pages. */
    memoryset((void *) elf.ro_vaddr, 0, elf.ro_pages * PAGE_SIZE);
    memoryset((void *) elf.rw_vaddr, 0, elf.rw_pages * PAGE_SIZE);

    stack_bottom = (USERLAND_STACK_TOP & PAGE_SIZE_MASK) -
            (CONFIG_USERLAND_STACK_SIZE - 1) * PAGE_SIZE;
    memoryset((void *) stack_bottom, 0, CONFIG_USERLAND_STACK_SIZE * PAGE_SIZE);

    /* Copy segments */

    if (elf.ro_size > 0) {
        /* Make sure that the segment is in proper place. */
        KERNEL_ASSERT(elf.ro_vaddr >= PAGE_SIZE);
        KERNEL_ASSERT(vfs_seek(file, elf.ro_location) == VFS_OK);
        KERNEL_ASSERT(vfs_read(file, (void *) elf.ro_vaddr, elf.ro_size)
                == (int) elf.ro_size);
    }

    if (elf.rw_size > 0) {
        /* Make sure that the segment is in proper place. */
        KERNEL_ASSERT(elf.rw_vaddr >= PAGE_SIZE);
        KERNEL_ASSERT(vfs_seek(file, elf.rw_location) == VFS_OK);
        KERNEL_ASSERT(vfs_read(file, (void *) elf.rw_vaddr, elf.rw_size)
                == (int) elf.rw_size);
    }


    /* Set the dirty bit to zero (read-only) on read-only pages. */
    for (i = 0; i < (int) elf.ro_pages; i++) {
        vm_set_dirty(my_entry->pagetable, elf.ro_vaddr + i*PAGE_SIZE, 0);
    }

    /* Insert page mappings again to TLB to take read-only bits into use */
    intr_status = _interrupt_disable();
    tlb_fill(my_entry->pagetable);
    _interrupt_set_state(intr_status);


    /* Initialize the user context. (Status register is handled by
       thread_goto_userland) */
    memoryset(&user_context, 0, sizeof (user_context));
    user_context.cpu_regs[MIPS_REGISTER_SP] = USERLAND_STACK_TOP;
    user_context.pc = elf.entry_point;

    thread_goto_userland(&user_context);

    KERNEL_PANIC("thread_goto_userland failed.");
}