arm64: Add RSI detection for CCA

Detect the presence of the Realm Services Interface (RSI). This detection is
performed early in bootup; PSCI initialisation has been moved to initarm() to
faciliate this.

Reviewed by:	andrew
Sponsored by:	Arm Ltd
Differential Revision:	https://reviews.freebsd.org/D56598
This commit is contained in:
Sarah Walker
2026-05-12 12:27:13 +01:00
committed by Andrew Turner
parent f9ba99eaa2
commit 76a2904c35
8 changed files with 338 additions and 1 deletions
+5
View File
@@ -86,6 +86,7 @@
#include <machine/metadata.h> #include <machine/metadata.h>
#include <machine/md_var.h> #include <machine/md_var.h>
#include <machine/pcb.h> #include <machine/pcb.h>
#include <machine/rsi.h>
#include <machine/undefined.h> #include <machine/undefined.h>
#include <machine/vmparam.h> #include <machine/vmparam.h>
@@ -103,6 +104,7 @@
#include <dev/ofw/openfirm.h> #include <dev/ofw/openfirm.h>
#endif #endif
#include <dev/psci/psci.h>
#include <dev/smbios/smbios.h> #include <dev/smbios/smbios.h>
_Static_assert(sizeof(struct pcb) == 1248, "struct pcb is incorrect size"); _Static_assert(sizeof(struct pcb) == 1248, "struct pcb is incorrect size");
@@ -889,6 +891,9 @@ initarm(struct arm64_bootparams *abp)
valid = bus_probe(); valid = bus_probe();
psci_init(NULL);
arm64_rsi_setup_memory();
cninit(); cninit();
set_ttbr0(abp->kern_ttbr0); set_ttbr0(abp->kern_ttbr0);
pmap_s1_invalidate_all_kernel(); pmap_s1_invalidate_all_kernel();
+2
View File
@@ -205,6 +205,8 @@ __exclusive_cache_line static struct pmap_large_md_page pv_dummy_large;
#define pv_dummy pv_dummy_large.pv_page #define pv_dummy pv_dummy_large.pv_page
__read_mostly static struct pmap_large_md_page *pv_table; __read_mostly static struct pmap_large_md_page *pv_table;
__read_mostly uint64_t prot_ns_shared_pa;
static struct pmap_large_md_page * static struct pmap_large_md_page *
_pa_to_pmdp(vm_paddr_t pa) _pa_to_pmdp(vm_paddr_t pa)
{ {
+185
View File
@@ -0,0 +1,185 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Arm Ltd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/physmem.h>
#include <sys/rwlock.h>
#include <vm/vm_page.h>
#include <machine/rsi.h>
#include <machine/vmparam.h>
#include <dev/psci/psci.h>
#include <dev/psci/smccc.h>
static struct realm_config config;
static bool rsi_present = false;
#define PHYSMAP_SIZE (2 * (VM_PHYSSEG_MAX - 1))
static vm_paddr_t physmap[PHYSMAP_SIZE];
static unsigned long
rsi_request_version(unsigned long req, unsigned long *out_lower,
unsigned long *out_higher)
{
struct arm_smccc_res res;
arm_smccc_invoke_smc(SMC_RSI_ABI_VERSION, req, &res);
if (out_lower != NULL)
*out_lower = res.a1;
if (out_higher != NULL)
*out_higher = res.a2;
return (res.a0);
}
static inline unsigned long
rsi_get_realm_config(struct realm_config *cfg)
{
struct arm_smccc_res res;
arm_smccc_invoke_smc(SMC_RSI_REALM_CONFIG, vtophys(cfg), &res);
return (res.a0);
}
static bool
rsi_version_matches(void)
{
unsigned long ver_lower, ver_higher;
unsigned long ret;
ret = rsi_request_version(RSI_ABI_VERSION, &ver_lower, &ver_higher);
if (ret == SMCCC_RET_NOT_SUPPORTED)
return (false);
if (ret != RSI_SUCCESS) {
printf("RME: RMM doesn't support RSI version %lu.%lu. Supported range: %lu.%lu-%lu.%lu\n",
RSI_ABI_VERSION_MAJOR, RSI_ABI_VERSION_MINOR,
RSI_ABI_VERSION_GET_MAJOR(ver_lower),
RSI_ABI_VERSION_GET_MINOR(ver_lower),
RSI_ABI_VERSION_GET_MAJOR(ver_higher),
RSI_ABI_VERSION_GET_MINOR(ver_higher));
return (false);
}
printf("RME: Using RSI version %lu.%lu\n",
RSI_ABI_VERSION_GET_MAJOR(ver_lower),
RSI_ABI_VERSION_GET_MINOR(ver_lower));
return (true);
}
unsigned long
rsi_set_addr_range_state(vm_paddr_t start, vm_paddr_t end, enum ripas state,
unsigned long flags, vm_paddr_t *top)
{
struct arm_smccc_res res;
arm_smccc_smc(SMC_RSI_IPA_STATE_SET, start, end, state, flags, 0, 0, 0,
&res);
if (top)
*top = res.a1;
return (res.a0);
}
static int
rsi_set_memory_range(vm_paddr_t start, vm_paddr_t end, enum ripas state,
unsigned long flags)
{
unsigned long ret;
vm_paddr_t top;
while (start != end) {
ret = rsi_set_addr_range_state(start, end, state, flags, &top);
if (ret || top < start || top > end)
return (-EINVAL);
start = top;
}
return (0);
}
/*
* Convert the specified range to RAM. Do not convert any pages that may have
* been DESTROYED, without our permission.
*/
static int
rsi_set_memory_range_protected_safe(vm_paddr_t start, vm_paddr_t end)
{
return (rsi_set_memory_range(start, end, RSI_RIPAS_RAM,
RSI_NO_CHANGE_DESTROYED));
}
void
arm64_rsi_setup_memory(void)
{
int physmap_idx;
int i;
if (!psci_conduit_is_smc())
return;
if (!rsi_version_matches())
return;
if (rsi_get_realm_config(&config))
return;
prot_ns_shared_pa = 1ul << (config.ipa_bits - 1);
if (bootverbose)
printf("arm64_rsi_setup_memory: rsi_present, ipa_bits=%lu prot_ns_shared_pa=%lx\n",
config.ipa_bits, prot_ns_shared_pa);
rsi_present = true;
physmap_idx = physmem_all(physmap, nitems(physmap));
if (bootverbose)
printf("physmap:\n");
for (i = 0; i < physmap_idx; i += 2) {
if (bootverbose)
printf(" %lx %lx\n", physmap[i], physmap[i + 1]);
if (rsi_set_memory_range_protected_safe(physmap[i],
physmap[i + 1]))
panic("rsi_set_memory_range_protected_safe failed");
}
}
bool
in_realm(void)
{
return (rsi_present);
}
+3
View File
@@ -43,6 +43,7 @@
#ifndef LOCORE #ifndef LOCORE
#include <sys/queue.h> #include <sys/queue.h>
#include <sys/systm.h>
#include <sys/_lock.h> #include <sys/_lock.h>
#include <sys/_mutex.h> #include <sys/_mutex.h>
#include <sys/_pv_entry.h> #include <sys/_pv_entry.h>
@@ -132,6 +133,8 @@ extern vm_offset_t virtual_end;
extern pt_entry_t pmap_sh_attr; extern pt_entry_t pmap_sh_attr;
extern __read_mostly uint64_t prot_ns_shared_pa;
/* /*
* Macros to test if a mapping is mappable with an L1 Section mapping * Macros to test if a mapping is mappable with an L1 Section mapping
* or an L2 Large Page mapping. * or an L2 Large Page mapping.
+129
View File
@@ -0,0 +1,129 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Arm Ltd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _MACHINE_RSI_H_
#define _MACHINE_RSI_H_
extern uint64_t prot_ns_shared;
bool in_realm(void);
void arm64_rsi_setup_memory(void);
/*
* The major version number of the RSI implementation. This is increased when
* the binary format or semantics of the SMC calls change.
*/
#define RSI_ABI_VERSION_MAJOR UL(1)
/*
* The minor version number of the RSI implementation. This is increased when
* a bug is fixed, or a feature is added without breaking binary compatibility.
*/
#define RSI_ABI_VERSION_MINOR UL(0)
#define RSI_ABI_VERSION ((RSI_ABI_VERSION_MAJOR << 16) | \
RSI_ABI_VERSION_MINOR)
#define RSI_ABI_VERSION_GET_MAJOR(_version) ((_version) >> 16)
#define RSI_ABI_VERSION_GET_MINOR(_version) ((_version) & 0xFFFF)
#define RSI_SUCCESS UL(0)
#define RSI_ERROR_INPUT UL(1)
#define RSI_ERROR_STATE UL(2)
#define RSI_INCOMPLETE UL(3)
#define RSI_ERROR_UNKNOWN UL(4)
#define SMC_RSI_FID(n) SMCCC_FUNC_ID(SMCCC_FAST_CALL, \
SMCCC_64BIT_CALL, \
SMCCC_STD_SECURE_SERVICE_CALLS, \
n)
/*
* Returns RSI version.
*
* arg1 == Requested interface revision
* ret0 == Status / error
* ret1 == Lower implemented interface revision
* ret2 == Higher implemented interface revision
*/
#define SMC_RSI_ABI_VERSION SMC_RSI_FID(0x190)
/*
* Read configuration for the current Realm.
*
* arg1 == struct realm_config addr
* ret0 == Status / error
*/
#define SMC_RSI_REALM_CONFIG SMC_RSI_FID(0x196)
struct realm_config {
union {
struct {
unsigned long ipa_bits; /* Width of IPA in bits */
unsigned long hash_algo; /* Hash algorithm */
};
uint8_t pad[0x200];
};
union {
uint8_t rpv[64]; /* Realm Personalization Value */
uint8_t pad2[0xe00];
};
/*
* The RMM requires the configuration structure to be aligned to a 4k
* boundary, ensure this happens by aligning this structure.
*/
} __aligned(0x1000);
/*
* Request RIPAS of a target IPA range to be changed to a specified value.
*
* arg1 == Base IPA address of target region
* arg2 == Top of the region
* arg3 == RIPAS value
* arg4 == flags
* ret0 == Status / error
* ret1 == Top of modified IPA range
* ret2 == Whether the Host accepted or rejected the request
*/
#define SMC_RSI_IPA_STATE_SET SMC_RSI_FID(0x197)
#define RSI_NO_CHANGE_DESTROYED UL(0)
#define RSI_CHANGE_DESTROYED UL(1)
#define RSI_ACCEPT UL(0)
#define RSI_REJECT UL(1)
enum ripas {
RSI_RIPAS_EMPTY = 0,
RSI_RIPAS_RAM
};
unsigned long rsi_set_addr_range_state(vm_paddr_t start, vm_paddr_t end,
enum ripas state, unsigned long flags, vm_paddr_t *top);
#endif /* !_MACHINE_RSI_H_ */
+1
View File
@@ -77,6 +77,7 @@ arm64/arm64/ptrauth.c standard \
compile-with "${NORMAL_C:N-mbranch-protection*} -mbranch-protection=bti" compile-with "${NORMAL_C:N-mbranch-protection*} -mbranch-protection=bti"
arm64/arm64/pmap.c standard arm64/arm64/pmap.c standard
arm64/arm64/ptrace_machdep.c standard arm64/arm64/ptrace_machdep.c standard
arm64/arm64/rsi.c standard
arm64/arm64/sdt_machdep.c optional kdtrace_hooks arm64/arm64/sdt_machdep.c optional kdtrace_hooks
arm64/arm64/sigtramp.S standard arm64/arm64/sigtramp.S standard
arm64/arm64/spec_workaround.c standard arm64/arm64/spec_workaround.c standard
+10 -1
View File
@@ -133,7 +133,7 @@ static int psci_def_callfn(register_t, register_t, register_t, register_t,
psci_callfn_t psci_callfn = psci_def_callfn; psci_callfn_t psci_callfn = psci_def_callfn;
static void void
psci_init(void *dummy) psci_init(void *dummy)
{ {
psci_callfn_t new_callfn; psci_callfn_t new_callfn;
@@ -146,8 +146,11 @@ psci_init(void *dummy)
psci_callfn = new_callfn; psci_callfn = new_callfn;
psci_present = true; psci_present = true;
} }
#ifdef __arm__
/* This needs to be before cpu_mp at SI_SUB_CPU, SI_ORDER_THIRD */ /* This needs to be before cpu_mp at SI_SUB_CPU, SI_ORDER_THIRD */
SYSINIT(psci_start, SI_SUB_CPU, SI_ORDER_FIRST, psci_init, NULL); SYSINIT(psci_start, SI_SUB_CPU, SI_ORDER_FIRST, psci_init, NULL);
#endif
static int static int
psci_def_callfn(register_t a __unused, register_t b __unused, psci_def_callfn(register_t a __unused, register_t b __unused,
@@ -631,3 +634,9 @@ psci_v0_2_init(device_t dev, int default_version)
device_printf(dev, "PSCI version number mismatched with DT\n"); device_printf(dev, "PSCI version number mismatched with DT\n");
return (1); return (1);
} }
bool
psci_conduit_is_smc(void)
{
return (psci_callfn == arm_smccc_smc);
}
+3
View File
@@ -44,6 +44,9 @@ void psci_reset(void);
int32_t psci_features(uint32_t); int32_t psci_features(uint32_t);
int psci_get_version(void); int psci_get_version(void);
void psci_init(void *dummy);
bool psci_conduit_is_smc(void);
/* Handler to let us call into the PSCI/SMCCC firmware */ /* Handler to let us call into the PSCI/SMCCC firmware */
extern psci_callfn_t psci_callfn; extern psci_callfn_t psci_callfn;
static inline int static inline int