2018-09-07 15:00:13 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-02-22 23:32:47 +00:00
|
|
|
#include <string.h>
|
2018-02-20 04:18:53 +00:00
|
|
|
|
|
|
|
#include "utils.h"
|
|
|
|
#include "userpage.h"
|
2018-02-25 09:31:45 +00:00
|
|
|
#include "memory_map.h"
|
2018-02-27 21:29:47 +00:00
|
|
|
#include "arm.h"
|
2018-02-20 04:18:53 +00:00
|
|
|
|
2018-02-26 21:09:35 +00:00
|
|
|
static uintptr_t g_user_page_user_address = 0ull;
|
2018-02-25 14:05:52 +00:00
|
|
|
|
|
|
|
static inline uintptr_t get_page_for_address(void *address) {
|
2018-02-25 19:00:50 +00:00
|
|
|
return ((uintptr_t)(address)) & ~0xFFFULL;
|
2018-02-25 14:05:52 +00:00
|
|
|
}
|
2018-02-20 04:18:53 +00:00
|
|
|
|
|
|
|
/* Create a user page reference for the desired address. */
|
|
|
|
/* Returns 1 on success, 0 on failure. */
|
2018-02-23 03:58:39 +00:00
|
|
|
bool upage_init(upage_ref_t *upage, void *user_address) {
|
2018-02-25 14:05:52 +00:00
|
|
|
upage->user_address = get_page_for_address(user_address);
|
2018-02-26 21:09:35 +00:00
|
|
|
upage->secure_monitor_address = 0ull;
|
2018-02-23 03:58:39 +00:00
|
|
|
|
2018-02-26 21:09:35 +00:00
|
|
|
if (g_user_page_user_address != 0ull) {
|
2018-02-20 21:51:17 +00:00
|
|
|
/* Different physical address indicate SPL was rebooted, or another process got access to svcCallSecureMonitor. Panic. */
|
2018-02-25 14:05:52 +00:00
|
|
|
if (g_user_page_user_address != upage->user_address) {
|
2018-02-24 14:20:45 +00:00
|
|
|
generic_panic();
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
2018-02-25 14:05:52 +00:00
|
|
|
upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR;
|
2018-02-20 04:18:53 +00:00
|
|
|
} else {
|
2018-03-01 11:30:24 +00:00
|
|
|
/* Validate SPL's physically random address (must be in DRAM (supports up to 6GB, retail console have 4GB) and page-aligned). */
|
|
|
|
if ((upage->user_address - 0x80000000ull) < (6ull << 30) && ((uintptr_t)user_address & 0xFFF) == 0) {
|
2018-02-25 19:00:50 +00:00
|
|
|
static const uint64_t userpage_attributes = MMU_PTE_BLOCK_XN | MMU_PTE_BLOCK_INNER_SHAREBLE | MMU_PTE_BLOCK_NS | ATTRIB_MEMTYPE_NORMAL;
|
2018-02-26 21:09:35 +00:00
|
|
|
uintptr_t *mmu_l3_tbl = (uintptr_t *)TZRAM_GET_SEGMENT_ADDRESS(TZRAM_SEGMENT_ID_L3_TRANSLATION_TABLE);
|
2018-02-25 14:05:52 +00:00
|
|
|
g_user_page_user_address = upage->user_address;
|
|
|
|
mmu_map_page(mmu_l3_tbl, USER_PAGE_SECURE_MONITOR_ADDR, upage->user_address, userpage_attributes);
|
|
|
|
tlb_invalidate_page_inner_shareable((void *)USER_PAGE_SECURE_MONITOR_ADDR);
|
|
|
|
upage->secure_monitor_address = USER_PAGE_SECURE_MONITOR_ADDR;
|
2018-03-08 12:59:00 +00:00
|
|
|
} else {
|
|
|
|
generic_panic();
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-23 03:58:39 +00:00
|
|
|
|
2018-02-26 21:09:35 +00:00
|
|
|
return upage->secure_monitor_address != 0ull;
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
|
|
|
|
2018-02-23 03:58:39 +00:00
|
|
|
bool user_copy_to_secure(upage_ref_t *upage, void *secure_dst, void *user_src, size_t size) {
|
2018-02-20 04:18:53 +00:00
|
|
|
/* Fail if the page doesn't match. */
|
2018-02-25 14:05:52 +00:00
|
|
|
if (get_page_for_address(user_src) != upage->user_address) {
|
2018-02-23 03:58:39 +00:00
|
|
|
return false;
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
2018-02-23 03:58:39 +00:00
|
|
|
|
2018-02-20 04:18:53 +00:00
|
|
|
/* Fail if we go past the page boundary. */
|
2018-02-25 14:05:52 +00:00
|
|
|
if (size != 0 && get_page_for_address(user_src + size - 1) != upage->user_address) {
|
2018-02-23 03:58:39 +00:00
|
|
|
return false;
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
2018-02-23 03:58:39 +00:00
|
|
|
|
2018-02-25 14:05:52 +00:00
|
|
|
void *secure_src = (void *)(upage->secure_monitor_address + ((uintptr_t)user_src - upage->user_address));
|
2018-03-08 10:17:46 +00:00
|
|
|
if (size != 0) {
|
|
|
|
memcpy(secure_dst, secure_src, size);
|
|
|
|
}
|
2018-02-23 03:58:39 +00:00
|
|
|
return true;
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
|
|
|
|
2018-02-23 03:58:39 +00:00
|
|
|
bool secure_copy_to_user(upage_ref_t *upage, void *user_dst, void *secure_src, size_t size) {
|
2018-02-20 04:18:53 +00:00
|
|
|
/* Fail if the page doesn't match. */
|
2018-02-25 14:05:52 +00:00
|
|
|
if (get_page_for_address(user_dst) != upage->user_address) {
|
2018-02-23 03:58:39 +00:00
|
|
|
return false;
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
2018-02-23 03:58:39 +00:00
|
|
|
|
2018-02-20 04:18:53 +00:00
|
|
|
/* Fail if we go past the page boundary. */
|
2018-02-25 14:05:52 +00:00
|
|
|
if (size != 0 && get_page_for_address(user_dst + size - 1) != upage->user_address) {
|
2018-02-23 03:58:39 +00:00
|
|
|
return false;
|
2018-02-20 04:18:53 +00:00
|
|
|
}
|
|
|
|
|
2018-02-25 14:05:52 +00:00
|
|
|
void *secure_dst = (void *)(upage->secure_monitor_address + ((uintptr_t)user_dst - upage->user_address));
|
2018-03-08 10:17:46 +00:00
|
|
|
if(size != 0) {
|
|
|
|
memcpy(secure_dst, secure_src, size);
|
|
|
|
}
|
2018-02-23 03:58:39 +00:00
|
|
|
return true;
|
2018-02-25 14:05:52 +00:00
|
|
|
}
|