In TCG mode, cpu_pre_save is called when saving state and cpu_post_load
is called when restore state. There are two key functions write_cpustate_to_list
and write_list_to_cpustate that use kvm_to_cpreg_id to obtain the regidx.

However, during the processing of kvm_to_cpreg_id, secure state information will
be lost in aarch32 cpu.
The code in kvm_to_cpreg_id always add the NS flag.
     "cpregid |= CP_REG_AA32_NS_MASK;"

The final result is that after migration, some secure registers were not 
restored, 
such as DACR_S. And this register will affect the CPU's access to the address.

To fix this issue, a new function kvm_to_cpreg_id_with_secure is added to 
restore secure bit information. Call different functions according to 
different modes to keep KVM unaffected.

Signed-off-by: Tao Ding <[email protected]>
---
Steps to reproduce:
1. prepare uboot
     git clone https://github.com/u-boot/u-boot
     cd u-boot && make xilinx_zynq_virt_defconfig && make -j4
2. compile qemu
     mkdir build
     cd build && ../configure --target-list="arm-softmmu"
3. run qemu and save vmstate
     ./build/qemu-system-arm -M xilinx-zynq-a9 -machine boot-mode=sd -m 1024 \
     -display none -serial null -serial mon:stdio \
     -device loader,file=~/u-boot/u-boot-dtb.bin,addr=0x04000000,cpu-num=0 

Model: Xilinx ZC706 board
......
Hit any key to stop autoboot: 0
Zynq> 
(ctrl + a + c)
QEMU 11.0.90 monitor - type 'help' for more information
(qemu) migrate -d file:vmstate
(qemu) q

4. restore qemu
     ./build/qemu-system-arm -M xilinx-zynq-a9 -machine boot-mode=sd -m 1024 \
     -display none -serial null -serial mon:stdio \
     -device loader,file=~/u-boot/u-boot-dtb.bin,addr=0x04000000,cpu-num=0 \
     -incoming file:vmstate

Before fixed, the terminal will hang in step 4.
After fixed, Can enter uboot interaction normally in step 4.

 target/arm/cpregs.h | 11 +++++++++++
 target/arm/helper.c | 14 ++++++++++++--
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h
index 391c0e322b..c0d1235625 100644
--- a/target/arm/cpregs.h
+++ b/target/arm/cpregs.h
@@ -258,6 +258,17 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
     return kvmid;
 }
 
+/*
+ * Convert kvmid to cpreg_id but keep secure mask.
+ */
+static inline uint32_t kvm_to_cpreg_id_with_secure(uint64_t kvmid)
+{
+    uint32_t cpregid = kvm_to_cpreg_id(kvmid);
+    cpregid &= ~CP_REG_AA32_NS_MASK;
+    cpregid |= (kvmid & CP_REG_AA32_NS_MASK);
+    return cpregid;
+}
+
 /*
  * Valid values for ARMCPRegInfo state field, indicating which of
  * the AArch32 and AArch64 execution states this register is visible in.
diff --git a/target/arm/helper.c b/target/arm/helper.c
index af45234ad2..c8fccf9c0e 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -160,7 +160,12 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync)
     bool ok = true;
 
     for (i = 0; i < cpu->cpreg_array_len; i++) {
-        uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
+        uint32_t regidx;
+        if (kvm_enabled()) {
+            regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
+        } else {
+            regidx = kvm_to_cpreg_id_with_secure(cpu->cpreg_indexes[i]);
+        }
         const ARMCPRegInfo *ri;
         uint64_t newval;
 
@@ -205,7 +210,12 @@ bool write_list_to_cpustate(ARMCPU *cpu)
     bool ok = true;
 
     for (i = 0; i < cpu->cpreg_array_len; i++) {
-        uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
+        uint32_t regidx;
+        if (kvm_enabled()) {
+            regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]);
+        } else {
+            regidx = kvm_to_cpreg_id_with_secure(cpu->cpreg_indexes[i]);
+        }
         uint64_t v = cpu->cpreg_values[i];
         const ARMCPRegInfo *ri;
 
-- 
2.43.0


Reply via email to