/* Copyright 2013 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Context switching */ #include "config.h" #include "cpu.h" .section .ram_code /** * Task context switching * * Change the task scheduled after returning from an interruption. * * This function must be called in interrupt context. * * Save the registers of the current task below the interrupt context on * its task, then restore the live registers of the next task and set the * process stack pointer to the new stack. * * $r0: pointer to the task to switch from * $r1: pointer to the task to switch to * $r2: pointer to the stack where the interrupt entry context is saved * * the structure of the saved context on the stack is : * (top to bottom) * sp, lp, fp, r15, r5, r4, r3, r2, r1, r0, r10, r9, r8, r7, r6, ipc, ipsw * interrupt entry frame <|> */ .global __switch_task __switch_task: /* get the (new) highest priority task pointer in r0 */ jal next_sched_task movi55 $r3, 0 /* pointer to the current task (which are switching from) */ lwi.gp $r1, [ + current_task] /* reset the re-scheduling request */ swi.gp $r3, [ + need_resched] /* Nothing to do: let's return to keep the same task scheduled */ beq $r1, $r0, 1f /* save our new scheduled task */ swi.gp $r0, [ + current_task] /* get the program status word saved at exception entry */ mfsr $r4, $IPSW /* to save SP_ADJ bit */ /* get the task program counter saved at exception entry */ mfsr $r5, $IPC /* get the new scheduled task stack pointer */ lw $r3, [$r0] /* save ipsw, ipc, r6, r7, r8, r9, r10 on the current process stack */ smw.adm $r4, [$fp], $r10, 0 /* restore ipsw, ipc, r6, r7, r8, r9, r10 from the next stack context */ lmw.bim $r4, [$r3], $r10, 0 /* set the program status word to restore SP_ADJ bit */ mtsr $r4, $IPSW /* set the task program counter to restore at exception exit */ mtsr $r5, $IPC /* save the task stack pointer in its context */ sw $fp, [$r1] /* barrier: ensure IPC is taken into account before IRET */ dsb /* exception frame pointer for the new task */ mov55 $fp, $r3 1: /* un-pile the interruption entry context */ /* isr exit */ jal end_irq_handler /* restore r0-r5 */ lmw.bim $r0, [$fp], $r5, 0 /* restore r15, fp, lp and sp */ lmw.bi $r15, [$fp], $r15, 0xb /* restore PC and PSW */ iret .text /** * Start the task scheduling. * * $r0 is a pointer to task_stack_ready, which is set to 1 after * the task stack is set up. */ .global __task_start __task_start: /* * Disable global interrupt here to ensure below sequence won't be * broken. The "iret" instruction of ISR will enable GIE again. */ setgie.d /* area used as thread stack for the first switch */ la $r3, scratchpad movi55 $r4, 1 movi55 $r2, 0 /* syscall 3rd parameter : not an IRQ emulation */ movi55 $r1, 0 /* syscall 2nd parameter : re-schedule nothing */ movi55 $r0, 0 /* syscall 1st parameter : de-schedule nothing */ /* put the stack pointer at the top of the stack in scratchpad */ addi $sp, $r3, 4 * TASK_SCRATCHPAD_SIZE /* we are ready to re-schedule */ swi.gp $r4, [ + need_resched] swi.gp $r4, [ + start_called] /* trigger scheduling to execute the task with the highest priority */ syscall 0 /* we should never return here: set code to EC_ERROR_UNKNOWN */ movi55 $r0, 0x1 ret5 $lp