/*
* Copyright ( c ) 2022 , Google , Inc . All rights reserved
*
* Permission is hereby granted , free of charge , to any person obtaining
* a copy of this software and associated documentation files
* ( the " Software " ) , to deal in the Software without restriction ,
* including without limitation the rights to use , copy , modify , merge ,
* publish , distribute , sublicense , and / or sell copies of the Software ,
* and to permit persons to whom the Software is furnished to do so ,
* subject to the following conditions :
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT ,
* TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
/*
* This trusty library module contains the SMC API for communication with
* the Linux trusty - driver for shared memory registration / unregistration .
* After a register request ( SMC - Call ) is received from the trusty - driver ,
* access to the shared - memory block , created by the trusty - driver , is
* established . Likewise , when an unregister request is received , the
* associated resources are release and the access is removed .
*
* This library module also provides the APIs to the trusty kernel for
* exchanging various information in both directions . One such information
* exchanged is the per - cpu trusty shadow - priorities .
*/
#include <err.h>
#include <kernel/spinlock.h>
#include <kernel/thread.h>
#include <kernel/vm.h>
#include <lib/extmem/extmem.h>
#include <lib/sm.h>
#include <lib/sm/sm_err.h>
#include <lib/sm/trusty_sched_share.h>
#include <platform.h>
#include <string.h>
#include <trace.h>
#define LOCAL_TRACE (0 )
/* Trusty Shared Resources Info */
struct share_info {
ext_mem_client_id_t client_id;
ext_mem_obj_id_t buf_id;
uint32_t cpu_count;
uint32_t header_size;
uint32_t percpu_data_size;
};
static struct share_info shareinfo = {0 };
struct trusty_sched_shared_mem* sched_shared_mem = NULL;
static spin_lock_t sched_shared_datalock = SPIN_LOCK_INITIAL_VALUE;
static ext_mem_obj_id_t args_get_id(struct smc32_args* args) {
return (((uint64_t)args->params[1 ] << 32 ) | args->params[0 ]);
}
static size_t args_get_sz(struct smc32_args* args) {
return (size_t)args->params[2 ];
}
static long trusty_share_register(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id,
uint32_t buf_size) {
struct trusty_sched_shared_mem* share_ptr;
spin_lock_saved_state_t state;
uint32_t needed_buf_sz;
uint32_t nr_cpu;
uint32_t hdr_sz;
uint32_t percpu_data_sz;
uint32_t struct_sz;
void * va;
status_t status;
int retval = SM_ERR_INVALID_PARAMETERS;
LTRACEF_LEVEL(5 , "client_id=%llx, buf_id= %llx, buf_size=%d\n" ,
(unsigned long long )client_id, (unsigned long long )buf_id,
buf_size);
status = ext_mem_map_obj_id(vmm_get_kernel_aspace(),
"trusty_sched_shared_mem" , client_id, buf_id, 0 ,
0 , buf_size, &va, PAGE_SIZE_SHIFT, 0 ,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
if (status) {
TRACEF("Error: ext_mem_map_obj_id() failed.\n" );
return SM_ERR_INTERNAL_FAILURE;
}
share_ptr = va;
nr_cpu = READ_ONCE(share_ptr->cpu_count);
hdr_sz = READ_ONCE(share_ptr->hdr_size);
percpu_data_sz = READ_ONCE(share_ptr->percpu_data_size);
struct_sz = sizeof (struct trusty_sched_shared_mem);
if (hdr_sz < struct_sz) {
TRACEF("Error: mismatched header-size=%d, struct-size=%d\n" , hdr_sz,
struct_sz);
goto err_invalid_params;
}
LTRACEF_LEVEL(45 , "header-size=%d, struct-size=%d\n" , hdr_sz, struct_sz);
struct_sz = sizeof (struct trusty_percpu_shared_data);
if (percpu_data_sz < struct_sz) {
TRACEF("Error: mismatched percpu-data-size=%d, struct-size=%d\n" ,
percpu_data_sz, struct_sz);
goto err_invalid_params;
}
LTRACEF_LEVEL(45 , "percpu-data-size=%d, struct-size=%d\n" , percpu_data_sz,
struct_sz);
if (__builtin_mul_overflow(nr_cpu, percpu_data_sz, &needed_buf_sz)) {
TRACEF("Error: multiply overflow while computing (nr_cpu * percpu_data_sz).\n" );
goto err_invalid_params;
}
if (__builtin_add_overflow(needed_buf_sz, hdr_sz, &needed_buf_sz)) {
TRACEF("Error: Add overflow while adding header_size.\n" );
goto err_invalid_params;
}
if (needed_buf_sz > buf_size) {
TRACEF("Error: Buffer size is not adequate.\n" );
goto err_invalid_params;
}
spin_lock_irqsave(&sched_shared_datalock, state);
if (sched_shared_mem) {
spin_unlock_irqrestore(&sched_shared_datalock, state);
TRACEF("Error: Shared-Memory is already present from a previous call.\n" );
goto err_shared_mem_busy;
}
shareinfo.cpu_count = nr_cpu;
shareinfo.header_size = hdr_sz;
shareinfo.percpu_data_size = percpu_data_sz;
shareinfo.client_id = client_id;
shareinfo.buf_id = buf_id;
sched_shared_mem = share_ptr;
spin_unlock_irqrestore(&sched_shared_datalock, state);
return 0 ;
err_shared_mem_busy:
err_invalid_params:
status = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)share_ptr);
if (status) {
TRACEF("Error: failed to free the allocated virtual-memory.\n" );
retval = SM_ERR_INTERNAL_FAILURE;
}
return retval;
}
static long trusty_share_unregister(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id) {
struct trusty_sched_shared_mem* share_ptr;
spin_lock_saved_state_t state;
status_t status;
int retval = SM_ERR_INVALID_PARAMETERS;
spin_lock_irqsave(&sched_shared_datalock, state);
share_ptr = sched_shared_mem;
if (!share_ptr) {
spin_unlock_irqrestore(&sched_shared_datalock, state);
TRACEF("Error: Trusty ShareInfo not setup by register call.\n" );
return retval;
}
if ((client_id != shareinfo.client_id) || (buf_id != shareinfo.buf_id)) {
spin_unlock_irqrestore(&sched_shared_datalock, state);
TRACEF("Error: invalid arguments.\n" );
return retval;
}
sched_shared_mem = NULL;
memset(&shareinfo, 0 , sizeof (struct share_info));
spin_unlock_irqrestore(&sched_shared_datalock, state);
retval = 0 ;
status = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)share_ptr);
if (status) {
TRACEF("Error: failed to free the allocated virtual-memory.\n" );
retval = SM_ERR_INTERNAL_FAILURE;
}
return retval;
}
long smc_trusty_sched_share_register(struct smc32_args* args) {
ext_mem_client_id_t client_id = args->client_id;
ext_mem_obj_id_t buf_id = args_get_id(args);
uint32_t buf_size = args_get_sz(args);
if (!IS_PAGE_ALIGNED(buf_size)) {
TRACEF("Error: argument buffer-size is not page-aligned.\n" );
return SM_ERR_INVALID_PARAMETERS;
}
return trusty_share_register(client_id, buf_id, buf_size);
}
long smc_trusty_sched_share_unregister(struct smc32_args* args) {
ext_mem_client_id_t client_id = args->client_id;
ext_mem_obj_id_t buf_id = args_get_id(args);
return trusty_share_unregister(client_id, buf_id);
}
static struct trusty_percpu_shared_data* get_percpu_share_ptr(uint32_t cpu_nr) {
struct trusty_percpu_shared_data* percpu_data_ptr;
unsigned char * tmp;
DEBUG_ASSERT(cpu_nr < shareinfo.cpu_count);
tmp = (unsigned char *)sched_shared_mem;
tmp += shareinfo.header_size;
tmp += cpu_nr * shareinfo.percpu_data_size;
percpu_data_ptr = (struct trusty_percpu_shared_data*)tmp;
return percpu_data_ptr;
}
static uint32_t cpu_priority_to_shadow(uint32_t current_priority,
uint32_t priority) {
/* Ignore the set request by irq-ns-switch-* threads, which exclusively
* run at the HIGHEST_PRIORITY . The problem is that the irq - ns - switch - *
* threads run on behalf of linux ( or any other normal world client os )
* and are always the threads that return to linux ( client os ) while
* trusty is busy , but those are not the threads whose priority , the
* linux side wants to know .
*
* The only exception is TRUSTY_SHADOW_PRIORITY_IDLE .
* We use that to signal to linux that we ' re done , but we want
* the opposite if we got here from the irq threads .
* It means an idle thread got interrupted ; we would like to actually
* return the priority of the next ready thread , but we do not have
* that so we return HIGH just in case the next thread could
* want that priority .
*/
if (priority >= HIGHEST_PRIORITY &&
current_priority != TRUSTY_SHADOW_PRIORITY_IDLE) {
return current_priority;
}
if (priority >= HIGH_PRIORITY) {
return TRUSTY_SHADOW_PRIORITY_HIGH;
} else if (priority <= LOWEST_PRIORITY + 1 ) {
return TRUSTY_SHADOW_PRIORITY_IDLE;
} else if (priority <= LOW_PRIORITY) {
return TRUSTY_SHADOW_PRIORITY_LOW;
} else {
return TRUSTY_SHADOW_PRIORITY_NORMAL;
}
}
/*
* Following function is called from trusty kernel thread . c
*/
void platform_cpu_priority_set(uint32_t cpu_nr, uint32_t priority) {
spin_lock_saved_state_t state;
struct trusty_percpu_shared_data* percpu_data_ptr;
/*
* if the shared - memory is established and the reuesting
* cpu_nr is less than the max number of CPUs supported by
* the Linux side , proceed with the value change .
*/
spin_lock_irqsave(&sched_shared_datalock, state);
if ((sched_shared_mem) && (cpu_nr < shareinfo.cpu_count)) {
percpu_data_ptr = get_percpu_share_ptr(cpu_nr);
percpu_data_ptr->ask_shadow_priority = cpu_priority_to_shadow(
percpu_data_ptr->ask_shadow_priority, priority);
}
spin_unlock_irqrestore(&sched_shared_datalock, state);
}
Messung V0.5 in Prozent C=95 H=95 G=94
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-27)
¤
*© Formatika GbR, Deutschland