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