34,12 → 34,71 |
|
#include <arch/mm/page.h> |
#include <genarch/mm/page_pt.h> |
#include <arch.h> |
#include <mm/page.h> |
#include <mm/tlb.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; |
60,20 → 119,204 |
page_mapping_insert(AS_KERNEL, 0x15000000, 0x15000000, flags); |
page_mapping_insert(AS_KERNEL, 0x16000000, 0x16000000, flags); |
|
tlb_invalidate_all(); |
SET_PTL0_ADDRESS_ARCH(AS_KERNEL->genarch.page_table); |
|
exc_register(EXC_DATA_ABORT, "page_fault data abort", (iroutine) data_abourt); |
exc_register(EXC_PREFETCH_ABORT, "page_fault prefetch abort", (iroutine) prefetch_abourt); |
|
/* Sets mapping to kernel mapping. It's nessesary to hw_map changes take place immediately |
* after hw_map function is called. */ |
as_switch( NULL, AS_KERNEL); |
|
// note for Alf: kernel part of page table is copied in generic/mm/as_pt.c/ptl0_create |
// TODO: register fault routine |
} |
|
/** Map device into kernel space. */ |
/** |
* 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) |
{ |
/* TODO */ |
return NULL; |
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"); |
} |
|
/** @} |
*/ |
|