[PATCH v3 11/23] s390: Read/write ARM system registers
From: Steffen Eiden
Date: Fri Nov 06 2026 - 11:14:36 EST
Introduce Extract Arm System Register and Set Arm System Register to
enable s390 hosts to read and write system registers for arm64 guests.
The new instructions use the new RIE_H instruction format. Add assembler
macros to create instructions in RIE_H format manually. Add Support for
disassembling the new instructions.
Co-developed-by: Andreas Grapentin <gra@xxxxxxxxxxxxx>
Signed-off-by: Andreas Grapentin <gra@xxxxxxxxxxxxx>
Signed-off-by: Steffen Eiden <seiden@xxxxxxxxxxxxx>
---
arch/s390/include/asm/sae-asm.h | 45 +++++++++++++++++++++++++++
arch/s390/include/asm/sae.h | 67 +++++++++++++++++++++++++++++++++++++++++
arch/s390/kernel/dis.c | 1 +
arch/s390/tools/opcodes.txt | 2 ++
4 files changed, 115 insertions(+)
diff --git a/arch/s390/include/asm/sae-asm.h b/arch/s390/include/asm/sae-asm.h
new file mode 100644
index 000000000000..a1abb07b5a77
--- /dev/null
+++ b/arch/s390/include/asm/sae-asm.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_S390_SAE_ASM_H
+#define __ASM_S390_SAE_ASM_H
+
+#ifndef __ASM_S390_SAE_H
+#error only <asm/sae.h> can be included directly
+#endif
+
+#include <asm/insn-common-asm.h>
+
+#ifdef __ASSEMBLER__
+
+/*
+ * INSN_RIE_H - RIE-h instruction format
+ *
+ * RIE-h format: <insn> R1, R3, I2, M4
+ * +--------+----+----+----+-----------------+----+--------+
+ * | OpCode | R1 |////| R3 | I2 | M4 | Opcode |
+ * +--------+----+----+----+-----------------+----+--------+
+ * 0 8 12 16 20 36 40 47
+ */
+.macro INSN_RIE_H opc, gr1, gr3, imm2, m4
+ GR_NUM r1, \gr1
+ GR_NUM r3, \gr3
+ ASSERT_U16 \opc
+ ASSERT_U16 \imm2
+ ASSERT_U4 \m4
+ .byte (\opc & 0xff00) >> 8
+ .byte r1 << 4
+ .byte (r3 << 4) | ((\imm2 & 0xf000) >> 12)
+ .byte ((\imm2 & 0x0ff0) >> 4)
+ .byte ((\imm2 & 0x000f) << 4) | (\m4 & 0xf)
+ .byte \opc & 0xff
+.endm
+
+.macro SASR r1, r3, i2, m4
+ INSN_RIE_H 0xed99, \r1, \r3, \i2, \m4
+.endm
+
+.macro EASR r1, r3, i2, m4
+ INSN_RIE_H 0xed9b, \r1, \r3, \i2, \m4
+.endm
+
+#endif /* __ASSEMBLER__ */
+#endif /* __ASM_S390_SAE_ASM_H */
diff --git a/arch/s390/include/asm/sae.h b/arch/s390/include/asm/sae.h
index 26d89fae0836..cac4fbf58e88 100644
--- a/arch/s390/include/asm/sae.h
+++ b/arch/s390/include/asm/sae.h
@@ -4,6 +4,7 @@
#include <linux/linkage.h>
#include <linux/types.h>
+#include <asm/sae-asm.h>
#ifndef __ASSEMBLER__
@@ -12,6 +13,72 @@ asmlinkage void sae(phys_addr_t sae_block_phys);
#include <linux/io.h>
#include <asm/kvm_host_arm64_types.h>
+#include <asm/insn-common-asm-emit.h>
+
+asm(".include \"asm/sae-asm.h\"\n");
+
+#define _SAE_ASR_REG_SHIFT 5
+#define SASR_FLAG_INITIALIZED 0x8
+#define EASR_FLAG_SA 0x8
+
+/**
+ * sasr() - Set Arm System Register
+ * @arm_reg: ARM system register identifier; compile-time constant
+ * @val: Value to set
+ * @save_area: Pointer to SAE save area
+ * @flags: Operation flags; compile-time constant
+ *
+ * Sets an ARM system register value.
+ */
+static __always_inline void sasr(unsigned int arm_reg, u64 val,
+ struct kvm_sae_save_area *save_area,
+ u64 flags)
+{
+ struct kvm_sae_block *sdo = phys_to_virt(save_area->sdo);
+ u16 reg = arm_reg >> _SAE_ASR_REG_SHIFT;
+
+ /*
+ * SASR expects an U16 for imm2 but s390 inline asm can only provide S16 values
+ * via the K modifier. Convert i2 to unsigned by and-ing with 0xffff
+ */
+ asm_inline volatile(
+ " SASR %[r1],%[r3],%[i2] & 0xffff,%[m4]\n"
+ : "+m" (*save_area), "+m" (*sdo)
+ : [r1] "d" (val),
+ [r3] "a" (save_area), [i2] "K" (reg), [m4] "I" (flags)
+ );
+}
+
+/**
+ * easr() - Extract Arm System Register
+ * @arm_reg: ARM system register identifier; compile-time constant
+ * @save_area: Pointer to SAE save area
+ * @flags: Operation flags; compile-time constant
+ *
+ * Reads an ARM system register value.
+ *
+ * Return: Register value
+ */
+static __always_inline u64 easr(unsigned int arm_reg,
+ const struct kvm_sae_save_area *save_area,
+ u64 flags)
+{
+ struct kvm_sae_block *sdo = phys_to_virt(save_area->sdo);
+ u16 reg = arm_reg >> _SAE_ASR_REG_SHIFT;
+ u64 val;
+
+ /*
+ * EASR expects an U16 for imm2 but s390 inline asm can only provide S16 values
+ * via the K modifier. Convert i2 to unsigned by and-ing with 0xffff
+ */
+ asm_inline volatile(
+ " EASR %[r1],%[r3],%[i2] & 0xffff,%[m4]\n"
+ : [r1] "=d" (val)
+ : "m" (*save_area),
+ "m" (*sdo), [r3] "a" (save_area), [i2] "K" (reg), [m4] "I" (flags)
+ );
+ return val;
+}
/**
* stiasrm() - STore and Invalidate Arm System Register Multiple
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index d2d299ba8b86..065264667db3 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -208,6 +208,7 @@ static const unsigned char formats[][6] = {
[INSTR_RIE_RUI0] = { R_8, I16_16, U4_12, 0, 0, 0 },
[INSTR_RIE_RUPI] = { R_8, I8_32, U4_12, J16_16, 0, 0 },
[INSTR_RIE_RUPU] = { R_8, U8_32, U4_12, J16_16, 0, 0 },
+ [INSTR_RIE_R0RUU] = { R_8, R_16, U16_20, U4_36, 0, 0 },
[INSTR_RIL_RI] = { R_8, I32_16, 0, 0, 0, 0 },
[INSTR_RIL_RP] = { R_8, J32_16, 0, 0, 0, 0 },
[INSTR_RIL_RU] = { R_8, U32_16, 0, 0, 0, 0 },
diff --git a/arch/s390/tools/opcodes.txt b/arch/s390/tools/opcodes.txt
index 0e4773c94af0..22fb9c500a56 100644
--- a/arch/s390/tools/opcodes.txt
+++ b/arch/s390/tools/opcodes.txt
@@ -1255,6 +1255,8 @@ ed64 ley RXY_FRRD
ed65 ldy RXY_FRRD
ed66 stey RXY_FRRD
ed67 stdy RXY_FRRD
+ed99 sasr RIE_R0RUU
+ed9b easr RIE_R0RUU
eda8 czdt RSL_LRDFU
eda9 czxt RSL_LRDFU
edaa cdzt RSL_LRDFU
--
2.56.0