133960accSRijo Thomas // SPDX-License-Identifier: MIT 233960accSRijo Thomas /* 333960accSRijo Thomas * AMD Trusted Execution Environment (TEE) interface 433960accSRijo Thomas * 533960accSRijo Thomas * Author: Rijo Thomas <Rijo-john.Thomas@amd.com> 633960accSRijo Thomas * Author: Devaraj Rangasamy <Devaraj.Rangasamy@amd.com> 733960accSRijo Thomas * 84a5eed17SRijo Thomas * Copyright (C) 2019,2021 Advanced Micro Devices, Inc. 933960accSRijo Thomas */ 1033960accSRijo Thomas 11*1c5c1dafSMario Limonciello #include <linux/bitfield.h> 1233960accSRijo Thomas #include <linux/types.h> 1333960accSRijo Thomas #include <linux/mutex.h> 1433960accSRijo Thomas #include <linux/delay.h> 1533960accSRijo Thomas #include <linux/slab.h> 1633960accSRijo Thomas #include <linux/gfp.h> 17ae7d45fbSMario Limonciello #include <linux/psp.h> 18632b0b53SRijo Thomas #include <linux/psp-tee.h> 1933960accSRijo Thomas 2033960accSRijo Thomas #include "psp-dev.h" 2133960accSRijo Thomas #include "tee-dev.h" 2233960accSRijo Thomas 2333960accSRijo Thomas static bool psp_dead; 2433960accSRijo Thomas 2533960accSRijo Thomas static int tee_alloc_ring(struct psp_tee_device *tee, int ring_size) 2633960accSRijo Thomas { 2733960accSRijo Thomas struct ring_buf_manager *rb_mgr = &tee->rb_mgr; 2833960accSRijo Thomas void *start_addr; 2933960accSRijo Thomas 3033960accSRijo Thomas if (!ring_size) 3133960accSRijo Thomas return -EINVAL; 3233960accSRijo Thomas 3333960accSRijo Thomas /* We need actual physical address instead of DMA address, since 3433960accSRijo Thomas * Trusted OS running on AMD Secure Processor will map this region 3533960accSRijo Thomas */ 3633960accSRijo Thomas start_addr = (void *)__get_free_pages(GFP_KERNEL, get_order(ring_size)); 3733960accSRijo Thomas if (!start_addr) 3833960accSRijo Thomas return -ENOMEM; 3933960accSRijo Thomas 4000aa6e65SRijo Thomas memset(start_addr, 0x0, ring_size); 4133960accSRijo Thomas rb_mgr->ring_start = start_addr; 4233960accSRijo Thomas rb_mgr->ring_size = ring_size; 4333960accSRijo Thomas rb_mgr->ring_pa = __psp_pa(start_addr); 44632b0b53SRijo Thomas mutex_init(&rb_mgr->mutex); 4533960accSRijo Thomas 4633960accSRijo Thomas return 0; 4733960accSRijo Thomas } 4833960accSRijo Thomas 4933960accSRijo Thomas static void tee_free_ring(struct psp_tee_device *tee) 5033960accSRijo Thomas { 5133960accSRijo Thomas struct ring_buf_manager *rb_mgr = &tee->rb_mgr; 5233960accSRijo Thomas 5333960accSRijo Thomas if (!rb_mgr->ring_start) 5433960accSRijo Thomas return; 5533960accSRijo Thomas 5633960accSRijo Thomas free_pages((unsigned long)rb_mgr->ring_start, 5733960accSRijo Thomas get_order(rb_mgr->ring_size)); 5833960accSRijo Thomas 5933960accSRijo Thomas rb_mgr->ring_start = NULL; 6033960accSRijo Thomas rb_mgr->ring_size = 0; 6133960accSRijo Thomas rb_mgr->ring_pa = 0; 62632b0b53SRijo Thomas mutex_destroy(&rb_mgr->mutex); 6333960accSRijo Thomas } 6433960accSRijo Thomas 6533960accSRijo Thomas static int tee_wait_cmd_poll(struct psp_tee_device *tee, unsigned int timeout, 6633960accSRijo Thomas unsigned int *reg) 6733960accSRijo Thomas { 6833960accSRijo Thomas /* ~10ms sleep per loop => nloop = timeout * 100 */ 6933960accSRijo Thomas int nloop = timeout * 100; 7033960accSRijo Thomas 7133960accSRijo Thomas while (--nloop) { 7233960accSRijo Thomas *reg = ioread32(tee->io_regs + tee->vdata->cmdresp_reg); 73*1c5c1dafSMario Limonciello if (FIELD_GET(PSP_CMDRESP_RESP, *reg)) 7433960accSRijo Thomas return 0; 7533960accSRijo Thomas 7633960accSRijo Thomas usleep_range(10000, 10100); 7733960accSRijo Thomas } 7833960accSRijo Thomas 7933960accSRijo Thomas dev_err(tee->dev, "tee: command timed out, disabling PSP\n"); 8033960accSRijo Thomas psp_dead = true; 8133960accSRijo Thomas 8233960accSRijo Thomas return -ETIMEDOUT; 8333960accSRijo Thomas } 8433960accSRijo Thomas 8533960accSRijo Thomas static 8633960accSRijo Thomas struct tee_init_ring_cmd *tee_alloc_cmd_buffer(struct psp_tee_device *tee) 8733960accSRijo Thomas { 8833960accSRijo Thomas struct tee_init_ring_cmd *cmd; 8933960accSRijo Thomas 9033960accSRijo Thomas cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 9133960accSRijo Thomas if (!cmd) 9233960accSRijo Thomas return NULL; 9333960accSRijo Thomas 9433960accSRijo Thomas cmd->hi_addr = upper_32_bits(tee->rb_mgr.ring_pa); 9533960accSRijo Thomas cmd->low_addr = lower_32_bits(tee->rb_mgr.ring_pa); 9633960accSRijo Thomas cmd->size = tee->rb_mgr.ring_size; 9733960accSRijo Thomas 9833960accSRijo Thomas dev_dbg(tee->dev, "tee: ring address: high = 0x%x low = 0x%x size = %u\n", 9933960accSRijo Thomas cmd->hi_addr, cmd->low_addr, cmd->size); 10033960accSRijo Thomas 10133960accSRijo Thomas return cmd; 10233960accSRijo Thomas } 10333960accSRijo Thomas 10433960accSRijo Thomas static inline void tee_free_cmd_buffer(struct tee_init_ring_cmd *cmd) 10533960accSRijo Thomas { 10633960accSRijo Thomas kfree(cmd); 10733960accSRijo Thomas } 10833960accSRijo Thomas 10933960accSRijo Thomas static int tee_init_ring(struct psp_tee_device *tee) 11033960accSRijo Thomas { 11133960accSRijo Thomas int ring_size = MAX_RING_BUFFER_ENTRIES * sizeof(struct tee_ring_cmd); 11233960accSRijo Thomas struct tee_init_ring_cmd *cmd; 11333960accSRijo Thomas phys_addr_t cmd_buffer; 11433960accSRijo Thomas unsigned int reg; 11533960accSRijo Thomas int ret; 11633960accSRijo Thomas 11733960accSRijo Thomas BUILD_BUG_ON(sizeof(struct tee_ring_cmd) != 1024); 11833960accSRijo Thomas 11933960accSRijo Thomas ret = tee_alloc_ring(tee, ring_size); 12033960accSRijo Thomas if (ret) { 12133960accSRijo Thomas dev_err(tee->dev, "tee: ring allocation failed %d\n", ret); 12233960accSRijo Thomas return ret; 12333960accSRijo Thomas } 12433960accSRijo Thomas 12533960accSRijo Thomas tee->rb_mgr.wptr = 0; 12633960accSRijo Thomas 12733960accSRijo Thomas cmd = tee_alloc_cmd_buffer(tee); 12833960accSRijo Thomas if (!cmd) { 12933960accSRijo Thomas tee_free_ring(tee); 13033960accSRijo Thomas return -ENOMEM; 13133960accSRijo Thomas } 13233960accSRijo Thomas 13333960accSRijo Thomas cmd_buffer = __psp_pa((void *)cmd); 13433960accSRijo Thomas 13533960accSRijo Thomas /* Send command buffer details to Trusted OS by writing to 13633960accSRijo Thomas * CPU-PSP message registers 13733960accSRijo Thomas */ 13833960accSRijo Thomas 13933960accSRijo Thomas iowrite32(lower_32_bits(cmd_buffer), 14033960accSRijo Thomas tee->io_regs + tee->vdata->cmdbuff_addr_lo_reg); 14133960accSRijo Thomas iowrite32(upper_32_bits(cmd_buffer), 14233960accSRijo Thomas tee->io_regs + tee->vdata->cmdbuff_addr_hi_reg); 14333960accSRijo Thomas iowrite32(TEE_RING_INIT_CMD, 14433960accSRijo Thomas tee->io_regs + tee->vdata->cmdresp_reg); 14533960accSRijo Thomas 14633960accSRijo Thomas ret = tee_wait_cmd_poll(tee, TEE_DEFAULT_TIMEOUT, ®); 14733960accSRijo Thomas if (ret) { 14833960accSRijo Thomas dev_err(tee->dev, "tee: ring init command timed out\n"); 14933960accSRijo Thomas tee_free_ring(tee); 15033960accSRijo Thomas goto free_buf; 15133960accSRijo Thomas } 15233960accSRijo Thomas 153*1c5c1dafSMario Limonciello if (FIELD_GET(PSP_CMDRESP_STS, reg)) { 154*1c5c1dafSMario Limonciello dev_err(tee->dev, "tee: ring init command failed (%#010lx)\n", 155*1c5c1dafSMario Limonciello FIELD_GET(PSP_CMDRESP_STS, reg)); 15633960accSRijo Thomas tee_free_ring(tee); 15733960accSRijo Thomas ret = -EIO; 15833960accSRijo Thomas } 15933960accSRijo Thomas 16033960accSRijo Thomas free_buf: 16133960accSRijo Thomas tee_free_cmd_buffer(cmd); 16233960accSRijo Thomas 16333960accSRijo Thomas return ret; 16433960accSRijo Thomas } 16533960accSRijo Thomas 16633960accSRijo Thomas static void tee_destroy_ring(struct psp_tee_device *tee) 16733960accSRijo Thomas { 16833960accSRijo Thomas unsigned int reg; 16933960accSRijo Thomas int ret; 17033960accSRijo Thomas 17133960accSRijo Thomas if (!tee->rb_mgr.ring_start) 17233960accSRijo Thomas return; 17333960accSRijo Thomas 17433960accSRijo Thomas if (psp_dead) 17533960accSRijo Thomas goto free_ring; 17633960accSRijo Thomas 17733960accSRijo Thomas iowrite32(TEE_RING_DESTROY_CMD, 17833960accSRijo Thomas tee->io_regs + tee->vdata->cmdresp_reg); 17933960accSRijo Thomas 18033960accSRijo Thomas ret = tee_wait_cmd_poll(tee, TEE_DEFAULT_TIMEOUT, ®); 18133960accSRijo Thomas if (ret) { 18233960accSRijo Thomas dev_err(tee->dev, "tee: ring destroy command timed out\n"); 183*1c5c1dafSMario Limonciello } else if (FIELD_GET(PSP_CMDRESP_STS, reg)) { 184*1c5c1dafSMario Limonciello dev_err(tee->dev, "tee: ring destroy command failed (%#010lx)\n", 185*1c5c1dafSMario Limonciello FIELD_GET(PSP_CMDRESP_STS, reg)); 18633960accSRijo Thomas } 18733960accSRijo Thomas 18833960accSRijo Thomas free_ring: 18933960accSRijo Thomas tee_free_ring(tee); 19033960accSRijo Thomas } 19133960accSRijo Thomas 19233960accSRijo Thomas int tee_dev_init(struct psp_device *psp) 19333960accSRijo Thomas { 19433960accSRijo Thomas struct device *dev = psp->dev; 19533960accSRijo Thomas struct psp_tee_device *tee; 19633960accSRijo Thomas int ret; 19733960accSRijo Thomas 19833960accSRijo Thomas ret = -ENOMEM; 19933960accSRijo Thomas tee = devm_kzalloc(dev, sizeof(*tee), GFP_KERNEL); 20033960accSRijo Thomas if (!tee) 20133960accSRijo Thomas goto e_err; 20233960accSRijo Thomas 20333960accSRijo Thomas psp->tee_data = tee; 20433960accSRijo Thomas 20533960accSRijo Thomas tee->dev = dev; 20633960accSRijo Thomas tee->psp = psp; 20733960accSRijo Thomas 20833960accSRijo Thomas tee->io_regs = psp->io_regs; 20933960accSRijo Thomas 21033960accSRijo Thomas tee->vdata = (struct tee_vdata *)psp->vdata->tee; 21133960accSRijo Thomas if (!tee->vdata) { 21233960accSRijo Thomas ret = -ENODEV; 21333960accSRijo Thomas dev_err(dev, "tee: missing driver data\n"); 21433960accSRijo Thomas goto e_err; 21533960accSRijo Thomas } 21633960accSRijo Thomas 21733960accSRijo Thomas ret = tee_init_ring(tee); 21833960accSRijo Thomas if (ret) { 21933960accSRijo Thomas dev_err(dev, "tee: failed to init ring buffer\n"); 22033960accSRijo Thomas goto e_err; 22133960accSRijo Thomas } 22233960accSRijo Thomas 22333960accSRijo Thomas dev_notice(dev, "tee enabled\n"); 22433960accSRijo Thomas 22533960accSRijo Thomas return 0; 22633960accSRijo Thomas 22733960accSRijo Thomas e_err: 22833960accSRijo Thomas psp->tee_data = NULL; 22933960accSRijo Thomas 23033960accSRijo Thomas dev_notice(dev, "tee initialization failed\n"); 23133960accSRijo Thomas 23233960accSRijo Thomas return ret; 23333960accSRijo Thomas } 23433960accSRijo Thomas 23533960accSRijo Thomas void tee_dev_destroy(struct psp_device *psp) 23633960accSRijo Thomas { 23733960accSRijo Thomas struct psp_tee_device *tee = psp->tee_data; 23833960accSRijo Thomas 23933960accSRijo Thomas if (!tee) 24033960accSRijo Thomas return; 24133960accSRijo Thomas 24233960accSRijo Thomas tee_destroy_ring(tee); 24333960accSRijo Thomas } 244632b0b53SRijo Thomas 245632b0b53SRijo Thomas static int tee_submit_cmd(struct psp_tee_device *tee, enum tee_cmd_id cmd_id, 246632b0b53SRijo Thomas void *buf, size_t len, struct tee_ring_cmd **resp) 247632b0b53SRijo Thomas { 248632b0b53SRijo Thomas struct tee_ring_cmd *cmd; 249632b0b53SRijo Thomas int nloop = 1000, ret = 0; 25000aa6e65SRijo Thomas u32 rptr; 251632b0b53SRijo Thomas 252632b0b53SRijo Thomas *resp = NULL; 253632b0b53SRijo Thomas 254632b0b53SRijo Thomas mutex_lock(&tee->rb_mgr.mutex); 255632b0b53SRijo Thomas 25600aa6e65SRijo Thomas /* Loop until empty entry found in ring buffer */ 257632b0b53SRijo Thomas do { 25800aa6e65SRijo Thomas /* Get pointer to ring buffer command entry */ 25900aa6e65SRijo Thomas cmd = (struct tee_ring_cmd *) 26000aa6e65SRijo Thomas (tee->rb_mgr.ring_start + tee->rb_mgr.wptr); 26100aa6e65SRijo Thomas 262632b0b53SRijo Thomas rptr = ioread32(tee->io_regs + tee->vdata->ring_rptr_reg); 263632b0b53SRijo Thomas 26400aa6e65SRijo Thomas /* Check if ring buffer is full or command entry is waiting 26500aa6e65SRijo Thomas * for response from TEE 26600aa6e65SRijo Thomas */ 26700aa6e65SRijo Thomas if (!(tee->rb_mgr.wptr + sizeof(struct tee_ring_cmd) == rptr || 26800aa6e65SRijo Thomas cmd->flag == CMD_WAITING_FOR_RESPONSE)) 269632b0b53SRijo Thomas break; 270632b0b53SRijo Thomas 27100aa6e65SRijo Thomas dev_dbg(tee->dev, "tee: ring buffer full. rptr = %u wptr = %u\n", 27200aa6e65SRijo Thomas rptr, tee->rb_mgr.wptr); 273632b0b53SRijo Thomas 27400aa6e65SRijo Thomas /* Wait if ring buffer is full or TEE is processing data */ 275632b0b53SRijo Thomas mutex_unlock(&tee->rb_mgr.mutex); 276632b0b53SRijo Thomas schedule_timeout_interruptible(msecs_to_jiffies(10)); 277632b0b53SRijo Thomas mutex_lock(&tee->rb_mgr.mutex); 278632b0b53SRijo Thomas 279632b0b53SRijo Thomas } while (--nloop); 280632b0b53SRijo Thomas 28100aa6e65SRijo Thomas if (!nloop && 28200aa6e65SRijo Thomas (tee->rb_mgr.wptr + sizeof(struct tee_ring_cmd) == rptr || 28300aa6e65SRijo Thomas cmd->flag == CMD_WAITING_FOR_RESPONSE)) { 28400aa6e65SRijo Thomas dev_err(tee->dev, "tee: ring buffer full. rptr = %u wptr = %u response flag %u\n", 28500aa6e65SRijo Thomas rptr, tee->rb_mgr.wptr, cmd->flag); 286632b0b53SRijo Thomas ret = -EBUSY; 287632b0b53SRijo Thomas goto unlock; 288632b0b53SRijo Thomas } 289632b0b53SRijo Thomas 29000aa6e65SRijo Thomas /* Do not submit command if PSP got disabled while processing any 29100aa6e65SRijo Thomas * command in another thread 29200aa6e65SRijo Thomas */ 29300aa6e65SRijo Thomas if (psp_dead) { 29400aa6e65SRijo Thomas ret = -EBUSY; 29500aa6e65SRijo Thomas goto unlock; 29600aa6e65SRijo Thomas } 297632b0b53SRijo Thomas 298632b0b53SRijo Thomas /* Write command data into ring buffer */ 299632b0b53SRijo Thomas cmd->cmd_id = cmd_id; 300632b0b53SRijo Thomas cmd->cmd_state = TEE_CMD_STATE_INIT; 301632b0b53SRijo Thomas memset(&cmd->buf[0], 0, sizeof(cmd->buf)); 302632b0b53SRijo Thomas memcpy(&cmd->buf[0], buf, len); 303632b0b53SRijo Thomas 30400aa6e65SRijo Thomas /* Indicate driver is waiting for response */ 30500aa6e65SRijo Thomas cmd->flag = CMD_WAITING_FOR_RESPONSE; 30600aa6e65SRijo Thomas 307632b0b53SRijo Thomas /* Update local copy of write pointer */ 308632b0b53SRijo Thomas tee->rb_mgr.wptr += sizeof(struct tee_ring_cmd); 309632b0b53SRijo Thomas if (tee->rb_mgr.wptr >= tee->rb_mgr.ring_size) 310632b0b53SRijo Thomas tee->rb_mgr.wptr = 0; 311632b0b53SRijo Thomas 312632b0b53SRijo Thomas /* Trigger interrupt to Trusted OS */ 313632b0b53SRijo Thomas iowrite32(tee->rb_mgr.wptr, tee->io_regs + tee->vdata->ring_wptr_reg); 314632b0b53SRijo Thomas 315632b0b53SRijo Thomas /* The response is provided by Trusted OS in same 316632b0b53SRijo Thomas * location as submitted data entry within ring buffer. 317632b0b53SRijo Thomas */ 318632b0b53SRijo Thomas *resp = cmd; 319632b0b53SRijo Thomas 320632b0b53SRijo Thomas unlock: 321632b0b53SRijo Thomas mutex_unlock(&tee->rb_mgr.mutex); 322632b0b53SRijo Thomas 323632b0b53SRijo Thomas return ret; 324632b0b53SRijo Thomas } 325632b0b53SRijo Thomas 326632b0b53SRijo Thomas static int tee_wait_cmd_completion(struct psp_tee_device *tee, 327632b0b53SRijo Thomas struct tee_ring_cmd *resp, 328632b0b53SRijo Thomas unsigned int timeout) 329632b0b53SRijo Thomas { 3304a5eed17SRijo Thomas /* ~1ms sleep per loop => nloop = timeout * 1000 */ 3314a5eed17SRijo Thomas int nloop = timeout * 1000; 332632b0b53SRijo Thomas 333632b0b53SRijo Thomas while (--nloop) { 334632b0b53SRijo Thomas if (resp->cmd_state == TEE_CMD_STATE_COMPLETED) 335632b0b53SRijo Thomas return 0; 336632b0b53SRijo Thomas 3374a5eed17SRijo Thomas usleep_range(1000, 1100); 338632b0b53SRijo Thomas } 339632b0b53SRijo Thomas 340632b0b53SRijo Thomas dev_err(tee->dev, "tee: command 0x%x timed out, disabling PSP\n", 341632b0b53SRijo Thomas resp->cmd_id); 342632b0b53SRijo Thomas 343632b0b53SRijo Thomas psp_dead = true; 344632b0b53SRijo Thomas 345632b0b53SRijo Thomas return -ETIMEDOUT; 346632b0b53SRijo Thomas } 347632b0b53SRijo Thomas 348632b0b53SRijo Thomas int psp_tee_process_cmd(enum tee_cmd_id cmd_id, void *buf, size_t len, 349632b0b53SRijo Thomas u32 *status) 350632b0b53SRijo Thomas { 351632b0b53SRijo Thomas struct psp_device *psp = psp_get_master_device(); 352632b0b53SRijo Thomas struct psp_tee_device *tee; 353632b0b53SRijo Thomas struct tee_ring_cmd *resp; 354632b0b53SRijo Thomas int ret; 355632b0b53SRijo Thomas 356632b0b53SRijo Thomas if (!buf || !status || !len || len > sizeof(resp->buf)) 357632b0b53SRijo Thomas return -EINVAL; 358632b0b53SRijo Thomas 359632b0b53SRijo Thomas *status = 0; 360632b0b53SRijo Thomas 361632b0b53SRijo Thomas if (!psp || !psp->tee_data) 362632b0b53SRijo Thomas return -ENODEV; 363632b0b53SRijo Thomas 364632b0b53SRijo Thomas if (psp_dead) 365632b0b53SRijo Thomas return -EBUSY; 366632b0b53SRijo Thomas 367632b0b53SRijo Thomas tee = psp->tee_data; 368632b0b53SRijo Thomas 369632b0b53SRijo Thomas ret = tee_submit_cmd(tee, cmd_id, buf, len, &resp); 370632b0b53SRijo Thomas if (ret) 371632b0b53SRijo Thomas return ret; 372632b0b53SRijo Thomas 373632b0b53SRijo Thomas ret = tee_wait_cmd_completion(tee, resp, TEE_DEFAULT_TIMEOUT); 37400aa6e65SRijo Thomas if (ret) { 37500aa6e65SRijo Thomas resp->flag = CMD_RESPONSE_TIMEDOUT; 376632b0b53SRijo Thomas return ret; 37700aa6e65SRijo Thomas } 378632b0b53SRijo Thomas 379632b0b53SRijo Thomas memcpy(buf, &resp->buf[0], len); 380632b0b53SRijo Thomas *status = resp->status; 381632b0b53SRijo Thomas 38200aa6e65SRijo Thomas resp->flag = CMD_RESPONSE_COPIED; 38300aa6e65SRijo Thomas 384632b0b53SRijo Thomas return 0; 385632b0b53SRijo Thomas } 386632b0b53SRijo Thomas EXPORT_SYMBOL(psp_tee_process_cmd); 387bade7e1fSRijo Thomas 388bade7e1fSRijo Thomas int psp_check_tee_status(void) 389bade7e1fSRijo Thomas { 390bade7e1fSRijo Thomas struct psp_device *psp = psp_get_master_device(); 391bade7e1fSRijo Thomas 392bade7e1fSRijo Thomas if (!psp || !psp->tee_data) 393bade7e1fSRijo Thomas return -ENODEV; 394bade7e1fSRijo Thomas 395bade7e1fSRijo Thomas return 0; 396bade7e1fSRijo Thomas } 397bade7e1fSRijo Thomas EXPORT_SYMBOL(psp_check_tee_status); 398