/*
* 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
*/
#include <arch/mm/page.h>
#include <genarch/mm/page_pt.h>
#include <arch.h>
#include <mm/page.h>
#include <align.h>
#include <config.h>
#include <arch/exception.h>
#include <typedefs.h>
#include <arch/types.h>
#include <interrupt.h>
//TODO: remove in final version
#include "../aux_print/printf.h"
// localy used types
/**
* Decribes structure of fault status register in coprocessor 15
*/
typedef struct {
unsigned status : 3;
unsigned domain : 4;
unsigned zero : 1;
unsigned should_be_zero : 24;
} __attribute__ ((packed)) fault_status_t;
/**
* Help union used for overcasting integer value into fault_status_t type
*/
typedef union {
fault_status_t fsr;
uint32_t dummy;
} fault_status_union_t;
/**
* Very simplyfied description of instruction code structure intended for
* recognising memmory access of instruction ( reads or writes into memmory)
* more details: see ARM architecture preference chapter:3.1 Instruction set encoding
*/
typedef struct {
unsigned dummy1 : 4;
unsigned bit4 : 1;
unsigned bits567 : 3;
unsigned dummy : 12;
unsigned access : 1;
unsigned opcode : 4;
unsigned instr_type : 3;
unsigned condition : 4;
} __attribute__ ((packed)) instruction_t;
/**
* Help union used for overcasting ip register (uint_32_t) value into instruction_t pointer
*/
typedef union {
instruction_t* instr;
uint32_t ip;
} instruction_union_t;
// localy used functions
static fault_status_t read_fault_status_register();
static uintptr_t read_fault_address_register();
static pf_access_t get_memmory_access_type(uint32_t instr_addr, uintptr_t badvaddr);
/**
* Initializes kernel adress space page tables, sets abourts exceptions vectors
*/
void page_arch_init(void)
{
uintptr_t cur;
int flags;
page_mapping_operations = &pt_mapping_operations;
flags = PAGE_CACHEABLE;
/* PA2KA(identity) mapping for all frames until last_frame */
for (cur = 0; cur < last_frame; cur += FRAME_SIZE) {
page_mapping_insert(AS_KERNEL, PA2KA(cur), cur, flags);
}
// TODO: move to the kernel space
page_mapping_insert(AS_KERNEL, 0x00000000, 0x00000000, flags);
// TODO: remove when aux_printf not needed
page_mapping_insert(AS_KERNEL, 0x10000000, 0x10000000, flags);
exc_register(EXC_DATA_ABORT, "page_fault data abort", (iroutine) data_abourt);
exc_register(EXC_PREFETCH_ABORT, "page_fault prefetch abort", (iroutine) prefetch_abourt);
as_switch(NULL, AS_KERNEL);
// TODO: register fault routine
}
/**
* Map device into kernel space.
*
* This function adds mapping of physical address that is read/write only
* from kernel and not bufferable.
*
* \param physaddr Physical addres where device is connected
* \param size Length of area where device is present
* \return Virtual address where device will be accessable
* Note: This is copy of IA32 hw_map code
*/
uintptr_t hw_map(uintptr_t physaddr, size_t size)
{
if (last_frame + ALIGN_UP(size, PAGE_SIZE) > KA2PA(KERNEL_ADDRESS_SPACE_END_ARCH))
panic("Unable to map physical memory %p (%d bytes)", physaddr, size)
uintptr_t virtaddr = PA2KA(last_frame);
pfn_t i;
for (i = 0; i < ADDR2PFN(ALIGN_UP(size, PAGE_SIZE)); i++)
page_mapping_insert(AS_KERNEL, virtaddr + PFN2ADDR(i), physaddr + PFN2ADDR(i), PAGE_NOT_CACHEABLE | PAGE_READ | PAGE_WRITE | PAGE_KERNEL);
last_frame = ALIGN_UP(last_frame + size, FRAME_SIZE);
return virtaddr;
}
//TODO: remove in final version
static void print_istate(istate_t* istate);
static void print_istate(istate_t* istate) {
aux_printf("\nIstate dump:\n");
aux_printf(" r0:%X r1:%X r2:%X r3:%X\n", istate->r0, istate->r1, istate->r2, istate->r3);
aux_printf(" r4:%X r5:%X r6:%X r7:%X\n", istate->r4, istate->r5, istate->r6, istate->r7);
aux_printf(" r8:%X r8:%X r10:%X r11:%X\n", istate->r8, istate->r9, istate->r10, istate->r11);
aux_printf(" r12:%X sp:%X lr:%X spsr:%X\n", istate->r12, istate->sp, istate->lr, istate->spsr);
}
/**
* \return Value stored in fault status register
*/
static fault_status_t read_fault_status_register() {
fault_status_union_t tmp;
asm volatile (
"mrc p15, 0, %0, c5, c0, 0"
: "=r"(tmp.dummy)
);
return tmp.fsr;
}
/**
* \return Virtual adress. Access on this addres caused exception
*/
static uintptr_t read_fault_address_register() {
uintptr_t tmp;
// Fault adress is stored in coprocessor15, register 6
asm volatile (
"mrc p15, 0, %0, c6, c0, 0"
: "=r"(tmp)
);
return tmp;
};
/**
* Decode instruction and decide if try to read or write into memmory.
*
* \param instr_addr address of instruction which attempts to access into memmory
* \param badvaddr Virtual address on which instruction tries to access
* \return type of access into memmory
* Note: return PF_ACESS_EXEC if no memmory acess
*/
//TODO: remove debug print in final version ... instead panic return PF_ACESS_EXEC
pf_access_t get_memmory_access_type(uint32_t instr_addr, uintptr_t badvaddr) {
instruction_union_t tmp;
tmp.ip = instr_addr;
// get instruction op code
instruction_t i_code = *(tmp.instr);
aux_printf("get_instruction_memmory_access\n");
aux_printf(" i_code:%X\n",i_code);
aux_printf(" i_code.condition:%d\n", i_code.condition);
aux_printf(" i_code.instr_type:%d\n",i_code.instr_type);
aux_printf(" i_code.opcode:%d\n",i_code.opcode);
aux_printf(" i_code.acess:%d\n", i_code.access);
aux_printf(" i_code.dummy:%d\n", i_code.dummy);
aux_printf(" i_code.bits567%d\n", i_code.bits567);
aux_printf(" i_code.bit4:%d\n", i_code.bit4);
aux_printf(" i_code.dummy1:%d\n", i_code.dummy1);
// undefined instructions ... (or special instructions)
if ( i_code.condition == 0xf ) {
panic("page_fault - on instruction not acessing to memmory (instr_code:%X, badvaddr:%X)",i_code, badvaddr);
return PF_ACCESS_EXEC;
}
// load store instructions
if ( ( i_code.instr_type == 0x2 ) || // load store immediate offset
( i_code.instr_type == 0x3 && i_code.bit4 == 0) || // load store register offset
( i_code.instr_type == 0x4 ) || // load store multiple
( i_code.instr_type == 0x6 ) // coprocessor load / strore
) {
if ( i_code.access == 1) {
return PF_ACCESS_READ;
} else {
return PF_ACCESS_WRITE;
}
};
// swap, swpb instruction
if ( i_code.instr_type == 0x0 && (i_code.opcode == 0x8 || i_code.opcode == 0xA) &&
i_code.access == 0x0 && i_code.bits567 == 0x4 && i_code.bit4 == 1 )
{
/* Swap instructions make read and write in one step.
* Type of access that caused exception have to page tables and access rights.
*/
//TODO: ALF!!!!! cann't use AS as is define as THE->as and THE structure is sored after stack_base of current thread
// but now ... in exception we have separate stacks <==> different stack_pointer ... so AS contains nonsence data
// same case as_page_fault .... it's nessesary to solve "stack" problem
pte_level1_t* pte = (pte_level1_t*)pt_mapping_operations.mapping_find(AS, badvaddr);
ASSERT(pte);
/* check if read possible
* Note: Don't check PTE_READABLE because it returns 1 everytimes */
if ( !PTE_PRESENT(pte) ) {
return PF_ACCESS_READ;
}
if ( !PTE_WRITABLE(pte) ) {
return PF_ACCESS_WRITE;
}
else
// badvaddr is present readable and writeable but error occured ... why?
panic("page_fault - swap instruction, but address readable and writeable (instr_code:%X, badvaddr:%X)",i_code, badvaddr);
}
panic("page_fault - on instruction not acessing to memmory (instr_code:%X, badvaddr:%X)",i_code, badvaddr);
return PF_ACCESS_EXEC;
}
/**
* Routine that solves exception data_abourt
* ... you try to load or store value into invalid memmory address
* \param istate State of CPU when data abourt occured
* \param n number of exception
*/
//TODO: remove debug prints in final tested version
void data_abourt(int n, istate_t *istate) {
fault_status_t fsr = read_fault_status_register();
uintptr_t page = read_fault_address_register();
pf_access_t access = get_memmory_access_type( istate->lr, page);
print_istate(istate);
aux_printf(" page fault : ip:%X, va:%X, status:%x(%x), access:%d\n", istate->lr, page, fsr.status,fsr, access);
int ret = as_page_fault(page, access, istate);
aux_printf(" as_page_fault ret:%d\n", ret);
if (ret == AS_PF_FAULT) {
fault_if_from_uspace(istate, "Page fault: %#x", page);
panic("page fault\n");
}
// TODO: Remove this ... now for testing purposes ... it's bad to test page faults in kernel, where no page faults should occures
panic("page fault ... solved\n");
}
/**
* Routine that solves exception prefetch_about
* ... you try to execute instruction on invalid address
* \param istate State of CPU when prefetch abourt occured
* \param n number of exception
*/
void prefetch_abourt(int n, istate_t *istate) {
// Prefetch can be made be bkpt instruction
print_istate(istate);
aux_printf(" prefetch_abourt ... instruction on adress:%x can't be fetched\n", istate->lr);
int ret = as_page_fault(istate->lr, PF_ACCESS_EXEC, istate);
aux_printf(" as_page_fault ret:%d\n", ret);
if (ret == AS_PF_FAULT) {
panic("page fault - instruction fetch at addr:%X\n", istate->lr);
}
panic("Prefetch abourt ... solved");
}
/** @}
*/