From patchwork Thu Mar 17 05:05:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Henderson X-Patchwork-Id: 552248 Delivered-To: patch@linaro.org Received: by 2002:a05:7000:6713:0:0:0:0 with SMTP id u19csp893965mag; Wed, 16 Mar 2022 22:52:36 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzpdN1eWmL4OdVelPW2JME/3k1fe+vlKZq86/Z6c9g5UFf/FqonVV9nzca+idimfTyldD/I X-Received: by 2002:ac8:5ccb:0:b0:2e1:c6dc:70ae with SMTP id s11-20020ac85ccb000000b002e1c6dc70aemr2539251qta.414.1647496356076; Wed, 16 Mar 2022 22:52:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1647496356; cv=none; d=google.com; s=arc-20160816; b=cQuFVOTgRfE/N7W8qoeSq0Jpi/tu6Xl92qw0+sREpQIqZYAt2UsL54pjw+qpoq0n84 bk3bbIvJk83ESe1m2x/8bSr3pD5E3h3h0pMd+FRmOOSIL+eOQxtbXG67rhe3TS3aL0nx 8R8hIDsYiTfpmdaKHSglRSibSZvzvgUZ/zyh34CW5Kbmeucynju8ibHJLh/lDjjFuJMN rrDirHN6Qd+Mvsprm3ysHLCPdnRJXfHxskEOynF7U8LKlZSb5y4Og324bJ21UfKn9AUL ufSqqs7cY0bnQMCUM/df2BG+OmBsT4ulNQh5snTMuSo4WDCecUCpsKhiSqQNKxCaWYkr xHTw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:to:from :dkim-signature; bh=U2yPZvOlMYKvtTPlyeLmVSFVWnXL5/rMkhu8LYRz224=; b=ItJ7OEguQQ1bsFlWqc5wVGaWljlg43bNvHTRY9mPC4to4OFHvYFvlhb60kiHJ1AwuV GBiFVaQF+0qUMafyeB+DwoYdKjIUo/UBfGB8gRwtjJGnK1ovTzcMnscJ7A9U6EZlS1UC PA0lpmSdOKmRBKQCO+bmf5Q7obXqeCnTsqeFLuWUDLUthOWsWcsfFfkQWmM6p+tGMKzS Uo1n1eENwm4WWmWiOMTFAXgPVOH5PbGvzCDDNc/5y2StlTB7q4K6ThYDGK6ejnYiI9Ht mxZWr2lIDwafvVm3SIvfUSzVyWCp/1YCkfsktypu1PbOBKhENgqjNTMgZZv1d9Vkgjm+ 0Rpw== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@linaro.org header.s=google header.b=BkDbBNxq; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id a13-20020a0562140c2d00b00440ea8db648si161149qvd.544.2022.03.16.22.52.36 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Wed, 16 Mar 2022 22:52:36 -0700 (PDT) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; dkim=fail header.i=@linaro.org header.s=google header.b=BkDbBNxq; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from localhost ([::1]:51320 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nUj3r-0005Wf-Jw for patch@linaro.org; Thu, 17 Mar 2022 01:52:35 -0400 Received: from eggs.gnu.org ([209.51.188.92]:51030) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nUiKv-0001MD-Rt for qemu-devel@nongnu.org; Thu, 17 Mar 2022 01:06:09 -0400 Received: from [2607:f8b0:4864:20::62b] (port=38574 helo=mail-pl1-x62b.google.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1nUiKt-0002HT-NN for qemu-devel@nongnu.org; Thu, 17 Mar 2022 01:06:09 -0400 Received: by mail-pl1-x62b.google.com with SMTP id n18so3560395plg.5 for ; Wed, 16 Mar 2022 22:06:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=U2yPZvOlMYKvtTPlyeLmVSFVWnXL5/rMkhu8LYRz224=; b=BkDbBNxqj1ieYNaDO6X6C8B9Gjg1caYwydPQC6cb8KiZg66zglavNTx9Gm/AvvJrqF vQ3AXW4ze8C/F9TLWr+2I9wH7Yvp1VzKVsRYH0s71iZtRXX1B+lJWLf2SRWHMoUKwbgF zxaau93oHwCXd8we4pqQALEHJPsydFcb5VHgCOrYjlg558eoVyyZmVAHa/GDy74+mqQS 9u/ne0rKNvHZxZqK1y55Re0rvhd6A1UJ/snWUfF5Wbr6JNCSuMl4H3KCKhZc22BXEHNj 5KGI4A+FcwQoXAjvJtwyH6ex+PKOEHWlM6adtHJaulyHR+dZ15zMJzgiJnAAd8RPIaAj 1spw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=U2yPZvOlMYKvtTPlyeLmVSFVWnXL5/rMkhu8LYRz224=; b=jKtMupIpdTi+MGyCH3+T5czqz3uaR+969DvQIudR1lbNC9VSiZP+RzF8xFvoVnItWd 47qiVsGu8Mjlm0BaFfGfmVHdnwDxJFvYrnbUH5ivLMWNvdiuX6s36MlMUsDwJEY6bZcF Bem/CMWF1ZD8Emj5Tirobf+HZUtCliEs3a0+fMfBjpvFLHz9KiVsgPsEYQF0ovl3vImY qjKN6dVZEo9P81zeDJgGh8pwQuKZrgkYhZ1A/wt2KB7usE8SdM2PmAMK4RVATRGK86uV 7ChDQvTpaKdILFhrAdu2OkiJKGJtnLxHozalAbII0xMkcdr+sM5FAF3UfliswZbg4BK3 PH9w== X-Gm-Message-State: AOAM531aNRABpxnJqDGRKSyg890yC4TR6dLJArGBYYfvK+aZlkzNxVIy uhSIa3jMAHk32Fdb368jiGB/8nEcldfxmw== X-Received: by 2002:a17:902:f686:b0:151:d866:f657 with SMTP id l6-20020a170902f68600b00151d866f657mr3009148plg.112.1647493565573; Wed, 16 Mar 2022 22:06:05 -0700 (PDT) Received: from localhost.localdomain (174-21-142-130.tukw.qwest.net. [174.21.142.130]) by smtp.gmail.com with ESMTPSA id h22-20020a056a001a5600b004f7c17b291asm5101357pfv.87.2022.03.16.22.06.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 16 Mar 2022 22:06:05 -0700 (PDT) From: Richard Henderson To: qemu-devel@nongnu.org Subject: [PATCH for-7.1 v6 26/51] target/nios2: Prevent writes to read-only or reserved control fields Date: Wed, 16 Mar 2022 22:05:13 -0700 Message-Id: <20220317050538.924111-27-richard.henderson@linaro.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220317050538.924111-1-richard.henderson@linaro.org> References: <20220317050538.924111-1-richard.henderson@linaro.org> MIME-Version: 1.0 X-Host-Lookup-Failed: Reverse DNS lookup failed for 2607:f8b0:4864:20::62b (failed) Received-SPF: pass client-ip=2607:f8b0:4864:20::62b; envelope-from=richard.henderson@linaro.org; helo=mail-pl1-x62b.google.com X-Spam_score_int: -6 X-Spam_score: -0.7 X-Spam_bar: / X-Spam_report: (-0.7 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, PDS_HP_HELO_NORDNS=0.659, RCVD_IN_DNSWL_NONE=-0.0001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: marex@denx.de, amir.gonnen@neuroblade.ai Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: "Qemu-devel" Create an array of masks which detail the writable and readonly bits for each control register. Apply them when writing to control registers, including the write to status during eret. Signed-off-by: Richard Henderson --- target/nios2/cpu.h | 13 +++++ target/nios2/cpu.c | 100 +++++++++++++++++++++++++++++++++------ target/nios2/op_helper.c | 9 ++++ target/nios2/translate.c | 80 ++++++++++++++++++++++++------- 4 files changed, 171 insertions(+), 31 deletions(-) diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index c925cdd8e3..410e76ccbb 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -190,6 +190,11 @@ struct CPUArchState { int error_code; }; +typedef struct { + uint32_t writable; + uint32_t readonly; +} ControlRegState; + /** * Nios2CPU: * @env: #CPUNios2State @@ -213,9 +218,17 @@ struct ArchCPU { uint32_t reset_addr; uint32_t exception_addr; uint32_t fast_tlb_miss_addr; + + /* Bits within each control register which are reserved or readonly. */ + ControlRegState cr_state[NUM_CR_REGS]; }; +static inline bool nios2_cr_reserved(const ControlRegState *s) +{ + return (s->writable | s->readonly) == 0; +} + void nios2_tcg_init(void); void nios2_cpu_do_interrupt(CPUState *cs); void dump_mmu(CPUNios2State *env); diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 182ddcc18f..8189937857 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -107,6 +107,64 @@ static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model) return object_class_by_name(TYPE_NIOS2_CPU); } +static void realize_cr_status(CPUState *cs) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + + /* Begin with all fields of all registers are reserved. */ + memset(cpu->cr_state, 0, sizeof(cpu->cr_state)); + + /* + * The combination of writable and readonly is the set of all + * non-reserved fields. We apply writable as a mask to bits, + * and merge in existing readonly bits, before storing. + */ +#define WR_REG(C) cpu->cr_state[C].writable = -1 +#define RO_REG(C) cpu->cr_state[C].readonly = -1 +#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK +#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK + + WR_FIELD(CR_STATUS, PIE); + WR_REG(CR_ESTATUS); + WR_REG(CR_BSTATUS); + RO_REG(CR_CPUID); + RO_REG(CR_EXCEPTION); + WR_REG(CR_BADADDR); + + /* TODO: These control registers are not present with the EIC. */ + WR_REG(CR_IENABLE); + RO_REG(CR_IPENDING); + + if (cpu->mmu_present) { + WR_FIELD(CR_STATUS, U); + WR_FIELD(CR_STATUS, EH); + + WR_FIELD(CR_PTEADDR, VPN); + WR_FIELD(CR_PTEADDR, PTBASE); + + RO_FIELD(CR_TLBMISC, D); + RO_FIELD(CR_TLBMISC, PERM); + RO_FIELD(CR_TLBMISC, BAD); + RO_FIELD(CR_TLBMISC, DBL); + WR_FIELD(CR_TLBMISC, PID); + WR_FIELD(CR_TLBMISC, WE); + WR_FIELD(CR_TLBMISC, RD); + WR_FIELD(CR_TLBMISC, WAY); + + WR_REG(CR_TLBACC); + } + + /* + * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are + * unimplemented, so their corresponding control regs remain reserved. + */ + +#undef WR_REG +#undef RO_REG +#undef WR_FIELD +#undef RO_FIELD +} + static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -119,6 +177,7 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) return; } + realize_cr_status(cs); qemu_init_vcpu(cs); cpu_reset(cs); @@ -152,23 +211,26 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { Nios2CPU *cpu = NIOS2_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); CPUNios2State *env = &cpu->env; + uint32_t val; - if (n > cc->gdb_num_core_regs) { + if (n < 32) { /* GP regs */ + val = env->regs[n]; + } else if (n == 32) { /* PC */ + val = env->pc; + } else if (n < 49) { /* Status regs */ + unsigned cr = n - 33; + if (nios2_cr_reserved(&cpu->cr_state[cr])) { + val = 0; + } else { + val = env->ctrl[n - 33]; + } + } else { + /* Invalid regs */ return 0; } - if (n < 32) { /* GP regs */ - return gdb_get_reg32(mem_buf, env->regs[n]); - } else if (n == 32) { /* PC */ - return gdb_get_reg32(mem_buf, env->pc); - } else if (n < 49) { /* Status regs */ - return gdb_get_reg32(mem_buf, env->ctrl[n - 33]); - } - - /* Invalid regs */ - return 0; + return gdb_get_reg32(mem_buf, val); } static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) @@ -176,17 +238,25 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) Nios2CPU *cpu = NIOS2_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); CPUNios2State *env = &cpu->env; + uint32_t val; if (n > cc->gdb_num_core_regs) { return 0; } + val = ldl_p(mem_buf); if (n < 32) { /* GP regs */ - env->regs[n] = ldl_p(mem_buf); + env->regs[n] = val; } else if (n == 32) { /* PC */ - env->pc = ldl_p(mem_buf); + env->pc = val; } else if (n < 49) { /* Status regs */ - env->ctrl[n - 33] = ldl_p(mem_buf); + unsigned cr = n - 33; + /* ??? Maybe allow the debugger to write to readonly fields. */ + val &= cpu->cr_state[cr].writable; + val |= cpu->cr_state[cr].readonly & env->ctrl[cr]; + env->ctrl[cr] = val; + } else { + g_assert_not_reached(); } return 4; diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c index 08ed3b4598..c56fc15283 100644 --- a/target/nios2/op_helper.c +++ b/target/nios2/op_helper.c @@ -34,6 +34,15 @@ void helper_raise_exception(CPUNios2State *env, uint32_t index) #ifndef CONFIG_USER_ONLY void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc) { + Nios2CPU *cpu = env_archcpu(env); + + /* + * Both estatus and bstatus have no constraints on write; + * do not allow reserved fields in status to be set. + */ + new_status &= (cpu->cr_state[CR_STATUS].writable | + cpu->cr_state[CR_STATUS].readonly); + env->ctrl[CR_STATUS] = new_status; env->pc = new_pc; cpu_loop_exit(env_cpu(env)); diff --git a/target/nios2/translate.c b/target/nios2/translate.c index cfad110186..21dc6947cf 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -102,6 +102,7 @@ typedef struct DisasContext { TCGv_i32 zero; target_ulong pc; int mem_idx; + const ControlRegState *cr_state; } DisasContext; static TCGv cpu_R[NUM_GP_REGS]; @@ -471,17 +472,26 @@ static void callr(DisasContext *dc, uint32_t code, uint32_t flags) /* rC <- ctlN */ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) { - R_TYPE(instr, code); - TCGv t1, t2; - if (!gen_check_supervisor(dc)) { return; } +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + R_TYPE(instr, code); + TCGv t1, t2; + if (unlikely(instr.c == R_ZERO)) { return; } + /* Reserved registers read as zero. */ + if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) { + tcg_gen_movi_tl(cpu_R[instr.c], 0); + return; + } + switch (instr.imm5) { case CR_IPENDING: /* @@ -505,6 +515,7 @@ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) offsetof(CPUNios2State, ctrl[instr.imm5])); break; } +#endif } /* ctlN <- rA */ @@ -519,6 +530,14 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) #else R_TYPE(instr, code); TCGv v = load_gpr(dc, instr.a); + uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]); + uint32_t wr = dc->cr_state[instr.imm5].writable; + uint32_t ro = dc->cr_state[instr.imm5].readonly; + + /* Skip reserved or readonly registers. */ + if (wr == 0) { + return; + } switch (instr.imm5) { case CR_PTEADDR: @@ -530,17 +549,35 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) case CR_TLBMISC: gen_helper_mmu_write_tlbmisc(cpu_env, v); break; - case CR_IPENDING: - /* ipending is read only, writes ignored. */ - break; case CR_STATUS: case CR_IENABLE: /* If interrupts were enabled using WRCTL, trigger them. */ dc->base.is_jmp = DISAS_UPDATE; /* fall through */ default: - tcg_gen_st_tl(v, cpu_env, - offsetof(CPUNios2State, ctrl[instr.imm5])); + if (wr == -1) { + /* The register is entirely writable. */ + tcg_gen_st_tl(v, cpu_env, ofs); + } else { + /* + * The register is partially read-only or reserved: + * merge the value. + */ + TCGv n = tcg_temp_new(); + + tcg_gen_andi_tl(n, v, wr); + + if (ro != 0) { + TCGv o = tcg_temp_new(); + tcg_gen_ld_tl(o, cpu_env, ofs); + tcg_gen_andi_tl(o, o, ro); + tcg_gen_or_tl(n, n, o); + tcg_temp_free(o); + } + + tcg_gen_st_tl(n, cpu_env, ofs); + tcg_temp_free(n); + } break; } #endif @@ -818,9 +855,11 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUNios2State *env = cs->env_ptr; + Nios2CPU *cpu = env_archcpu(env); int page_insns; dc->mem_idx = cpu_mmu_index(env, false); + dc->cr_state = cpu->cr_state; /* Bound the number of insns to execute to those left on the page. */ page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; @@ -931,16 +970,25 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) } #if !defined(CONFIG_USER_ONLY) - for (i = 0; i < NUM_CR_REGS; i++) { - qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); + int j; + + for (i = j = 0; i < NUM_CR_REGS; i++) { + if (!nios2_cr_reserved(&cpu->cr_state[i])) { + qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); + if (++j % 4 == 0) { + qemu_fprintf(f, "\n"); + } } } - qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", - env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, - FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), - env->mmu.tlbacc_wr); + if (j % 4 != 0) { + qemu_fprintf(f, "\n"); + } + if (cpu->mmu_present) { + qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", + env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, + FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), + env->mmu.tlbacc_wr); + } #endif qemu_fprintf(f, "\n\n"); }