209 lines
7.8 KiB
Diff
209 lines
7.8 KiB
Diff
|
|
From c5938b5f858ee8904893e08999df1af1ae13b063 Mon Sep 17 00:00:00 2001
|
||
|
|
From: Xianglai Li <lixianglai@loongson.cn>
|
||
|
|
Date: Mon, 18 Mar 2024 15:03:32 +0800
|
||
|
|
Subject: [PATCH] target/loongarch: Fix tlb huge page loading issue
|
||
|
|
|
||
|
|
When we use qemu tcg simulation, the page size of bios is 4KB.
|
||
|
|
When using the level 2 super huge page (page size is 1G) to create the page table,
|
||
|
|
it is found that the content of the corresponding address space is abnormal,
|
||
|
|
resulting in the bios can not start the operating system and graphical interface normally.
|
||
|
|
|
||
|
|
The lddir and ldpte instruction emulation has
|
||
|
|
a problem with the use of super huge page processing above level 2.
|
||
|
|
The page size is not correctly calculated,
|
||
|
|
resulting in the wrong page size of the table entry found by tlb.
|
||
|
|
|
||
|
|
Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
|
||
|
|
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
|
||
|
|
Signed-off-by: Song Gao <gaosong@loongson.cn>
|
||
|
|
Message-Id: <20240318070332.1273939-1-lixianglai@loongson.cn>
|
||
|
|
---
|
||
|
|
target/loongarch/cpu-csr.h | 3 +
|
||
|
|
target/loongarch/internals.h | 5 --
|
||
|
|
target/loongarch/tcg/tlb_helper.c | 113 +++++++++++++++++++++---------
|
||
|
|
3 files changed, 82 insertions(+), 39 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h
|
||
|
|
index c59d7a9fcb..0834e91f30 100644
|
||
|
|
--- a/target/loongarch/cpu-csr.h
|
||
|
|
+++ b/target/loongarch/cpu-csr.h
|
||
|
|
@@ -67,6 +67,9 @@ FIELD(TLBENTRY, D, 1, 1)
|
||
|
|
FIELD(TLBENTRY, PLV, 2, 2)
|
||
|
|
FIELD(TLBENTRY, MAT, 4, 2)
|
||
|
|
FIELD(TLBENTRY, G, 6, 1)
|
||
|
|
+FIELD(TLBENTRY, HUGE, 6, 1)
|
||
|
|
+FIELD(TLBENTRY, HGLOBAL, 12, 1)
|
||
|
|
+FIELD(TLBENTRY, LEVEL, 13, 2)
|
||
|
|
FIELD(TLBENTRY_32, PPN, 8, 24)
|
||
|
|
FIELD(TLBENTRY_64, PPN, 12, 36)
|
||
|
|
FIELD(TLBENTRY_64, NR, 61, 1)
|
||
|
|
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
|
||
|
|
index a2fc54c8a7..944153b180 100644
|
||
|
|
--- a/target/loongarch/internals.h
|
||
|
|
+++ b/target/loongarch/internals.h
|
||
|
|
@@ -16,11 +16,6 @@
|
||
|
|
#define TARGET_PHYS_MASK MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS)
|
||
|
|
#define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS)
|
||
|
|
|
||
|
|
-/* Global bit used for lddir/ldpte */
|
||
|
|
-#define LOONGARCH_PAGE_HUGE_SHIFT 6
|
||
|
|
-/* Global bit for huge page */
|
||
|
|
-#define LOONGARCH_HGLOBAL_SHIFT 12
|
||
|
|
-
|
||
|
|
void loongarch_translate_init(void);
|
||
|
|
|
||
|
|
void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
|
||
|
|
diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c
|
||
|
|
index 804ab7a263..eedd1ac376 100644
|
||
|
|
--- a/target/loongarch/tcg/tlb_helper.c
|
||
|
|
+++ b/target/loongarch/tcg/tlb_helper.c
|
||
|
|
@@ -17,6 +17,34 @@
|
||
|
|
#include "exec/log.h"
|
||
|
|
#include "cpu-csr.h"
|
||
|
|
|
||
|
|
+static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base,
|
||
|
|
+ uint64_t *dir_width, target_ulong level)
|
||
|
|
+{
|
||
|
|
+ switch (level) {
|
||
|
|
+ case 1:
|
||
|
|
+ *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
|
||
|
|
+ *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
|
||
|
|
+ break;
|
||
|
|
+ case 2:
|
||
|
|
+ *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
|
||
|
|
+ *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
|
||
|
|
+ break;
|
||
|
|
+ case 3:
|
||
|
|
+ *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
|
||
|
|
+ *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
|
||
|
|
+ break;
|
||
|
|
+ case 4:
|
||
|
|
+ *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
|
||
|
|
+ *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
|
||
|
|
+ break;
|
||
|
|
+ default:
|
||
|
|
+ /* level may be zero for ldpte */
|
||
|
|
+ *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
|
||
|
|
+ *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
|
||
|
|
+ break;
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
static void raise_mmu_exception(CPULoongArchState *env, target_ulong address,
|
||
|
|
MMUAccessType access_type, int tlb_error)
|
||
|
|
{
|
||
|
|
@@ -486,7 +514,25 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
|
||
|
|
target_ulong badvaddr, index, phys, ret;
|
||
|
|
int shift;
|
||
|
|
uint64_t dir_base, dir_width;
|
||
|
|
- bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
|
||
|
|
+
|
||
|
|
+ if (unlikely((level == 0) || (level > 4))) {
|
||
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
||
|
|
+ "Attepted LDDIR with level %"PRId64"\n", level);
|
||
|
|
+ return base;
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (FIELD_EX64(base, TLBENTRY, HUGE)) {
|
||
|
|
+ if (unlikely(level == 4)) {
|
||
|
|
+ qemu_log_mask(LOG_GUEST_ERROR,
|
||
|
|
+ "Attempted use of level 4 huge page\n");
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (FIELD_EX64(base, TLBENTRY, LEVEL)) {
|
||
|
|
+ return base;
|
||
|
|
+ } else {
|
||
|
|
+ return FIELD_DP64(base, TLBENTRY, LEVEL, level);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
|
||
|
|
badvaddr = env->CSR_TLBRBADV;
|
||
|
|
base = base & TARGET_PHYS_MASK;
|
||
|
|
@@ -495,30 +541,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base,
|
||
|
|
shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH);
|
||
|
|
shift = (shift + 1) * 3;
|
||
|
|
|
||
|
|
- if (huge) {
|
||
|
|
- return base;
|
||
|
|
- }
|
||
|
|
- switch (level) {
|
||
|
|
- case 1:
|
||
|
|
- dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE);
|
||
|
|
- dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH);
|
||
|
|
- break;
|
||
|
|
- case 2:
|
||
|
|
- dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE);
|
||
|
|
- dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH);
|
||
|
|
- break;
|
||
|
|
- case 3:
|
||
|
|
- dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE);
|
||
|
|
- dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH);
|
||
|
|
- break;
|
||
|
|
- case 4:
|
||
|
|
- dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE);
|
||
|
|
- dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH);
|
||
|
|
- break;
|
||
|
|
- default:
|
||
|
|
- do_raise_exception(env, EXCCODE_INE, GETPC());
|
||
|
|
- return 0;
|
||
|
|
- }
|
||
|
|
+ get_dir_base_width(env, &dir_base, &dir_width, level);
|
||
|
|
index = (badvaddr >> dir_base) & ((1 << dir_width) - 1);
|
||
|
|
phys = base | index << shift;
|
||
|
|
ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK;
|
||
|
|
@@ -531,20 +554,42 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd,
|
||
|
|
CPUState *cs = env_cpu(env);
|
||
|
|
target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv;
|
||
|
|
int shift;
|
||
|
|
- bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1;
|
||
|
|
uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE);
|
||
|
|
uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH);
|
||
|
|
+ uint64_t dir_base, dir_width;
|
||
|
|
|
||
|
|
+ /*
|
||
|
|
+ * The parameter "base" has only two types,
|
||
|
|
+ * one is the page table base address,
|
||
|
|
+ * whose bit 6 should be 0,
|
||
|
|
+ * and the other is the huge page entry,
|
||
|
|
+ * whose bit 6 should be 1.
|
||
|
|
+ */
|
||
|
|
base = base & TARGET_PHYS_MASK;
|
||
|
|
+ if (FIELD_EX64(base, TLBENTRY, HUGE)) {
|
||
|
|
+ /*
|
||
|
|
+ * Gets the huge page level and Gets huge page size.
|
||
|
|
+ * Clears the huge page level information in the entry.
|
||
|
|
+ * Clears huge page bit.
|
||
|
|
+ * Move HGLOBAL bit to GLOBAL bit.
|
||
|
|
+ */
|
||
|
|
+ get_dir_base_width(env, &dir_base, &dir_width,
|
||
|
|
+ FIELD_EX64(base, TLBENTRY, LEVEL));
|
||
|
|
+
|
||
|
|
+ base = FIELD_DP64(base, TLBENTRY, LEVEL, 0);
|
||
|
|
+ base = FIELD_DP64(base, TLBENTRY, HUGE, 0);
|
||
|
|
+ if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) {
|
||
|
|
+ base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0);
|
||
|
|
+ base = FIELD_DP64(base, TLBENTRY, G, 1);
|
||
|
|
+ }
|
||
|
|
|
||
|
|
- if (huge) {
|
||
|
|
- /* Huge Page. base is paddr */
|
||
|
|
- tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT);
|
||
|
|
- /* Move Global bit */
|
||
|
|
- tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >>
|
||
|
|
- LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT |
|
||
|
|
- (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT)));
|
||
|
|
- ps = ptbase + ptwidth - 1;
|
||
|
|
+ ps = dir_base + dir_width - 1;
|
||
|
|
+ /*
|
||
|
|
+ * Huge pages are evenly split into parity pages
|
||
|
|
+ * when loaded into the tlb,
|
||
|
|
+ * so the tlb page size needs to be divided by 2.
|
||
|
|
+ */
|
||
|
|
+ tmp0 = base;
|
||
|
|
if (odd) {
|
||
|
|
tmp0 += MAKE_64BIT_MASK(ps, 1);
|
||
|
|
}
|
||
|
|
--
|
||
|
|
2.33.0
|
||
|
|
|