uint32_t version = FFA_VERSION(major, minor); /* Bit 31 must be cleared. */
ASSERT(!(version >> 31));
smc_ret = arm_smccc_invoke8(SMC_FC_FFA_VERSION, version, 0, 0, 0, 0, 0, 0); if ((int32_t)smc_ret.r0 == FFA_ERROR_NOT_SUPPORTED) { return ERR_NOT_SUPPORTED;
}
*major_ret = FFA_VERSION_TO_MAJOR(smc_ret.r0);
*minor_ret = FFA_VERSION_TO_MINOR(smc_ret.r0);
return NO_ERROR;
}
/* TODO: When adding support for FFA version 1.1 feature ids should be added. */ static status_t arm_ffa_call_features(ulong id, bool* is_implemented,
ffa_features2_t* features2,
ffa_features3_t* features3) { struct smc_ret8 smc_ret;
/* Copy and flip the sender from the direct message request */
sender_receiver_id = ((uint32_t)direct_req_regs->r1 >> 16) |
((uint32_t)ffa_local_id << 16); /* Copy the flags as well */
flags = direct_req_regs->r2;
switch ((uint32_t)direct_req_regs->r0) { case SMC_FC_FFA_MSG_SEND_DIRECT_REQ: return arm_smccc_invoke8_ret18(SMC_FC_FFA_MSG_SEND_DIRECT_RESP,
sender_receiver_id, flags, a0, a1, a2,
a3, a4); case SMC_FC64_FFA_MSG_SEND_DIRECT_REQ: return arm_smccc_invoke8_ret18(SMC_FC64_FFA_MSG_SEND_DIRECT_RESP,
sender_receiver_id, flags, a0, a1, a2,
a3, a4); default:
dprintf(CRITICAL, "Invalid direct request function id %lx\n",
direct_req_regs->r0); return arm_ffa_call_error(FFA_ERROR_INVALID_PARAMETERS);
}
DEBUG_ASSERT(direct_req_regs);
DEBUG_ASSERT(args); if ((uint32_t)direct_req_regs->r0 != SMC_FC64_FFA_MSG_SEND_DIRECT_REQ2) {
dprintf(CRITICAL, "Invalid direct request function id %x\n",
(uint32_t)direct_req_regs->r0); return arm_ffa_call_error(FFA_ERROR_INVALID_PARAMETERS);
}
/* Copy and flip the sender from the direct message request */
sender_receiver_id =
(direct_req_regs->r1 >> 16) | ((uint32_t)ffa_local_id << 16);
switch ((uint32_t)smc_ret.r0) { case SMC_FC64_FFA_MSG_SEND_DIRECT_RESP2:
*resp = smc_ret; return NO_ERROR;
case SMC_FC_FFA_ERROR: switch ((int32_t)smc_ret.r2) { case FFA_ERROR_NOT_SUPPORTED:
send_direct_req2_is_unsupported = true; return ERR_NOT_SUPPORTED; case FFA_ERROR_INVALID_PARAMETERS:
dprintf(CRITICAL, "Invalid parameters for direct request2\n"); return ERR_INVALID_ARGS; case FFA_ERROR_BUSY:
LTRACEF("FFA_MSG_SEND_DIRECT_REQ2 returned FFA_ERROR_BUSY\n"); return ERR_BUSY; default: return ERR_NOT_VALID;
}
case SMC_UNKNOWN:
send_direct_req2_is_unsupported = true; return ERR_NOT_SUPPORTED;
case SMC_FC_FFA_INTERRUPT: /* *SMC_FC_FFA_INTERRUPTorSMC_FC_FFA_YIELDcanbereturnedpertheFF-A *specbutitshouldn'thappenwhenTrustyisthereceiverofrequests.
*/
panic("Received SMC_FC_FFA_INTERRUPT in response to direct request2");
case SMC_FC_FFA_YIELD: /* See previous case */
panic("Received SMC_FC_FFA_YIELD in response to direct request2");
default:
dprintf(CRITICAL, "Unexpected response (%x) to direct request2\n",
(uint32_t)smc_ret.r0); return ERR_NOT_VALID;
}
}
status_t arm_ffa_msg_send_direct_req2(
uuid_t uuid,
uint16_t receiver_id, const uint64_t args[static ARM_FFA_MSG_EXTENDED_ARGS_COUNT], struct smc_ret18* resp) {
status_t res;
lk_time_ns_t t0; /* Use an initial 1 ms delay */
lk_time_t delay = 1;
res = arm_ffa_msg_send_direct_req2_oneshot(uuid, receiver_id, args, resp); while (res == ERR_BUSY) { /* Record the current time before the smc */
t0 = current_time_ns();
res = arm_ffa_msg_send_direct_req2_oneshot(uuid, receiver_id, args,
resp); if (res != ERR_BUSY) { return res;
} if (delay >= 1000) {
TRACEF("Retrying FFA_MSG_SEND_DIRECT_REQ2 with delay longer than 1 second\n");
}
thread_sleep_until_ns(t0 + (delay * 1000ULL * 1000)); if (delay < 60000) { /* double the delay for the next retry */
delay *= 2;
}
} return res;
} #endif
if (console_log_is_unsupported) { return ERR_NOT_SUPPORTED;
} if (!len) { /* Nothing to print, just return */ return0;
} if (len != 1) { /* TODO: support more than one character */
len = 1;
}
smc_ret = arm_smccc_invoke8(SMC_FC_FFA_CONSOLE_LOG, len, (ulong)buf[0], 0, 0, 0, 0, 0); switch ((uint32_t)smc_ret.r0) { case SMC_FC_FFA_SUCCESS: case SMC_FC64_FFA_SUCCESS: return len;
case SMC_FC_FFA_ERROR: switch ((int32_t)smc_ret.r2) { case FFA_ERROR_NOT_SUPPORTED:
console_log_is_unsupported = true; return ERR_NOT_SUPPORTED; case FFA_ERROR_INVALID_PARAMETERS: return ERR_INVALID_ARGS; case FFA_ERROR_RETRY: /* FFA_ERROR_RETRY returns how many characters were printed */ return (uint32_t)smc_ret.r3; default: return ERR_NOT_VALID;
}
case SMC_UNKNOWN:
console_log_is_unsupported = true; return ERR_NOT_SUPPORTED;
default: return ERR_NOT_VALID;
}
}
static status_t arm_ffa_rx_release_is_implemented(bool* is_implemented) { bool is_implemented_val;
status_t res = arm_ffa_call_features(SMC_FC_FFA_RX_RELEASE,
&is_implemented_val, NULL, NULL); if (res != NO_ERROR) {
TRACEF("Failed to query for feature FFA_RX_RELEASE, err = %d\n", res); return res;
} if (is_implemented) {
*is_implemented = is_implemented_val;
} return NO_ERROR;
}
switch (arch_mmu_flags & ARCH_MMU_FLAG_CACHE_MASK) { case ARCH_MMU_FLAG_UNCACHED_DEVICE:
attributes |= FFA_MEM_ATTR_DEVICE_NGNRE; break; case ARCH_MMU_FLAG_UNCACHED:
attributes |= FFA_MEM_ATTR_NORMAL_MEMORY_UNCACHED; break; case ARCH_MMU_FLAG_CACHED:
attributes |= FFA_MEM_ATTR_NORMAL_MEMORY_CACHED_WB |
FFA_MEM_ATTR_INNER_SHAREABLE; break;
}
req->memory_region_attributes = attributes;
req->flags = 0; /* We must use the same tag as the one used by the receiver to share . */
req->tag = tag; /* MBZ for MEM_SHARE */
req->handle = 0;
/* *desc_buffer is malloc'd and on success passes responsibility to free to
the caller. Populate the tx buffer before calling. */ static status_t arm_ffa_mem_retrieve(uint16_t sender_id,
uint64_t handle,
uint32_t* len,
uint32_t* fragment_len) {
status_t res = NO_ERROR;
uint32_t len_out, fragment_len_out;
res = arm_ffa_call_mem_retrieve_req(&len_out, &fragment_len_out);
LTRACEF("total_len: %u, fragment_len: %u\n", len_out, fragment_len_out); if (res != NO_ERROR) {
TRACEF("FF-A memory retrieve request failed, err = %d\n", res); return res;
} if (fragment_len_out > len_out) {
TRACEF("Fragment length larger than total length %u > %u\n",
fragment_len_out, len_out); return ERR_IO;
}
/* Check that the first fragment fits in our buffer */ if (fragment_len_out > ffa_buf_size) {
TRACEF("Fragment length %u larger than buffer size\n",
fragment_len_out); return ERR_IO;
}
if (fragment_len) {
*fragment_len = fragment_len_out;
} if (len) {
*len = len_out;
}
mtd = ffa_rx; if (ffa_version < FFA_VERSION(1, 1)) { if (fragment_len < sizeof(struct ffa_mtd_v1_0)) {
TRACEF("Fragment too short for memory transaction descriptor\n"); return ERR_IO;
}
mtd_v1_0 = ffa_rx; if (fragment_len <
offsetof(struct ffa_mtd_v1_0, emad) + sizeof(struct ffa_emad_v1_0)) {
TRACEF("Fragment too short for endpoint memory access descriptor\n"); return ERR_IO;
}
emad = &mtd_v1_0->emad[0].common;
/* *Wedon'tretrievethememoryonbehalfofanyoneelse,soweonly *expectonereceiveraddressrangedescriptor.
*/ if (mtd_v1_0->emad_count != 1) {
TRACEF("unexpected response count %d != 1\n", mtd_v1_0->emad_count); return ERR_IO;
}
} else { if (fragment_len < sizeof(struct ffa_mtd_v1_1)) {
TRACEF("Fragment too short for memory transaction descriptor\n"); return ERR_IO;
}
mtd_v1_1 = ffa_rx; if (mtd_v1_1->emad_size < sizeof(struct ffa_emad_v1_0)) {
TRACEF("Endpoint memory access descriptor too short\n"); return ERR_IO;
} if (fragment_len < mtd_v1_1->emad_offset) {
TRACEF("Fragment too short for endpoint memory access descriptor\n"); return ERR_IO;
} if (fragment_len - mtd_v1_1->emad_offset < mtd_v1_1->emad_size) {
TRACEF("Fragment too short for endpoint memory access descriptor\n"); return ERR_IO;
} if (mtd_v1_1->emad_offset < sizeof(struct ffa_mtd_v1_1)) {
TRACEF("Endpoint memory access descriptor offset too short\n"); return ERR_IO;
} if (!IS_ALIGNED(mtd_v1_1->emad_offset, 16)) {
TRACEF("Endpoint memory access descriptor not aligned to 16 bytes\n"); return ERR_IO;
}
emad = (struct ffa_emad_common*)((uint8_t*)mtd_v1_1 + mtd_v1_1->emad_offset);
if (!supports_ns_bit ||
(mtd->memory_region_attributes & FFA_MEM_ATTR_NONSECURE)) {
arch_mmu_flags_out |= ARCH_MMU_FLAG_NS; /* Regardless of origin, we don't want to execute out of NS memory. */
arch_mmu_flags_out |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
}
if (arch_mmu_flags) {
*arch_mmu_flags = arch_mmu_flags_out;
} if (address_range_count) {
*address_range_count = address_range_count_out;
}
return res;
}
/* This assumes that the fragment is completely composed of memory
region descriptors (struct ffa_cons_mrd) */
status_t arm_ffa_mem_retrieve_next_frag(
uint64_t handle, struct arm_ffa_mem_frag_info* frag_info) {
status_t res;
uint32_t fragment_len;
mutex_acquire(&ffa_rxtx_buffer_lock);
res = arm_ffa_call_mem_frag_rx(handle, frag_info->received_len,
&fragment_len);
if (res != NO_ERROR) {
TRACEF("Failed to get memory retrieve fragment, err = %d\n", res); return res;
}
if (buffer % FFA_PAGE_SIZE) {
LTRACEF("Buffer address must be page-aligned\n"); return ERR_INVALID_ARGS;
} if (!(arch_mmu_flags & ARCH_MMU_FLAG_PERM_NO_EXECUTE)) {
LTRACEF("Only non-executable buffers may be shared over FFA\n"); return ERR_INVALID_ARGS;
}
mutex_acquire(&ffa_rxtx_buffer_lock);
/* Populate the tx buffer with 1 composite mrd and 1 constituent mrd */
arm_ffa_populate_share_tx_buffer(receiver_id, buffer, num_ffa_pages,
arch_mmu_flags, 0);
res = arm_ffa_call_mem_share(1, 1, &len_out, &fragment_len_out, handle);
LTRACEF("total_len: %u, fragment_len: %u, handle: %" PRIx64 "\n", len_out,
fragment_len_out, *handle); if (res != NO_ERROR) {
TRACEF("FF-A memory share failed, err= %d\n", res);
}
/* As flags are set to 0 no request to zero the memory is made */
res = arm_ffa_call_mem_relinquish(handle, 0, 1, &ffa_local_id); if (res != NO_ERROR) {
TRACEF("Failed to relinquish memory region, err = %d\n", res);
}
res = arm_ffa_call_version(FFA_CURRENT_VERSION_MAJOR,
FFA_CURRENT_VERSION_MINOR, &ver_major_ret,
&ver_minor_ret); if (res != NO_ERROR) {
TRACEF("No compatible FF-A version found\n"); return res;
} elseif (FFA_CURRENT_VERSION_MAJOR != ver_major_ret) { /* Allow downgrade within the same major version */
TRACEF("Incompatible FF-A interface version, %" PRIu16 ".%" PRIu16 "\n",
ver_major_ret, ver_minor_ret); return ERR_NOT_SUPPORTED;
}
ffa_version = FFA_VERSION(ver_major_ret, ver_minor_ret); if (ffa_version > FFA_CURRENT_VERSION) { /* The SPMC supports a newer version, downgrade us */
ffa_version = FFA_CURRENT_VERSION;
}
LTRACEF("Negotiated FF-A version %" PRIu16 ".%" PRIu16 "\n",
FFA_VERSION_TO_MAJOR(ffa_version),
FFA_VERSION_TO_MINOR(ffa_version));
res = arm_ffa_call_id_get(&ffa_local_id); if (res != NO_ERROR) {
TRACEF("Failed to get FF-A partition id (err=%d)\n", res); return res;
}
res = arm_ffa_rx_release_is_implemented(&is_implemented); if (res != NO_ERROR) {
TRACEF("Error checking if FFA_RX_RELEASE is implemented (err=%d)\n",
res); return res;
} if (is_implemented) {
supports_rx_release = true;
} else {
LTRACEF("FFA_RX_RELEASE is not implemented\n");
}
res = arm_ffa_rxtx_map_is_implemented(&is_implemented, &buf_size_log2); if (res != NO_ERROR) {
TRACEF("Error checking if FFA_RXTX_MAP is implemented (err=%d)\n", res); return res;
} if (!is_implemented) {
TRACEF("FFA_RXTX_MAP is not implemented\n"); return ERR_NOT_SUPPORTED;
}
res = arm_ffa_mem_retrieve_req_is_implemented(
&is_implemented, NULL, &supports_ns_bit, &ref_count_num_bits); if (res != NO_ERROR) {
TRACEF("Error checking if FFA_MEM_RETRIEVE_REQ is implemented (err=%d)\n",
res); return res;
} if (!is_implemented) {
TRACEF("FFA_MEM_RETRIEVE_REQ is not implemented\n");
} elseif (ref_count_num_bits < 64) { /* *Expect64bitreferencecount.Ifwedon'thaveit,futurecallsto *SMC_FC_FFA_MEM_RETRIEVE_REQcanfailifwereceivethesamehandle *multipletimes.Warnaboutthis,butdon'treturnanerrorasweonly *receiveeachhandleonceinthetypicalcase.
*/
TRACEF("Warning FFA_MEM_RETRIEVE_REQ does not have 64 bit reference count (%zu)\n",
ref_count_num_bits);
}
DEBUG_ASSERT(count_out); /* FF-A adds a flag to request count only so the rx buffer isn't acquired */
DEBUG_ASSERT((flags & FFA_PARTITION_INFO_GET_FLAG_RETURN_COUNT_ONLY) ||
is_mutex_held(&ffa_rxtx_buffer_lock));
uuid_to_le64_pair(uuid_obj, uuid);
smc_ret = arm_smccc_invoke18(SMC_FC_FFA_PARTITION_INFO_GET,
uuid[0] & 0xFFFFFFFFU, uuid[0] >> 32,
uuid[1] & 0xFFFFFFFFU, uuid[1] >> 32, flags, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); switch ((uint32_t)smc_ret.r0) { case SMC_FC_FFA_SUCCESS: /* FF-A 1.0 does not include descriptor size in the response */ if (num_desc > 0 && ffa_version > FFA_VERSION(1, 0)) { /* If Bit[0] in flags is 0, r3 holds the size of each descriptor */ if ((uint32_t)smc_ret.r3 != sizeof(struct ffa_part_info_desc)) {
panic("Expected descriptor size (%zu) != size in response (%u)\n", sizeof(struct ffa_part_info_desc), (uint32_t)smc_ret.r3);
}
}
*count_out = (uint32_t)smc_ret.r2; return NO_ERROR; case SMC_FC_FFA_ERROR:
error = (int32_t)smc_ret.r2; switch (error) { case FFA_ERROR_BUSY: return ERR_BUSY; case FFA_ERROR_INVALID_PARAMETERS: return ERR_INVALID_ARGS; case FFA_ERROR_NO_MEMORY: return ERR_NO_MEMORY; case FFA_ERROR_DENIED: return ERR_BAD_STATE; case FFA_ERROR_NOT_SUPPORTED: return ERR_NOT_SUPPORTED; case FFA_ERROR_NOT_READY: return ERR_NOT_READY; default:
TRACEF("Unexpected FFA_ERROR: %x\n", error); return ERR_NOT_VALID;
} default: return ERR_NOT_VALID;
}
}
status_t arm_ffa_partition_info_get_count(uuid_t uuid_obj, size_t* count_out) {
status_t res;
size_t num_desc = 0;
uint32_t flags = 0; if (ffa_version == FFA_VERSION(1, 0)) { /* FF-A version 1.0 acquires the rx buffer */
mutex_acquire(&ffa_rxtx_buffer_lock);
} else { /* FF-A version 1.1 and later does not need rx buffer to get count */
flags |= FFA_PARTITION_INFO_GET_FLAG_RETURN_COUNT_ONLY;
}
res = arm_ffa_call_partition_info_get(uuid_obj, num_desc, flags, count_out); if (res != NO_ERROR) {
TRACEF("Call to PARTITION_INFO_GET failed, err = %d\n", res);
}
if (ffa_version == FFA_VERSION(1, 0)) { if (res != NO_ERROR) {
mutex_release(&ffa_rxtx_buffer_lock); return res;
} /* This also releases the rxtx buffer lock */
arm_ffa_rx_release();
}
if (!count_out || !desc) { return ERR_INVALID_ARGS;
}
mutex_acquire(&ffa_rxtx_buffer_lock);
/* Clear the entire rx buffer */
memset(ffa_rx, 0, ffa_buf_size);
res = arm_ffa_call_partition_info_get(uuid_obj, num_desc, flags, &count); /* On failure, only release rxtx lock; release buffer and lock otherwise */ if (res != NO_ERROR) {
TRACEF("Call to PARTITION_INFO_GET failed, err = %d\n", res);
mutex_release(&ffa_rxtx_buffer_lock); return res;
}
/* Do we have enough space to store all descriptors returned by FFA? */ if (count > num_desc) {
res = ERR_NOT_ENOUGH_BUFFER; goto err_not_enough_buffer;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.