diff --git a/exosphere/smc_api.c b/exosphere/smc_api.c index 1f3038d6e..db5f804cf 100644 --- a/exosphere/smc_api.c +++ b/exosphere/smc_api.c @@ -6,6 +6,7 @@ #include "smc_api.h" #include "smc_user.h" #include "se.h" +#include "userpage.h" #define SMC_USER_HANDLERS 0x13 #define SMC_PRIV_HANDLERS 0x9 @@ -225,6 +226,10 @@ uint32_t smc_check_status(smc_args_t *args) { uint32_t smc_get_result(smc_args_t *) { uint32_t status; unsigned char result_buf[0x400]; + upage_ref_t page_ref; + + void *user_address = (void *)args->X[2]; + if (g_smc_callback_key == 0) { return 4; } @@ -241,7 +246,16 @@ uint32_t smc_get_result(smc_args_t *) { args->X[1] = g_smc_callback(result_buf, args->X[3]); g_smc_callback_key = 0; - /* TODO: Copy result from result_buf into output in args->X[2] */ + /* Initialize page reference. */ + if (upage_init(&page_ref, user_address) == 0) { + return 2; + } + + /* Copy result output back to user. */ + if (secure_copy_to_user(&page_ref, user_address, result_buf, (size_t)args->X[3]) == 0) { + return 2; + } + return 0; } diff --git a/exosphere/userpage.c b/exosphere/userpage.c new file mode 100644 index 000000000..5099a8a0a --- /dev/null +++ b/exosphere/userpage.c @@ -0,0 +1,62 @@ +#include + +#include "utils.h" +#include "userpage.h" + +uint64_t g_secure_page_user_address = NULL; + +/* Create a user page reference for the desired address. */ +/* Returns 1 on success, 0 on failure. */ +int upage_init(upage_ref_t *upage, void *user_address) { + upage->user_page = get_page_for_address(user_address); + upage->secure_page = 0ULL; + + if (g_secure_page_user_address != NULL) { + /* Different ASLR'd address indicate SPL was rebooted. Panic. */ + if (g_secure_page_user_address != upage->user_page) { + panic(); + } + upage->secure_page = SECURE_USER_PAGE_ADDR; + } else { + /* Official (weak) validation for SPL's ASLR'd address. */ + if (upage->user_page >> 31) { + g_secure_page_user_address = upage->user_page; + /* TODO: Map this page into the MMU and invalidate the TLB. */ + upage->secure_page = SECURE_USER_PAGE_ADDR; + } + } + + return upage->secure_page != 0ULL; +} + +int user_copy_to_secure(upage_ref_t *upage, void *secure_dst, void *user_src, size_t size) { + /* Fail if the page doesn't match. */ + if (get_page_for_address(user_src) != upage->user_page) { + return 0; + } + + /* Fail if we go past the page boundary. */ + if (size != 0 && get_page_for_address(user_src + size - 1) != upage->user_page) { + return 0; + } + + void *secure_src = (void *)(upage->secure_page + ((uint64_t)user_src - upage->user_page)); + memcpy(secure_dst, secure_src, size); + return 1; +} + +int secure_copy_to_user(upage_ref_t *upage, void *user_dst, void *secure_src, size_t size) { + /* Fail if the page doesn't match. */ + if (get_page_for_address(user_dst) != upage->user_page) { + return 0; + } + + /* Fail if we go past the page boundary. */ + if (size != 0 && get_page_for_address(user_dst + size - 1) != upage->user_page) { + return 0; + } + + void *secure_dst = (void *)(upage->secure_page + ((uint64_t)user_dst - upage->user_page)); + memcpy(secure_dst, secure_src, size); + return 1; +} \ No newline at end of file diff --git a/exosphere/userpage.h b/exosphere/userpage.h new file mode 100644 index 000000000..9f64b2fde --- /dev/null +++ b/exosphere/userpage.h @@ -0,0 +1,22 @@ +#ifndef EXOSPHERE_USERPAGE_H +#define EXOSPHERE_USERPAGE_H + +#include + +#define SECURE_USER_PAGE_ADDR (0x1F01F4000ULL) + +typedef struct { + uint64_t user_page; + uint64_t secure_page; +} upage_ref_t; + +int upage_init(upage_ref_t *user_page, void *user_address); + +void user_copy_to_secure(upage_ref_t *user_page, void *secure_dst, void *user_src, size_t size); +void secure_copy_to_user(upage_ref_t *user_page, void *user_dst, void *secure_src, size_t size); + +static inline uint64_t get_page_for_address(void *address) { + return ((uint64_t)(address)) & 0xFFFFFFFFFFFFF000ULL; +} + +#endif \ No newline at end of file