#include <sys/param.h>
#include <sys/systm.h>
#include <sys/physmem.h>
#include <sys/rwlock.h>
#include <vm/vm.h>
#include <vm/pmap.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);
}
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);
}