Files
acrn-hypervisor/hypervisor/arch/riscv/sbi.c
Haoyu Tang 3b5847a67b hv: riscv add SBI interface to query machine IDs
Add wrapper functions to query machine vendor ID (mvendorid),
architecture ID (marchid), and implementation ID (mimpid) through
the SBI base extension.

These IDs identify the RISC-V processor vendor, microarchitecture,
and specific implementation respectively. The hypervisor needs to
retrieve these values from the underlying SBI firmware to forward
them to the Service VM via vSBI, allowing the Service VM to see
the actual hardware characteristics.

Tracked-On: #8851
Signed-off-by: Haoyu Tang <haoyu.tang@intel.com>
Acked-by: Wang Yu1 <yu1.wang@intel.com>
2025-12-26 07:57:51 +08:00

260 lines
7.1 KiB
C

/*
* Copyright (C) 2025 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Authors:
* Haicheng Li <haicheng.li@intel.com>
*/
#include <types.h>
#include <asm/sbi.h>
#include <logmsg.h>
#include <per_cpu.h>
#include <bits.h>
/**
* An ECALL is used as the control transfer instruction between the
* supervisor and the SEE.
*
* a7 encodes the SBI extension ID (EID).
*
* a6 encodes the SBI function ID (FID) for a given extension ID
* encoded in a7 for any SBI extension defined in or after SBI v0.2.
*
* a0 through a5 contain the arguments for the SBI function call.
* Registers that are not defined in the SBI function call are not
* reserved.
*
* All registers except a0 & a1 must be preserved across an SBI call
* by the callee.
*
* SBI functions must return a pair of values in a0 and a1, with a0
* returning an error code. This is analogous to returning the C
* structure.
*/
sbiret sbi_ecall(uint64_t arg0, uint64_t arg1, uint64_t arg2,
uint64_t arg3, uint64_t arg4, uint64_t arg5,
uint64_t func, uint64_t ext)
{
sbiret ret;
register uint64_t a0 asm ("a0") = arg0;
register uint64_t a1 asm ("a1") = arg1;
register uint64_t a2 asm ("a2") = arg2;
register uint64_t a3 asm ("a3") = arg3;
register uint64_t a4 asm ("a4") = arg4;
register uint64_t a5 asm ("a5") = arg5;
register uint64_t a6 asm ("a6") = func;
register uint64_t a7 asm ("a7") = ext;
asm volatile (
"ecall \n\t"
:"+r" (a0), "+r" (a1)
:"r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
: "memory"
);
ret.error = a0;
ret.value = a1;
return ret;
}
/**
* Implemented IPI functionality using the SBI IPI Extension (EID #0x735049).
* Legacy SBI extensions are not supported in ACRN.
*/
static int64_t sbi_send_ipi(uint64_t mask, uint64_t mask_base)
{
sbiret ret = sbi_ecall(mask, mask_base, 0UL, 0UL, 0UL, 0UL, SBI_IPI_FID_SEND_IPI, SBI_EID_IPI);
if (ret.error != SBI_SUCCESS) {
pr_err("%s: Failed to send IPI by SBI, error code: %lx", __func__, ret.error);
}
return ret.error;
}
/**
* sbi_hsm_start_hart - Start a RISC-V hart using SBI HSM extension
* @hartid: The hardware thread (hart) ID to start
* @addr: The start address for the hart
* @arg: Argument to pass to the hart at startup
*
* This function invokes the SBI HSM extension to start a specified hart
* at the given address with the provided argument.
*
* Returns the SBI error code from the operation.
*/
int64_t sbi_hsm_start_hart(uint64_t hartid, uint64_t addr, uint64_t arg)
{
sbiret ret;
ret = sbi_ecall(hartid, addr, arg, 0, 0, 0, SBI_HSM_FID_HART_START,
SBI_EID_HSM);
if (ret.error != SBI_SUCCESS) {
pr_err("%s: Failed to start hart (%x) by SBI, error code: %lx",
__func__, hartid, ret.error);
}
return ret.error;
}
/**
* msg_type is currently unused.
*
* At present, only IPI_NOTIFY_CPU is supported, covering two use cases:
* - SMP call
* - Kick pCPU out of non-root mode
*
* Callers should invoke this function with:
* send_single_ipi(pcpu_id, IPI_NOTIFY_CPU);
*
* msg_type is retained for future extensions and to stay aligned with
* the function prototype used on other architectures (e.g. x86).
*/
void send_single_ipi(uint16_t pcpu_id, __unused uint32_t msg_type)
{
uint64_t hmask = 1UL;
uint64_t hbase = per_cpu(arch.hart_id, pcpu_id);
sbi_send_ipi(hmask, hbase);
}
/**
* Similar to send_single_ipi() regards to msg_type.
*
* Callers should invoke this function with:
* send_dest_ipi_mask(dest_mask, IPI_NOTIFY_CPU);
*/
void send_dest_ipi_mask(uint64_t dest_mask, __unused uint32_t msg_type)
{
uint16_t i;
uint32_t hart_id, hart_id_max = 0U;
uint64_t hmask = 0UL, hbase = 0UL;
int64_t ret;
i = ffs64(dest_mask);
while (i != INVALID_BIT_INDEX) {
bitmap_clear_non_atomic(i, &dest_mask);
hart_id = per_cpu(arch.hart_id, i);
if (hmask) {
if ((hbase + BITS_PER_LONG <= hart_id) ||
(hart_id + BITS_PER_LONG <= hart_id_max)) {
/*
* Issue the SBI IPI when
* 1) The next hart_id is too large
* 2) The next hart_id is too small
*/
ret = sbi_send_ipi(hmask, hbase);
if (ret != SBI_SUCCESS) {
pr_err("Failed to send ipi to cpus[0x%lx, 0x%lx] by SBI",
hbase, hmask);
}
hmask = 0UL;
} else if (hart_id < hbase) {
/*
* Hart IDs corresponding to logical pCPU IDs
* are not sorted in ascending order, need to
* adjust the hbase and hmask
*/
hmask <<= (hbase - hart_id);
hbase = hart_id;
} else if (hart_id > hart_id_max) {
hart_id_max = hart_id;
}
}
if (!hmask) {
hbase = hart_id;
hart_id_max = hart_id;
}
bitmap_set_non_atomic(hart_id - hbase, &hmask);
i = ffs64(dest_mask);
}
if (hmask) {
ret = sbi_send_ipi(hmask, hbase);
if (ret != SBI_SUCCESS) {
pr_err("Failed to send ipi to cpus[0x%lx, 0x%lx] by SBI",
hbase, hmask);
}
}
}
/**
* Implemented Timer functionality using the SBI Timer Extension (EID #0x54494D45).
*/
int sbi_set_timer(uint64_t stime_value)
{
sbiret ret = sbi_ecall(stime_value, 0, 0, 0, 0, 0, SBI_TIMER_FID_SET_TIMER, SBI_EID_TIMER);
if (ret.error != SBI_SUCCESS) {
pr_err("%s: Failed to set Timer by SBI, error code: %lx", __func__, ret.error);
}
return ret.error;
}
/**
* @brief Get the machine vendor ID via SBI call.
*
* This function retrieves the machine vendor ID (mvendorid) by making an SBI
* (Supervisor Binary Interface) ecall to the SBI base extension. The mvendorid
* CSR identifies the vendor of the RISC-V implementation.
*
* @return The machine vendor ID value on success, or 0 on failure.
* On error, an error message is logged with the error code.
*/
uint64_t sbi_get_mvendorid(void)
{
sbiret ret;
ret.value = 0;
ret = sbi_ecall(0, 0, 0, 0, 0, 0, SBI_BASE_FID_GET_MVENDORID, SBI_EID_BASE);
if (ret.error != SBI_SUCCESS)
pr_err("%s: %lx", __func__, ret.error);
return ret.value;
}
/**
* @brief Retrieve the machine architecture ID via SBI call.
*
* This function invokes the SBI (Supervisor Binary Interface) base extension
* to query the machine architecture ID (MARCHID). The MARCHID identifies the
* microarchitecture of the hart.
*
* @return The machine architecture ID value on success, or 0 if the SBI call fails.
* In case of failure, an error message is logged via pr_err().
*/
uint64_t sbi_get_marchid(void)
{
sbiret ret;
ret.value = 0;
ret = sbi_ecall(0, 0, 0, 0, 0, 0, SBI_BASE_FID_GET_MARCHID, SBI_EID_BASE);
if (ret.error != SBI_SUCCESS)
pr_err("%s: %lx", __func__, ret.error);
return ret.value;
}
/**
* @brief Get the machine implementation ID (mimpid) through SBI call.
*
* This function retrieves the machine implementation ID by invoking the SBI
* (Supervisor Binary Interface) ecall with the GET_MIMPID function ID.
* The mimpid provides information about the specific implementation of the
* RISC-V processor.
*
* @return The machine implementation ID value on success, or 0 if the SBI
* call fails. If an error occurs, an error message is logged with
* the error code.
*/
uint64_t sbi_get_mimpid(void)
{
sbiret ret;
ret.value = 0;
ret = sbi_ecall(0, 0, 0, 0, 0, 0, SBI_BASE_FID_GET_MIMPID, SBI_EID_BASE);
if (ret.error != SBI_SUCCESS)
pr_err("%s: %lx", __func__, ret.error);
return ret.value;
}