From: Julien Grall <julien.grall@linaro.org>
To: Volodymyr Babchuk <volodymyr_babchuk@epam.com>,
Stefano Stabellini <sstabellini@kernel.org>,
Julien Grall <julien.grall@arm.com>,
xen-devel@lists.xen.org
Subject: Re: [RFC 3/4] arm: tee: add OP-TEE header files
Date: Mon, 16 Oct 2017 15:04:44 +0100 [thread overview]
Message-ID: <a0d1705e-35a7-4faf-a3e8-e361b99fc823@linaro.org> (raw)
In-Reply-To: <1507748484-16871-4-git-send-email-volodymyr_babchuk@epam.com>
Hi Volodymyr,
On 11/10/17 20:01, Volodymyr Babchuk wrote:
> This header files describe protocol between OP-TEE and OP-TEE client
> driver in Linux. They are needed for upcomient OP-TEE mediator, which
> is added in the next patch.
> Reason to add those headers in separate patch is to ease up review.
> Those files were taken from linux tree (drivers/tee/optee/) and mangled
> a bit to compile with XEN.
>
> Signed-off-by: Volodymyr Babchuk <volodymyr_babchuk@epam.com>
> ---
> xen/arch/arm/tee/optee_msg.h | 444 +++++++++++++++++++++++++++++++++++++++++
> xen/arch/arm/tee/optee_smc.h | 457 +++++++++++++++++++++++++++++++++++++++++++
Headers should go in include/asm-arm/tee and not arch/arm.
Cheers,
> 2 files changed, 901 insertions(+)
> create mode 100644 xen/arch/arm/tee/optee_msg.h
> create mode 100644 xen/arch/arm/tee/optee_smc.h
>
> diff --git a/xen/arch/arm/tee/optee_msg.h b/xen/arch/arm/tee/optee_msg.h
> new file mode 100644
> index 0000000..10747b2
> --- /dev/null
> +++ b/xen/arch/arm/tee/optee_msg.h
> @@ -0,0 +1,444 @@
> +/*
> + * Copyright (c) 2015-2016, Linaro Limited
> + * All rights reserved.
> + *
> + * 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.
> + *
> + * 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 HOLDER 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.
> + */
> +#ifndef _OPTEE_MSG_H
> +#define _OPTEE_MSG_H
> +
> +#include <xen/bitops.h>
> +#include <xen/types.h>
> +
> +/*
> + * This file defines the OP-TEE message protocol used to communicate
> + * with an instance of OP-TEE running in secure world.
> + *
> + * This file is divided into three sections.
> + * 1. Formatting of messages.
> + * 2. Requests from normal world
> + * 3. Requests from secure world, Remote Procedure Call (RPC), handled by
> + * tee-supplicant.
> + */
> +
> +/*****************************************************************************
> + * Part 1 - formatting of messages
> + *****************************************************************************/
> +
> +#define OPTEE_MSG_ATTR_TYPE_NONE 0x0
> +#define OPTEE_MSG_ATTR_TYPE_VALUE_INPUT 0x1
> +#define OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT 0x2
> +#define OPTEE_MSG_ATTR_TYPE_VALUE_INOUT 0x3
> +#define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5
> +#define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6
> +#define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7
> +#define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9
> +#define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa
> +#define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb
> +
> +#define OPTEE_MSG_ATTR_TYPE_MASK GENMASK(7, 0)
> +
> +/*
> + * Meta parameter to be absorbed by the Secure OS and not passed
> + * to the Trusted Application.
> + *
> + * Currently only used with OPTEE_MSG_CMD_OPEN_SESSION.
> + */
> +#define OPTEE_MSG_ATTR_META BIT(8)
> +
> +/*
> + * Pointer to a list of pages used to register user-defined SHM buffer.
> + * Used with OPTEE_MSG_ATTR_TYPE_TMEM_*.
> + * buf_ptr should point to the beginning of the buffer. Buffer will contain
> + * list of page addresses. OP-TEE core can reconstruct contiguous buffer from
> + * that page addresses list. Page addresses are stored as 64 bit values.
> + * Last entry on a page should point to the next page of buffer.
> + * Every entry in buffer should point to a 4k page beginning (12 least
> + * significant bits must be equal to zero).
> + *
> + * 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page
> + * offset of the user buffer.
> + *
> + * So, entries should be placed like members of this structure:
> + *
> + * struct page_data {
> + * uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1];
> + * uint64_t next_page_data;
> + * };
> + *
> + * Structure is designed to exactly fit into the page size
> + * OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page.
> + *
> + * The size of 4KB is chosen because this is the smallest page size for ARM
> + * architectures. If REE uses larger pages, it should divide them to 4KB ones.
> + */
> +#define OPTEE_MSG_ATTR_NONCONTIG BIT(9)
> +
> +/*
> + * Memory attributes for caching passed with temp memrefs. The actual value
> + * used is defined outside the message protocol with the exception of
> + * OPTEE_MSG_ATTR_CACHE_PREDEFINED which means the attributes already
> + * defined for the memory range should be used. If optee_smc.h is used as
> + * bearer of this protocol OPTEE_SMC_SHM_* is used for values.
> + */
> +#define OPTEE_MSG_ATTR_CACHE_SHIFT 16
> +#define OPTEE_MSG_ATTR_CACHE_MASK GENMASK(2, 0)
> +#define OPTEE_MSG_ATTR_CACHE_PREDEFINED 0
> +
> +/*
> + * Same values as TEE_LOGIN_* from TEE Internal API
> + */
> +#define OPTEE_MSG_LOGIN_PUBLIC 0x00000000
> +#define OPTEE_MSG_LOGIN_USER 0x00000001
> +#define OPTEE_MSG_LOGIN_GROUP 0x00000002
> +#define OPTEE_MSG_LOGIN_APPLICATION 0x00000004
> +#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005
> +#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006
> +
> +/*
> + * Page size used in non-contiguous buffer entries
> + */
> +#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
> +
> +/**
> + * struct optee_msg_param_tmem - temporary memory reference parameter
> + * @buf_ptr: Address of the buffer
> + * @size: Size of the buffer
> + * @shm_ref: Temporary shared memory reference, pointer to a struct tee_shm
> + *
> + * Secure and normal world communicates pointers as physical address
> + * instead of the virtual address. This is because secure and normal world
> + * have completely independent memory mapping. Normal world can even have a
> + * hypervisor which need to translate the guest physical address (AKA IPA
> + * in ARM documentation) to a real physical address before passing the
> + * structure to secure world.
> + */
> +struct optee_msg_param_tmem {
> + u64 buf_ptr;
> + u64 size;
> + u64 shm_ref;
> +};
> +
> +/**
> + * struct optee_msg_param_rmem - registered memory reference parameter
> + * @offs: Offset into shared memory reference
> + * @size: Size of the buffer
> + * @shm_ref: Shared memory reference, pointer to a struct tee_shm
> + */
> +struct optee_msg_param_rmem {
> + u64 offs;
> + u64 size;
> + u64 shm_ref;
> +};
> +
> +/**
> + * struct optee_msg_param_value - opaque value parameter
> + *
> + * Value parameters are passed unchecked between normal and secure world.
> + */
> +struct optee_msg_param_value {
> + u64 a;
> + u64 b;
> + u64 c;
> +};
> +
> +/**
> + * struct optee_msg_param - parameter used together with struct optee_msg_arg
> + * @attr: attributes
> + * @tmem: parameter by temporary memory reference
> + * @rmem: parameter by registered memory reference
> + * @value: parameter by opaque value
> + *
> + * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
> + * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value,
> + * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
> + * OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem,
> + * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
> + */
> +struct optee_msg_param {
> + u64 attr;
> + union {
> + struct optee_msg_param_tmem tmem;
> + struct optee_msg_param_rmem rmem;
> + struct optee_msg_param_value value;
> + } u;
> +};
> +
> +/**
> + * struct optee_msg_arg - call argument
> + * @cmd: Command, one of OPTEE_MSG_CMD_* or OPTEE_MSG_RPC_CMD_*
> + * @func: Trusted Application function, specific to the Trusted Application,
> + * used if cmd == OPTEE_MSG_CMD_INVOKE_COMMAND
> + * @session: In parameter for all OPTEE_MSG_CMD_* except
> + * OPTEE_MSG_CMD_OPEN_SESSION where it's an output parameter instead
> + * @cancel_id: Cancellation id, a unique value to identify this request
> + * @ret: return value
> + * @ret_origin: origin of the return value
> + * @num_params: number of parameters supplied to the OS Command
> + * @params: the parameters supplied to the OS Command
> + *
> + * All normal calls to Trusted OS uses this struct. If cmd requires further
> + * information than what these field holds it can be passed as a parameter
> + * tagged as meta (setting the OPTEE_MSG_ATTR_META bit in corresponding
> + * attrs field). All parameters tagged as meta has to come first.
> + *
> + * Temp memref parameters can be fragmented if supported by the Trusted OS
> + * (when optee_smc.h is bearer of this protocol this is indicated with
> + * OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM). If a logical memref parameter is
> + * fragmented then has all but the last fragment the
> + * OPTEE_MSG_ATTR_FRAGMENT bit set in attrs. Even if a memref is fragmented
> + * it will still be presented as a single logical memref to the Trusted
> + * Application.
> + */
> +struct optee_msg_arg {
> + u32 cmd;
> + u32 func;
> + u32 session;
> + u32 cancel_id;
> + u32 pad;
> + u32 ret;
> + u32 ret_origin;
> + u32 num_params;
> +
> + /* num_params tells the actual number of element in params */
> + struct optee_msg_param params[0];
> +};
> +
> +/**
> + * OPTEE_MSG_GET_ARG_SIZE - return size of struct optee_msg_arg
> + *
> + * @num_params: Number of parameters embedded in the struct optee_msg_arg
> + *
> + * Returns the size of the struct optee_msg_arg together with the number
> + * of embedded parameters.
> + */
> +#define OPTEE_MSG_GET_ARG_SIZE(num_params) \
> + (sizeof(struct optee_msg_arg) + \
> + sizeof(struct optee_msg_param) * (num_params))
> +
> +/*****************************************************************************
> + * Part 2 - requests from normal world
> + *****************************************************************************/
> +
> +/*
> + * Return the following UID if using API specified in this file without
> + * further extensions:
> + * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b.
> + * Represented in 4 32-bit words in OPTEE_MSG_UID_0, OPTEE_MSG_UID_1,
> + * OPTEE_MSG_UID_2, OPTEE_MSG_UID_3.
> + */
> +#define OPTEE_MSG_UID_0 0x384fb3e0
> +#define OPTEE_MSG_UID_1 0xe7f811e3
> +#define OPTEE_MSG_UID_2 0xaf630002
> +#define OPTEE_MSG_UID_3 0xa5d5c51b
> +#define OPTEE_MSG_FUNCID_CALLS_UID 0xFF01
> +
> +/*
> + * Returns 2.0 if using API specified in this file without further
> + * extensions. Represented in 2 32-bit words in OPTEE_MSG_REVISION_MAJOR
> + * and OPTEE_MSG_REVISION_MINOR
> + */
> +#define OPTEE_MSG_REVISION_MAJOR 2
> +#define OPTEE_MSG_REVISION_MINOR 0
> +#define OPTEE_MSG_FUNCID_CALLS_REVISION 0xFF03
> +
> +/*
> + * Get UUID of Trusted OS.
> + *
> + * Used by non-secure world to figure out which Trusted OS is installed.
> + * Note that returned UUID is the UUID of the Trusted OS, not of the API.
> + *
> + * Returns UUID in 4 32-bit words in the same way as
> + * OPTEE_MSG_FUNCID_CALLS_UID described above.
> + */
> +#define OPTEE_MSG_OS_OPTEE_UUID_0 0x486178e0
> +#define OPTEE_MSG_OS_OPTEE_UUID_1 0xe7f811e3
> +#define OPTEE_MSG_OS_OPTEE_UUID_2 0xbc5e0002
> +#define OPTEE_MSG_OS_OPTEE_UUID_3 0xa5d5c51b
> +#define OPTEE_MSG_FUNCID_GET_OS_UUID 0x0000
> +
> +/*
> + * Get revision of Trusted OS.
> + *
> + * Used by non-secure world to figure out which version of the Trusted OS
> + * is installed. Note that the returned revision is the revision of the
> + * Trusted OS, not of the API.
> + *
> + * Returns revision in 2 32-bit words in the same way as
> + * OPTEE_MSG_CALLS_REVISION described above.
> + */
> +#define OPTEE_MSG_FUNCID_GET_OS_REVISION 0x0001
> +
> +/*
> + * Do a secure call with struct optee_msg_arg as argument
> + * The OPTEE_MSG_CMD_* below defines what goes in struct optee_msg_arg::cmd
> + *
> + * OPTEE_MSG_CMD_OPEN_SESSION opens a session to a Trusted Application.
> + * The first two parameters are tagged as meta, holding two value
> + * parameters to pass the following information:
> + * param[0].u.value.a-b uuid of Trusted Application
> + * param[1].u.value.a-b uuid of Client
> + * param[1].u.value.c Login class of client OPTEE_MSG_LOGIN_*
> + *
> + * OPTEE_MSG_CMD_INVOKE_COMMAND invokes a command a previously opened
> + * session to a Trusted Application. struct optee_msg_arg::func is Trusted
> + * Application function, specific to the Trusted Application.
> + *
> + * OPTEE_MSG_CMD_CLOSE_SESSION closes a previously opened session to
> + * Trusted Application.
> + *
> + * OPTEE_MSG_CMD_CANCEL cancels a currently invoked command.
> + *
> + * OPTEE_MSG_CMD_REGISTER_SHM registers a shared memory reference. The
> + * information is passed as:
> + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_TMEM_INPUT
> + * [| OPTEE_MSG_ATTR_FRAGMENT]
> + * [in] param[0].u.tmem.buf_ptr physical address (of first fragment)
> + * [in] param[0].u.tmem.size size (of first fragment)
> + * [in] param[0].u.tmem.shm_ref holds shared memory reference
> + * ...
> + * The shared memory can optionally be fragmented, temp memrefs can follow
> + * each other with all but the last with the OPTEE_MSG_ATTR_FRAGMENT bit set.
> + *
> + * OPTEE_MSG_CMD_UNREGISTER_SHM unregisteres a previously registered shared
> + * memory reference. The information is passed as:
> + * [in] param[0].attr OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
> + * [in] param[0].u.rmem.shm_ref holds shared memory reference
> + * [in] param[0].u.rmem.offs 0
> + * [in] param[0].u.rmem.size 0
> + */
> +#define OPTEE_MSG_CMD_OPEN_SESSION 0
> +#define OPTEE_MSG_CMD_INVOKE_COMMAND 1
> +#define OPTEE_MSG_CMD_CLOSE_SESSION 2
> +#define OPTEE_MSG_CMD_CANCEL 3
> +#define OPTEE_MSG_CMD_REGISTER_SHM 4
> +#define OPTEE_MSG_CMD_UNREGISTER_SHM 5
> +#define OPTEE_MSG_FUNCID_CALL_WITH_ARG 0x0004
> +
> +/*****************************************************************************
> + * Part 3 - Requests from secure world, RPC
> + *****************************************************************************/
> +
> +/*
> + * All RPC is done with a struct optee_msg_arg as bearer of information,
> + * struct optee_msg_arg::arg holds values defined by OPTEE_MSG_RPC_CMD_* below
> + *
> + * RPC communication with tee-supplicant is reversed compared to normal
> + * client communication desribed above. The supplicant receives requests
> + * and sends responses.
> + */
> +
> +/*
> + * Load a TA into memory, defined in tee-supplicant
> + */
> +#define OPTEE_MSG_RPC_CMD_LOAD_TA 0
> +
> +/*
> + * Reserved
> + */
> +#define OPTEE_MSG_RPC_CMD_RPMB 1
> +
> +/*
> + * File system access, defined in tee-supplicant
> + */
> +#define OPTEE_MSG_RPC_CMD_FS 2
> +
> +/*
> + * Get time
> + *
> + * Returns number of seconds and nano seconds since the Epoch,
> + * 1970-01-01 00:00:00 +0000 (UTC).
> + *
> + * [out] param[0].u.value.a Number of seconds
> + * [out] param[0].u.value.b Number of nano seconds.
> + */
> +#define OPTEE_MSG_RPC_CMD_GET_TIME 3
> +
> +/*
> + * Wait queue primitive, helper for secure world to implement a wait queue.
> + *
> + * If secure world need to wait for a secure world mutex it issues a sleep
> + * request instead of spinning in secure world. Conversely is a wakeup
> + * request issued when a secure world mutex with a thread waiting thread is
> + * unlocked.
> + *
> + * Waiting on a key
> + * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP
> + * [in] param[0].u.value.b wait key
> + *
> + * Waking up a key
> + * [in] param[0].u.value.a OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP
> + * [in] param[0].u.value.b wakeup key
> + */
> +#define OPTEE_MSG_RPC_CMD_WAIT_QUEUE 4
> +#define OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP 0
> +#define OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP 1
> +
> +/*
> + * Suspend execution
> + *
> + * [in] param[0].value .a number of milliseconds to suspend
> + */
> +#define OPTEE_MSG_RPC_CMD_SUSPEND 5
> +
> +/*
> + * Allocate a piece of shared memory
> + *
> + * Shared memory can optionally be fragmented, to support that additional
> + * spare param entries are allocated to make room for eventual fragments.
> + * The spare param entries has .attr = OPTEE_MSG_ATTR_TYPE_NONE when
> + * unused. All returned temp memrefs except the last should have the
> + * OPTEE_MSG_ATTR_FRAGMENT bit set in the attr field.
> + *
> + * [in] param[0].u.value.a type of memory one of
> + * OPTEE_MSG_RPC_SHM_TYPE_* below
> + * [in] param[0].u.value.b requested size
> + * [in] param[0].u.value.c required alignment
> + *
> + * [out] param[0].u.tmem.buf_ptr physical address (of first fragment)
> + * [out] param[0].u.tmem.size size (of first fragment)
> + * [out] param[0].u.tmem.shm_ref shared memory reference
> + * ...
> + * [out] param[n].u.tmem.buf_ptr physical address
> + * [out] param[n].u.tmem.size size
> + * [out] param[n].u.tmem.shm_ref shared memory reference (same value
> + * as in param[n-1].u.tmem.shm_ref)
> + */
> +#define OPTEE_MSG_RPC_CMD_SHM_ALLOC 6
> +/* Memory that can be shared with a non-secure user space application */
> +#define OPTEE_MSG_RPC_SHM_TYPE_APPL 0
> +/* Memory only shared with non-secure kernel */
> +#define OPTEE_MSG_RPC_SHM_TYPE_KERNEL 1
> +
> +/*
> + * Free shared memory previously allocated with OPTEE_MSG_RPC_CMD_SHM_ALLOC
> + *
> + * [in] param[0].u.value.a type of memory one of
> + * OPTEE_MSG_RPC_SHM_TYPE_* above
> + * [in] param[0].u.value.b value of shared memory reference
> + * returned in param[0].u.tmem.shm_ref
> + * above
> + */
> +#define OPTEE_MSG_RPC_CMD_SHM_FREE 7
> +
> +#endif /* _OPTEE_MSG_H */
> diff --git a/xen/arch/arm/tee/optee_smc.h b/xen/arch/arm/tee/optee_smc.h
> new file mode 100644
> index 0000000..92f4d54
> --- /dev/null
> +++ b/xen/arch/arm/tee/optee_smc.h
> @@ -0,0 +1,457 @@
> +/*
> + * Copyright (c) 2015-2016, Linaro Limited
> + * All rights reserved.
> + *
> + * 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.
> + *
> + * 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 HOLDER 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.
> + */
> +#ifndef OPTEE_SMC_H
> +#define OPTEE_SMC_H
> +
> +#include <asm/smccc.h>
> +#include <xen/bitops.h>
> +
> +#define OPTEE_SMC_STD_CALL_VAL(func_num) \
> + ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_CONV_32, \
> + ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
> +#define OPTEE_SMC_FAST_CALL_VAL(func_num) \
> + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_CONV_32, \
> + ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
> +
> +/*
> + * Function specified by SMC Calling convention.
> + */
> +#define OPTEE_SMC_FUNCID_CALLS_COUNT 0xFF00
> +#define OPTEE_SMC_CALLS_COUNT \
> + ARM_SMCCC_CALL_VAL(OPTEE_SMC_FAST_CALL, SMCCC_SMC_32, \
> + SMCCC_OWNER_TRUSTED_OS_END, \
> + OPTEE_SMC_FUNCID_CALLS_COUNT)
> +
> +/*
> + * Normal cached memory (write-back), shareable for SMP systems and not
> + * shareable for UP systems.
> + */
> +#define OPTEE_SMC_SHM_CACHED 1
> +
> +/*
> + * a0..a7 is used as register names in the descriptions below, on arm32
> + * that translates to r0..r7 and on arm64 to w0..w7. In both cases it's
> + * 32-bit registers.
> + */
> +
> +/*
> + * Function specified by SMC Calling convention
> + *
> + * Return one of the following UIDs if using API specified in this file
> + * without further extentions:
> + * 65cb6b93-af0c-4617-8ed6-644a8d1140f8
> + * see also OPTEE_SMC_UID_* in optee_msg.h
> + */
> +#define OPTEE_SMC_FUNCID_CALLS_UID OPTEE_MSG_FUNCID_CALLS_UID
> +#define OPTEE_SMC_CALLS_UID \
> + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_CONV_32, \
> + ARM_SMCCC_OWNER_TRUSTED_OS_END, \
> + OPTEE_SMC_FUNCID_CALLS_UID)
> +
> +/*
> + * Function specified by SMC Calling convention
> + *
> + * Returns 2.0 if using API specified in this file without further extentions.
> + * see also OPTEE_MSG_REVISION_* in optee_msg.h
> + */
> +#define OPTEE_SMC_FUNCID_CALLS_REVISION OPTEE_MSG_FUNCID_CALLS_REVISION
> +#define OPTEE_SMC_CALLS_REVISION \
> + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_CONV_32, \
> + ARM_SMCCC_OWNER_TRUSTED_OS_END, \
> + OPTEE_SMC_FUNCID_CALLS_REVISION)
> +
> +struct optee_smc_calls_revision_result {
> + unsigned long major;
> + unsigned long minor;
> + unsigned long reserved0;
> + unsigned long reserved1;
> +};
> +
> +/*
> + * Get UUID of Trusted OS.
> + *
> + * Used by non-secure world to figure out which Trusted OS is installed.
> + * Note that returned UUID is the UUID of the Trusted OS, not of the API.
> + *
> + * Returns UUID in a0-4 in the same way as OPTEE_SMC_CALLS_UID
> + * described above.
> + */
> +#define OPTEE_SMC_FUNCID_GET_OS_UUID OPTEE_MSG_FUNCID_GET_OS_UUID
> +#define OPTEE_SMC_CALL_GET_OS_UUID \
> + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_UUID)
> +
> +/*
> + * Get revision of Trusted OS.
> + *
> + * Used by non-secure world to figure out which version of the Trusted OS
> + * is installed. Note that the returned revision is the revision of the
> + * Trusted OS, not of the API.
> + *
> + * Returns revision in a0-1 in the same way as OPTEE_SMC_CALLS_REVISION
> + * described above.
> + */
> +#define OPTEE_SMC_FUNCID_GET_OS_REVISION OPTEE_MSG_FUNCID_GET_OS_REVISION
> +#define OPTEE_SMC_CALL_GET_OS_REVISION \
> + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_OS_REVISION)
> +
> +/*
> + * Call with struct optee_msg_arg as argument
> + *
> + * Call register usage:
> + * a0 SMC Function ID, OPTEE_SMC*CALL_WITH_ARG
> + * a1 Upper 32bit of a 64bit physical pointer to a struct optee_msg_arg
> + * a2 Lower 32bit of a 64bit physical pointer to a struct optee_msg_arg
> + * a3 Cache settings, not used if physical pointer is in a predefined shared
> + * memory area else per OPTEE_SMC_SHM_*
> + * a4-6 Not used
> + * a7 Hypervisor Client ID register
> + *
> + * Normal return register usage:
> + * a0 Return value, OPTEE_SMC_RETURN_*
> + * a1-3 Not used
> + * a4-7 Preserved
> + *
> + * OPTEE_SMC_RETURN_ETHREAD_LIMIT return register usage:
> + * a0 Return value, OPTEE_SMC_RETURN_ETHREAD_LIMIT
> + * a1-3 Preserved
> + * a4-7 Preserved
> + *
> + * RPC return register usage:
> + * a0 Return value, OPTEE_SMC_RETURN_IS_RPC(val)
> + * a1-2 RPC parameters
> + * a3-7 Resume information, must be preserved
> + *
> + * Possible return values:
> + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
> + * function.
> + * OPTEE_SMC_RETURN_OK Call completed, result updated in
> + * the previously supplied struct
> + * optee_msg_arg.
> + * OPTEE_SMC_RETURN_ETHREAD_LIMIT Number of Trusted OS threads exceeded,
> + * try again later.
> + * OPTEE_SMC_RETURN_EBADADDR Bad physcial pointer to struct
> + * optee_msg_arg.
> + * OPTEE_SMC_RETURN_EBADCMD Bad/unknown cmd in struct optee_msg_arg
> + * OPTEE_SMC_RETURN_IS_RPC() Call suspended by RPC call to normal
> + * world.
> + */
> +#define OPTEE_SMC_FUNCID_CALL_WITH_ARG OPTEE_MSG_FUNCID_CALL_WITH_ARG
> +#define OPTEE_SMC_CALL_WITH_ARG \
> + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG)
> +
> +/*
> + * Get Shared Memory Config
> + *
> + * Returns the Secure/Non-secure shared memory config.
> + *
> + * Call register usage:
> + * a0 SMC Function ID, OPTEE_SMC_GET_SHM_CONFIG
> + * a1-6 Not used
> + * a7 Hypervisor Client ID register
> + *
> + * Have config return register usage:
> + * a0 OPTEE_SMC_RETURN_OK
> + * a1 Physical address of start of SHM
> + * a2 Size of of SHM
> + * a3 Cache settings of memory, as defined by the
> + * OPTEE_SMC_SHM_* values above
> + * a4-7 Preserved
> + *
> + * Not available register usage:
> + * a0 OPTEE_SMC_RETURN_ENOTAVAIL
> + * a1-3 Not used
> + * a4-7 Preserved
> + */
> +#define OPTEE_SMC_FUNCID_GET_SHM_CONFIG 7
> +#define OPTEE_SMC_GET_SHM_CONFIG \
> + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_SHM_CONFIG)
> +
> +struct optee_smc_get_shm_config_result {
> + unsigned long status;
> + unsigned long start;
> + unsigned long size;
> + unsigned long settings;
> +};
> +
> +/*
> + * Exchanges capabilities between normal world and secure world
> + *
> + * Call register usage:
> + * a0 SMC Function ID, OPTEE_SMC_EXCHANGE_CAPABILITIES
> + * a1 bitfield of normal world capabilities OPTEE_SMC_NSEC_CAP_*
> + * a2-6 Not used
> + * a7 Hypervisor Client ID register
> + *
> + * Normal return register usage:
> + * a0 OPTEE_SMC_RETURN_OK
> + * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_*
> + * a2-7 Preserved
> + *
> + * Error return register usage:
> + * a0 OPTEE_SMC_RETURN_ENOTAVAIL, can't use the capabilities from normal world
> + * a1 bitfield of secure world capabilities OPTEE_SMC_SEC_CAP_*
> + * a2-7 Preserved
> + */
> +/* Normal world works as a uniprocessor system */
> +#define OPTEE_SMC_NSEC_CAP_UNIPROCESSOR BIT(0)
> +/* Secure world has reserved shared memory for normal world to use */
> +#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0)
> +/* Secure world can communicate via previously unregistered shared memory */
> +#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1)
> +
> +/*
> + * Secure world supports commands "register/unregister shared memory",
> + * secure world accepts command buffers located in any parts of non-secure RAM
> + */
> +#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2)
> +
> +#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
> +#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
> + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)
> +
> +struct optee_smc_exchange_capabilities_result {
> + unsigned long status;
> + unsigned long capabilities;
> + unsigned long reserved0;
> + unsigned long reserved1;
> +};
> +
> +/*
> + * Disable and empties cache of shared memory objects
> + *
> + * Secure world can cache frequently used shared memory objects, for
> + * example objects used as RPC arguments. When secure world is idle this
> + * function returns one shared memory reference to free. To disable the
> + * cache and free all cached objects this function has to be called until
> + * it returns OPTEE_SMC_RETURN_ENOTAVAIL.
> + *
> + * Call register usage:
> + * a0 SMC Function ID, OPTEE_SMC_DISABLE_SHM_CACHE
> + * a1-6 Not used
> + * a7 Hypervisor Client ID register
> + *
> + * Normal return register usage:
> + * a0 OPTEE_SMC_RETURN_OK
> + * a1 Upper 32bit of a 64bit Shared memory cookie
> + * a2 Lower 32bit of a 64bit Shared memory cookie
> + * a3-7 Preserved
> + *
> + * Cache empty return register usage:
> + * a0 OPTEE_SMC_RETURN_ENOTAVAIL
> + * a1-7 Preserved
> + *
> + * Not idle return register usage:
> + * a0 OPTEE_SMC_RETURN_EBUSY
> + * a1-7 Preserved
> + */
> +#define OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE 10
> +#define OPTEE_SMC_DISABLE_SHM_CACHE \
> + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_DISABLE_SHM_CACHE)
> +
> +struct optee_smc_disable_shm_cache_result {
> + unsigned long status;
> + unsigned long shm_upper32;
> + unsigned long shm_lower32;
> + unsigned long reserved0;
> +};
> +
> +/*
> + * Enable cache of shared memory objects
> + *
> + * Secure world can cache frequently used shared memory objects, for
> + * example objects used as RPC arguments. When secure world is idle this
> + * function returns OPTEE_SMC_RETURN_OK and the cache is enabled. If
> + * secure world isn't idle OPTEE_SMC_RETURN_EBUSY is returned.
> + *
> + * Call register usage:
> + * a0 SMC Function ID, OPTEE_SMC_ENABLE_SHM_CACHE
> + * a1-6 Not used
> + * a7 Hypervisor Client ID register
> + *
> + * Normal return register usage:
> + * a0 OPTEE_SMC_RETURN_OK
> + * a1-7 Preserved
> + *
> + * Not idle return register usage:
> + * a0 OPTEE_SMC_RETURN_EBUSY
> + * a1-7 Preserved
> + */
> +#define OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE 11
> +#define OPTEE_SMC_ENABLE_SHM_CACHE \
> + OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_ENABLE_SHM_CACHE)
> +
> +/*
> + * Resume from RPC (for example after processing a foreign interrupt)
> + *
> + * Call register usage:
> + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC
> + * a1-3 Value of a1-3 when OPTEE_SMC_CALL_WITH_ARG returned
> + * OPTEE_SMC_RETURN_RPC in a0
> + *
> + * Return register usage is the same as for OPTEE_SMC_*CALL_WITH_ARG above.
> + *
> + * Possible return values
> + * OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
> + * function.
> + * OPTEE_SMC_RETURN_OK Original call completed, result
> + * updated in the previously supplied.
> + * struct optee_msg_arg
> + * OPTEE_SMC_RETURN_RPC Call suspended by RPC call to normal
> + * world.
> + * OPTEE_SMC_RETURN_ERESUME Resume failed, the opaque resume
> + * information was corrupt.
> + */
> +#define OPTEE_SMC_FUNCID_RETURN_FROM_RPC 3
> +#define OPTEE_SMC_CALL_RETURN_FROM_RPC \
> + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_RETURN_FROM_RPC)
> +
> +#define OPTEE_SMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000
> +#define OPTEE_SMC_RETURN_RPC_PREFIX 0xFFFF0000
> +#define OPTEE_SMC_RETURN_RPC_FUNC_MASK 0x0000FFFF
> +
> +#define OPTEE_SMC_RETURN_GET_RPC_FUNC(ret) \
> + ((ret) & OPTEE_SMC_RETURN_RPC_FUNC_MASK)
> +
> +#define OPTEE_SMC_RPC_VAL(func) ((func) | OPTEE_SMC_RETURN_RPC_PREFIX)
> +
> +/*
> + * Allocate memory for RPC parameter passing. The memory is used to hold a
> + * struct optee_msg_arg.
> + *
> + * "Call" register usage:
> + * a0 This value, OPTEE_SMC_RETURN_RPC_ALLOC
> + * a1 Size in bytes of required argument memory
> + * a2 Not used
> + * a3 Resume information, must be preserved
> + * a4-5 Not used
> + * a6-7 Resume information, must be preserved
> + *
> + * "Return" register usage:
> + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
> + * a1 Upper 32bits of 64bit physical pointer to allocated
> + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
> + * be allocated.
> + * a2 Lower 32bits of 64bit physical pointer to allocated
> + * memory, (a1 == 0 && a2 == 0) if size was 0 or if memory can't
> + * be allocated
> + * a3 Preserved
> + * a4 Upper 32bits of 64bit Shared memory cookie used when freeing
> + * the memory or doing an RPC
> + * a5 Lower 32bits of 64bit Shared memory cookie used when freeing
> + * the memory or doing an RPC
> + * a6-7 Preserved
> + */
> +#define OPTEE_SMC_RPC_FUNC_ALLOC 0
> +#define OPTEE_SMC_RETURN_RPC_ALLOC \
> + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_ALLOC)
> +
> +/*
> + * Free memory previously allocated by OPTEE_SMC_RETURN_RPC_ALLOC
> + *
> + * "Call" register usage:
> + * a0 This value, OPTEE_SMC_RETURN_RPC_FREE
> + * a1 Upper 32bits of 64bit shared memory cookie belonging to this
> + * argument memory
> + * a2 Lower 32bits of 64bit shared memory cookie belonging to this
> + * argument memory
> + * a3-7 Resume information, must be preserved
> + *
> + * "Return" register usage:
> + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
> + * a1-2 Not used
> + * a3-7 Preserved
> + */
> +#define OPTEE_SMC_RPC_FUNC_FREE 2
> +#define OPTEE_SMC_RETURN_RPC_FREE \
> + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FREE)
> +
> +/*
> + * Deliver foreign interrupt to normal world.
> + *
> + * "Call" register usage:
> + * a0 OPTEE_SMC_RETURN_RPC_FOREIGN_INTR
> + * a1-7 Resume information, must be preserved
> + *
> + * "Return" register usage:
> + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
> + * a1-7 Preserved
> + */
> +#define OPTEE_SMC_RPC_FUNC_FOREIGN_INTR 4
> +#define OPTEE_SMC_RETURN_RPC_FOREIGN_INTR \
> + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_FOREIGN_INTR)
> +
> +/*
> + * Do an RPC request. The supplied struct optee_msg_arg tells which
> + * request to do and the parameters for the request. The following fields
> + * are used (the rest are unused):
> + * - cmd the Request ID
> + * - ret return value of the request, filled in by normal world
> + * - num_params number of parameters for the request
> + * - params the parameters
> + * - param_attrs attributes of the parameters
> + *
> + * "Call" register usage:
> + * a0 OPTEE_SMC_RETURN_RPC_CMD
> + * a1 Upper 32bit of a 64bit Shared memory cookie holding a
> + * struct optee_msg_arg, must be preserved, only the data should
> + * be updated
> + * a2 Lower 32bit of a 64bit Shared memory cookie holding a
> + * struct optee_msg_arg, must be preserved, only the data should
> + * be updated
> + * a3-7 Resume information, must be preserved
> + *
> + * "Return" register usage:
> + * a0 SMC Function ID, OPTEE_SMC_CALL_RETURN_FROM_RPC.
> + * a1-2 Not used
> + * a3-7 Preserved
> + */
> +#define OPTEE_SMC_RPC_FUNC_CMD 5
> +#define OPTEE_SMC_RETURN_RPC_CMD \
> + OPTEE_SMC_RPC_VAL(OPTEE_SMC_RPC_FUNC_CMD)
> +
> +/* Returned in a0 */
> +#define OPTEE_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
> +
> +/* Returned in a0 only from Trusted OS functions */
> +#define OPTEE_SMC_RETURN_OK 0x0
> +#define OPTEE_SMC_RETURN_ETHREAD_LIMIT 0x1
> +#define OPTEE_SMC_RETURN_EBUSY 0x2
> +#define OPTEE_SMC_RETURN_ERESUME 0x3
> +#define OPTEE_SMC_RETURN_EBADADDR 0x4
> +#define OPTEE_SMC_RETURN_EBADCMD 0x5
> +#define OPTEE_SMC_RETURN_ENOMEM 0x6
> +#define OPTEE_SMC_RETURN_ENOTAVAIL 0x7
> +#define OPTEE_SMC_RETURN_IS_RPC(ret) __optee_smc_return_is_rpc((ret))
> +
> +static inline bool __optee_smc_return_is_rpc(u32 ret)
> +{
> + return ret != OPTEE_SMC_RETURN_UNKNOWN_FUNCTION &&
> + (ret & OPTEE_SMC_RETURN_RPC_PREFIX_MASK) ==
> + OPTEE_SMC_RETURN_RPC_PREFIX;
> +}
> +
> +#endif /* OPTEE_SMC_H */
>
--
Julien Grall
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
next prev parent reply other threads:[~2017-10-16 14:04 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-11 19:01 [RFC 0/4] TEE mediator framework + OP-TEE mediator Volodymyr Babchuk
2017-10-11 19:01 ` [RFC 1/4] arm: add SMC wrapper that is compatible with SMCCC Volodymyr Babchuk
2017-10-16 14:53 ` Julien Grall
2017-10-11 19:01 ` [RFC 2/4] arm: add generic TEE mediator framework Volodymyr Babchuk
2017-10-16 13:00 ` Julien Grall
2017-10-17 16:22 ` Volodymyr Babchuk
2017-10-17 16:39 ` Julien Grall
2017-10-17 17:22 ` Volodymyr Babchuk
2017-10-17 17:35 ` Julien Grall
2017-10-11 19:01 ` [RFC 3/4] arm: tee: add OP-TEE header files Volodymyr Babchuk
2017-10-16 14:04 ` Julien Grall [this message]
2017-10-17 16:24 ` Volodymyr Babchuk
2017-10-17 16:41 ` Julien Grall
2017-10-11 19:01 ` [RFC 4/4] arm: tee: add basic OP-TEE mediator Volodymyr Babchuk
2017-10-16 14:36 ` Julien Grall
2017-10-17 17:08 ` Volodymyr Babchuk
2017-10-17 17:30 ` Julien Grall
2017-10-17 18:57 ` Volodymyr Babchuk
2017-10-19 14:01 ` Julien Grall
2017-10-19 15:33 ` Volodymyr Babchuk
2017-10-19 16:12 ` Julien Grall
2017-10-19 16:37 ` Volodymyr Babchuk
2017-10-19 16:52 ` Julien Grall
2017-10-16 12:00 ` [RFC 0/4] TEE mediator framework + " Julien Grall
2017-10-17 15:59 ` Volodymyr Babchuk
2017-10-20 13:11 ` Julien Grall
2017-10-20 16:57 ` Tamas K Lengyel
2017-10-20 17:46 ` Volodymyr Babchuk
2017-10-20 17:37 ` Volodymyr Babchuk
2017-10-23 16:59 ` Julien Grall
2017-10-23 20:11 ` Volodymyr Babchuk
2017-10-23 21:26 ` Stefano Stabellini
2017-10-24 16:33 ` Volodymyr Babchuk
2017-10-24 21:33 ` Stefano Stabellini
2017-10-27 13:47 ` Julien Grall
2017-10-27 19:59 ` Stefano Stabellini
2017-10-27 20:06 ` Julien Grall
2017-10-24 17:33 ` Julien Grall
2017-10-24 19:02 ` Volodymyr Babchuk
2017-11-02 13:17 ` Julien Grall
2017-11-02 16:53 ` Volodymyr Babchuk
2017-11-02 17:49 ` Julien Grall
2017-11-02 20:07 ` Volodymyr Babchuk
2017-11-07 16:03 ` Julien Grall
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=a0d1705e-35a7-4faf-a3e8-e361b99fc823@linaro.org \
--to=julien.grall@linaro.org \
--cc=julien.grall@arm.com \
--cc=sstabellini@kernel.org \
--cc=volodymyr_babchuk@epam.com \
--cc=xen-devel@lists.xen.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).