/** * Interrupt handler for the CPU status device. Currently the CPU * status device driver only supports ping-ponging interrupts between * CPUs. * * @param device Pointer to the CPU status device */ void cpustatus_interrupt_handle(device_t *dev){ volatile cpu_io_area_t *iobase = (cpu_io_area_t *)dev->io_address; cpu_real_device_t *cpu = (cpu_real_device_t *)dev->real_device; uint32_t this_cpu; KERNEL_ASSERT(dev != NULL || cpu != NULL); this_cpu = _interrupt_getcpu(); /* Check that dev is the status device of this CPU */ if (this_cpu != (dev->type & 0xFF)) return; /* Check that dev has actually generated the interrupt */ if (!(CPU_STATUS_IRQ(iobase->status))) return; spinlock_acquire(&cpu->slock); /* If you really want to do something with inter-cpu interrupts, do it here. */ /* Clear the interrupt */ iobase->command = CPU_COMMAND_CLEAR_IRQ; spinlock_release(&cpu->slock); }
void scheduler_schedule(void) { TID_t t; thread_table_t *current_thread; int this_cpu; this_cpu = _interrupt_getcpu(); spinlock_acquire(&thread_table_slock); current_thread = &(thread_table[scheduler_current_thread[this_cpu]]); if(current_thread->state == THREAD_DYING) { current_thread->state = THREAD_FREE; } else if(current_thread->sleeps_on != 0) { current_thread->state = THREAD_SLEEPING; } else { if(scheduler_current_thread[this_cpu] != IDLE_THREAD_TID) scheduler_add_to_ready_list(scheduler_current_thread[this_cpu]); current_thread->state = THREAD_READY; } t = scheduler_remove_first_ready(); thread_table[t].state = THREAD_RUNNING; spinlock_release(&thread_table_slock); scheduler_current_thread[this_cpu] = t; /* Schedule timer interrupt to occur after thread timeslice is spent */ timer_set_ticks(_get_rand(CONFIG_SCHEDULER_TIMESLICE) + CONFIG_SCHEDULER_TIMESLICE / 2); }
/** 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()); } }
thread_table_t *thread_get_current_thread_entry(void) { TID_t t; interrupt_status_t intr_status; intr_status = _interrupt_disable(); t = scheduler_current_thread[_interrupt_getcpu()]; _interrupt_set_state(intr_status); return &thread_table[t]; }
/** 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(); tlb_fill(thread_get_current_thread_entry()->pagetable); } /* thread_table_t *thread = thread_get_current_thread_entry(); process_id_t pid = thread->process_id; if(pid == -1) { * Not a process thread. Use thread id with most significant bit flipped as ASID. Note: this limits both PROCESS_MAX_PROCESSES and the number of kernel work threads to 128 since ASID is one byte and the ASID address space is divided into two. * uint8_t asid = thread_get_current_thread() | 0x8; _tlb_set_asid(asid); return; } else { * Use PID as ASID. This ensures that threads within a process shares the same ASID _tlb_set_asid(pid); / } }*/ }
/** 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); } }
void scheduler_schedule(void) { TID_t t; TID_t lowestDL = -1; thread_table_t *current_thread; int this_cpu; this_cpu = _interrupt_getcpu(); spinlock_acquire(&thread_table_slock); current_thread = &(thread_table[scheduler_current_thread[this_cpu]]); if(current_thread->state == THREAD_DYING) { current_thread->state = THREAD_FREE; } else if(current_thread->sleeps_on != 0) { current_thread->state = THREAD_SLEEPING; } else { if(scheduler_current_thread[this_cpu] != IDLE_THREAD_TID) scheduler_add_to_ready_list(scheduler_current_thread[this_cpu]); current_thread->state = THREAD_READY; } t = scheduler_ready_to_run.head; /* Initialize the TID lowestDL to a thread with deadline larger than 0 */ /* if possible */ while (lowestDL == -1 && t != -1) { if (thread_table[t].deadline > 0) { lowestDL = t; break; } t = thread_table[t].next; } t = scheduler_ready_to_run.head; if (lowestDL != -1) { /* If a deadline was found, find the lowest deadline */ while (t != -1) { if (thread_table[t].deadline < thread_table[lowestDL].deadline && thread_table[t].deadline > 0) { lowestDL = t; } t = thread_table[t].next; } /* Set the thread with this deadline to the head */ if (lowestDL == scheduler_ready_to_run.tail && lowestDL != scheduler_ready_to_run.head) { scheduler_ready_to_run.tail = thread_table[lowestDL].previous; thread_table[scheduler_ready_to_run.tail].next = -1; thread_table[lowestDL].next = scheduler_ready_to_run.head; thread_table[scheduler_ready_to_run.head].previous = lowestDL; thread_table[lowestDL].previous = -1; scheduler_ready_to_run.head = lowestDL; } else if (lowestDL != scheduler_ready_to_run.head) { thread_table[thread_table[lowestDL].previous].next = thread_table[lowestDL].next; thread_table[thread_table[lowestDL].next].previous = thread_table[lowestDL].previous; thread_table[lowestDL].next = scheduler_ready_to_run.head; thread_table[scheduler_ready_to_run.head].previous = lowestDL; thread_table[lowestDL].previous = -1; scheduler_ready_to_run.head = lowestDL; } } /* Remove the head and run it */ t = scheduler_remove_first_ready(); thread_table[t].state = THREAD_RUNNING; spinlock_release(&thread_table_slock); scheduler_current_thread[this_cpu] = t; /* Schedule timer interrupt to occur after thread timeslice is spent */ timer_set_ticks(_get_rand(CONFIG_SCHEDULER_TIMESLICE) + CONFIG_SCHEDULER_TIMESLICE / 2); }