QEMU-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH] target/riscv: Adjust privilege level for HLV(X)/HSV instructions
@ 2020-10-18 12:03 Georg Kotheimer
  2020-10-18 15:57 ` Richard Henderson
  0 siblings, 1 reply; 4+ messages in thread
From: Georg Kotheimer @ 2020-10-18 12:03 UTC (permalink / raw)
  To: qemu-devel, qemu-riscv; +Cc: Georg Kotheimer

According to the specification the "field SPVP of hstatus controls the
privilege level of the access" for the hypervisor virtual-machine load
and store instructions HLV, HLVX and HSV.

We introduce the new virtualization register field HS_HYP_LD_ST,
similar to HS_TWO_STAGE, which tracks whether we are currently
executing a hypervisor virtual-macine load or store instruction.

Signed-off-by: Georg Kotheimer <georg.kotheimer@kernkonzept.com>
---
 target/riscv/cpu.h        |  2 ++
 target/riscv/cpu_bits.h   |  1 +
 target/riscv/cpu_helper.c | 60 ++++++++++++++++++++++++++++++---------
 target/riscv/op_helper.c  |  7 +++++
 4 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index de275782e6..a39674a84d 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -323,6 +323,8 @@ bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env);
 void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable);
 bool riscv_cpu_two_stage_lookup(CPURISCVState *env);
 void riscv_cpu_set_two_stage_lookup(CPURISCVState *env, bool enable);
+bool riscv_cpu_hyp_load_store_inst(CPURISCVState *env);
+void riscv_cpu_set_hyp_load_store_inst(CPURISCVState *env, bool enable);
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
 hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index bd36062877..0983ec6471 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -481,6 +481,7 @@
  */
 #define FORCE_HS_EXCEP      2
 #define HS_TWO_STAGE        4
+#define HS_HYP_LD_ST        8
 
 /* RV32 satp CSR field masks */
 #define SATP32_MODE         0x80000000
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 904899054d..58d1fa2675 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -238,6 +238,24 @@ void riscv_cpu_set_two_stage_lookup(CPURISCVState *env, bool enable)
     env->virt = set_field(env->virt, HS_TWO_STAGE, enable);
 }
 
+bool riscv_cpu_hyp_load_store_inst(CPURISCVState *env)
+{
+    if (!riscv_has_ext(env, RVH)) {
+        return false;
+    }
+
+    return get_field(env->virt, HS_HYP_LD_ST);
+}
+
+void riscv_cpu_set_hyp_load_store_inst(CPURISCVState *env, bool enable)
+{
+    if (!riscv_has_ext(env, RVH)) {
+        return;
+    }
+
+    env->virt = set_field(env->virt, HS_HYP_LD_ST, enable);
+}
+
 int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts)
 {
     CPURISCVState *env = &cpu->env;
@@ -346,7 +364,11 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
         use_background = true;
     }
 
-    if (mode == PRV_M && access_type != MMU_INST_FETCH) {
+    /* MPRV does not affect the virtual-machine load/store
+       instructions, HLV, HLVX, and HSV. */
+    if (riscv_cpu_hyp_load_store_inst(env)) {
+        mode = get_field(env->hstatus, HSTATUS_SPVP);
+    } else if (mode == PRV_M && access_type != MMU_INST_FETCH) {
         if (get_field(env->mstatus, MSTATUS_MPRV)) {
             mode = get_field(env->mstatus, MSTATUS_MPP);
         }
@@ -711,17 +733,21 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n",
                   __func__, address, access_type, mmu_idx);
 
-    if (mode == PRV_M && access_type != MMU_INST_FETCH) {
+    /* MPRV does not affect the virtual-machine load/store
+       instructions, HLV, HLVX, and HSV. */
+    if (riscv_cpu_hyp_load_store_inst(env)) {
+        mode = get_field(env->hstatus, HSTATUS_SPVP);
+    } else if (mode == PRV_M && access_type != MMU_INST_FETCH) {
         if (get_field(env->mstatus, MSTATUS_MPRV)) {
             mode = get_field(env->mstatus, MSTATUS_MPP);
         }
-    }
 
-    if (riscv_has_ext(env, RVH) && env->priv == PRV_M &&
-        access_type != MMU_INST_FETCH &&
-        get_field(env->mstatus, MSTATUS_MPRV) &&
-        MSTATUS_MPV_ISSET(env)) {
-        riscv_cpu_set_two_stage_lookup(env, true);
+        if (riscv_has_ext(env, RVH) && env->priv == PRV_M &&
+            access_type != MMU_INST_FETCH &&
+            get_field(env->mstatus, MSTATUS_MPRV) &&
+            MSTATUS_MPV_ISSET(env)) {
+            riscv_cpu_set_two_stage_lookup(env, true);
+        }
     }
 
     if (riscv_cpu_virt_enabled(env) ||
@@ -777,12 +803,16 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
                       __func__, address, ret, pa, prot);
     }
 
-    /* We did the two stage lookup based on MPRV, unset the lookup */
-    if (riscv_has_ext(env, RVH) && env->priv == PRV_M &&
-        access_type != MMU_INST_FETCH &&
-        get_field(env->mstatus, MSTATUS_MPRV) &&
-        MSTATUS_MPV_ISSET(env)) {
-        riscv_cpu_set_two_stage_lookup(env, false);
+    /* MPRV does not affect the virtual-machine load/store
+       instructions, HLV, HLVX, and HSV. */
+    if (!riscv_cpu_hyp_load_store_inst(env)) {
+        /* We did the two stage lookup based on MPRV, unset the lookup */
+        if (riscv_has_ext(env, RVH) && env->priv == PRV_M &&
+            access_type != MMU_INST_FETCH &&
+            get_field(env->mstatus, MSTATUS_MPRV) &&
+            MSTATUS_MPV_ISSET(env)) {
+            riscv_cpu_set_two_stage_lookup(env, false);
+        }
     }
 
     if (riscv_feature(env, RISCV_FEATURE_PMP) &&
@@ -949,6 +979,8 @@ void riscv_cpu_do_interrupt(CPUState *cs)
                 riscv_cpu_set_two_stage_lookup(env, false);
                 htval = env->guest_phys_fault_addr;
             }
+
+            riscv_cpu_set_hyp_load_store_inst(env, false);
         }
 
         s = env->mstatus;
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 9b9ada45a9..905a924255 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -240,6 +240,7 @@ target_ulong helper_hyp_load(CPURISCVState *env, target_ulong address,
             get_field(env->hstatus, HSTATUS_HU))) {
         target_ulong pte;
 
+        riscv_cpu_set_hyp_load_store_inst(env, true);
         riscv_cpu_set_two_stage_lookup(env, true);
 
         switch (memop) {
@@ -269,6 +270,7 @@ target_ulong helper_hyp_load(CPURISCVState *env, target_ulong address,
         }
 
         riscv_cpu_set_two_stage_lookup(env, false);
+        riscv_cpu_set_hyp_load_store_inst(env, false);
 
         return pte;
     }
@@ -288,6 +290,8 @@ void helper_hyp_store(CPURISCVState *env, target_ulong address,
         (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) ||
         (env->priv == PRV_U && !riscv_cpu_virt_enabled(env) &&
             get_field(env->hstatus, HSTATUS_HU))) {
+
+        riscv_cpu_set_hyp_load_store_inst(env, true);
         riscv_cpu_set_two_stage_lookup(env, true);
 
         switch (memop) {
@@ -311,6 +315,7 @@ void helper_hyp_store(CPURISCVState *env, target_ulong address,
         }
 
         riscv_cpu_set_two_stage_lookup(env, false);
+        riscv_cpu_set_hyp_load_store_inst(env, false);
 
         return;
     }
@@ -331,6 +336,7 @@ target_ulong helper_hyp_x_load(CPURISCVState *env, target_ulong address,
             get_field(env->hstatus, HSTATUS_HU))) {
         target_ulong pte;
 
+        riscv_cpu_set_hyp_load_store_inst(env, true);
         riscv_cpu_set_two_stage_lookup(env, true);
 
         switch (memop) {
@@ -345,6 +351,7 @@ target_ulong helper_hyp_x_load(CPURISCVState *env, target_ulong address,
         }
 
         riscv_cpu_set_two_stage_lookup(env, false);
+        riscv_cpu_set_hyp_load_store_inst(env, false);
 
         return pte;
     }
-- 
2.25.1



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] target/riscv: Adjust privilege level for HLV(X)/HSV instructions
  2020-10-18 12:03 [PATCH] target/riscv: Adjust privilege level for HLV(X)/HSV instructions Georg Kotheimer
@ 2020-10-18 15:57 ` Richard Henderson
  2020-10-21 19:21   ` Alistair Francis
  0 siblings, 1 reply; 4+ messages in thread
From: Richard Henderson @ 2020-10-18 15:57 UTC (permalink / raw)
  To: Georg Kotheimer, qemu-devel, qemu-riscv, Alistair Francis

On 10/18/20 5:03 AM, Georg Kotheimer wrote:
> According to the specification the "field SPVP of hstatus controls the
> privilege level of the access" for the hypervisor virtual-machine load
> and store instructions HLV, HLVX and HSV.
> 
> We introduce the new virtualization register field HS_HYP_LD_ST,
> similar to HS_TWO_STAGE, which tracks whether we are currently
> executing a hypervisor virtual-macine load or store instruction.
> 
> Signed-off-by: Georg Kotheimer <georg.kotheimer@kernkonzept.com>

Well, let me start by mentioning the existing implementation of hyp_load et al
is broken.  I guess I wasn't paying attention when Alistair implemented them.

Here's the problem: When you change how riscv_cpu_tlb_fill works, as you are by
modifying get_physical_address, you have to remember that those results are
*saved* in the qemu tlb.

So by setting some flags, performing one memory operation, and resetting the
flags, we have in fact corrupted the tlb for the next operation without those
flags.

You need to either:

(1) perform the memory operation *without* using the qemu tlb machinery (i.e.
use get_physical_address directly, then use address_space_ld/st), or

(2) add a new mmu index for the HLV/HSV operation, with all of the differences
implied.

The second is imo preferable, as it means that helper_hyp_load et al can go
away and become normal qemu_ld/st opcodes with the new mmu indexes.

Annoyingly, for the moment you wouldn't be able to remove helper_hyp_x_load,
because there's no qemu_ld variant that uses execute permission.  But it can be
done with helpers.  I'll note that the current implementation is broken,
because it uses cpu_lduw_data_ra, and not cpu_lduw_code_ra, which is exactly
the difference that uses execute permissions.  After the conversion to the new
mmuidx, you would use cpu_lduw_mmuidx_ra.  I would also split the function into
two, so that one performs HLVX.HU and the other HLVX.WU, so that you don't have
to pass the size as a parameter.

Finally, this patch, changing behaviour when hstatus.SPVP changes... depends on
how often this bit is expected to be toggled.

If the bit toggles frequently, e.g. around some small section of kernel code,
then one might copy the SPVP bit into tlb_flags and use two different mmu
indexes to imply the state of the SPVP bit.

If the bit does not toggle frequently, e.g. whatever bit of kernel code runs
infrequently, or it only happens around priv level changes, then simply
flushing the qemu tlb when the SPVP bit changes is sufficient.  You then get to
look at SPVP directly within tlb_fill.


r~


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] target/riscv: Adjust privilege level for HLV(X)/HSV instructions
  2020-10-18 15:57 ` Richard Henderson
@ 2020-10-21 19:21   ` Alistair Francis
  2020-10-22 15:21     ` Richard Henderson
  0 siblings, 1 reply; 4+ messages in thread
From: Alistair Francis @ 2020-10-21 19:21 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Alistair Francis, open list:RISC-V, Georg Kotheimer,
	qemu-devel@nongnu.org Developers

On Sun, Oct 18, 2020 at 8:58 AM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 10/18/20 5:03 AM, Georg Kotheimer wrote:
> > According to the specification the "field SPVP of hstatus controls the
> > privilege level of the access" for the hypervisor virtual-machine load
> > and store instructions HLV, HLVX and HSV.
> >
> > We introduce the new virtualization register field HS_HYP_LD_ST,
> > similar to HS_TWO_STAGE, which tracks whether we are currently
> > executing a hypervisor virtual-macine load or store instruction.
> >
> > Signed-off-by: Georg Kotheimer <georg.kotheimer@kernkonzept.com>
>
> Well, let me start by mentioning the existing implementation of hyp_load et al
> is broken.  I guess I wasn't paying attention when Alistair implemented them.
>
> Here's the problem: When you change how riscv_cpu_tlb_fill works, as you are by
> modifying get_physical_address, you have to remember that those results are
> *saved* in the qemu tlb.
>
> So by setting some flags, performing one memory operation, and resetting the
> flags, we have in fact corrupted the tlb for the next operation without those
> flags.
>
> You need to either:
>
> (1) perform the memory operation *without* using the qemu tlb machinery (i.e.
> use get_physical_address directly, then use address_space_ld/st), or
>
> (2) add a new mmu index for the HLV/HSV operation, with all of the differences
> implied.
>
> The second is imo preferable, as it means that helper_hyp_load et al can go
> away and become normal qemu_ld/st opcodes with the new mmu indexes.

Ok, I have some patches to fix this. I'll send them soon.


>
> Annoyingly, for the moment you wouldn't be able to remove helper_hyp_x_load,
> because there's no qemu_ld variant that uses execute permission.  But it can be
> done with helpers.  I'll note that the current implementation is broken,
> because it uses cpu_lduw_data_ra, and not cpu_lduw_code_ra, which is exactly
> the difference that uses execute permissions.  After the conversion to the new

I have tried to fix this as well, but it seems to break a virtual
Linux guest booting.

> mmuidx, you would use cpu_lduw_mmuidx_ra.  I would also split the function into
> two, so that one performs HLVX.HU and the other HLVX.WU, so that you don't have
> to pass the size as a parameter.

I'm not clear what you mean here.

Alistair

>
> Finally, this patch, changing behaviour when hstatus.SPVP changes... depends on
> how often this bit is expected to be toggled.
>
> If the bit toggles frequently, e.g. around some small section of kernel code,
> then one might copy the SPVP bit into tlb_flags and use two different mmu
> indexes to imply the state of the SPVP bit.
>
> If the bit does not toggle frequently, e.g. whatever bit of kernel code runs
> infrequently, or it only happens around priv level changes, then simply
> flushing the qemu tlb when the SPVP bit changes is sufficient.  You then get to
> look at SPVP directly within tlb_fill.
>
>
> r~
>


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] target/riscv: Adjust privilege level for HLV(X)/HSV instructions
  2020-10-21 19:21   ` Alistair Francis
@ 2020-10-22 15:21     ` Richard Henderson
  0 siblings, 0 replies; 4+ messages in thread
From: Richard Henderson @ 2020-10-22 15:21 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Alistair Francis, open list:RISC-V, Georg Kotheimer,
	qemu-devel@nongnu.org Developers

On 10/21/20 12:21 PM, Alistair Francis wrote:
>> mmuidx, you would use cpu_lduw_mmuidx_ra.  I would also split the function into
>> two, so that one performs HLVX.HU and the other HLVX.WU, so that you don't have
>> to pass the size as a parameter.
> 
> I'm not clear what you mean here.

target_ulong helper_hyp_x_load(CPURISCVState *env,
  target_ulong address, target_ulong attrs, target_ulong memop)

should be split to

target_ulong helper_hlvx_hu(CPURISCVState *env,
                            target_ulong address)
target_ulong helper_hlvx_hw(CPURISCVState *env,
                            target_ulong address)

(attrs is already unused?) so that memop is not required.


r~


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-18 12:03 [PATCH] target/riscv: Adjust privilege level for HLV(X)/HSV instructions Georg Kotheimer
2020-10-18 15:57 ` Richard Henderson
2020-10-21 19:21   ` Alistair Francis
2020-10-22 15:21     ` Richard Henderson

QEMU-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/qemu-devel/0 qemu-devel/git/0.git
	git clone --mirror https://lore.kernel.org/qemu-devel/1 qemu-devel/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 qemu-devel qemu-devel/ https://lore.kernel.org/qemu-devel \
		qemu-devel@nongnu.org
	public-inbox-index qemu-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.nongnu.qemu-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git