1 /* 2 * FreeRTOS Kernel V11.1.0 3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 * 5 * SPDX-License-Identifier: MIT 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 * this software and associated documentation files (the "Software"), to deal in 9 * the Software without restriction, including without limitation the rights to 10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 * the Software, and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all 15 * copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * https://www.FreeRTOS.org 25 * https://github.com/FreeRTOS 26 * 27 */ 28 29 #include "FreeRTOSConfig.h" 30 31 #define portCONTEXT_SIZE 160 32 #define portEPC_STACK_LOCATION 152 33 #define portSTATUS_STACK_LOCATION 156 34 #define portFPCSR_STACK_LOCATION 0 35 #define portTASK_HAS_FPU_STACK_LOCATION 0 36 #define portFPU_CONTEXT_SIZE 264 37 38 /******************************************************************/ 39 .macro portSAVE_FPU_REGS offset, base 40 /* Macro to assist with saving just the FPU registers to the 41 * specified address and base offset, 42 * offset is a constant, base is the base pointer register */ 43 44 sdc1 $f31, \offset + 248(\base) 45 sdc1 $f30, \offset + 240(\base) 46 sdc1 $f29, \offset + 232(\base) 47 sdc1 $f28, \offset + 224(\base) 48 sdc1 $f27, \offset + 216(\base) 49 sdc1 $f26, \offset + 208(\base) 50 sdc1 $f25, \offset + 200(\base) 51 sdc1 $f24, \offset + 192(\base) 52 sdc1 $f23, \offset + 184(\base) 53 sdc1 $f22, \offset + 176(\base) 54 sdc1 $f21, \offset + 168(\base) 55 sdc1 $f20, \offset + 160(\base) 56 sdc1 $f19, \offset + 152(\base) 57 sdc1 $f18, \offset + 144(\base) 58 sdc1 $f17, \offset + 136(\base) 59 sdc1 $f16, \offset + 128(\base) 60 sdc1 $f15, \offset + 120(\base) 61 sdc1 $f14, \offset + 112(\base) 62 sdc1 $f13, \offset + 104(\base) 63 sdc1 $f12, \offset + 96(\base) 64 sdc1 $f11, \offset + 88(\base) 65 sdc1 $f10, \offset + 80(\base) 66 sdc1 $f9, \offset + 72(\base) 67 sdc1 $f8, \offset + 64(\base) 68 sdc1 $f7, \offset + 56(\base) 69 sdc1 $f6, \offset + 48(\base) 70 sdc1 $f5, \offset + 40(\base) 71 sdc1 $f4, \offset + 32(\base) 72 sdc1 $f3, \offset + 24(\base) 73 sdc1 $f2, \offset + 16(\base) 74 sdc1 $f1, \offset + 8(\base) 75 sdc1 $f0, \offset + 0(\base) 76 77 .endm 78 79 /******************************************************************/ 80 .macro portLOAD_FPU_REGS offset, base 81 /* Macro to assist with loading just the FPU registers from the 82 * specified address and base offset, offset is a constant, 83 * base is the base pointer register */ 84 85 ldc1 $f0, \offset + 0(\base) 86 ldc1 $f1, \offset + 8(\base) 87 ldc1 $f2, \offset + 16(\base) 88 ldc1 $f3, \offset + 24(\base) 89 ldc1 $f4, \offset + 32(\base) 90 ldc1 $f5, \offset + 40(\base) 91 ldc1 $f6, \offset + 48(\base) 92 ldc1 $f7, \offset + 56(\base) 93 ldc1 $f8, \offset + 64(\base) 94 ldc1 $f9, \offset + 72(\base) 95 ldc1 $f10, \offset + 80(\base) 96 ldc1 $f11, \offset + 88(\base) 97 ldc1 $f12, \offset + 96(\base) 98 ldc1 $f13, \offset + 104(\base) 99 ldc1 $f14, \offset + 112(\base) 100 ldc1 $f15, \offset + 120(\base) 101 ldc1 $f16, \offset + 128(\base) 102 ldc1 $f17, \offset + 136(\base) 103 ldc1 $f18, \offset + 144(\base) 104 ldc1 $f19, \offset + 152(\base) 105 ldc1 $f20, \offset + 160(\base) 106 ldc1 $f21, \offset + 168(\base) 107 ldc1 $f22, \offset + 176(\base) 108 ldc1 $f23, \offset + 184(\base) 109 ldc1 $f24, \offset + 192(\base) 110 ldc1 $f25, \offset + 200(\base) 111 ldc1 $f26, \offset + 208(\base) 112 ldc1 $f27, \offset + 216(\base) 113 ldc1 $f28, \offset + 224(\base) 114 ldc1 $f29, \offset + 232(\base) 115 ldc1 $f30, \offset + 240(\base) 116 ldc1 $f31, \offset + 248(\base) 117 118 .endm 119 120 /******************************************************************/ 121 .macro portSAVE_CONTEXT 122 123 /* Make room for the context. First save the current status so it can be 124 manipulated, and the cause and EPC registers so their original values are 125 captured. */ 126 mfc0 k0, _CP0_CAUSE 127 addiu sp, sp, -portCONTEXT_SIZE 128 129 #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 ) 130 /* Test if we are already using the system stack. Only tasks may use the 131 FPU so if we are already in a nested interrupt then the FPU context does 132 not require saving. */ 133 la k1, uxInterruptNesting 134 lw k1, 0(k1) 135 bne k1, zero, 2f 136 nop 137 138 /* Test if the current task needs the FPU context saving. */ 139 la k1, ulTaskHasFPUContext 140 lw k1, 0(k1) 141 beq k1, zero, 1f 142 nop 143 144 /* Adjust the stack to account for the additional FPU context.*/ 145 addiu sp, sp, -portFPU_CONTEXT_SIZE 146 147 1: 148 /* Save the ulTaskHasFPUContext flag. */ 149 sw k1, portTASK_HAS_FPU_STACK_LOCATION(sp) 150 151 2: 152 #endif 153 154 mfc0 k1, _CP0_STATUS 155 156 /* Also save s7, s6 and s5 so they can be used. Any nesting interrupts 157 should maintain the values of these registers across the ISR. */ 158 sw s7, 48(sp) 159 sw s6, 44(sp) 160 sw s5, 40(sp) 161 sw k1, portSTATUS_STACK_LOCATION(sp) 162 163 /* Prepare to enable interrupts above the current priority. */ 164 srl k0, k0, 0xa 165 ins k1, k0, 10, 7 166 srl k0, k0, 0x7 /* This copies the MSB of the IPL, but it would be an error if it was set anyway. */ 167 ins k1, k0, 18, 1 168 ins k1, zero, 1, 4 169 170 /* s5 is used as the frame pointer. */ 171 add s5, zero, sp 172 173 /* Check the nesting count value. */ 174 la k0, uxInterruptNesting 175 lw s6, (k0) 176 177 /* If the nesting count is 0 then swap to the the system stack, otherwise 178 the system stack is already being used. */ 179 bne s6, zero, 1f 180 nop 181 182 /* Swap to the system stack. */ 183 la sp, xISRStackTop 184 lw sp, (sp) 185 186 /* Increment and save the nesting count. */ 187 1: addiu s6, s6, 1 188 sw s6, 0(k0) 189 190 /* s6 holds the EPC value, this is saved after interrupts are re-enabled. */ 191 mfc0 s6, _CP0_EPC 192 193 /* Re-enable interrupts. */ 194 mtc0 k1, _CP0_STATUS 195 196 /* Save the context into the space just created. s6 is saved again 197 here as it now contains the EPC value. No other s registers need be 198 saved. */ 199 sw ra, 120(s5) 200 sw s8, 116(s5) 201 sw t9, 112(s5) 202 sw t8, 108(s5) 203 sw t7, 104(s5) 204 sw t6, 100(s5) 205 sw t5, 96(s5) 206 sw t4, 92(s5) 207 sw t3, 88(s5) 208 sw t2, 84(s5) 209 sw t1, 80(s5) 210 sw t0, 76(s5) 211 sw a3, 72(s5) 212 sw a2, 68(s5) 213 sw a1, 64(s5) 214 sw a0, 60(s5) 215 sw v1, 56(s5) 216 sw v0, 52(s5) 217 sw s6, portEPC_STACK_LOCATION(s5) 218 sw $1, 16(s5) 219 220 /* Save the AC0, AC1, AC2, AC3 registers from the DSP. s6 is used as a 221 scratch register. */ 222 mfhi s6, $ac1 223 sw s6, 128(s5) 224 mflo s6, $ac1 225 sw s6, 124(s5) 226 227 mfhi s6, $ac2 228 sw s6, 136(s5) 229 mflo s6, $ac2 230 sw s6, 132(s5) 231 232 mfhi s6, $ac3 233 sw s6, 144(s5) 234 mflo s6, $ac3 235 sw s6, 140(s5) 236 237 /* Save the DSP Control register */ 238 rddsp s6 239 sw s6, 148(s5) 240 241 /* ac0 is done separately to match the MX port. */ 242 mfhi s6, $ac0 243 sw s6, 12(s5) 244 mflo s6, $ac0 245 sw s6, 8(s5) 246 247 /* Save the FPU context if the nesting count was zero. */ 248 #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 ) 249 la s6, uxInterruptNesting 250 lw s6, 0(s6) 251 addiu s6, s6, -1 252 bne s6, zero, 1f 253 nop 254 255 /* Test if the current task needs the FPU context saving. */ 256 lw s6, portTASK_HAS_FPU_STACK_LOCATION(s5) 257 beq s6, zero, 1f 258 nop 259 260 /* Save the FPU registers. */ 261 portSAVE_FPU_REGS ( portCONTEXT_SIZE + 8 ), s5 262 263 /* Save the FPU status register */ 264 cfc1 s6, $f31 265 sw s6, (portCONTEXT_SIZE + portFPCSR_STACK_LOCATION)(s5) 266 267 1: 268 #endif 269 270 /* Update the task stack pointer value if nesting is zero. */ 271 la s6, uxInterruptNesting 272 lw s6, (s6) 273 addiu s6, s6, -1 274 bne s6, zero, 1f 275 nop 276 277 /* Save the stack pointer. */ 278 la s6, uxSavedTaskStackPointer 279 sw s5, (s6) 280 1: 281 .endm 282 283 /******************************************************************/ 284 .macro portRESTORE_CONTEXT 285 286 /* Restore the stack pointer from the TCB. This is only done if the 287 nesting count is 1. */ 288 la s6, uxInterruptNesting 289 lw s6, (s6) 290 addiu s6, s6, -1 291 bne s6, zero, 1f 292 nop 293 la s6, uxSavedTaskStackPointer 294 lw s5, (s6) 295 296 #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 ) 297 /* Restore the FPU context if required. */ 298 lw s6, portTASK_HAS_FPU_STACK_LOCATION(s5) 299 beq s6, zero, 1f 300 nop 301 302 /* Restore the FPU registers. */ 303 portLOAD_FPU_REGS ( portCONTEXT_SIZE + 8 ), s5 304 305 /* Restore the FPU status register. */ 306 lw s6, ( portCONTEXT_SIZE + portFPCSR_STACK_LOCATION )(s5) 307 ctc1 s6, $f31 308 #endif 309 310 1: 311 312 /* Restore the context. */ 313 lw s6, 128(s5) 314 mthi s6, $ac1 315 lw s6, 124(s5) 316 mtlo s6, $ac1 317 318 lw s6, 136(s5) 319 mthi s6, $ac2 320 lw s6, 132(s5) 321 mtlo s6, $ac2 322 323 lw s6, 144(s5) 324 mthi s6, $ac3 325 lw s6, 140(s5) 326 mtlo s6, $ac3 327 328 /* Restore DSPControl. */ 329 lw s6, 148(s5) 330 wrdsp s6 331 332 lw s6, 8(s5) 333 mtlo s6, $ac0 334 lw s6, 12(s5) 335 mthi s6, $ac0 336 lw $1, 16(s5) 337 338 /* s6 is loaded as it was used as a scratch register and therefore saved 339 as part of the interrupt context. */ 340 lw s7, 48(s5) 341 lw s6, 44(s5) 342 lw v0, 52(s5) 343 lw v1, 56(s5) 344 lw a0, 60(s5) 345 lw a1, 64(s5) 346 lw a2, 68(s5) 347 lw a3, 72(s5) 348 lw t0, 76(s5) 349 lw t1, 80(s5) 350 lw t2, 84(s5) 351 lw t3, 88(s5) 352 lw t4, 92(s5) 353 lw t5, 96(s5) 354 lw t6, 100(s5) 355 lw t7, 104(s5) 356 lw t8, 108(s5) 357 lw t9, 112(s5) 358 lw s8, 116(s5) 359 lw ra, 120(s5) 360 361 /* Protect access to the k registers, and others. */ 362 di 363 ehb 364 365 /* Decrement the nesting count. */ 366 la k0, uxInterruptNesting 367 lw k1, (k0) 368 addiu k1, k1, -1 369 sw k1, 0(k0) 370 371 #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 ) 372 /* If the nesting count is now zero then the FPU context may be restored. */ 373 bne k1, zero, 1f 374 nop 375 376 /* Restore the value of ulTaskHasFPUContext */ 377 la k0, ulTaskHasFPUContext 378 lw k1, 0(s5) 379 sw k1, 0(k0) 380 381 /* If the task does not have an FPU context then adjust the stack normally. */ 382 beq k1, zero, 1f 383 nop 384 385 /* Restore the STATUS and EPC registers */ 386 lw k0, portSTATUS_STACK_LOCATION(s5) 387 lw k1, portEPC_STACK_LOCATION(s5) 388 389 /* Leave the stack in its original state. First load sp from s5, then 390 restore s5 from the stack. */ 391 add sp, zero, s5 392 lw s5, 40(sp) 393 394 /* Adjust the stack pointer to remove the FPU context */ 395 addiu sp, sp, portFPU_CONTEXT_SIZE 396 beq zero, zero, 2f 397 nop 398 399 1: /* Restore the STATUS and EPC registers */ 400 lw k0, portSTATUS_STACK_LOCATION(s5) 401 lw k1, portEPC_STACK_LOCATION(s5) 402 403 /* Leave the stack in its original state. First load sp from s5, then 404 restore s5 from the stack. */ 405 add sp, zero, s5 406 lw s5, 40(sp) 407 408 2: /* Adjust the stack pointer */ 409 addiu sp, sp, portCONTEXT_SIZE 410 411 #else 412 413 /* Restore the frame when there is no hardware FP support. */ 414 lw k0, portSTATUS_STACK_LOCATION(s5) 415 lw k1, portEPC_STACK_LOCATION(s5) 416 417 /* Leave the stack in its original state. First load sp from s5, then 418 restore s5 from the stack. */ 419 add sp, zero, s5 420 lw s5, 40(sp) 421 422 addiu sp, sp, portCONTEXT_SIZE 423 424 #endif // ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 ) 425 426 mtc0 k0, _CP0_STATUS 427 mtc0 k1, _CP0_EPC 428 ehb 429 eret 430 nop 431 432 .endm 433