From nobody Thu Nov 6 08:26:17 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1539780710181559.1128304390905; Wed, 17 Oct 2018 05:51:50 -0700 (PDT) Received: from localhost ([::1]:36473 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gClIe-0007Qr-LB for importer@patchew.org; Wed, 17 Oct 2018 08:51:44 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44809) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gCl2x-00020N-G3 for qemu-devel@nongnu.org; Wed, 17 Oct 2018 08:35:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gCl2u-0006wt-DH for qemu-devel@nongnu.org; Wed, 17 Oct 2018 08:35:31 -0400 Received: from mx2.rt-rk.com ([89.216.37.149]:39649 helo=mail.rt-rk.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gCl2t-0006gd-Ri for qemu-devel@nongnu.org; Wed, 17 Oct 2018 08:35:28 -0400 Received: from localhost (localhost [127.0.0.1]) by mail.rt-rk.com (Postfix) with ESMTP id 1825D1A21C5; Wed, 17 Oct 2018 14:34:09 +0200 (CEST) Received: from rtrkw774-lin.domain.local (rtrkw774-lin.domain.local [10.10.13.43]) by mail.rt-rk.com (Postfix) with ESMTPSA id D9A861A2218; Wed, 17 Oct 2018 14:34:08 +0200 (CEST) X-Virus-Scanned: amavisd-new at rt-rk.com From: Aleksandar Markovic To: qemu-devel@nongnu.org Date: Wed, 17 Oct 2018 14:33:52 +0200 Message-Id: <1539779635-15445-25-git-send-email-aleksandar.markovic@rt-rk.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1539779635-15445-1-git-send-email-aleksandar.markovic@rt-rk.com> References: <1539779635-15445-1-git-send-email-aleksandar.markovic@rt-rk.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 89.216.37.149 Subject: [Qemu-devel] [PULL 24/27] target/mips: Implement hardware page table walker for MIPS32 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Yongbok Kim Implement hardware page table walker. This implementation is limiter only to MIPS32. Reviewed-by: Aleksandar Markovic Signed-off-by: Yongbok Kim Signed-off-by: Aleksandar Markovic --- target/mips/helper.c | 365 ++++++++++++++++++++++++++++++++++++++++++++= +++- target/mips/internal.h | 1 + target/mips/op_helper.c | 7 +- 3 files changed, 370 insertions(+), 3 deletions(-) diff --git a/target/mips/helper.c b/target/mips/helper.c index f0c268b..8988452 100644 --- a/target/mips/helper.c +++ b/target/mips/helper.c @@ -537,6 +537,342 @@ hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vad= dr addr) } #endif =20 +#if !defined(CONFIG_USER_ONLY) +#if !defined(TARGET_MIPS64) + +/* + * Perform hardware page table walk + * + * Memory accesses are performed using the KERNEL privilege level. + * Synchronous exceptions detected on memory accesses cause a silent exit + * from page table walking, resulting in a TLB or XTLB Refill exception. + * + * Implementations are not required to support page table walk memory + * accesses from mapped memory regions. When an unsupported access is + * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill + * exception. + * + * Note that if an exception is caused by AddressTranslation or LoadMemory + * functions, the exception is not taken, a silent exit is taken, + * resulting in a TLB or XTLB Refill exception. + */ + +static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, + uint64_t *pte) +{ + if ((vaddr & ((entry_size >> 3) - 1)) !=3D 0) { + return false; + } + if (entry_size =3D=3D 64) { + *pte =3D cpu_ldq_code(env, vaddr); + } else { + *pte =3D cpu_ldl_code(env, vaddr); + } + return true; +} + +static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, + int entry_size, int ptei) +{ + uint64_t result =3D entry; + uint64_t rixi; + if (ptei > entry_size) { + ptei -=3D 32; + } + result >>=3D (ptei - 2); + rixi =3D result & 3; + result >>=3D 2; + result |=3D rixi << CP0EnLo_XI; + return result; +} + +static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, + int directory_index, bool *huge_page, bool *hgpg_directory_hit, + uint64_t *pw_entrylo0, uint64_t *pw_entrylo1) +{ + int dph =3D (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; + int psn =3D (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; + int hugepg =3D (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; + int pf_ptew =3D (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + int ptew =3D (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; + int native_shift =3D (((env->CP0_PWSize >> CP0PS_PS) & 1) =3D=3D 0) ? = 2 : 3; + int directory_shift =3D (ptew > 1) ? -1 : + (hugepg && (ptew =3D=3D 1)) ? native_shift + 1 : native_shift; + int leaf_shift =3D (ptew > 1) ? -1 : + (ptew =3D=3D 1) ? native_shift + 1 : native_shift; + uint32_t direntry_size =3D 1 << (directory_shift + 3); + uint32_t leafentry_size =3D 1 << (leaf_shift + 3); + uint64_t entry; + uint64_t paddr; + int prot; + uint64_t lsb =3D 0; + uint64_t w =3D 0; + + if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) !=3D + TLBRET_MATCH) { + /* wrong base address */ + return 0; + } + if (!get_pte(env, *vaddr, direntry_size, &entry)) { + return 0; + } + + if ((entry & (1 << psn)) && hugepg) { + *huge_page =3D true; + *hgpg_directory_hit =3D true; + entry =3D get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew= ); + w =3D directory_index - 1; + if (directory_index & 0x1) { + /* Generate adjacent page from same PTE for odd TLB page */ + lsb =3D (1 << w) >> 6; + *pw_entrylo0 =3D entry & ~lsb; /* even page */ + *pw_entrylo1 =3D entry | lsb; /* odd page */ + } else if (dph) { + int oddpagebit =3D 1 << leaf_shift; + uint64_t vaddr2 =3D *vaddr ^ oddpagebit; + if (*vaddr & oddpagebit) { + *pw_entrylo1 =3D entry; + } else { + *pw_entrylo0 =3D entry; + } + if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_= LOAD, + ACCESS_INT, cpu_mmu_index(env, false)= ) !=3D + TLBRET_MATCH) { + return 0; + } + if (!get_pte(env, vaddr2, leafentry_size, &entry)) { + return 0; + } + entry =3D get_tlb_entry_layout(env, entry, leafentry_size, pf_= ptew); + if (*vaddr & oddpagebit) { + *pw_entrylo0 =3D entry; + } else { + *pw_entrylo1 =3D entry; + } + } else { + return 0; + } + return 1; + } else { + *vaddr =3D entry; + return 2; + } +} + +static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, int r= w, + int mmu_idx) +{ + int gdw =3D (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; + int udw =3D (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; + int mdw =3D (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; + int ptw =3D (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; + int ptew =3D (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; + + /* Initial values */ + bool huge_page =3D false; + bool hgpg_bdhit =3D false; + bool hgpg_gdhit =3D false; + bool hgpg_udhit =3D false; + bool hgpg_mdhit =3D false; + + int32_t pw_pagemask =3D 0; + target_ulong pw_entryhi =3D 0; + uint64_t pw_entrylo0 =3D 0; + uint64_t pw_entrylo1 =3D 0; + + /* Native pointer size */ + /*For the 32-bit architectures, this bit is fixed to 0.*/ + int native_shift =3D (((env->CP0_PWSize >> CP0PS_PS) & 1) =3D=3D 0) ? = 2 : 3; + + /* Indices from PWField */ + int pf_gdw =3D (env->CP0_PWField >> CP0PF_GDW) & 0x3F; + int pf_udw =3D (env->CP0_PWField >> CP0PF_UDW) & 0x3F; + int pf_mdw =3D (env->CP0_PWField >> CP0PF_MDW) & 0x3F; + int pf_ptw =3D (env->CP0_PWField >> CP0PF_PTW) & 0x3F; + int pf_ptew =3D (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + + /* Indices computed from faulting address */ + int gindex =3D (address >> pf_gdw) & ((1 << gdw) - 1); + int uindex =3D (address >> pf_udw) & ((1 << udw) - 1); + int mindex =3D (address >> pf_mdw) & ((1 << mdw) - 1); + int ptindex =3D (address >> pf_ptw) & ((1 << ptw) - 1); + + /* Other HTW configs */ + int hugepg =3D (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; + + /* HTW Shift values (depend on entry size) */ + int directory_shift =3D (ptew > 1) ? -1 : + (hugepg && (ptew =3D=3D 1)) ? native_shift + 1 : native_shift; + int leaf_shift =3D (ptew > 1) ? -1 : + (ptew =3D=3D 1) ? native_shift + 1 : native_shift; + + /* Offsets into tables */ + int goffset =3D gindex << directory_shift; + int uoffset =3D uindex << directory_shift; + int moffset =3D mindex << directory_shift; + int ptoffset0 =3D (ptindex >> 1) << (leaf_shift + 1); + int ptoffset1 =3D ptoffset0 | (1 << (leaf_shift)); + + uint32_t leafentry_size =3D 1 << (leaf_shift + 3); + + /* Starting address - Page Table Base */ + uint64_t vaddr =3D env->CP0_PWBase; + + uint64_t dir_entry; + uint64_t paddr; + int prot; + int m; + + if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { + /* walker is unimplemented */ + return false; + } + if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { + /* walker is disabled */ + return false; + } + if (!(gdw > 0 || udw > 0 || mdw > 0)) { + /* no structure to walk */ + return false; + } + if ((directory_shift =3D=3D -1) || (leaf_shift =3D=3D -1)) { + return false; + } + + /* Global Directory */ + if (gdw > 0) { + vaddr |=3D goffset; + switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhi= t, + &pw_entrylo0, &pw_entrylo1)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Upper directory */ + if (udw > 0) { + vaddr |=3D uoffset; + switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhi= t, + &pw_entrylo0, &pw_entrylo1)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Middle directory */ + if (mdw > 0) { + vaddr |=3D moffset; + switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhi= t, + &pw_entrylo0, &pw_entrylo1)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Leaf Level Page Table - First half of PTE pair */ + vaddr |=3D ptoffset0; + if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) !=3D + TLBRET_MATCH) { + return false; + } + if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { + return false; + } + dir_entry =3D get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_= ptew); + pw_entrylo0 =3D dir_entry; + + /* Leaf Level Page Table - Second half of PTE pair */ + vaddr |=3D ptoffset1; + if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) !=3D + TLBRET_MATCH) { + return false; + } + if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { + return false; + } + dir_entry =3D get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_= ptew); + pw_entrylo1 =3D dir_entry; + +refill: + + m =3D (1 << pf_ptw) - 1; + + if (huge_page) { + switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | + hgpg_mdhit) + { + case 4: + m =3D (1 << pf_gdw) - 1; + if (pf_gdw & 1) { + m >>=3D 1; + } + break; + case 2: + m =3D (1 << pf_udw) - 1; + if (pf_udw & 1) { + m >>=3D 1; + } + break; + case 1: + m =3D (1 << pf_mdw) - 1; + if (pf_mdw & 1) { + m >>=3D 1; + } + break; + } + } + pw_pagemask =3D m >> 12; + update_pagemask(env, pw_pagemask << 13, &pw_pagemask); + pw_entryhi =3D (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); + { + target_ulong tmp_entryhi =3D env->CP0_EntryHi; + int32_t tmp_pagemask =3D env->CP0_PageMask; + uint64_t tmp_entrylo0 =3D env->CP0_EntryLo0; + uint64_t tmp_entrylo1 =3D env->CP0_EntryLo1; + + env->CP0_EntryHi =3D pw_entryhi; + env->CP0_PageMask =3D pw_pagemask; + env->CP0_EntryLo0 =3D pw_entrylo0; + env->CP0_EntryLo1 =3D pw_entrylo1; + + /* + * The hardware page walker inserts a page into the TLB in a manner + * identical to a TLBWR instruction as executed by the software re= fill + * handler. + */ + r4k_helper_tlbwr(env); + + env->CP0_EntryHi =3D tmp_entryhi; + env->CP0_PageMask =3D tmp_pagemask; + env->CP0_EntryLo0 =3D tmp_entrylo0; + env->CP0_EntryLo1 =3D tmp_entrylo1; + } + return true; +} +#endif +#endif + int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int r= w, int mmu_idx) { @@ -558,8 +894,7 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr addre= ss, int size, int rw, =20 /* data access */ #if !defined(CONFIG_USER_ONLY) - /* XXX: put correct access by using cpu_restore_state() - correctly */ + /* XXX: put correct access by using cpu_restore_state() correctly */ access_type =3D ACCESS_INT; ret =3D get_physical_address(env, &physical, &prot, address, rw, access_type, mmu_idx); @@ -583,6 +918,32 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr addr= ess, int size, int rw, } else if (ret < 0) #endif { +#if !defined(CONFIG_USER_ONLY) +#if !defined(TARGET_MIPS64) + if ((ret =3D=3D TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { + /* + * Memory reads during hardware page table walking are perform= ed + * as if they were kernel-mode load instructions. + */ + int mode =3D (env->hflags & MIPS_HFLAG_KSU); + bool ret_walker; + env->hflags &=3D ~MIPS_HFLAG_KSU; + ret_walker =3D page_table_walk_refill(env, address, rw, mmu_id= x); + env->hflags |=3D mode; + if (ret_walker) { + ret =3D get_physical_address(env, &physical, &prot, + address, rw, access_type, mmu_i= dx); + if (ret =3D=3D TLBRET_MATCH) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + mmu_idx, TARGET_PAGE_SIZE); + ret =3D 0; + return ret; + } + } + } +#endif +#endif raise_mmu_exception(env, address, rw, ret); ret =3D 1; } diff --git a/target/mips/internal.h b/target/mips/internal.h index 96f9d8b..8b1b245 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -211,6 +211,7 @@ uint64_t float_class_d(uint64_t arg, float_status *fst); =20 extern unsigned int ieee_rm[]; int ieee_ex_to_mips(int xcpt); +void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagema= sk); =20 static inline void restore_rounding_mode(CPUMIPSState *env) { diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index ada22e6..d1f1d1a 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -1400,7 +1400,7 @@ void helper_mtc0_context(CPUMIPSState *env, target_ul= ong arg1) env->CP0_Context =3D (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007F= FFFF); } =20 -void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) +void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagema= sk) { uint64_t mask =3D arg1 >> (TARGET_PAGE_BITS + 1); if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 =3D=3D ~0) || @@ -1411,6 +1411,11 @@ void helper_mtc0_pagemask(CPUMIPSState *env, target_= ulong arg1) } } =20 +void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) +{ + update_pagemask(env, arg1, &env->CP0_PageMask); +} + void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) { /* SmartMIPS not implemented */ --=20 2.7.4