From patchwork Wed Jun 27 09:54:01 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jia Liu X-Patchwork-Id: 167585 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 1633E1007D5 for ; Wed, 27 Jun 2012 19:55:17 +1000 (EST) Received: from localhost ([::1]:49754 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Sjoxv-0003Tx-1P for incoming@patchwork.ozlabs.org; Wed, 27 Jun 2012 05:55:15 -0400 Received: from eggs.gnu.org ([208.118.235.92]:52525) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Sjoxf-0003EX-V5 for qemu-devel@nongnu.org; Wed, 27 Jun 2012 05:55:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SjoxZ-0003qH-1j for qemu-devel@nongnu.org; Wed, 27 Jun 2012 05:54:59 -0400 Received: from mail-pb0-f45.google.com ([209.85.160.45]:35557) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SjoxY-0003Vw-OE for qemu-devel@nongnu.org; Wed, 27 Jun 2012 05:54:52 -0400 Received: by mail-pb0-f45.google.com with SMTP id ro12so1427578pbb.4 for ; Wed, 27 Jun 2012 02:54:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:x-mailer:in-reply-to:references :content-type; bh=+XYLirL+xedd/LLXhMPQD/NAHAN6YwB7y1GFJCJBHJU=; b=Q7WlfQq/CsdH/OD6TI/YYSx+y3+5+DkGNDJnDT4Pa3WlnIFJ3eTqZ8DQ+8y/6fQVw+ E7zPx93GQ/JoR2d4SJssIide+11OrkB+OUcC8zoCHuvmJhRDSpbolDnQNm8b3YTC7BLx ShtNIbwUZZ9bJYjkbDD+Y/bhrBc9Jftu30kiNuL0ra72AeNMPidpkIZyo6pl9DtaNWxF 4PrcukRGozPv9vfvYQj+JV6MdVgcL8c4dhZYnWVOEy/8cexEDgYndnSef1fxH9z35Cla Mf2bUGXBHs7td5EgYrlhm9YP0h6+luEEny6uSWFW4qusLjhmspgw4reHK41rA2z8GtCp l0yQ== Received: by 10.68.240.73 with SMTP id vy9mr61322885pbc.102.1340790891675; Wed, 27 Jun 2012 02:54:51 -0700 (PDT) Received: from localhost ([1.202.183.51]) by mx.google.com with ESMTPS id vz9sm14715176pbc.12.2012.06.27.02.54.47 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 27 Jun 2012 02:54:50 -0700 (PDT) From: Jia Liu To: qemu-devel@nongnu.org Date: Wed, 27 Jun 2012 17:54:01 +0800 Message-Id: <1340790854-15580-4-git-send-email-proljc@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1340790854-15580-1-git-send-email-proljc@gmail.com> References: <1340790854-15580-1-git-send-email-proljc@gmail.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.160.45 Subject: [Qemu-devel] [PATCH v7 03/16] target-or32: Add MMU support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add OpenRISC MMU support. Signed-off-by: Jia Liu --- target-openrisc/cpu.h | 75 +++++++++++++++- target-openrisc/mmu.c | 199 +++++++++++++++++++++++++++++++++++++++++- target-openrisc/mmu_helper.c | 20 +++++ 3 files changed, 292 insertions(+), 2 deletions(-) diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 58c8081..dbb3f17 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -43,6 +43,12 @@ struct CPUOpenRISCState; #define NB_MMU_MODES 3 +enum { + MMU_NOMMU_IDX = 0, + MMU_SUPERVISOR_IDX = 1, + MMU_USER_IDX = 2, +}; + #define TARGET_PAGE_BITS 13 #define TARGET_PHYS_ADDR_SPACE_BITS 32 @@ -206,6 +212,54 @@ enum { TTMR_M = (3 << 30), }; +/* TLB size */ +enum { + DTLB_WAYS = 1, + DTLB_SIZE = 64, + DTLB_MASK = (DTLB_SIZE-1), + ITLB_WAYS = 1, + ITLB_SIZE = 64, + ITLB_MASK = (ITLB_SIZE-1), +}; + +/* TLB prot */ +enum { + URE = (1 << 6), + UWE = (1 << 7), + SRE = (1 << 8), + SWE = (1 << 9), + + SXE = (1 << 6), + UXE = (1 << 7), +}; + +/* check if tlb available */ +enum { + TLBRET_INVALID = -3, + TLBRET_NOMATCH = -2, + TLBRET_BADADDR = -1, + TLBRET_MATCH = 0 +}; + +typedef struct OpenRISCTLBEntry { + uint32_t mr; + uint32_t tr; +} OpenRISCTLBEntry; + +#ifndef CONFIG_USER_ONLY +typedef struct CPUOpenRISCTLBContext { + OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE]; + OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE]; + + int (*map_address_code)(struct CPUOpenRISCState *env, + target_phys_addr_t *physical, int *prot, + target_ulong address, int rw); + int (*map_address_data)(struct CPUOpenRISCState *env, + target_phys_addr_t *physical, int *prot, + target_ulong address, int rw); +} CPUOpenRISCTLBContext; +#endif + typedef struct CPUOpenRISCState CPUOpenRISCState; struct CPUOpenRISCState { target_ulong gpr[32]; /* General registers */ @@ -240,6 +294,8 @@ struct CPUOpenRISCState { CPU_COMMON #ifndef CONFIG_USER_ONLY + CPUOpenRISCTLBContext * tlb; + struct QEMUTimer *timer; uint32_t ttmr; /* Timer tick mode register */ uint32_t ttcr; /* Timer tick count register */ @@ -301,19 +357,33 @@ OpenRISCCPU *cpu_openrisc_init(const char *cpu_model); int cpu_openrisc_exec(CPUOpenRISCState *s); void do_interrupt(CPUOpenRISCState *env); void openrisc_translate_init(void); +int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, target_ulong address, + int rw, int mmu_idx); #define cpu_list cpu_openrisc_list #define cpu_exec cpu_openrisc_exec #define cpu_gen_code cpu_openrisc_gen_code +#define cpu_handle_mmu_fault cpu_openrisc_handle_mmu_fault #define CPU_SAVE_VERSION 1 #ifndef CONFIG_USER_ONLY void cpu_openrisc_pic_reset(CPUOpenRISCState *env); +void cpu_openrisc_store_picsr(CPUOpenRISCState *env, uint32_t value); +void cpu_openrisc_store_picmr(CPUOpenRISCState *env, uint32_t value); void cpu_openrisc_timer_reset(CPUOpenRISCState *env); +void cpu_openrisc_store_count(CPUOpenRISCState *env, target_ulong count); +void cpu_openrisc_store_compare(CPUOpenRISCState *env, target_ulong value); +uint32_t cpu_openrisc_get_count(CPUOpenRISCState *env); void openrisc_mmu_init(CPUOpenRISCState *env); +int get_phys_nommu(CPUOpenRISCState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw); +int get_phys_code(CPUOpenRISCState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw); +int get_phys_data(CPUOpenRISCState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw); #endif static inline CPUOpenRISCState *cpu_init(const char *cpu_model) @@ -339,7 +409,10 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, static inline int cpu_mmu_index(CPUOpenRISCState *env) { - return 0; + if (!(env->sr & SR_IME)) { + return MMU_NOMMU_IDX; + } + return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; } static inline bool cpu_has_work(CPUOpenRISCState *env) diff --git a/target-openrisc/mmu.c b/target-openrisc/mmu.c index cd82b7a..ecef2e1 100644 --- a/target-openrisc/mmu.c +++ b/target-openrisc/mmu.c @@ -27,13 +27,210 @@ #endif #ifndef CONFIG_USER_ONLY +int get_phys_nommu(CPUOpenRISCState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw) +{ + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + return TLBRET_MATCH; +} + +int get_phys_code(CPUOpenRISCState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw) +{ + int vpn = address >> TARGET_PAGE_BITS; + int idx = vpn & ITLB_MASK; + int right = 0; + + if ((env->tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + return TLBRET_NOMATCH; + } + if (!(env->tlb->itlb[0][idx].mr & 1)) { + return TLBRET_INVALID; + } + + if (env->sr & SR_SM) { /* supervisor mode */ + if (env->tlb->itlb[0][idx].tr & SXE) { + right |= PAGE_EXEC; + } + } else { + if (env->tlb->itlb[0][idx].tr & UXE) { + right |= PAGE_EXEC; + } + } + + if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { + return TLBRET_BADADDR; + } + + *physical = (env->tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) | + (address & (TARGET_PAGE_SIZE-1)); + *prot = right; + return TLBRET_MATCH; +} + +int get_phys_data(CPUOpenRISCState *env, target_phys_addr_t *physical, + int *prot, target_ulong address, int rw) +{ + int vpn = address >> TARGET_PAGE_BITS; + int idx = vpn & DTLB_MASK; + int right = 0; + + if ((env->tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { + return TLBRET_NOMATCH; + } + if (!(env->tlb->dtlb[0][idx].mr & 1)) { + return TLBRET_INVALID; + } + + if (env->sr & SR_SM) { /* supervisor mode */ + if (env->tlb->dtlb[0][idx].tr & SRE) { + right |= PAGE_READ; + } + if (env->tlb->dtlb[0][idx].tr & SWE) { + right |= PAGE_WRITE; + } + } else { + if (env->tlb->dtlb[0][idx].tr & URE) { + right |= PAGE_READ; + } + if (env->tlb->dtlb[0][idx].tr & UWE) { + right |= PAGE_WRITE; + } + } + + if ((rw & 0) && ((right & PAGE_READ) == 0)) { + return TLBRET_BADADDR; + } + if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { + return TLBRET_BADADDR; + } + + *physical = (env->tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) | + (address & (TARGET_PAGE_SIZE-1)); + *prot = right; + return TLBRET_MATCH; +} + +static int get_physical_address(CPUOpenRISCState *env, + target_phys_addr_t *physical, + int *prot, target_ulong address, + int rw) +{ + int ret = TLBRET_MATCH; + + /* [0x0000--0x2000]: unmapped */ + if (address < 0x2000 && (env->sr & SR_SM)) { + *physical = address; + *prot = PAGE_READ | PAGE_WRITE; + return ret; + } + + if (rw == 2) { /* ITLB */ + *physical = 0; + ret = env->tlb->map_address_code(env, physical, + prot, address, rw); + } else { /* DTLB */ + ret = env->tlb->map_address_data(env, physical, + prot, address, rw); + } + + return ret; +} +#endif + +static void raise_mmu_exception(CPUOpenRISCState *env, target_ulong address, + int rw, int tlb_error) +{ + int exception = 0; + + switch (tlb_error) { + default: + if (rw == 2) { + exception = EXCP_IPF; + } else { + exception = EXCP_DPF; + } + break; +#ifndef CONFIG_USER_ONLY + case TLBRET_BADADDR: + if (rw == 2) { + exception = EXCP_IPF; + } else { + exception = EXCP_DPF; + } + break; + case TLBRET_INVALID: + case TLBRET_NOMATCH: + /* No TLB match for a mapped address */ + if (rw == 2) { + exception = EXCP_ITLBMISS; + } else { + exception = EXCP_DTLBMISS; + } + break; +#endif + } + + env->exception_index = exception; + env->eear = address; +} + +#ifndef CONFIG_USER_ONLY +int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, + target_ulong address, int rw, int mmu_idx) +{ + int ret = 0; + target_phys_addr_t physical = 0; + int prot = 0; + + ret = get_physical_address(env, &physical, &prot, + address, rw); + + if (ret == TLBRET_MATCH) { + tlb_set_page(env, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + } else if (ret < 0) { + raise_mmu_exception(env, address, rw, ret); + ret = 1; + } + + return ret; +} +#else +int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, + target_ulong address, int rw, int mmu_idx) +{ + int ret = 0; + + raise_mmu_exception(env, address, rw, ret); + ret = 1; + + return ret; +} +#endif + +#ifndef CONFIG_USER_ONLY target_phys_addr_t cpu_get_phys_page_debug(CPUOpenRISCState *env, target_ulong addr) { - return addr; + target_phys_addr_t phys_addr; + int prot; + + if (get_physical_address(env, &phys_addr, &prot, addr, 0)) { + return -1; + } + + return phys_addr; } void openrisc_mmu_init(CPUOpenRISCState *env) { + env->tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); + + env->tlb->map_address_code = &get_phys_nommu; + env->tlb->map_address_data = &get_phys_nommu; } #endif diff --git a/target-openrisc/mmu_helper.c b/target-openrisc/mmu_helper.c index f526ba8..29387e3 100644 --- a/target-openrisc/mmu_helper.c +++ b/target-openrisc/mmu_helper.c @@ -39,5 +39,25 @@ void tlb_fill(CPUOpenRISCState *env, target_ulong addr, int is_write, int mmu_idx, uintptr_t retaddr) { + TranslationBlock *tb; + unsigned long pc; + int ret; + + ret = cpu_openrisc_handle_mmu_fault(env, addr, is_write, mmu_idx); + + if (ret) { + if (retaddr) { + /* now we have a real cpu fault. */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we + have a virtual CPU fault. */ + cpu_restore_state(tb, env, pc); + } + } + /* Raise Exception. */ + cpu_loop_exit(env); + } } #endif