Andreas Dannenberg | d86f7af | 2016-06-27 09:19:18 -0500 | [diff] [blame] | 1 | /* |
| 2 | * |
| 3 | * Common security related functions for OMAP devices |
| 4 | * |
| 5 | * (C) Copyright 2016 |
| 6 | * Texas Instruments, <www.ti.com> |
| 7 | * |
| 8 | * Daniel Allred <d-allred@ti.com> |
| 9 | * Andreas Dannenberg <dannenberg@ti.com> |
| 10 | * |
| 11 | * SPDX-License-Identifier: GPL-2.0+ |
| 12 | */ |
| 13 | |
| 14 | #include <common.h> |
| 15 | #include <stdarg.h> |
| 16 | |
| 17 | #include <asm/arch/sys_proto.h> |
| 18 | #include <asm/omap_common.h> |
| 19 | #include <asm/omap_sec_common.h> |
Andreas Dannenberg | 1bb0a21 | 2016-06-27 09:19:19 -0500 | [diff] [blame] | 20 | #include <asm/spl.h> |
| 21 | #include <spl.h> |
| 22 | |
| 23 | /* Index for signature verify ROM API */ |
Andrew F. Davis | 4ac19ba | 2017-01-11 10:19:52 -0600 | [diff] [blame] | 24 | #ifdef CONFIG_AM33XX |
| 25 | #define API_HAL_KM_VERIFYCERTIFICATESIGNATURE_INDEX (0x0000000C) |
| 26 | #else |
Andreas Dannenberg | 1bb0a21 | 2016-06-27 09:19:19 -0500 | [diff] [blame] | 27 | #define API_HAL_KM_VERIFYCERTIFICATESIGNATURE_INDEX (0x0000000E) |
Andrew F. Davis | 4ac19ba | 2017-01-11 10:19:52 -0600 | [diff] [blame] | 28 | #endif |
Andreas Dannenberg | d86f7af | 2016-06-27 09:19:18 -0500 | [diff] [blame] | 29 | |
| 30 | static uint32_t secure_rom_call_args[5] __aligned(ARCH_DMA_MINALIGN); |
| 31 | |
| 32 | u32 secure_rom_call(u32 service, u32 proc_id, u32 flag, ...) |
| 33 | { |
| 34 | int i; |
| 35 | u32 num_args; |
| 36 | va_list ap; |
| 37 | |
| 38 | va_start(ap, flag); |
| 39 | |
| 40 | num_args = va_arg(ap, u32); |
| 41 | |
| 42 | if (num_args > 4) |
| 43 | return 1; |
| 44 | |
| 45 | /* Copy args to aligned args structure */ |
| 46 | for (i = 0; i < num_args; i++) |
| 47 | secure_rom_call_args[i + 1] = va_arg(ap, u32); |
| 48 | |
| 49 | secure_rom_call_args[0] = num_args; |
| 50 | |
| 51 | va_end(ap); |
| 52 | |
| 53 | /* if data cache is enabled, flush the aligned args structure */ |
| 54 | flush_dcache_range( |
| 55 | (unsigned int)&secure_rom_call_args[0], |
| 56 | (unsigned int)&secure_rom_call_args[0] + |
| 57 | roundup(sizeof(secure_rom_call_args), ARCH_DMA_MINALIGN)); |
| 58 | |
| 59 | return omap_smc_sec(service, proc_id, flag, secure_rom_call_args); |
| 60 | } |
Andreas Dannenberg | 1bb0a21 | 2016-06-27 09:19:19 -0500 | [diff] [blame] | 61 | |
| 62 | static u32 find_sig_start(char *image, size_t size) |
| 63 | { |
| 64 | char *image_end = image + size; |
| 65 | char *sig_start_magic = "CERT_"; |
| 66 | int magic_str_len = strlen(sig_start_magic); |
| 67 | char *ch; |
| 68 | |
| 69 | while (--image_end > image) { |
| 70 | if (*image_end == '_') { |
| 71 | ch = image_end - magic_str_len + 1; |
| 72 | if (!strncmp(ch, sig_start_magic, magic_str_len)) |
| 73 | return (u32)ch; |
| 74 | } |
| 75 | } |
| 76 | return 0; |
| 77 | } |
| 78 | |
| 79 | int secure_boot_verify_image(void **image, size_t *size) |
| 80 | { |
| 81 | int result = 1; |
| 82 | u32 cert_addr, sig_addr; |
| 83 | size_t cert_size; |
| 84 | |
| 85 | /* Perform cache writeback on input buffer */ |
| 86 | flush_dcache_range( |
| 87 | (u32)*image, |
| 88 | (u32)*image + roundup(*size, ARCH_DMA_MINALIGN)); |
| 89 | |
| 90 | cert_addr = (uint32_t)*image; |
| 91 | sig_addr = find_sig_start((char *)*image, *size); |
| 92 | |
| 93 | if (sig_addr == 0) { |
| 94 | printf("No signature found in image!\n"); |
| 95 | result = 1; |
| 96 | goto auth_exit; |
| 97 | } |
| 98 | |
| 99 | *size = sig_addr - cert_addr; /* Subtract out the signature size */ |
| 100 | cert_size = *size; |
| 101 | |
| 102 | /* Check if image load address is 32-bit aligned */ |
| 103 | if (!IS_ALIGNED(cert_addr, 4)) { |
| 104 | printf("Image is not 4-byte aligned!\n"); |
| 105 | result = 1; |
| 106 | goto auth_exit; |
| 107 | } |
| 108 | |
| 109 | /* Image size also should be multiple of 4 */ |
| 110 | if (!IS_ALIGNED(cert_size, 4)) { |
| 111 | printf("Image size is not 4-byte aligned!\n"); |
| 112 | result = 1; |
| 113 | goto auth_exit; |
| 114 | } |
| 115 | |
| 116 | /* Call ROM HAL API to verify certificate signature */ |
| 117 | debug("%s: load_addr = %x, size = %x, sig_addr = %x\n", __func__, |
| 118 | cert_addr, cert_size, sig_addr); |
| 119 | |
| 120 | result = secure_rom_call( |
| 121 | API_HAL_KM_VERIFYCERTIFICATESIGNATURE_INDEX, 0, 0, |
| 122 | 4, cert_addr, cert_size, sig_addr, 0xFFFFFFFF); |
Andrew F. Davis | 4f65ee3 | 2017-02-22 17:46:39 -0600 | [diff] [blame^] | 123 | |
| 124 | /* Perform cache writeback on output buffer */ |
| 125 | flush_dcache_range( |
| 126 | (u32)*image, |
| 127 | (u32)*image + roundup(*size, ARCH_DMA_MINALIGN)); |
| 128 | |
Andreas Dannenberg | 1bb0a21 | 2016-06-27 09:19:19 -0500 | [diff] [blame] | 129 | auth_exit: |
| 130 | if (result != 0) { |
| 131 | printf("Authentication failed!\n"); |
| 132 | printf("Return Value = %08X\n", result); |
| 133 | hang(); |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | * Output notification of successful authentication as well the name of |
| 138 | * the signing certificate used to re-assure the user that the secure |
| 139 | * code is being processed as expected. However suppress any such log |
| 140 | * output in case of building for SPL and booting via YMODEM. This is |
| 141 | * done to avoid disturbing the YMODEM serial protocol transactions. |
| 142 | */ |
| 143 | if (!(IS_ENABLED(CONFIG_SPL_BUILD) && |
| 144 | IS_ENABLED(CONFIG_SPL_YMODEM_SUPPORT) && |
| 145 | spl_boot_device() == BOOT_DEVICE_UART)) |
| 146 | printf("Authentication passed: %s\n", (char *)sig_addr); |
| 147 | |
| 148 | return result; |
| 149 | } |