/* * Copyright (c) 2007 Pavel Jancik, Michal Kebrt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup arm32mm * @{ */ /** @file * @brief Page fault related functions. */ #include #include #include #include #include #include #include #include #include /** Returns value stored in fault status register. * * @return Value stored in CP15 fault status register (FSR). */ static inline fault_status_t read_fault_status_register(void) { fault_status_union_t fsu; /* fault status is stored in CP15 register 5 */ asm volatile ( "mrc p15, 0, %0, c5, c0, 0" : "=r"(fsu.dummy) ); return fsu.fs; } /** Returns FAR (fault address register) content. * * @return FAR (fault address register) content (address that caused a page * fault) */ static inline uintptr_t read_fault_address_register(void) { uintptr_t ret; /* fault adress is stored in CP15 register 6 */ asm volatile ( "mrc p15, 0, %0, c6, c0, 0" : "=r"(ret) ); return ret; } /** Decides whether the instruction is load/store or not. * * @param instr Instruction * * @return true when instruction is load/store, false otherwise */ static inline bool is_load_store_instruction(instruction_t instr) { /* load store immediate offset */ if (instr.type == 0x2) { return true; } /* load store register offset */ if (instr.type == 0x3 && instr.bit4 == 0) { return true; } /* load store multiple */ if (instr.type == 0x4) { return true; } /* oprocessor load/store */ if (instr.type == 0x6) { return true; } return false; } /** Decides whether the instruction is swap or not. * * @param instr Instruction * * @return true when instruction is swap, false otherwise */ static inline bool is_swap_instruction(instruction_t instr) { /* swap, swapb instruction */ if (instr.type == 0x0 && (instr.opcode == 0x8 || instr.opcode == 0xa) && instr.access == 0x0 && instr.bits567 == 0x4 && instr.bit4 == 1) { return true; } return false; } /** Decides whether read or write into memory is requested. * * @param instr_addr Address of instruction which tries to access memory. * @param badvaddr Virtual address the instruction tries to access. * * @return Type of access into memory, PF_ACCESS_EXEC if no memory access is * requested. */ static pf_access_t get_memory_access_type(uint32_t instr_addr, uintptr_t badvaddr) { instruction_union_t instr_union; instr_union.pc = instr_addr; instruction_t instr = *(instr_union.instr); /* undefined instructions */ if (instr.condition == 0xf) { panic("page_fault - instruction does not access memory " "(instr_code: %x, badvaddr:%x).", instr, badvaddr); return PF_ACCESS_EXEC; } /* load store instructions */ if (is_load_store_instruction(instr)) { if (instr.access == 1) { return PF_ACCESS_READ; } else { return PF_ACCESS_WRITE; } } /* swap, swpb instruction */ if (is_swap_instruction(instr)) { return PF_ACCESS_WRITE; } panic("page_fault - instruction doesn't access memory " "(instr_code: %x, badvaddr:%x).", instr, badvaddr); return PF_ACCESS_EXEC; } /** Handles "data abort" exception (load or store at invalid address). * * @param exc_no Exception number. * @param istate CPU state when exception occured. */ void data_abort(int exc_no, istate_t *istate) { fault_status_t fsr __attribute__ ((unused)) = read_fault_status_register(); uintptr_t badvaddr = read_fault_address_register(); pf_access_t access = get_memory_access_type(istate->pc, badvaddr); int ret = as_page_fault(badvaddr, access, istate); if (ret == AS_PF_FAULT) { print_istate(istate); dprintf("page fault - pc: %x, va: %x, status: %x(%x), " "access:%d\n", istate->pc, badvaddr, fsr.status, fsr, access); fault_if_from_uspace(istate, "Page fault: %#x.", badvaddr); panic("Page fault."); } } /** Handles "prefetch abort" exception (instruction couldn't be executed). * * @param exc_no Exception number. * @param istate CPU state when exception occured. */ void prefetch_abort(int exc_no, istate_t *istate) { int ret = as_page_fault(istate->pc, PF_ACCESS_EXEC, istate); if (ret == AS_PF_FAULT) { dprintf("prefetch_abort\n"); print_istate(istate); panic("page fault - prefetch_abort at address: %x.", istate->pc); } } /** @} */