Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | */ |
| 6 | |
| 7 | #include <stdint.h> |
| 8 | #include <stdbool.h> |
| 9 | #include <string.h> |
| 10 | |
| 11 | #include <lib/debugfs.h> |
| 12 | #include <lib/smccc.h> |
| 13 | #include <lib/spinlock.h> |
| 14 | #include <lib/xlat_tables/xlat_tables_v2.h> |
| 15 | #include <smccc_helpers.h> |
| 16 | |
| 17 | #define MAX_PATH_LEN 256 |
| 18 | |
| 19 | #define MOUNT 0 |
| 20 | #define CREATE 1 |
| 21 | #define OPEN 2 |
| 22 | #define CLOSE 3 |
| 23 | #define READ 4 |
| 24 | #define WRITE 5 |
| 25 | #define SEEK 6 |
| 26 | #define BIND 7 |
| 27 | #define STAT 8 |
| 28 | #define INIT 10 |
| 29 | #define VERSION 11 |
| 30 | |
| 31 | /* This is the virtual address to which we map the NS shared buffer */ |
| 32 | #define DEBUGFS_SHARED_BUF_VIRT ((void *)0x81000000U) |
| 33 | |
| 34 | static union debugfs_parms { |
| 35 | struct { |
| 36 | char fname[MAX_PATH_LEN]; |
| 37 | } open; |
| 38 | |
| 39 | struct { |
| 40 | char srv[MAX_PATH_LEN]; |
| 41 | char where[MAX_PATH_LEN]; |
| 42 | char spec[MAX_PATH_LEN]; |
| 43 | } mount; |
| 44 | |
| 45 | struct { |
| 46 | char path[MAX_PATH_LEN]; |
| 47 | dir_t dir; |
| 48 | } stat; |
| 49 | |
| 50 | struct { |
| 51 | char oldpath[MAX_PATH_LEN]; |
| 52 | char newpath[MAX_PATH_LEN]; |
| 53 | } bind; |
| 54 | } parms; |
| 55 | |
| 56 | /* debugfs_access_lock protects shared buffer and internal */ |
| 57 | /* FS functions from concurrent acccesses. */ |
| 58 | static spinlock_t debugfs_access_lock; |
| 59 | |
| 60 | static bool debugfs_initialized; |
| 61 | |
| 62 | uintptr_t debugfs_smc_handler(unsigned int smc_fid, |
| 63 | u_register_t cmd, |
| 64 | u_register_t arg2, |
| 65 | u_register_t arg3, |
| 66 | u_register_t arg4, |
| 67 | void *cookie, |
| 68 | void *handle, |
| 69 | u_register_t flags) |
| 70 | { |
| 71 | int64_t smc_ret = DEBUGFS_E_INVALID_PARAMS, smc_resp = 0; |
| 72 | int ret; |
| 73 | |
| 74 | /* Allow calls from non-secure only */ |
| 75 | if (is_caller_secure(flags)) { |
| 76 | SMC_RET1(handle, DEBUGFS_E_DENIED); |
| 77 | } |
| 78 | |
| 79 | /* Expect a SiP service fast call */ |
| 80 | if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) || |
| 81 | (GET_SMC_OEN(smc_fid) != OEN_SIP_START)) { |
| 82 | SMC_RET1(handle, SMC_UNK); |
| 83 | } |
| 84 | |
| 85 | /* Truncate parameters if 32b SMC convention call */ |
| 86 | if (GET_SMC_CC(smc_fid) == SMC_32) { |
| 87 | arg2 &= 0xffffffff; |
| 88 | arg3 &= 0xffffffff; |
| 89 | arg4 &= 0xffffffff; |
| 90 | } |
| 91 | |
| 92 | spin_lock(&debugfs_access_lock); |
| 93 | |
| 94 | if (debugfs_initialized == true) { |
| 95 | /* Copy NS shared buffer to internal secure location */ |
| 96 | memcpy(&parms, (void *)DEBUGFS_SHARED_BUF_VIRT, |
| 97 | sizeof(union debugfs_parms)); |
| 98 | } |
| 99 | |
| 100 | switch (cmd) { |
| 101 | case INIT: |
| 102 | if (debugfs_initialized == false) { |
| 103 | /* TODO: check PA validity e.g. whether */ |
| 104 | /* it is an NS region. */ |
| 105 | ret = mmap_add_dynamic_region(arg2, |
| 106 | (uintptr_t)DEBUGFS_SHARED_BUF_VIRT, |
| 107 | PAGE_SIZE_4KB, |
| 108 | MT_MEMORY | MT_RW | MT_NS); |
| 109 | if (ret == 0) { |
| 110 | debugfs_initialized = true; |
| 111 | smc_ret = SMC_OK; |
| 112 | smc_resp = 0; |
| 113 | } |
| 114 | } |
| 115 | break; |
| 116 | |
| 117 | case VERSION: |
| 118 | smc_ret = SMC_OK; |
| 119 | smc_resp = DEBUGFS_VERSION; |
| 120 | break; |
| 121 | |
| 122 | case MOUNT: |
| 123 | ret = mount(parms.mount.srv, |
| 124 | parms.mount.where, |
| 125 | parms.mount.spec); |
| 126 | if (ret == 0) { |
| 127 | smc_ret = SMC_OK; |
| 128 | smc_resp = 0; |
| 129 | } |
| 130 | break; |
| 131 | |
| 132 | case OPEN: |
| 133 | ret = open(parms.open.fname, arg2); |
| 134 | if (ret >= 0) { |
| 135 | smc_ret = SMC_OK; |
| 136 | smc_resp = ret; |
| 137 | } |
| 138 | break; |
| 139 | |
| 140 | case CLOSE: |
| 141 | ret = close(arg2); |
| 142 | if (ret == 0) { |
| 143 | smc_ret = SMC_OK; |
| 144 | smc_resp = 0; |
| 145 | } |
| 146 | break; |
| 147 | |
| 148 | case READ: |
| 149 | ret = read(arg2, DEBUGFS_SHARED_BUF_VIRT, arg3); |
| 150 | if (ret >= 0) { |
| 151 | smc_ret = SMC_OK; |
| 152 | smc_resp = ret; |
| 153 | } |
| 154 | break; |
| 155 | |
| 156 | case SEEK: |
| 157 | ret = seek(arg2, arg3, arg4); |
| 158 | if (ret == 0) { |
| 159 | smc_ret = SMC_OK; |
| 160 | smc_resp = 0; |
| 161 | } |
| 162 | break; |
| 163 | |
| 164 | case BIND: |
| 165 | ret = bind(parms.bind.oldpath, parms.bind.newpath); |
| 166 | if (ret == 0) { |
| 167 | smc_ret = SMC_OK; |
| 168 | smc_resp = 0; |
| 169 | } |
| 170 | break; |
| 171 | |
| 172 | case STAT: |
| 173 | ret = stat(parms.stat.path, &parms.stat.dir); |
| 174 | if (ret == 0) { |
| 175 | memcpy((void *)DEBUGFS_SHARED_BUF_VIRT, &parms, |
| 176 | sizeof(union debugfs_parms)); |
| 177 | smc_ret = SMC_OK; |
| 178 | smc_resp = 0; |
| 179 | } |
| 180 | break; |
| 181 | |
| 182 | /* Not implemented */ |
| 183 | case CREATE: |
| 184 | /* Intentional fall-through */ |
| 185 | |
| 186 | /* Not implemented */ |
| 187 | case WRITE: |
| 188 | /* Intentional fall-through */ |
| 189 | |
| 190 | default: |
| 191 | smc_ret = SMC_UNK; |
| 192 | smc_resp = 0; |
| 193 | } |
| 194 | |
| 195 | spin_unlock(&debugfs_access_lock); |
| 196 | |
| 197 | SMC_RET2(handle, smc_ret, smc_resp); |
| 198 | |
| 199 | /* Not reached */ |
| 200 | return smc_ret; |
| 201 | } |
| 202 | |
| 203 | int debugfs_smc_setup(void) |
| 204 | { |
| 205 | debugfs_initialized = false; |
| 206 | debugfs_access_lock.lock = 0; |
| 207 | |
| 208 | return 0; |
| 209 | } |