Skip to content

Commit

Permalink
smc_cap: Allow a custom smc handler
Browse files Browse the repository at this point in the history
This required a new callback to be added to the vm_arch struct for
ARM. The old handle_smc function gets registered as the smc handler by
default. The callback allows a user to register a custom smc handler
instead.

This is especially useful to allow SMC forwarding in the user's custom
handler.

Signed-off-by: Robbie VanVossen <[email protected]>
  • Loading branch information
Alex Pavey authored and Furao committed Aug 31, 2023
1 parent 82e0e3f commit 92c64f0
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 37 deletions.
1 change: 1 addition & 0 deletions libsel4vm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ target_include_directories(
)
target_link_libraries(
sel4vm
sel4vmmplatsupport
muslc
sel4
sel4simple
Expand Down
31 changes: 30 additions & 1 deletion libsel4vm/arch_include/arm/sel4vm/arch/guest_vm_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,24 @@
#include <sel4vm/guest_vm.h>

typedef struct fault fault_t;
typedef struct vm vm_t;
typedef struct vm_vcpu vm_vcpu_t;

typedef int (*smc_handler_callback_fn)(vm_vcpu_t *vcpu, seL4_UserContext *regs);
typedef int (*unhandled_vcpu_fault_callback_fn)(vm_vcpu_t *vcpu, uint32_t hsr, void *cookie);

#define VM_CSPACE_SIZE_BITS 4
#define VM_FAULT_EP_SLOT 1
#define VM_CSPACE_SLOT VM_FAULT_EP_SLOT + CONFIG_MAX_NUM_NODES

struct vm_arch {};
/***
* @struct vm_arch
* Structure representing ARM specific vm properties
* @param {smc_handler_callback_fn} vm_smc_handler A callback for a custom SMC call handler
*/
struct vm_arch {
smc_handler_callback_fn vm_smc_handler;
};

/***
* @struct vm_vcpu_arch
Expand All @@ -48,3 +57,23 @@ struct vm_vcpu_arch {
*/
int vm_register_unhandled_vcpu_fault_callback(vm_vcpu_t *vcpu, unhandled_vcpu_fault_callback_fn vcpu_fault_callback,
void *cookie);

/***
* @function vm_register_smc_handler_callback(vm, vm_smc_handler)
* Register a callback for a user level SMC handler to replace vm_smc_handle_default.
* This is useful when custom behavior, such as SMC emulation or forwarding device
* specific SMC calls to the Secure Monitor is needed.
*
* If default behavior is still required after custom processing the default handler can
* still be called from the callback.
*
* To forward an SMC call to the Secure Monitor, use smc_forward.
*
* On error return the guest will fault. Return 0 and use smc_set_return_value to
* return -1 to the VM without stopping it.
*
* @param {vm_t *} vm A handle to the VM
* @param {smc_handler_callback_fn} A user supplied callback to handle VM SMC calls
* @return 0 on success, -1 on error
*/
int vm_register_smc_handler_callback(vm_t *vm, smc_handler_callback_fn vm_smc_handler);
5 changes: 5 additions & 0 deletions libsel4vm/src/arch/arm/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <sel4vm/guest_vm.h>
#include <sel4vm/guest_vm_util.h>
#include <sel4vm/arch/guest_arm_context.h>
#include <sel4vmmplatsupport/arch/smc.h>

#include "arm_vm.h"
#include "vm_boot.h"
Expand All @@ -41,6 +42,10 @@ int vm_init_arch(vm_t *vm)
return -1;
}

/* Set arch default values */
err = vm_register_smc_handler_callback(vm, vm_smc_handle_default);
assert(!err);

/* Create a cspace */
vka = vm->vka;
err = vka_alloc_cnode_object(vka, VM_CSPACE_SIZE_BITS, &vm->cspace.cspace_obj);
Expand Down
16 changes: 16 additions & 0 deletions libsel4vm/src/arch/arm/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,22 @@ int vm_register_unhandled_vcpu_fault_callback(vm_vcpu_t *vcpu, unhandled_vcpu_fa

}

int vm_register_smc_handler_callback(vm_t *vm, smc_handler_callback_fn vm_smc_handler)
{
if (!vm) {
ZF_LOGE("Failed to register smc_handler callback: Invalid VCPU handle");
return -1;
}

if (!vm_smc_handler) {
ZF_LOGE("Failed to register smc_handler callback: Invalid callback");
return -1;
}
vm->arch.vm_smc_handler = vm_smc_handler;

return 0;
}

int vm_run_arch(vm_t *vm)
{
int err;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2023, DornerWorks
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <sel4vm/guest_vm.h>

/* SMC Helpers */
seL4_Word smc_get_function_id(seL4_UserContext *u);
seL4_Word smc_set_return_value(seL4_UserContext *u, seL4_Word val);
seL4_Word smc_get_arg(seL4_UserContext *u, seL4_Word arg);
void smc_set_arg(seL4_UserContext *u, seL4_Word arg, seL4_Word val);

/***
* @function smc_forward(vm_vcpu_t *vcpu, seL4_UserContext *regs, seL4_ARM_SMC smc_cap)
* Forward an SMC call using the appropriate capability
* @param {vm_vcpu_t *} vcpu A handle to the VCPU
* @param {seL4_UserContext *} regs A handle to the registers from the calling thread that want to make an SMC call
* @param {seL4_ARM_SMC} smc_cap The SMC capability for the requested call
* @return 0 on success, -1 on error
*/
int smc_forward(vm_vcpu_t *vcpu, seL4_UserContext *regs, seL4_ARM_SMC smc_cap);

/***
* @function vm_smc_handle_default(vm_vcpu_t *vcpu, seL4_UserContext *regs)
* The default handler SMC faults. Will be called if a custom handler is not set for any given VM.
* @param {vm_vcpu_t *} vcpu A handle to the VCPU
* @param {seL4_UserContext *} regs A handle to the registers from the calling thread that want to make an SMC call
* @return 0 on success, -1 on error
*/
int vm_smc_handle_default(vm_vcpu_t *vcpu, seL4_UserContext *regs);
36 changes: 12 additions & 24 deletions libsel4vmmplatsupport/src/arch/arm/psci.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,61 +36,49 @@ static int start_new_vcpu(vm_vcpu_t *vcpu, uintptr_t entry_address, uintptr_t c
return 0;
}

int handle_psci(vm_vcpu_t *vcpu, seL4_Word fn_number, bool convention)
int handle_psci(vm_vcpu_t *vcpu, seL4_UserContext *regs, seL4_Word fn_number, bool convention)
{
int err;
seL4_UserContext regs;
err = vm_get_thread_context(vcpu, &regs);
if (err) {
ZF_LOGE("Failed to get vcpu registers");
return -1;
}
switch (fn_number) {
case PSCI_VERSION:
smc_set_return_value(&regs, 0x00010000); /* version 1 */
smc_set_return_value(regs, 0x00010000); /* version 1 */
break;
case PSCI_CPU_ON: {
uintptr_t target_cpu = smc_get_arg(&regs, 1);
uintptr_t entry_point_address = smc_get_arg(&regs, 2);
uintptr_t context_id = smc_get_arg(&regs, 3);
uintptr_t target_cpu = smc_get_arg(regs, 1);
uintptr_t entry_point_address = smc_get_arg(regs, 2);
uintptr_t context_id = smc_get_arg(regs, 3);
vm_vcpu_t *target_vcpu = vm_vcpu_for_target_cpu(vcpu->vm, target_cpu);
if (target_vcpu == NULL) {
target_vcpu = vm_find_free_unassigned_vcpu(vcpu->vm);
if (target_vcpu && start_new_vcpu(target_vcpu, entry_point_address, context_id, target_cpu) == 0) {
smc_set_return_value(&regs, PSCI_SUCCESS);
smc_set_return_value(regs, PSCI_SUCCESS);
} else {
smc_set_return_value(&regs, PSCI_INTERNAL_FAILURE);
smc_set_return_value(regs, PSCI_INTERNAL_FAILURE);
}
} else {
if (is_vcpu_online(target_vcpu)) {
smc_set_return_value(&regs, PSCI_ALREADY_ON);
smc_set_return_value(regs, PSCI_ALREADY_ON);
} else {
smc_set_return_value(&regs, PSCI_INTERNAL_FAILURE);
smc_set_return_value(regs, PSCI_INTERNAL_FAILURE);
}
}

break;
}
case PSCI_MIGRATE_INFO_TYPE:
/* trusted OS does not require migration */
smc_set_return_value(&regs, 2);
smc_set_return_value(regs, 2);
break;
case PSCI_FEATURES:
/* TODO Not sure if required */
smc_set_return_value(&regs, PSCI_NOT_SUPPORTED);
smc_set_return_value(regs, PSCI_NOT_SUPPORTED);
break;
case PSCI_SYSTEM_RESET:
smc_set_return_value(&regs, PSCI_SUCCESS);
smc_set_return_value(regs, PSCI_SUCCESS);
break;
default:
ZF_LOGE("Unhandled PSCI function id %lu\n", fn_number);
return -1;
}
err = vm_set_thread_context(vcpu, regs);
if (err) {
ZF_LOGE("Failed to set vcpu context registers");
return -1;
}
advance_vcpu_fault(vcpu);
return 0;
}
2 changes: 1 addition & 1 deletion libsel4vmmplatsupport/src/arch/arm/psci.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ typedef enum psci {
PSCI_MAX = 0x1f
} psci_id_t;

int handle_psci(vm_vcpu_t *vcpu, seL4_Word fn_number, bool convention);
int handle_psci(vm_vcpu_t *vcpu, seL4_UserContext *regs, seL4_Word fn_number, bool convention);
29 changes: 26 additions & 3 deletions libsel4vmmplatsupport/src/arch/arm/smc.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <sel4vm/guest_vm.h>
#include <sel4vm/arch/guest_arm_context.h>
#include <sel4vm/guest_vcpu_fault.h>

#include "smc.h"
#include "psci.h"
Expand Down Expand Up @@ -48,12 +49,34 @@ int handle_smc(vm_vcpu_t *vcpu, uint32_t hsr)
{
int err;
seL4_UserContext regs;

err = vm_get_thread_context(vcpu, &regs);
if (err) {
ZF_LOGE("Failed to get vcpu registers to decode smc fault");
return -1;
}
seL4_Word id = smc_get_function_id(&regs);

err = vcpu->vm->arch.vm_smc_handler(vcpu, &regs);
if (err) {
ZF_LOGE("SMC Handler failed");
return -1;
}

err = vm_set_thread_context(vcpu, regs);
if (err) {
ZF_LOGE("Failed to set vcpu context registers");
return -1;
}
advance_vcpu_fault(vcpu);

return 0;
}

int vm_smc_handle_default(vm_vcpu_t *vcpu, seL4_UserContext *regs)
{
int err;

seL4_Word id = smc_get_function_id(regs);
seL4_Word fn_number = smc_get_function_number(id);
smc_call_id_t service = smc_get_call(id);

Expand All @@ -65,14 +88,14 @@ int handle_smc(vm_vcpu_t *vcpu, uint32_t hsr)
ZF_LOGE("Unhandled SMC: CPU service call %lu\n", fn_number);
break;
case SMC_CALL_SIP_SERVICE:
ZF_LOGE("Got SiP service call %lu\n", fn_number);
ZF_LOGE("Unhandled SMC: Got SiP service call %lu\n", fn_number);
break;
case SMC_CALL_OEM_SERVICE:
ZF_LOGE("Unhandled SMC: OEM service call %lu\n", fn_number);
break;
case SMC_CALL_STD_SERVICE:
if (fn_number < PSCI_MAX) {
return handle_psci(vcpu, fn_number, smc_call_is_32(id));
return handle_psci(vcpu, regs, fn_number, smc_call_is_32(id));
}
ZF_LOGE("Unhandled SMC: standard service call %lu\n", fn_number);
break;
Expand Down
7 changes: 1 addition & 6 deletions libsel4vmmplatsupport/src/arch/arm/smc.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma once

#include <sel4vm/guest_vm.h>
#include <sel4vmmplatsupport/arch/smc.h>

/* Values in this file are taken from:
* SMC CALLING CONVENTION
Expand Down Expand Up @@ -40,9 +41,3 @@ typedef enum {

/* SMC VCPU fault handler */
int handle_smc(vm_vcpu_t *vcpu, uint32_t hsr);

/* SMC Helpers */
seL4_Word smc_get_function_id(seL4_UserContext *u);
seL4_Word smc_set_return_value(seL4_UserContext *u, seL4_Word val);
seL4_Word smc_get_arg(seL4_UserContext *u, seL4_Word arg);
void smc_set_arg(seL4_UserContext *u, seL4_Word arg, seL4_Word val);
45 changes: 43 additions & 2 deletions libsel4vmmplatsupport/src/sel4_arch/aarch64/smc.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ seL4_Word smc_get_arg(seL4_UserContext *u, seL4_Word arg)
return u->x5;
case 6:
return u->x6;
case 7:
return u->x7;
default:
ZF_LOGF("SMC only has 6 argument registers");
ZF_LOGF("SMC only has 7 argument registers");
}
}

Expand All @@ -60,7 +62,46 @@ void smc_set_arg(seL4_UserContext *u, seL4_Word arg, seL4_Word val)
case 6:
u->x6 = val;
break;
case 7:
u->x7 = val;
break;
default:
ZF_LOGF("SMC only has 6 argument registers");
ZF_LOGF("SMC only has 7 argument registers");
}
}

int smc_forward(vm_vcpu_t *vcpu, seL4_UserContext *regs, seL4_ARM_SMC smc_cap)
{
int err = 0;
seL4_ARM_SMCContext smc_args;
seL4_ARM_SMCContext smc_results;

/* Get function and arguments from guest */
smc_args.x0 = regs->x0;
smc_args.x1 = regs->x1;
smc_args.x2 = regs->x2;
smc_args.x3 = regs->x3;
smc_args.x4 = regs->x4;
smc_args.x5 = regs->x5;
smc_args.x6 = regs->x6;
smc_args.x7 = regs->x7;

/* Make systemcall */
err = seL4_ARM_SMC_Call(smc_cap, &smc_args, &smc_results);
if (err) {
ZF_LOGE("Failure during seL4_ARM_SMC_Call function %lu\n", smc_args.x0);
return -1;
}

/* Send SMC results back to guest */
regs->x0 = smc_results.x0;
regs->x1 = smc_results.x1;
regs->x2 = smc_results.x2;
regs->x3 = smc_results.x3;
regs->x4 = smc_results.x4;
regs->x5 = smc_results.x5;
regs->x6 = smc_results.x6;
regs->x7 = smc_results.x7;

return 0;
}

0 comments on commit 92c64f0

Please sign in to comment.