diff --git a/include/guest_shm.h b/include/guest_shm.h new file mode 100644 index 00000000..51ec36d5 --- /dev/null +++ b/include/guest_shm.h @@ -0,0 +1,134 @@ +/* + * Copyright 2018, QNX Software Systems Limited (“QSS”). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Additional Patent Grant + * + * QSS hereby grants to you a perpetual, worldwide, non-exclusive, + * no-charge, irrevocable (except as stated in this section) patent + * license to make, have made, use, offer to sell, sell, import, + * transfer, and otherwise run, modify and propagate the contents of this + * header file (“Implementation”) , where such license applies + * only to those patent claims, both currently owned by QSS and + * acquired in the future, licensable by QSS that are necessarily + * infringed by this Implementation. This grant does + * not include claims that would be infringed only as a consequence of + * further modification of this Implementation. If you or your agent or + * exclusive licensee institute or order or agree to the institution of + * patent litigation against any entity (including a cross-claim or + * counterclaim in a lawsuit) alleging that this Implementation constitutes + * direct or contributory patent infringement, or inducement of patent + * infringement, then any patent rights granted to you under this license for + * this Implementation shall terminate as of the date such litigation is filed. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +/** + * @file + * definitions guest shared memory device + */ + +#ifndef _QVM_GUEST_SHM_H +#define _QVM_GUEST_SHM_H + +#include + +/* + * Temporary VID definition until the updated propogates around + */ +#define PCI_VID_BlackBerry_QNX 0x1C05 + +#define PCI_DID_QNX_GUEST_SHM 0x0001 + +/** status of last creation request */ +enum guest_shm_status { + GSS_OK, /**< creation succeeded */ + GSS_UNKNOWN_FAILURE, /**< creation failed for an unknown reason */ + GSS_NOMEM, /**< creation failed due to lack of memory */ + GSS_CLIENT_MAX, /**< creation failed due to region already being used by the maximum number of guests */ + GSS_ILLEGAL_NAME, /**< creation failed due to illegal region name */ + GSS_NO_PERMISSION, /**< creation failed due to lack of permission */ + GSS_DOES_NOT_EXIST, /**< A find request failed */ +}; + +/** Maximum number of clients allowed to connect to a shared memory region */ +#define GUEST_SHM_MAX_CLIENTS 16 +#define GUEST_INTR_STATUS_MASK ((1u << GUEST_SHM_MAX_CLIENTS) - 1u) + +/** Maximum length allowed for region name */ +#define GUEST_SHM_MAX_NAME 32 + +/** Signature value to verify that vdev is present */ +#define GUEST_SHM_SIGNATURE_L 0x474d5651 +#define GUEST_SHM_SIGNATURE_H 0x4d534732 +#define GUEST_SHM_SIGNATURE 0x4d534732474d5651 + + +/** Register layout for factory registers */ +struct guest_shm_factory { + UINT64 signature; /**< == GUEST_SHM_SIGNATURE (R/O) */ + UINT64 shmem; /**< shared memory paddr (R/O) */ + UINT32 vector; /**< interrupt vector number (R/O) */ + UINT32 status; /**< status of last creation (R/O) */ + UINT32 size; /**< requested size in 4K pages, write causes creation */ + CHAR8 name[GUEST_SHM_MAX_NAME]; /**< name of shared memory region */ + UINT32 find; /**< find an existing shared memory connection */ +} __packed; + +/** Register layout for a region control page */ +struct guest_shm_control { + UINT32 status; /**< lower 16 bits: pending notification bitset, upper 16 bits: current active clients (R/O) */ + UINT32 idx; /**< connection index for this client (R/O) */ + UINT32 notify; /**< write a bitset of clients to notify */ + UINT32 detach; /**< write here to detach from the shared memory region */ +}; + + +static inline void +guest_shm_create(volatile struct guest_shm_factory *const __factory, UINT32 const __size) { + /* Surround the size assignment with memory barriers so that + * the compiler doesn't try to shift the assignment before/after + * necessary bits (e.g. setting the name of the region) */ + asm volatile( "" ::: "memory"); + __factory->size = __size; + asm volatile( "" ::: "memory"); +} + + +static inline void +guest_shm_find(volatile struct guest_shm_factory *const __factory, UINT32 const __find_num) { + /* Surround the find assignment with memory barriers so that + * the compiler doesn't try to shift the assignment before/after + * necessary bits (e.g. setting the name of the region) */ + asm volatile( "" ::: "memory"); + __factory->find = __find_num; + asm volatile( "" ::: "memory"); +} + +#endif diff --git a/include/ivshmem.h b/include/ivshmem.h index ff65d03d..20878add 100644 --- a/include/ivshmem.h +++ b/include/ivshmem.h @@ -48,6 +48,17 @@ #define TEE_TPM2_SHOW_INDEX 0x0000000A #define TEE_TPM2_DELETE_INDEX 0x0000000B +struct optee_vm_ids { + uint32_t ree_id; + uint32_t tee_id; +} __packed; + +typedef enum { + EVENT_KERNEL = 1, + EVENT_ROT, + EVENT_ROLLBACK, +} event_src; + EFI_STATUS ivshmem_init(void); void ivshmem_rot_interrupt(void); @@ -60,5 +71,6 @@ struct tpm2_int_req { }; void ivshmem_rollback_index_interrupt(struct tpm2_int_req* req); +void ivshmem_detach(void); #endif /* _IVSHMEM_H_ */ diff --git a/include/lib.h b/include/lib.h index 13796afd..6c95260d 100644 --- a/include/lib.h +++ b/include/lib.h @@ -273,5 +273,6 @@ EFI_STATUS string_to_argv(char *str, INTN *argc, CHAR8 *argv[], UINTN max_argc, const char *first_delim, const char *delim); int is_running_on_kvm(void); +int is_running_on_qnx(void); #endif diff --git a/libkernelflinger/android.c b/libkernelflinger/android.c index f1673bc0..d3120727 100644 --- a/libkernelflinger/android.c +++ b/libkernelflinger/android.c @@ -37,6 +37,7 @@ #include "android.h" #include "efilinux.h" +#include "ivshmem.h" #include "lib.h" #include "security.h" #include "vars.h" @@ -437,6 +438,8 @@ static inline EFI_STATUS handover_jump(EFI_HANDLE image, log(L"handover jump ...\n"); + ivshmem_detach(); + ret = setup_gdt(); if (EFI_ERROR(ret)) { efi_perror(ret, L"Failed to setup GDT"); diff --git a/libkernelflinger/ivshmem.c b/libkernelflinger/ivshmem.c index d1204a4e..c50db638 100644 --- a/libkernelflinger/ivshmem.c +++ b/libkernelflinger/ivshmem.c @@ -31,6 +31,7 @@ */ #include "ivshmem.h" +#include "guest_shm.h" #define PCI_MAX_DEV_NUM 32 #define PCI_MAX_FUNC_NUM 8 @@ -171,11 +172,16 @@ struct ivshmem_device { UINT32 bar1_len; UINT32 bar2_addr; UINT32 bar2_len; + + volatile struct guest_shm_factory *fact; + volatile struct guest_shm_control *ctrl; }; static struct ivshmem_device g_ivshmem_dev; UINT64 g_ivshmem_rot_addr = 0; +volatile struct optee_vm_ids *smc_vm_ids = NULL; +volatile uint32_t *smc_evt_src = NULL; static UINT8 hw_read_port_8(UINT16 port) { @@ -361,6 +367,12 @@ static UINT32 pci_resource_len(UINT8 bus, UINT8 device, UINT8 function, static bool ivshmem_get_dev_func(void) { UINT8 device, function; + UINT32 expect; + + if(is_running_on_qnx()) + expect = PCI_VID_BlackBerry_QNX | (PCI_DID_QNX_GUEST_SHM << 16); + else + expect = IVSHMEM_VENDOR_ID | (IVSHMEM_DEVICE_ID << 16); /* * PCI devices reside in bus zero by default. @@ -372,7 +384,9 @@ static bool ivshmem_get_dev_func(void) for (device = 0; device < PCI_MAX_DEV_NUM; device++) { for (function = 0; function < PCI_MAX_FUNC_NUM; function++) { if (pci_read32(0, device, function, PCI_CONFIG_VENDOR_ID_OFFSET) == - (IVSHMEM_VENDOR_ID | (IVSHMEM_DEVICE_ID << 16))) { + expect) { + if(device != 6) + continue; g_ivshmem_dev.dev = device; g_ivshmem_dev.func = function; return true; @@ -397,34 +411,83 @@ EFI_STATUS ivshmem_init(void) dev = g_ivshmem_dev.dev; func = g_ivshmem_dev.func; - g_ivshmem_dev.revision = pci_read8(0, dev, func, PCI_CONFIG_REVISION_OFFSET); - info(L"IVSHMEM device: revision=0x%x", g_ivshmem_dev.revision); - - /* Enable BAR address MMIO support. */ - val16 = pci_read16(0, dev, func, PCI_CONFIG_COMMAND_OFFSET); - val16 |= 1 << CMD_MEM_SPACE_BIT_POSITION; - pci_write16(0, dev, func, PCI_CONFIG_COMMAND_OFFSET, val16); - - g_ivshmem_dev.bar0_addr = pci_resource_start(0, dev, func, PCI_CONFIG_BAR0_OFFSET); - g_ivshmem_dev.bar0_len = pci_resource_len(0, dev, func, PCI_CONFIG_BAR0_OFFSET); - info(L"IVSHMEM device: bar0 addr=0x%x, len=0x%x", - g_ivshmem_dev.bar0_addr, g_ivshmem_dev.bar0_len); - - g_ivshmem_dev.bar2_addr = pci_resource_start(0, dev, func, PCI_CONFIG_BAR2_OFFSET); - g_ivshmem_dev.bar2_len = pci_resource_len(0, dev, func, PCI_CONFIG_BAR2_OFFSET); - info(L"IVSHMEM device: bar2 addr=0x%x, len=0x%x", - g_ivshmem_dev.bar2_addr, g_ivshmem_dev.bar2_len); - if (g_ivshmem_dev.bar2_len < IVSHMEM_DEFAULT_SIZE) { - error(L"IVSHMEM device: bar2 size too small"); - return EFI_BUFFER_TOO_SMALL; - } + if(is_running_on_qnx()) { + g_ivshmem_dev.bar0_addr = pci_resource_start(0, dev, func, PCI_CONFIG_BAR0_OFFSET); + g_ivshmem_dev.bar0_len = pci_resource_len(0, dev, func, PCI_CONFIG_BAR0_OFFSET); + info(L"IVSHMEM device: bar0 addr=0x%x, len=0x%x", + g_ivshmem_dev.bar0_addr, g_ivshmem_dev.bar0_len); + + g_ivshmem_dev.fact = (struct guest_shm_factory *)g_ivshmem_dev.bar0_addr; + if ((g_ivshmem_dev.fact->signature & 0xFFFFFFFF) != GUEST_SHM_SIGNATURE_L + || (g_ivshmem_dev.fact->signature >> 32) != GUEST_SHM_SIGNATURE_H) { + error(L"IVSHMEM device: Invalid ivshmem device"); + return EFI_NOT_FOUND; + } + + info(L"IVSHMEM device: valid device signature"); - g_ivshmem_rot_addr = g_ivshmem_dev.bar2_addr + IVSHMEM_ROT_OFFSET; - info(L"IVSHMEM device: rot_addr=0x%lx", g_ivshmem_rot_addr); + strcpy_s((CHAR8 *)g_ivshmem_dev.fact->name, GUEST_SHM_MAX_NAME, "tee_shmem"); + guest_shm_create(g_ivshmem_dev.fact, 0x500); - if (g_ivshmem_dev.revision == 1) { - info(L"IVSHMEM device: ivposition=%d", - io_read_32((void *)((UINT64)(g_ivshmem_dev.bar0_addr + IVPOSITION_OFF)))); + if (g_ivshmem_dev.fact->status != GSS_OK) { + error(L"IVSHMEM device: invalid device status"); + return EFI_DEVICE_ERROR; + } + + info(L"IVSHMEM device: valid device status"); + + g_ivshmem_dev.ctrl = (struct guest_shm_control *)g_ivshmem_dev.fact->shmem; + info(L"ivshmem region ctrl status is 0x%x", g_ivshmem_dev.ctrl->status); + + info(L"IVSHMEM device: shmem addr=0x%x, len=0x%x", + g_ivshmem_dev.fact->shmem + 0x1000, g_ivshmem_dev.fact->size); + + if (g_ivshmem_dev.fact->size * 0x1000 < IVSHMEM_DEFAULT_SIZE) { + error(L"IVSHMEM device: bar2 size too small"); + return EFI_BUFFER_TOO_SMALL; + } + info(L"IVSHMEM device: shmem len=0x%x", g_ivshmem_dev.fact->size); + + smc_evt_src = (uint32_t *)(g_ivshmem_dev.fact->shmem + 0x1000); + smc_vm_ids = (struct optee_vm_ids *)(g_ivshmem_dev.fact->shmem + 0x1000 + + sizeof(uint32_t)); + + smc_vm_ids->ree_id = g_ivshmem_dev.ctrl->idx; + info(L"IVSHMEM device: tee_id:%d ree_id:%d", smc_vm_ids->tee_id, smc_vm_ids->ree_id); + + g_ivshmem_rot_addr = g_ivshmem_dev.fact->shmem + 0x1000 + IVSHMEM_ROT_OFFSET; + info(L"IVSHMEM device: rot_addr=0x%lx", g_ivshmem_rot_addr); + + } else { + g_ivshmem_dev.revision = pci_read8(0, dev, func, PCI_CONFIG_REVISION_OFFSET); + info(L"IVSHMEM device: revision=0x%x", g_ivshmem_dev.revision); + + /* Enable BAR address MMIO support. */ + val16 = pci_read16(0, dev, func, PCI_CONFIG_COMMAND_OFFSET); + val16 |= 1 << CMD_MEM_SPACE_BIT_POSITION; + pci_write16(0, dev, func, PCI_CONFIG_COMMAND_OFFSET, val16); + + g_ivshmem_dev.bar0_addr = pci_resource_start(0, dev, func, PCI_CONFIG_BAR0_OFFSET); + g_ivshmem_dev.bar0_len = pci_resource_len(0, dev, func, PCI_CONFIG_BAR0_OFFSET); + info(L"IVSHMEM device: bar0 addr=0x%x, len=0x%x", + g_ivshmem_dev.bar0_addr, g_ivshmem_dev.bar0_len); + + g_ivshmem_dev.bar2_addr = pci_resource_start(0, dev, func, PCI_CONFIG_BAR2_OFFSET); + g_ivshmem_dev.bar2_len = pci_resource_len(0, dev, func, PCI_CONFIG_BAR2_OFFSET); + info(L"IVSHMEM device: bar2 addr=0x%x, len=0x%x", + g_ivshmem_dev.bar2_addr, g_ivshmem_dev.bar2_len); + if (g_ivshmem_dev.bar2_len < IVSHMEM_DEFAULT_SIZE) { + error(L"IVSHMEM device: bar2 size too small"); + return EFI_BUFFER_TOO_SMALL; + } + + g_ivshmem_rot_addr = g_ivshmem_dev.bar2_addr + IVSHMEM_ROT_OFFSET; + info(L"IVSHMEM device: rot_addr=0x%lx", g_ivshmem_rot_addr); + + if (g_ivshmem_dev.revision == 1) { + info(L"IVSHMEM device: ivposition=%d", + io_read_32((void *)((UINT64)(g_ivshmem_dev.bar0_addr + IVPOSITION_OFF)))); + } } return EFI_SUCCESS; @@ -432,8 +495,12 @@ EFI_STATUS ivshmem_init(void) void ivshmem_rot_interrupt(void) { - io_write_32((void *)((UINT64)(g_ivshmem_dev.bar0_addr + DOORBELL_OFF)), - ROT_INTERRUPT); + if(is_running_on_qnx()) { + *smc_evt_src = EVENT_ROT; + g_ivshmem_dev.ctrl->notify = 1 << smc_vm_ids->tee_id; + } else + io_write_32((void *)((UINT64)(g_ivshmem_dev.bar0_addr + DOORBELL_OFF)), + ROT_INTERRUPT); } #define NOT_READY_MAGIC 0x12ABCDEF @@ -460,7 +527,12 @@ void ivshmem_rollback_index_interrupt(struct tpm2_int_req* req) } memcpy(p_req, req, req_size); - io_write_32((void *)((UINT64)(g_ivshmem_dev.bar0_addr + DOORBELL_OFF)), ROLLBACK_INDEX_INTERRUPT); + if(is_running_on_qnx()) { + *smc_evt_src = EVENT_ROLLBACK; + g_ivshmem_dev.ctrl->notify = 1 << smc_vm_ids->tee_id; + } else + io_write_32((void *)((UINT64)(g_ivshmem_dev.bar0_addr + DOORBELL_OFF)), + ROLLBACK_INDEX_INTERRUPT); while (NOT_READY_MAGIC == p_req->ret) { //just wait for int handler return @@ -471,3 +543,7 @@ void ivshmem_rollback_index_interrupt(struct tpm2_int_req* req) return; } +void ivshmem_detach(void) { + if(is_running_on_qnx()) + g_ivshmem_dev.ctrl->detach = 1 << smc_vm_ids->ree_id; +} diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 6c3e6fcb..94f3e54c 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -1730,5 +1730,16 @@ int is_running_on_kvm(void) return 0; } +int is_running_on_qnx(void) +{ + UINT32 reg[4]; + + cpuid(0x40000000, reg); + if (reg[0] == 0x40000002 && reg[1] == 0x51584e51 && reg[2] == 0x53424d56 && reg[3] == 0x4751) + return 1; + + return 0; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */