📄 armmmu.cpp
字号:
/************************************************************************* Copyright (C) 2002 - 2007 Wei Qin See file COPYING for more information. 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; either version 2 of the License, or (at your option) any later version. 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.*************************************************************************//* armmmu.c - Memory Management Unit emulation. ARMulator extensions for the ARM7100 family. Copyright (C) 1999 Ben Williamson 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; either version 2 of the License, or (at your option) any later version. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include "armmmu.h"#include "arm_io.h"#include <cassert>#include <cstring>#include <iostream>#include <csignal>/*Macro defines for MMU state*/#define MMU_CTL (control)#define MMU_Enabled (control & CONTROL_MMU)#define MMU_Disabled (!(MMU_Enabled))#define MMU_Aligned (control & CONTROL_ALIGN_FAULT)#define MMU_ICacheEnabled (MMU_CTL & CONTROL_INSTRUCTION_CACHE)#define MMU_ICacheDisabled (!(MMU_ICacheDisabled))#define MMU_DCacheEnabled (MMU_CTL & CONTROL_DATA_CACHE)#define MMU_DCacheDisabled (!(MMU_DCacheEnabled))#define MMU_CacheEnabled (MMU_CTL & CONTROL_CACHE)#define MMU_CacheDisabled (!(MMU_CacheEnabled))#define MMU_WBEnabled (MMU_CTL & CONTROL_WRITE_BUFFER)#define MMU_WBDisabled (!(MMU_WBEnabled))#define FLUSH_MMU_PAGE_TABLE(a_) \ memset(a_, 0xff, (sizeof(mmu_page_table_entry_t) * MMU_HASH_SIZE))#define FLUSH_MMU_PAGE_ENTRY(a_) \ memset(a_, 0xff, sizeof(mmu_page_table_entry_t))using namespace simit;static word_t tlb_masks[] = { 0x00000000, /* TLB_INVALID */ 0xFFFFF000, /* TLB_SMALLPAGE */ 0xFFFF0000, /* TLB_LARGEPAGE */ 0xFFF00000, /* TLB_SECTION */ 0xFFFFF000, /* TLB_ESMALLPAGE, have TEX attirbute, only for XScale */ 0xFFFFFC00 /* TLB_TINYPAGE */}; static word_t tlb_va_to_pa(tlb_entry_t * tlb_e, word_t va) { /* The non-optimized function should be like this code. For more information see the code at the bottom of translation_walk function. return (tlb_e->phys_addr & tlb_e->mask) | (va & tlb_e->nmask); */ return tlb_e->phys_addr + va;}arm_mmu::arm_mmu (arm_emulator *emula) : emu(emula) { i_tlb = new tlb<MMU_ITLB_SIZE>; d_tlb = new tlb<MMU_DTLB_SIZE>; /* suppress valgrind complaints */ control = 0; rs_flag = 0; domain_access_control = 0; set_control(0x70); set_domain_access_control( 0xDEADC0DE); translation_table_base = 0xDEADC0DE; fault_status = 0; fault_address = 0; process_id = 0; fault_count = 0; dummy_entry.mapping = TLB_SMALLPAGE; dummy_entry.read_fault = NO_FAULT; dummy_entry.write_fault = NO_FAULT; register_write_checker(NULL,NULL,NULL);}arm_mmu::~arm_mmu () { delete i_tlb; delete d_tlb;}void arm_mmu::reset () { i_tlb->reset(); d_tlb->reset();} /*set the value of control register*/void arm_mmu::set_control(word_t value) { if( (control &1) != (value & 1)) { FLUSH_MMU_PAGE_TABLE(i_table); FLUSH_MMU_PAGE_TABLE(d_table); } if(rs_flag !=( (value >> 7) & 6) ) { rs_flag =( (value >> 7) & 6); evaluate_access_all(); } if (MMU_Aligned) { mask_array[MMU_BYTE - 1] = MMU_HASH_MASK; mask_array[MMU_HALF - 1] = 0xFFFFF001; mask_array[MMU_WORD - 1] = 0xFFFFF003; } else{ mask_array[MMU_BYTE - 1] = MMU_HASH_MASK; mask_array[MMU_HALF - 1] = MMU_HASH_MASK; mask_array[MMU_WORD - 1] = MMU_HASH_MASK; } control = value;}void arm_mmu::set_process_id (word_t value){ value &= MMU_PID_VA_MAP_MASK; if (process_id != value) { process_id = value; int i; for(i=0; i < MMU_HASH_SIZE; i++) if ( ( i_table[i].phys_addr != 0xFFFFFFFF) && ( (i_table[i].read_tag & MMU_PID_VA_MAP_MASK) == 0 || (i_table[i].write_tag & MMU_PID_VA_MAP_MASK) == 0 )) FLUSH_MMU_PAGE_ENTRY(&i_table[i]); for(i=0; i < MMU_HASH_SIZE; i++) if ( ( d_table[i].phys_addr != 0xFFFFFFFF) && ( (d_table[i].read_tag & MMU_PID_VA_MAP_MASK) == 0 || (d_table[i].write_tag & MMU_PID_VA_MAP_MASK) == 0 )) FLUSH_MMU_PAGE_ENTRY(&d_table[i]); }} void arm_mmu::add_mmu_page_entry(mmu_page_table_entry_t *x_table, tlb_entry_t *tlb_e,word_t phys_addr,word_t virt_addr){ byte_t *ptr = emu->mem->translate(phys_addr); if (ptr != NULL && tlb_e->mapping != TLB_TINYPAGE) { mmu_page_table_entry_t *pte = x_table + MMU_HASH_INDEX(virt_addr); if (tlb_e->read_fault == NO_FAULT) pte->read_tag = (virt_addr & MMU_HASH_MASK); else pte->read_tag = 0xffffffff; if (x_table == d_table) { if (tlb_e->write_fault == NO_FAULT) pte->write_tag = (virt_addr & MMU_HASH_MASK); else pte->write_tag = 0xffffffff; if(fcheck && fcheck(wchecker, phys_addr)) pte->write_tag = 0xffffffff; } pte->phys_addr = (phys_addr & MMU_HASH_MASK) - (virt_addr & MMU_HASH_MASK); pte->ptr = ptr + (phys_addr & MMU_HASH_MASK); //get host address pte->ptr -= (virt_addr & MMU_HASH_MASK); //prepare to converse virtual address to host address }} void arm_mmu::remove_mmu_page_entry(mmu_page_table_entry_t *x_table, tlb_entry_t *tlb_e){ FLUSH_MMU_PAGE_ENTRY(x_table + MMU_HASH_INDEX(tlb_e->virt_addr)); if (tlb_e->mapping == TLB_SECTION || tlb_e->mapping == TLB_LARGEPAGE) FLUSH_MMU_PAGE_TABLE(x_table); /* for(int i=0; i < MMU_HASH_SIZE; i++) if ( x_table[i].phys_addr != 0xFFFFFFFF) if ( ( (x_table[i].read_tag & tlb_e->mask) == tlb_e->virt_addr) ||( (x_table[i].write_tag & tlb_e->mask) == tlb_e->virt_addr) ) FLUSH_MMU_PAGE_ENTRY(x_table + i); */ } #ifdef RECORD_TLB_MISS #define D_TRANS(_a, _e) \ if ((_e=d_tlb->search(_a))==NULL) {\ _e = d_tlb->next();\ remove_mmu_page_entry(d_table,_e);\ fault = translation_walk(_a, _e); \ } \ else \ fault = NO_FAULT; #define I_TRANS(_a, _e) \ if ((_e=i_tlb->search(_a))==NULL) {\ _e = i_tlb->next();\ remove_mmu_page_entry(i_table,_e);\ fault = translation_walk(_a, _e); \ } \ else \ fault = NO_FAULT;#else#define D_TRANS(_a, _e) \ if ((_e=d_tlb->search(_a))==NULL) {\ _e = d_tlb->next(), fault = translation_walk(_a, _e); \ } \ else \ fault = NO_FAULT; #define I_TRANS(_a, _e) \ if ((_e=i_tlb->search(_a))==NULL) {\ _e = i_tlb->next(), fault = translation_walk(_a, _e); \ } \ else \ fault = NO_FAULT;#endifmmu_fault_t arm_mmu::translate_data_addr_slow (word_t virt_addr, mmu_access_t read, mmu_size_t size, word_t *phys_addr){ mmu_fault_t fault; tlb_entry_t *entry; word_t va = mmu_pid_va_map (virt_addr); if (MMU_Disabled) { *phys_addr = va; /*Cache Translation Process*/ add_mmu_page_entry(d_table ,&dummy_entry,*phys_addr,virt_addr); return NO_FAULT; } if (MMU_Aligned && (va & (size-1))) { return ALIGNMENT_FAULT; } /*Search for maching TLB entry*/ D_TRANS(va, entry); if (fault) {/*Translation fault*/ return fault; } /*Check access permission*/ fault = (read == MMU_READ)? entry->read_fault : entry->write_fault; if (fault) { fault = check_access(va, entry, read); if(fault) {/*Access fault*/ return fault; } } /*Convert virtual address to physical address*/ *phys_addr = tlb_va_to_pa (entry, va); /*Cache Translation Process*/ add_mmu_page_entry(d_table,entry,*phys_addr,virt_addr); return NO_FAULT;} mmu_fault_t arm_mmu::read_byte_slow (word_t virt_addr, byte_t * data){ mmu_fault_t fault; word_t pa; fault = translate_data_addr_slow (virt_addr, MMU_READ, MMU_BYTE, &pa); if (fault) { update_fsr_far (fault,virt_addr); return fault; } MEM_READ_BYTE(pa, data); return NO_FAULT;}mmu_fault_t arm_mmu::read_hword_slow (word_t virt_addr, hword_t *data){ mmu_fault_t fault; word_t pa; fault = translate_data_addr_slow (virt_addr, MMU_READ, MMU_HALF, &pa); if (fault) { update_fsr_far (fault,virt_addr); return fault; } MEM_READ_HALF_WORD(pa, data); return NO_FAULT;}mmu_fault_t arm_mmu::read_word_slow (word_t virt_addr, word_t * data){ mmu_fault_t fault; word_t pa; fault = translate_data_addr_slow (virt_addr, MMU_READ, MMU_WORD, &pa); if (fault) { update_fsr_far (fault,virt_addr); return fault; } MEM_READ_WORD(pa, data); return NO_FAULT;}mmu_fault_t arm_mmu::write_byte_slow (word_t virt_addr, byte_t data){ mmu_fault_t fault; word_t pa; fault = translate_data_addr_slow (virt_addr, MMU_WRITE, MMU_BYTE, &pa); if (fault) { update_fsr_far (fault,virt_addr); return fault; } if(falarm && falarm(wchecker, pa)) { return JIT_ALARM_FAULT; } MEM_WRITE_BYTE(pa, data); return NO_FAULT;}mmu_fault_t arm_mmu::write_hword_slow (word_t virt_addr, hword_t data){ mmu_fault_t fault; word_t pa; fault = translate_data_addr_slow (virt_addr, MMU_WRITE, MMU_HALF, &pa); if (fault) { update_fsr_far (fault,virt_addr); return fault; } if(falarm && falarm(wchecker, pa)) { return JIT_ALARM_FAULT; } MEM_WRITE_HALF_WORD(pa, data); return NO_FAULT;}mmu_fault_t arm_mmu::write_word_slow (word_t virt_addr, word_t data){ mmu_fault_t fault; word_t pa; fault = translate_data_addr_slow (virt_addr, MMU_WRITE, MMU_WORD, &pa); if (fault) { update_fsr_far (fault,virt_addr); return fault; } if(falarm && falarm(wchecker, pa)) { return JIT_ALARM_FAULT; } MEM_WRITE_WORD (pa, data); return NO_FAULT;}mmu_fault_t arm_mmu::translate_instr_addr_slow (word_t virt_addr, word_t *phys_addr){ mmu_fault_t fault; tlb_entry_t *entry; word_t va = mmu_pid_va_map (virt_addr); if (MMU_Disabled) { *phys_addr = va; add_mmu_page_entry(i_table,&dummy_entry,*phys_addr,virt_addr); return NO_FAULT; } /*Search for maching TLB entry*/ I_TRANS(va, entry); if (fault) {/*Translation fault*/ return fault; } /*Check read access permission*/ if (entry->read_fault) { fault = check_access(va, entry, MMU_READ); if(fault) {/*access fault*/ return fault; } } /*Convert virtual address to physical address*/ *phys_addr = tlb_va_to_pa (entry, va); /*Cache Translation Process*/ add_mmu_page_entry(i_table,entry,*phys_addr,virt_addr); return NO_FAULT;}mmu_fault_t arm_mmu::load_instr_slow (word_t virt_addr, word_t * instr){ mmu_fault_t fault; word_t pa; fault = translate_instr_addr_slow (virt_addr, &pa); if (fault) return fault; MEM_READ_WORD(pa, instr); return NO_FAULT;}word_t arm_mmu::mrc (word_t instr){ word_t data; unsigned creg = (instr>>16)&15; //unsigned opcode_2 = (instr>>5)&7; //unsigned CRm = (instr>>0)&15; switch (creg) { case MMU_ID: data = emu->get_cpu_id(); break; case MMU_CONTROL: data = control; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -