diff options
Diffstat (limited to 'plat/gxb/scp_bootloader.c')
-rw-r--r-- | plat/gxb/scp_bootloader.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/plat/gxb/scp_bootloader.c b/plat/gxb/scp_bootloader.c new file mode 100644 index 0000000..305029f --- /dev/null +++ b/plat/gxb/scp_bootloader.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 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. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * 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. + */ + +#include <arch_helpers.h> +#include <platform.h> +#include "plat_def.h" +#include "mhu.h" +#include "scp_bootloader.h" +#include "scpi.h" + +/* Boot commands sent from AP -> SCP */ +#define BOOT_CMD_START 0x01 +#define BOOT_CMD_DATA 0x02 + +typedef struct { + uint32_t image_size; +} cmd_start_payload; + +typedef struct { + uint32_t sequence_num; + uint32_t offset; + uint32_t size; +} cmd_data_payload; + +#define BOOT_DATA_MAX_SIZE 0x1000 + +/* Boot commands sent from SCP -> AP */ +#define BOOT_CMD_ACK 0x03 +#define BOOT_CMD_NACK 0x04 + +typedef struct { + uint32_t sequence_num; +} cmd_ack_payload; + +/* + * Unlike the runtime protocol, the boot protocol uses the same memory region + * for both AP -> SCP and SCP -> AP transfers; define the address of this... + */ +static void * const cmd_payload = (void *)(MHU_SECURE_BASE + 0x0080); + +static void *scp_boot_message_start(void) +{ + mhu_secure_message_start(); + + return cmd_payload; +} + +static void scp_boot_message_send(unsigned command, size_t size) +{ + /* Make sure payload can be seen by SCP */ + if (MHU_PAYLOAD_CACHED) + flush_dcache_range((unsigned long)cmd_payload, size); + + /* Send command to SCP */ + mhu_secure_message_send(command | (size << 8)); +} + +static uint32_t scp_boot_message_wait(size_t size) +{ + uint32_t response = mhu_secure_message_wait(); + + /* Make sure we see the reply from the SCP and not any stale data */ + if (MHU_PAYLOAD_CACHED) + inv_dcache_range((unsigned long)cmd_payload, size); + + return response & 0xff; +} + +static void scp_boot_message_end(void) +{ + mhu_secure_message_end(); +} + +static int transfer_block(uint32_t sequence_num, uint32_t offset, uint32_t size) +{ + cmd_data_payload *cmd_data = scp_boot_message_start(); + cmd_data->sequence_num = sequence_num; + cmd_data->offset = offset; + cmd_data->size = size; + + scp_boot_message_send(BOOT_CMD_DATA, sizeof(*cmd_data)); + + cmd_ack_payload *cmd_ack = cmd_payload; + int ok = scp_boot_message_wait(sizeof(*cmd_ack)) == BOOT_CMD_ACK + && cmd_ack->sequence_num == sequence_num; + + scp_boot_message_end(); + + return ok; +} + +int scp_bootloader_transfer(void *image, unsigned int image_size) +{ + uintptr_t offset = (uintptr_t)image - MHU_SECURE_BASE; + uintptr_t end = offset + image_size; + uint32_t response; + + mhu_secure_init(); + + /* Initiate communications with SCP */ + do { + cmd_start_payload *cmd_start = scp_boot_message_start(); + cmd_start->image_size = image_size; + + scp_boot_message_send(BOOT_CMD_START, sizeof(*cmd_start)); + + response = scp_boot_message_wait(0); + + scp_boot_message_end(); + } while (response != BOOT_CMD_ACK); + + /* Transfer image to SCP a block at a time */ + uint32_t sequence_num = 1; + size_t size; + while ((size = end - offset) != 0) { + if (size > BOOT_DATA_MAX_SIZE) + size = BOOT_DATA_MAX_SIZE; + while (!transfer_block(sequence_num, offset, size)) + ; /* Retry forever */ + offset += size; + sequence_num++; + } + + /* Wait for SCP to signal it's ready */ + return scpi_wait_ready(); +} |