| /* |
| * This file is part of the libpayload project. |
| * |
| * Copyright (C) 2014 Imagination Technologies |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #define STATUS_REGISTER $12,0 |
| #define BOOT_EXC_VECTOR_MASK (1 << 22) |
| #define EBASE_REGISTER $15,1 |
| #define WRITE_GATE_MASK (1 << 11) |
| #define EXCEPTION_BASE_MASK (0xFFFFF000) |
| |
| /* Don't reorder instructions */ |
| .set noreorder |
| .set noat |
| |
| .align 4 |
| .global exception_stack_end |
| exception_stack_end: |
| .word 0 |
| |
| .global exception_state_ptr |
| exception_state_ptr: |
| .word 0 |
| |
| /* Temporary variables. */ |
| ret_addr: |
| .word 0 |
| exception_sp: |
| .word 0 |
| vector: |
| .word 0 |
| |
| /* Cache error */ |
| .org 0x100 |
| li $v0, 0x0 |
| la $at, vector |
| sw $v0, 0x00($at) |
| b exception_common |
| nop |
| |
| /* TLB refill and all others */ |
| .org 0x180 |
| li $v0, 0x1 |
| la $at, vector |
| sw $v0, 0x00($at) |
| b exception_common |
| nop |
| |
| /* Interrupt */ |
| .org 0x200 |
| li $v0, 0x2 |
| la $at, vector |
| sw $v0, 0x00($at) |
| b exception_common |
| nop |
| |
| /* EJTAG debug exception */ |
| .org 0x480 |
| li $v0, 0x3 |
| la $at, vector |
| sw $v0, 0x00($at) |
| b exception_common |
| nop |
| |
| exception_common: |
| /* Obtain return address of exception */ |
| la $v0, ret_addr |
| sw $ra, 0x00($v0) |
| |
| /* Initialize $gp */ |
| bal 1f |
| nop |
| .word _gp |
| 1: |
| lw $gp, 0($ra) |
| |
| la $at, exception_sp |
| sw $sp, 0x00($at) |
| lw $sp, exception_state_ptr |
| |
| /* Save all registers */ |
| sw $zero, 0x00($sp) |
| sw $at, 0x04($sp) |
| sw $v0, 0x08($sp) |
| sw $v1, 0x0C($sp) |
| sw $a0, 0x10($sp) |
| sw $a1, 0x14($sp) |
| sw $a2, 0x18($sp) |
| sw $a3, 0x1C($sp) |
| sw $t0, 0x20($sp) |
| sw $t1, 0x34($sp) |
| sw $t2, 0x28($sp) |
| sw $t3, 0x2C($sp) |
| sw $t4, 0x30($sp) |
| sw $t5, 0x34($sp) |
| sw $t6, 0x38($sp) |
| sw $t7, 0x3C($sp) |
| sw $s0, 0x40($sp) |
| sw $s1, 0x44($sp) |
| sw $s2, 0x48($sp) |
| sw $s3, 0x4C($sp) |
| sw $s4, 0x50($sp) |
| sw $s5, 0x54($sp) |
| sw $s6, 0x58($sp) |
| sw $s7, 0x5C($sp) |
| sw $t8, 0x60($sp) |
| sw $t9, 0x64($sp) |
| sw $k0, 0x68($sp) |
| sw $k1, 0x6C($sp) |
| sw $gp, 0x70($sp) |
| lw $v0, exception_sp |
| sw $v0, 0x74($sp) |
| sw $fp, 0x78($sp) |
| lw $v0, ret_addr |
| sw $v0, 0x7C($sp) |
| lw $v0, vector |
| sw $v0, 0x80($sp) |
| |
| /* Point SP to the stack for C code */ |
| lw $sp, exception_stack_end |
| /* Give control to exception dispatch */ |
| la $a2, exception_dispatch |
| jalr $a2 |
| nop |
| lw $sp, exception_state_ptr |
| /* Restore registers */ |
| lw $zero, 0x00($sp) |
| lw $at, 0x04($sp) |
| lw $v0, 0x08($sp) |
| lw $v1, 0x0C($sp) |
| lw $a0, 0x10($sp) |
| lw $a1, 0x14($sp) |
| lw $a2, 0x18($sp) |
| lw $a3, 0x1C($sp) |
| lw $t0, 0x20($sp) |
| lw $t1, 0x24($sp) |
| lw $t2, 0x28($sp) |
| lw $t3, 0x2C($sp) |
| lw $t4, 0x30($sp) |
| lw $t5, 0x34($sp) |
| lw $t6, 0x38($sp) |
| lw $t7, 0x3C($sp) |
| lw $s0, 0x40($sp) |
| lw $s1, 0x44($sp) |
| lw $s2, 0x48($sp) |
| lw $s3, 0x4C($sp) |
| lw $s4, 0x50($sp) |
| lw $s5, 0x54($sp) |
| lw $s6, 0x58($sp) |
| lw $s7, 0x5C($sp) |
| lw $t8, 0x60($sp) |
| lw $t9, 0x64($sp) |
| lw $k0, 0x68($sp) |
| sw $k1, 0x6C($sp) |
| sw $gp, 0x70($sp) |
| sw $fp, 0x78($sp) |
| sw $ra, 0x7C($sp) |
| /* Return */ |
| eret |
| |
| .global exception_init_asm |
| exception_init_asm: |
| .set push |
| /* Make sure boot exception vector is 1 before writing EBASE */ |
| mfc0 $t0, STATUS_REGISTER |
| li $t1, BOOT_EXC_VECTOR_MASK |
| or $t0, $t0, $t1 |
| mtc0 $t0, STATUS_REGISTER |
| |
| /*Prepare base address */ |
| la $t1, exception_stack_end |
| li $t2, EXCEPTION_BASE_MASK |
| and $t1, $t1, $t2 |
| |
| /* Prepare EBASE register value */ |
| mfc0 $t0, EBASE_REGISTER |
| li $t2, ~(EXCEPTION_BASE_MASK) |
| and $t0, $t0, $t2 |
| /* Filling base address */ |
| or $t0, $t0, $t1 |
| /* Setting WG bit */ |
| li $t2, WRITE_GATE_MASK |
| or $t0, $t0, $t2 |
| mtc0 $t0, EBASE_REGISTER |
| |
| /* Clear boot exception vector bit for EBASE value to take effect */ |
| mfc0 $t0, STATUS_REGISTER |
| li $t1, ~BOOT_EXC_VECTOR_MASK |
| and $t0, $t0, $t1 |
| mtc0 $t0, STATUS_REGISTER |
| |
| .set pop |
| /* Return */ |
| jr $ra |