Newer
Older
mbed-os / components / TARGET_PSA / services / platform / COMPONENT_SPE / platform_partition.c
@Gabor Abonyi Gabor Abonyi on 21 Feb 2020 5 KB Add ioctl platform service
/* Copyright (c) 2019-2020 Arm Limited
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed_spm_partitions.h"
#include "platform_srv_impl.h"
#include "psa/lifecycle.h"
#include "psa/internal_trusted_storage.h"
#include "psa/service.h"

#define INPUT_BUFFER_SIZE  64
#define OUTPUT_BUFFER_SIZE 64

typedef psa_status_t (*SignalHandler)(psa_msg_t *);

static psa_status_t lifecycle_get(psa_msg_t *msg)
{
    uint32_t lc_state;

    if (msg->out_size[0] != sizeof(lc_state)) {
        return PSA_DROP_CONNECTION;
    }

    psa_status_t status = psa_platfrom_lifecycle_get_impl(&lc_state);
    if (status == PSA_SUCCESS) {
        psa_write(msg->handle, 0, &lc_state, sizeof(lc_state));
    }

    return status;
}

static psa_status_t lifecycle_change_request(psa_msg_t *msg)
{
    uint32_t lc_state;

    if (msg->in_size[0] != sizeof(lc_state)) {
        return PSA_DROP_CONNECTION;
    }
    if (psa_read(msg->handle, 0, &lc_state, sizeof(lc_state)) != sizeof(lc_state)) {
        return PSA_DROP_CONNECTION;
    }
    return psa_platfrom_lifecycle_change_request_impl(lc_state);

}

static MBED_NORETURN psa_status_t system_reset_request(psa_msg_t *msg)
{
    (void)msg;
    mbed_psa_system_reset_impl();
}

static enum tfm_platform_err_t
platform_sp_ioctl_ipc(const psa_msg_t *msg) {
    void *input = NULL;
    void *output = NULL;
    psa_invec invec = {0};
    psa_outvec outvec = {0};
    uint8_t input_buffer[INPUT_BUFFER_SIZE] = {0};
    uint8_t output_buffer[OUTPUT_BUFFER_SIZE] = {0};
    tfm_platform_ioctl_req_t request = 0;
    enum tfm_platform_err_t ret = TFM_PLATFORM_ERR_SYSTEM_ERROR;
    size_t num = 0;
    uint32_t in_len = PSA_MAX_IOVEC;
    uint32_t out_len = PSA_MAX_IOVEC;

    while ((in_len > 0) && (msg->in_size[in_len - 1] == 0))
    {
        in_len--;
    }

    while ((out_len > 0) && (msg->out_size[out_len - 1] == 0))
    {
        out_len--;
    }

    if ((in_len < 1) || (in_len > 2) ||
            (out_len > 1))
    {
        return TFM_PLATFORM_ERR_SYSTEM_ERROR;
    }

    num = psa_read(msg->handle, 0, &request, sizeof(request));
    if (num != sizeof(request))
    {
        return PSA_ERROR_PROGRAMMER_ERROR;
    }

    if (in_len > 1)
    {
        if (msg->in_size[1] > INPUT_BUFFER_SIZE) {
            return PSA_ERROR_PROGRAMMER_ERROR;
        }
        num = psa_read(msg->handle, 1, &input_buffer, msg->in_size[1]);
        if (num != msg->in_size[1]) {
            return PSA_ERROR_PROGRAMMER_ERROR;
        }
        invec.base = input_buffer;
        invec.len = msg->in_size[1];
        input = &invec;
    }

    if (out_len > 0)
    {
        if (msg->out_size[0] > OUTPUT_BUFFER_SIZE) {
            return PSA_ERROR_PROGRAMMER_ERROR;
        }
        outvec.base = output_buffer;
        outvec.len = msg->out_size[0];
        output = &outvec;
    }

    ret = tfm_platform_hal_ioctl(request, input, output);

    if (output != NULL)
    {
        psa_write(msg->handle, 0, outvec.base, outvec.len);
    }

    return ret;
}

static psa_status_t platform_ioctl(psa_msg_t *msg)
{
    /*  platform_sp_ioctl_ipc returns either psa_status_t or one of the
     *  following errorcodes:
     *  enum tfm_platform_err_t {
     *      TFM_PLATFORM_ERR_SUCCESS = 0,
     *      TFM_PLATFORM_ERR_SYSTEM_ERROR,
     *      TFM_PLATFORM_ERR_INVALID_PARAM,
     *      TFM_PLATFORM_ERR_NOT_SUPPORTED,
     *
     *      TFM_PLATFORM_ERR_FORCE_INT_SIZE = INT_MAX
     *  };
     */
    return platform_sp_ioctl_ipc(msg);
}

static void message_handler(psa_msg_t *msg, SignalHandler handler)
{
    psa_status_t status = PSA_SUCCESS;
    switch (msg->type) {
        case PSA_IPC_CONNECT: //fallthrough
        case PSA_IPC_DISCONNECT: {
            break;
        }
        case PSA_IPC_CALL: {
            status = handler(msg);
            break;
        }
        default: {
            SPM_PANIC("Unexpected message type %lu!", msg->type);
            break;
        }
    }
    psa_reply(msg->handle, status);
}

void platform_partition_entry(void *ptr)
{
    psa_signal_t signals = 0;
    psa_msg_t msg = {0};
    while (1) {
        signals = psa_wait(PLATFORM_WAIT_ANY_SID_MSK, PSA_BLOCK);
        if ((signals & PSA_PLATFORM_LC_GET_MSK) != 0) {
            if (PSA_SUCCESS != psa_get(PSA_PLATFORM_LC_GET_MSK, &msg)) {
                continue;
            }
            message_handler(&msg, lifecycle_get);
        }
        if ((signals & PSA_PLATFORM_LC_SET_MSK) != 0) {
            if (PSA_SUCCESS != psa_get(PSA_PLATFORM_LC_SET_MSK, &msg)) {
                continue;
            }
            message_handler(&msg, lifecycle_change_request);
        }
        if ((signals & PSA_PLATFORM_SYSTEM_RESET_MSK) != 0) {
            if (PSA_SUCCESS != psa_get(PSA_PLATFORM_SYSTEM_RESET_MSK, &msg)) {
                continue;
            }
            message_handler(&msg, system_reset_request);
        }
        if ((signals & PSA_PLATFORM_IOCTL_MSK)  != 0) {
            if (PSA_SUCCESS != psa_get(PSA_PLATFORM_IOCTL_MSK, &msg)) {
                continue;
            }
            message_handler(&msg, platform_ioctl);
        }
    }
}