linux/arch/arm/mach-msm/scm.c
<<
>>
Prefs
   1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 and
   5 * only version 2 as published by the Free Software Foundation.
   6 *
   7 * This program is distributed in the hope that it will be useful,
   8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10 * GNU General Public License for more details.
  11 *
  12 * You should have received a copy of the GNU General Public License
  13 * along with this program; if not, write to the Free Software
  14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15 * 02110-1301, USA.
  16 */
  17
  18#include <linux/slab.h>
  19#include <linux/io.h>
  20#include <linux/module.h>
  21#include <linux/mutex.h>
  22#include <linux/errno.h>
  23#include <linux/err.h>
  24
  25#include <asm/cacheflush.h>
  26
  27#include "scm.h"
  28
  29/* Cache line size for msm8x60 */
  30#define CACHELINESIZE 32
  31
  32#define SCM_ENOMEM              -5
  33#define SCM_EOPNOTSUPP          -4
  34#define SCM_EINVAL_ADDR         -3
  35#define SCM_EINVAL_ARG          -2
  36#define SCM_ERROR               -1
  37#define SCM_INTERRUPTED         1
  38
  39static DEFINE_MUTEX(scm_lock);
  40
  41/**
  42 * struct scm_command - one SCM command buffer
  43 * @len: total available memory for command and response
  44 * @buf_offset: start of command buffer
  45 * @resp_hdr_offset: start of response buffer
  46 * @id: command to be executed
  47 * @buf: buffer returned from scm_get_command_buffer()
  48 *
  49 * An SCM command is laid out in memory as follows:
  50 *
  51 *      ------------------- <--- struct scm_command
  52 *      | command header  |
  53 *      ------------------- <--- scm_get_command_buffer()
  54 *      | command buffer  |
  55 *      ------------------- <--- struct scm_response and
  56 *      | response header |      scm_command_to_response()
  57 *      ------------------- <--- scm_get_response_buffer()
  58 *      | response buffer |
  59 *      -------------------
  60 *
  61 * There can be arbitrary padding between the headers and buffers so
  62 * you should always use the appropriate scm_get_*_buffer() routines
  63 * to access the buffers in a safe manner.
  64 */
  65struct scm_command {
  66        u32     len;
  67        u32     buf_offset;
  68        u32     resp_hdr_offset;
  69        u32     id;
  70        u32     buf[0];
  71};
  72
  73/**
  74 * struct scm_response - one SCM response buffer
  75 * @len: total available memory for response
  76 * @buf_offset: start of response data relative to start of scm_response
  77 * @is_complete: indicates if the command has finished processing
  78 */
  79struct scm_response {
  80        u32     len;
  81        u32     buf_offset;
  82        u32     is_complete;
  83};
  84
  85/**
  86 * alloc_scm_command() - Allocate an SCM command
  87 * @cmd_size: size of the command buffer
  88 * @resp_size: size of the response buffer
  89 *
  90 * Allocate an SCM command, including enough room for the command
  91 * and response headers as well as the command and response buffers.
  92 *
  93 * Returns a valid &scm_command on success or %NULL if the allocation fails.
  94 */
  95static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
  96{
  97        struct scm_command *cmd;
  98        size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
  99                resp_size;
 100
 101        cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
 102        if (cmd) {
 103                cmd->len = len;
 104                cmd->buf_offset = offsetof(struct scm_command, buf);
 105                cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
 106        }
 107        return cmd;
 108}
 109
 110/**
 111 * free_scm_command() - Free an SCM command
 112 * @cmd: command to free
 113 *
 114 * Free an SCM command.
 115 */
 116static inline void free_scm_command(struct scm_command *cmd)
 117{
 118        kfree(cmd);
 119}
 120
 121/**
 122 * scm_command_to_response() - Get a pointer to a scm_response
 123 * @cmd: command
 124 *
 125 * Returns a pointer to a response for a command.
 126 */
 127static inline struct scm_response *scm_command_to_response(
 128                const struct scm_command *cmd)
 129{
 130        return (void *)cmd + cmd->resp_hdr_offset;
 131}
 132
 133/**
 134 * scm_get_command_buffer() - Get a pointer to a command buffer
 135 * @cmd: command
 136 *
 137 * Returns a pointer to the command buffer of a command.
 138 */
 139static inline void *scm_get_command_buffer(const struct scm_command *cmd)
 140{
 141        return (void *)cmd->buf;
 142}
 143
 144/**
 145 * scm_get_response_buffer() - Get a pointer to a response buffer
 146 * @rsp: response
 147 *
 148 * Returns a pointer to a response buffer of a response.
 149 */
 150static inline void *scm_get_response_buffer(const struct scm_response *rsp)
 151{
 152        return (void *)rsp + rsp->buf_offset;
 153}
 154
 155static int scm_remap_error(int err)
 156{
 157        switch (err) {
 158        case SCM_ERROR:
 159                return -EIO;
 160        case SCM_EINVAL_ADDR:
 161        case SCM_EINVAL_ARG:
 162                return -EINVAL;
 163        case SCM_EOPNOTSUPP:
 164                return -EOPNOTSUPP;
 165        case SCM_ENOMEM:
 166                return -ENOMEM;
 167        }
 168        return -EINVAL;
 169}
 170
 171static u32 smc(u32 cmd_addr)
 172{
 173        int context_id;
 174        register u32 r0 asm("r0") = 1;
 175        register u32 r1 asm("r1") = (u32)&context_id;
 176        register u32 r2 asm("r2") = cmd_addr;
 177        do {
 178                asm volatile(
 179                        __asmeq("%0", "r0")
 180                        __asmeq("%1", "r0")
 181                        __asmeq("%2", "r1")
 182                        __asmeq("%3", "r2")
 183#ifdef REQUIRES_SEC
 184                        ".arch_extension sec\n"
 185#endif
 186                        "smc    #0      @ switch to secure world\n"
 187                        : "=r" (r0)
 188                        : "r" (r0), "r" (r1), "r" (r2)
 189                        : "r3");
 190        } while (r0 == SCM_INTERRUPTED);
 191
 192        return r0;
 193}
 194
 195static int __scm_call(const struct scm_command *cmd)
 196{
 197        int ret;
 198        u32 cmd_addr = virt_to_phys(cmd);
 199
 200        /*
 201         * Flush the entire cache here so callers don't have to remember
 202         * to flush the cache when passing physical addresses to the secure
 203         * side in the buffer.
 204         */
 205        flush_cache_all();
 206        ret = smc(cmd_addr);
 207        if (ret < 0)
 208                ret = scm_remap_error(ret);
 209
 210        return ret;
 211}
 212
 213/**
 214 * scm_call() - Send an SCM command
 215 * @svc_id: service identifier
 216 * @cmd_id: command identifier
 217 * @cmd_buf: command buffer
 218 * @cmd_len: length of the command buffer
 219 * @resp_buf: response buffer
 220 * @resp_len: length of the response buffer
 221 *
 222 * Sends a command to the SCM and waits for the command to finish processing.
 223 */
 224int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
 225                void *resp_buf, size_t resp_len)
 226{
 227        int ret;
 228        struct scm_command *cmd;
 229        struct scm_response *rsp;
 230
 231        cmd = alloc_scm_command(cmd_len, resp_len);
 232        if (!cmd)
 233                return -ENOMEM;
 234
 235        cmd->id = (svc_id << 10) | cmd_id;
 236        if (cmd_buf)
 237                memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
 238
 239        mutex_lock(&scm_lock);
 240        ret = __scm_call(cmd);
 241        mutex_unlock(&scm_lock);
 242        if (ret)
 243                goto out;
 244
 245        rsp = scm_command_to_response(cmd);
 246        do {
 247                u32 start = (u32)rsp;
 248                u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
 249                start &= ~(CACHELINESIZE - 1);
 250                while (start < end) {
 251                        asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
 252                             : "memory");
 253                        start += CACHELINESIZE;
 254                }
 255        } while (!rsp->is_complete);
 256
 257        if (resp_buf)
 258                memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
 259out:
 260        free_scm_command(cmd);
 261        return ret;
 262}
 263EXPORT_SYMBOL(scm_call);
 264
 265u32 scm_get_version(void)
 266{
 267        int context_id;
 268        static u32 version = -1;
 269        register u32 r0 asm("r0");
 270        register u32 r1 asm("r1");
 271
 272        if (version != -1)
 273                return version;
 274
 275        mutex_lock(&scm_lock);
 276
 277        r0 = 0x1 << 8;
 278        r1 = (u32)&context_id;
 279        do {
 280                asm volatile(
 281                        __asmeq("%0", "r0")
 282                        __asmeq("%1", "r1")
 283                        __asmeq("%2", "r0")
 284                        __asmeq("%3", "r1")
 285#ifdef REQUIRES_SEC
 286                        ".arch_extension sec\n"
 287#endif
 288                        "smc    #0      @ switch to secure world\n"
 289                        : "=r" (r0), "=r" (r1)
 290                        : "r" (r0), "r" (r1)
 291                        : "r2", "r3");
 292        } while (r0 == SCM_INTERRUPTED);
 293
 294        version = r1;
 295        mutex_unlock(&scm_lock);
 296
 297        return version;
 298}
 299EXPORT_SYMBOL(scm_get_version);
 300