| /* PSA firmware framework client API */ |
| |
| /* |
| * Copyright The Mbed TLS Contributors |
| * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| */ |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <stddef.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <inttypes.h> |
| #include <sys/types.h> |
| #include <sys/ipc.h> |
| #include <sys/msg.h> |
| #include <psa/error.h> |
| |
| #include "common.h" |
| |
| typedef struct internal_handle { |
| int server_qid; |
| int client_qid; |
| int internal_server_qid; |
| int valid; |
| } internal_handle_t; |
| |
| /* Access to this global is not thread safe */ |
| #define MAX_HANDLES 32 |
| static internal_handle_t handles[MAX_HANDLES] = { { 0 } }; |
| |
| static int get_next_free_handle() |
| { |
| /* Never return handle 0 as it's a special null handle */ |
| for (int i = 1; i < MAX_HANDLES; i++) { |
| if (handles[i].valid == 0) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static int handle_is_valid(psa_handle_t handle) |
| { |
| if (handle > 0 && handle < MAX_HANDLES) { |
| if (handles[handle].valid == 1) { |
| return 1; |
| } |
| } |
| PROGRAMMER_ERROR("ERROR: Invalid handle"); |
| return 0; |
| } |
| |
| static int get_queue_info(char *path, int *cqid, int *sqid) |
| { |
| |
| key_t server_queue_key; |
| int rx_qid, server_qid; |
| |
| INFO("Attempting to contact a RoT service queue"); |
| |
| if ((rx_qid = msgget(IPC_PRIVATE, 0660)) == -1) { |
| INFO("msgget: rx_qid"); |
| return -1; |
| } |
| |
| if ((server_queue_key = ftok(path, PROJECT_ID)) == -1) { |
| INFO("ftok"); |
| return -2; |
| } |
| |
| if ((server_qid = msgget(server_queue_key, 0)) == -1) { |
| INFO("msgget: server_qid"); |
| return -3; |
| } |
| |
| *cqid = rx_qid; |
| *sqid = server_qid; |
| |
| return 0; |
| } |
| |
| static psa_status_t process_response(int rx_qid, vectors_t *vecs, int type, |
| int *internal_server_qid) |
| { |
| |
| struct message response, request; |
| psa_status_t ret = PSA_ERROR_CONNECTION_REFUSED; |
| size_t invec_seek[4] = { 0 }; |
| size_t data_size; |
| psa_status_t invec, outvec; /* TODO: Should these be size_t ? */ |
| |
| assert(internal_server_qid > 0); |
| |
| while (1) { |
| data_size = 0; |
| invec = 0; |
| outvec = 0; |
| |
| // read response from server |
| if (msgrcv(rx_qid, &response, sizeof(struct message_text), 0, 0) == -1) { |
| puts(" msgrcv failed"); |
| return ret; |
| } |
| |
| // process return message from server |
| switch (response.message_type) { |
| case PSA_REPLY: |
| memcpy(&ret, response.message_text.buf, sizeof(psa_status_t)); |
| printf(" Message received from server: %d\n", ret); |
| if (type == PSA_IPC_CONNECT && ret > 0) { |
| *internal_server_qid = ret; |
| INFO(" ASSSIGNED q ID %d", *internal_server_qid); |
| ret = PSA_SUCCESS; |
| } |
| return ret; |
| break; |
| case READ_REQUEST: |
| /* read data request */ |
| request.message_type = READ_RESPONSE; |
| |
| assert(vecs != 0); |
| |
| memcpy(&invec, response.message_text.buf, sizeof(psa_status_t)); |
| memcpy(&data_size, response.message_text.buf+sizeof(size_t), sizeof(size_t)); |
| INFO(" Partition asked for %lu bytes from invec %d", data_size, invec); |
| |
| /* need to add more checks here */ |
| assert(invec >= 0 && invec < PSA_MAX_IOVEC); |
| |
| if (data_size > MAX_FRAGMENT_SIZE) { |
| data_size = MAX_FRAGMENT_SIZE; |
| } |
| |
| /* send response */ |
| INFO(" invec_seek[invec] is %lu", invec_seek[invec]); |
| INFO(" Reading from offset %p", vecs->in_vec[invec].base + invec_seek[invec]); |
| memcpy(request.message_text.buf, |
| (vecs->in_vec[invec].base + invec_seek[invec]), |
| data_size); |
| |
| /* update invec base TODO: check me */ |
| invec_seek[invec] = invec_seek[invec] + data_size; |
| |
| INFO(" Sending message of type %li", request.message_type); |
| INFO(" with content %s\n", request.message_text.buf); |
| |
| if (msgsnd(*internal_server_qid, &request, |
| sizeof(int) + sizeof(uint32_t) + data_size, 0) == -1) { |
| INFO("Internal error: failed to respond to read request"); |
| } |
| break; |
| case WRITE_REQUEST: |
| assert(vecs != 0); |
| |
| request.message_type = WRITE_RESPONSE; |
| |
| memcpy(&outvec, response.message_text.buf, sizeof(psa_status_t)); |
| memcpy(&data_size, response.message_text.buf + sizeof(size_t), sizeof(size_t)); |
| INFO(" Partition wants to write %lu bytes to outvec %d", data_size, outvec); |
| |
| assert(outvec >= 0 && outvec < PSA_MAX_IOVEC); |
| |
| /* copy memory into message and send back amount written */ |
| size_t sofar = vecs->out_vec[outvec].len; |
| memcpy(vecs->out_vec[outvec].base + sofar, |
| response.message_text.buf+(sizeof(size_t)*2), data_size); |
| INFO(" Data size is %lu\n", data_size); |
| vecs->out_vec[outvec].len += data_size; |
| |
| INFO(" Sending message of type %li\n", request.message_type); |
| |
| /* send response */ |
| if (msgsnd(*internal_server_qid, &request, sizeof(int) + data_size, 0) == -1) { |
| INFO("Internal error: failed to respond to write request"); |
| } |
| break; |
| case SKIP_REQUEST: |
| memcpy(&invec, response.message_text.buf, sizeof(psa_status_t)); |
| memcpy(&data_size, response.message_text.buf+sizeof(size_t), sizeof(size_t)); |
| INFO(" Partition asked to skip %lu bytes in invec %d", data_size, invec); |
| assert(invec >= 0 && invec < PSA_MAX_IOVEC); |
| /* update invec base TODO: check me */ |
| invec_seek[invec] = invec_seek[invec] + data_size; |
| break; |
| |
| default: |
| FATAL(" ERROR: unknown internal message type: %ld\n", |
| response.message_type); |
| return ret; |
| } |
| } |
| } |
| |
| static psa_status_t send(int rx_qid, int server_qid, int *internal_server_qid, |
| int32_t type, uint32_t minor_version, vectors_t *vecs) |
| { |
| { |
| psa_status_t ret = PSA_ERROR_CONNECTION_REFUSED; |
| size_t request_msg_size = (sizeof(int) + sizeof(long)); /* msg type plus queue id */ |
| struct message request; |
| request.message_type = 1; /* TODO: change this */ |
| request.message_text.psa_type = type; |
| vector_sizes_t vec_sizes; |
| |
| /* If the client is non-secure then set the NS bit */ |
| if (__psa_ff_client_security_state != 0) { |
| request.message_type |= NON_SECURE; |
| } |
| |
| assert(request.message_type >= 0); |
| |
| INFO("SEND: Sending message of type %ld with psa_type %d", request.message_type, type); |
| INFO(" internal_server_qid = %i", *internal_server_qid); |
| |
| request.message_text.qid = rx_qid; |
| |
| if (type == PSA_IPC_CONNECT) { |
| memcpy(request.message_text.buf, &minor_version, sizeof(minor_version)); |
| request_msg_size = request_msg_size + sizeof(minor_version); |
| INFO(" Request msg size is %lu", request_msg_size); |
| } else { |
| assert(internal_server_qid > 0); |
| } |
| |
| if (vecs != NULL && type >= PSA_IPC_CALL) { |
| |
| bzero(&vec_sizes, sizeof(vec_sizes)); |
| |
| /* Copy invec sizes */ |
| for (size_t i = 0; i < (vecs->in_len); i++) { |
| vec_sizes.invec_sizes[i] = vecs->in_vec[i].len; |
| INFO(" Client sending vector %lu: %lu", i, vec_sizes.invec_sizes[i]); |
| } |
| |
| /* Copy outvec sizes */ |
| for (size_t i = 0; i < (vecs->out_len); i++) { |
| vec_sizes.outvec_sizes[i] = vecs->out_vec[i].len; |
| |
| /* Reset to 0 since we need to eventually fill in with bytes written */ |
| vecs->out_vec[i].len = 0; |
| } |
| |
| memcpy(request.message_text.buf, &vec_sizes, sizeof(vec_sizes)); |
| request_msg_size = request_msg_size + sizeof(vec_sizes); |
| } |
| |
| INFO(" Sending and then waiting"); |
| |
| // send message to server |
| if (msgsnd(server_qid, &request, request_msg_size, 0) == -1) { |
| puts(" msgsnd failed"); |
| return ret; |
| } |
| |
| return process_response(rx_qid, vecs, type, internal_server_qid); |
| } |
| } |
| |
| |
| uint32_t psa_framework_version(void) |
| { |
| return PSA_FRAMEWORK_VERSION; |
| } |
| |
| psa_handle_t psa_connect(uint32_t sid, uint32_t minor_version) |
| { |
| |
| int idx; |
| psa_status_t ret; |
| char pathname[PATHNAMESIZE] = { 0 }; |
| |
| idx = get_next_free_handle(); |
| |
| /* if there's a free handle available */ |
| if (idx >= 0) { |
| snprintf(pathname, PATHNAMESIZE - 1, "/tmp/psa_service_%u", sid); |
| INFO("Attempting to contact RoT service at %s", pathname); |
| |
| /* if communication is possible */ |
| if (get_queue_info(pathname, &handles[idx].client_qid, &handles[idx].server_qid) >= 0) { |
| |
| ret = send(handles[idx].client_qid, |
| handles[idx].server_qid, |
| &handles[idx].internal_server_qid, |
| PSA_IPC_CONNECT, |
| minor_version, |
| NULL); |
| |
| /* if connection accepted by RoT service */ |
| if (ret >= 0) { |
| handles[idx].valid = 1; |
| return idx; |
| } else { |
| INFO("Server didn't like you"); |
| } |
| } else { |
| INFO("Couldn't contact RoT service. Does it exist?"); |
| |
| if (__psa_ff_client_security_state == 0) { |
| PROGRAMMER_ERROR("Invalid SID"); |
| } |
| } |
| } |
| |
| INFO("Couldn't obtain a free handle"); |
| return PSA_ERROR_CONNECTION_REFUSED; |
| } |
| |
| uint32_t psa_version(uint32_t sid) |
| { |
| int idx; |
| psa_status_t ret; |
| char pathname[PATHNAMESIZE] = { 0 }; |
| |
| idx = get_next_free_handle(); |
| |
| if (idx >= 0) { |
| snprintf(pathname, PATHNAMESIZE, "/tmp/psa_service_%u", sid); |
| if (get_queue_info(pathname, &handles[idx].client_qid, &handles[idx].server_qid) >= 0) { |
| ret = send(handles[idx].client_qid, |
| handles[idx].server_qid, |
| &handles[idx].internal_server_qid, |
| VERSION_REQUEST, |
| 0, |
| NULL); |
| INFO("psa_version: Recieved from server %d\n", ret); |
| if (ret > 0) { |
| return ret; |
| } |
| } |
| } |
| INFO("psa_version failed: does the service exist?"); |
| return PSA_VERSION_NONE; |
| } |
| |
| psa_status_t psa_call(psa_handle_t handle, |
| int32_t type, |
| const psa_invec *in_vec, |
| size_t in_len, |
| psa_outvec *out_vec, |
| size_t out_len) |
| { |
| |
| handle_is_valid(handle); |
| |
| if ((in_len + out_len) > PSA_MAX_IOVEC) { |
| PROGRAMMER_ERROR("Too many iovecs: %lu + %lu", in_len, out_len); |
| } |
| |
| vectors_t vecs = { 0 }; |
| vecs.in_vec = in_vec; |
| vecs.in_len = in_len; |
| vecs.out_vec = out_vec; |
| vecs.out_len = out_len; |
| |
| return send(handles[handle].client_qid, |
| handles[handle].server_qid, |
| &handles[handle].internal_server_qid, |
| type, |
| 0, |
| &vecs); |
| } |
| |
| void psa_close(psa_handle_t handle) |
| { |
| handle_is_valid(handle); |
| if (send(handles[handle].client_qid, handles[handle].server_qid, |
| &handles[handle].internal_server_qid, PSA_IPC_DISCONNECT, 0, NULL)) { |
| puts("ERROR: Couldn't send disconnect msg"); |
| } else { |
| if (msgctl(handles[handle].client_qid, IPC_RMID, NULL) != 0) { |
| puts("ERROR: Failed to delete msg queue"); |
| } |
| } |
| INFO("Closing handle %u", handle); |
| handles[handle].valid = 0; |
| } |