Apple M4 removes FEAT_SSBS. However, older macOS releases
do misbehave in such a configuration and do not boot.
Use private API to trap SCTLR_EL1 accesses through FGT.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/hvf/hvf-accel-ops.c | 4 +--
include/system/hvf_int.h | 4 +++
target/arm/hvf/hvf.c | 68 +++++++++++++++++++++++++++++++++++++++
3 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c
index 8b794c2d41..64d7afc3bf 100644
--- a/accel/hvf/hvf-accel-ops.c
+++ b/accel/hvf/hvf-accel-ops.c
@@ -152,8 +152,8 @@ static int hvf_init_vcpu(CPUState *cpu)
sigdelset(&cpu->accel->unblock_ipi_mask, SIG_IPI);
#ifdef __aarch64__
- r = hv_vcpu_create(&cpu->accel->fd,
- (hv_vcpu_exit_t **)&cpu->accel->exit, NULL);
+ r = hvf_vcpu_create(&cpu->accel->fd,
+ (hv_vcpu_exit_t **)&cpu->accel->exit);
#else
r = hv_vcpu_create(&cpu->accel->fd, HV_VCPU_DEFAULT);
#endif
diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h
index a3b06a3e75..baee11d478 100644
--- a/include/system/hvf_int.h
+++ b/include/system/hvf_int.h
@@ -111,4 +111,8 @@ void hvf_arch_update_guest_debug(CPUState *cpu);
*/
bool hvf_arch_supports_guest_debug(void);
+#ifdef __aarch64__
+hv_return_t hvf_vcpu_create(hv_vcpu_t* vcpu_ptr, hv_vcpu_exit_t ** exit);
+#endif
+
#endif
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index 0658a99a2d..f503e1d1b8 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -22,6 +22,7 @@
#include "cpu-sysregs.h"
#include <mach/mach_time.h>
+#include <sys/sysctl.h>
#include "system/address-spaces.h"
#include "system/memory.h"
@@ -292,6 +293,8 @@ void hvf_arm_init_debug(void)
#define SYSREG_DBGWVR15_EL1 SYSREG(2, 0, 0, 15, 6)
#define SYSREG_DBGWCR15_EL1 SYSREG(2, 0, 0, 15, 7)
+#define SYSREG_SCTLR_EL1 SYSREG(3, 0, 1, 0, 0)
+
#define WFX_IS_WFE (1 << 0)
#define TMR_CTL_ENABLE (1 << 0)
@@ -1320,6 +1323,9 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val)
case SYSREG_DBGWCR15_EL1:
*val = env->cp15.dbgwcr[SYSREG_CRM(reg)];
return 0;
+ case SYSREG_SCTLR_EL1:
+ *val = env->cp15.sctlr_el[1] | SCTLR_DSSBS_64;
+ return 0;
default:
if (is_id_sysreg(reg)) {
/* ID system registers read as RES0 */
@@ -1643,6 +1649,10 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val)
case SYSREG_DBGWCR15_EL1:
env->cp15.dbgwcr[SYSREG_CRM(reg)] = val;
return 0;
+ case SYSREG_SCTLR_EL1:
+ env->cp15.sctlr_el[1] = val;
+ assert_hvf_ok(hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_SCTLR_EL1, val & ~SCTLR_DSSBS_64));
+ return 0;
}
cpu_synchronize_state(cpu);
@@ -2244,3 +2254,61 @@ bool hvf_arch_supports_guest_debug(void)
{
return true;
}
+
+/*
+ * Apple M4 removes FEAT_SSBS. However, older macOS releases
+ * do misbehave in such a configuration and do not boot.
+ *
+ * Using private API to trap SCTLR_EL1 accesses through FGT.
+ */
+
+void _hv_vcpu_config_set_fgt_enabled(hv_vcpu_config_t cfg, bool enabled);
+#define HV_CONTROL_FIELD_HFGRTR 0xb
+#define HV_CONTROL_FIELD_HFGWTR 0xc
+hv_return_t _hv_vcpu_get_control_field(hv_vcpu_t vcpu, int field, uint64_t* value);
+hv_return_t _hv_vcpu_set_control_field(hv_vcpu_t vcpu, int field, uint64_t value);
+
+static bool hvf_is_ssbs_implemented(void) {
+ int has_ssbs = -1;
+ size_t has_ssbs_sz = sizeof(has_ssbs);
+ if (sysctlbyname("hw.optional.arm.FEAT_SSBS", &has_ssbs, &has_ssbs_sz, NULL, 0) == -1) {
+ has_ssbs = 0;
+ }
+ return has_ssbs;
+}
+
+hv_return_t hvf_vcpu_create(hv_vcpu_t* vcpu_ptr, hv_vcpu_exit_t ** exit)
+{
+ hv_return_t r;
+ hv_vcpu_t vcpu;
+ uint64_t hfgwtr_el1, hfgrtr_el1;
+
+ hv_vcpu_config_t config;
+ config = hv_vcpu_config_create();
+ if (!hvf_is_ssbs_implemented()) {
+ _hv_vcpu_config_set_fgt_enabled(config, true);
+ }
+
+ r = hv_vcpu_create(&vcpu, (hv_vcpu_exit_t **)exit, config);
+
+ if (hvf_is_ssbs_implemented()) {
+ return r;
+ }
+
+ assert_hvf_ok(_hv_vcpu_get_control_field(vcpu, HV_CONTROL_FIELD_HFGWTR,&hfgwtr_el1));
+ assert_hvf_ok(_hv_vcpu_set_control_field(vcpu, HV_CONTROL_FIELD_HFGWTR,
+ hfgwtr_el1 | R_HFGWTR_EL2_SCTLR_EL1_MASK));
+ assert_hvf_ok(_hv_vcpu_get_control_field(vcpu, HV_CONTROL_FIELD_HFGWTR,
+ &hfgwtr_el1));
+
+ assert_hvf_ok(_hv_vcpu_get_control_field(vcpu, HV_CONTROL_FIELD_HFGRTR,
+ &hfgrtr_el1));
+ assert_hvf_ok(_hv_vcpu_set_control_field(vcpu, HV_CONTROL_FIELD_HFGRTR,
+ hfgrtr_el1 | R_HFGRTR_EL2_SCTLR_EL1_MASK));
+ assert_hvf_ok(_hv_vcpu_get_control_field(vcpu, HV_CONTROL_FIELD_HFGRTR,
+ &hfgrtr_el1));
+
+ *vcpu_ptr = vcpu;
+
+ return r;
+}
--
2.50.1 (Apple Git-155)
On 07.10.25 22:31, Mohamed Mediouni wrote: > Apple M4 removes FEAT_SSBS. However, older macOS releases > do misbehave in such a configuration and do not boot. > > Use private API to trap SCTLR_EL1 accesses through FGT. > > Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> These old macOS versions did not include M4 support, correct? So wouldn't it make more sense to make more recent macOS work instead? Alex
> On 7. Oct 2025, at 23:12, Alexander Graf <agraf@csgraf.de> wrote: > > > On 07.10.25 22:31, Mohamed Mediouni wrote: >> Apple M4 removes FEAT_SSBS. However, older macOS releases >> do misbehave in such a configuration and do not boot. >> >> Use private API to trap SCTLR_EL1 accesses through FGT. >> >> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> > > > These old macOS versions did not include M4 support, correct? So wouldn't it make more sense to make more recent macOS work instead? > > Alex > Apple ships such a workaround in Virtualization.framework to allow older macOS VMs to work
> Am 08.10.2025 um 13:36 schrieb Mohamed Mediouni <mohamed@unpredictable.fr>: > > > >> On 7. Oct 2025, at 23:12, Alexander Graf <agraf@csgraf.de> wrote: >> >> >>> On 07.10.25 22:31, Mohamed Mediouni wrote: >>> Apple M4 removes FEAT_SSBS. However, older macOS releases >>> do misbehave in such a configuration and do not boot. >>> >>> Use private API to trap SCTLR_EL1 accesses through FGT. >>> >>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> >> >> >> These old macOS versions did not include M4 support, correct? So wouldn't it make more sense to make more recent macOS work instead? >> >> Alex >> > Apple ships such a workaround in Virtualization.framework to allow older macOS VMs to work Fair, but we don't have to implement the same hacks :). I think from a user's point of view, running macOS 26 is more important than 12 :) Alex
> On 8. Oct 2025, at 22:00, Alexander Graf <agraf@csgraf.de> wrote: > > > >> Am 08.10.2025 um 13:36 schrieb Mohamed Mediouni <mohamed@unpredictable.fr>: >> >> >> >>> On 7. Oct 2025, at 23:12, Alexander Graf <agraf@csgraf.de> wrote: >>> >>> >>>> On 07.10.25 22:31, Mohamed Mediouni wrote: >>>> Apple M4 removes FEAT_SSBS. However, older macOS releases >>>> do misbehave in such a configuration and do not boot. >>>> >>>> Use private API to trap SCTLR_EL1 accesses through FGT. >>>> >>>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> >>> >>> >>> These old macOS versions did not include M4 support, correct? So wouldn't it make more sense to make more recent macOS work instead? >>> >>> Alex >>> >> Apple ships such a workaround in Virtualization.framework to allow older macOS VMs to work > > Fair, but we don't have to implement the same hacks :). I think from a user's point of view, running macOS 26 is more important than 12 :) > > Alex There are at least two mean things that newer macOS releases do on vmapples, but I’m pretty sure I’m missing some more: - an HVC #0 with x0 set to 0xc1000000 - this is not trapped all the way to the VMM but handled by the virtualisation stack. The value returned is zero if the VM is running with a private ISA level. The guest then checks it straight away and goes in an infinite loop if the value isn’t 0. Fine, let’s run with private ISA and see what happens, well... - An access to the GIC memory ranges that doesn’t meet ISV requirements - using pre-indexing with SP as the base register. That said I got pretty far with handling those two but I’m not sure that any of this is upstreamable really… -Mohamed
© 2016 - 2025 Red Hat, Inc.