xref: /optee_os/ta/remoteproc/src/elf_parser.c (revision 155cb3e861a53dbb9dad4400590748bad8e6c639)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2023, STMicroelectronics
4  */
5 
6 #include <assert.h>
7 #include <elf_parser.h>
8 #include <string.h>
9 #include <trace.h>
10 #include <types_ext.h>
11 #include <util.h>
12 
va_in_fwm_image_range(void * va,uint8_t * fw,size_t fw_size)13 static bool va_in_fwm_image_range(void *va, uint8_t *fw, size_t fw_size)
14 {
15 	uint8_t *vaddr = va;
16 
17 	return vaddr >= fw && vaddr < fw + fw_size;
18 }
19 
e32_parse_ehdr(uint8_t * fw,size_t size)20 TEE_Result e32_parse_ehdr(uint8_t *fw, size_t size)
21 {
22 	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(void *)fw;
23 
24 	if (!fw || !IS_ALIGNED_WITH_TYPE(fw, uint32_t)) {
25 		EMSG("Invalid firmware address");
26 		return TEE_ERROR_BAD_PARAMETERS;
27 	}
28 
29 	if (size < sizeof(Elf32_Ehdr) ||
30 	    size < (ehdr->e_shoff + sizeof(Elf32_Shdr)))
31 		return TEE_ERROR_BAD_FORMAT;
32 
33 	if (!IS_ELF(*ehdr) ||
34 	    ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
35 	    ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
36 	    ehdr->e_phentsize != sizeof(Elf32_Phdr) ||
37 	    ehdr->e_shentsize != sizeof(Elf32_Shdr)) {
38 		EMSG("Invalid header");
39 		return TEE_ERROR_BAD_FORMAT;
40 	}
41 
42 	if (ehdr->e_phnum == 0) {
43 		EMSG("No loadable segment found");
44 		return TEE_ERROR_BAD_FORMAT;
45 	}
46 
47 	return TEE_SUCCESS;
48 }
49 
e32_parser_load_elf_image(uint8_t * fw,size_t fw_size,TEE_Result (* load_seg)(uint8_t * src,uint32_t size,uint32_t da,uint32_t mem_size,void * priv),void * priv_data)50 TEE_Result e32_parser_load_elf_image(uint8_t *fw, size_t fw_size,
51 				     TEE_Result (*load_seg)(uint8_t *src,
52 							    uint32_t size,
53 							    uint32_t da,
54 							    uint32_t mem_size,
55 							    void *priv),
56 				     void *priv_data)
57 {
58 	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(void *)fw;
59 	Elf32_Phdr *phdr = (void *)((int8_t *)ehdr + ehdr->e_phoff);
60 	TEE_Result res = TEE_SUCCESS;
61 	unsigned int i = 0;
62 
63 	if (!load_seg || fw + fw_size <= fw)
64 		return TEE_ERROR_BAD_PARAMETERS;
65 
66 	if (!IS_ALIGNED_WITH_TYPE(phdr, uint32_t) ||
67 	    !va_in_fwm_image_range(phdr, fw, fw_size))
68 		return TEE_ERROR_BAD_FORMAT;
69 
70 	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
71 		uint32_t dst = phdr->p_paddr;
72 		uint8_t *src = NULL;
73 
74 		if (phdr->p_type != PT_LOAD)
75 			continue;
76 
77 		if (!va_in_fwm_image_range((void *)((vaddr_t)(phdr + 1) - 1),
78 					   fw, fw_size))
79 			return TEE_ERROR_BAD_FORMAT;
80 
81 		src = (uint8_t *)fw + phdr->p_offset;
82 
83 		if (!va_in_fwm_image_range(src, fw, fw_size) ||
84 		    !va_in_fwm_image_range(src + phdr->p_filesz, fw, fw_size))
85 			return TEE_ERROR_BAD_FORMAT;
86 
87 		res = load_seg(src, phdr->p_filesz, dst, phdr->p_memsz,
88 			       priv_data);
89 		if (res)
90 			return res;
91 	}
92 
93 	return TEE_SUCCESS;
94 }
95 
e32_parser_find_rsc_table(uint8_t * fw,size_t fw_size,Elf32_Addr * rsc_addr,Elf32_Word * rsc_size)96 TEE_Result e32_parser_find_rsc_table(uint8_t *fw, size_t fw_size,
97 				     Elf32_Addr *rsc_addr,
98 				     Elf32_Word *rsc_size)
99 {
100 	Elf32_Shdr *shdr = NULL;
101 	unsigned int i = 0;
102 	char *name_table = NULL;
103 	struct resource_table *table = NULL;
104 	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(void *)fw;
105 	uint8_t *elf_data = fw;
106 
107 	if (fw + fw_size <= fw || fw + ehdr->e_shoff < fw)
108 		return TEE_ERROR_BAD_PARAMETERS;
109 
110 	shdr = (void *)(fw + ehdr->e_shoff);
111 	if (!IS_ALIGNED_WITH_TYPE(shdr, uint32_t) ||
112 	    !va_in_fwm_image_range(shdr, fw, fw_size))
113 		return TEE_ERROR_BAD_FORMAT;
114 
115 	name_table = (char *)elf_data + shdr[ehdr->e_shstrndx].sh_offset;
116 	if (!va_in_fwm_image_range(name_table, fw, fw_size))
117 		return TEE_ERROR_BAD_FORMAT;
118 
119 	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
120 		size_t size = shdr->sh_size;
121 		size_t offset = shdr->sh_offset;
122 		size_t s = 0;
123 
124 		if (!va_in_fwm_image_range(shdr, fw, fw_size))
125 			return TEE_ERROR_BAD_FORMAT;
126 
127 		if (strcmp(name_table + shdr->sh_name, ".resource_table"))
128 			continue;
129 
130 		if (!shdr->sh_size)
131 			return TEE_ERROR_NO_DATA;
132 
133 		if (offset + size > fw_size || offset + size < size) {
134 			EMSG("Resource table truncated");
135 			return TEE_ERROR_BAD_FORMAT;
136 		}
137 
138 		if (sizeof(struct resource_table) > size) {
139 			EMSG("No header found in resource table");
140 			return TEE_ERROR_BAD_FORMAT;
141 		}
142 
143 		table = (struct resource_table *)(void *)(elf_data + offset);
144 		if (!IS_ALIGNED_WITH_TYPE(table, uint32_t))
145 			return TEE_ERROR_CORRUPT_OBJECT;
146 
147 		if (table->ver != 1) {
148 			EMSG("Unsupported firmware version %"PRId32,
149 			     table->ver);
150 			return TEE_ERROR_BAD_FORMAT;
151 		}
152 
153 		if (table->reserved[0] || table->reserved[1]) {
154 			EMSG("Non zero reserved bytes");
155 			return TEE_ERROR_BAD_FORMAT;
156 		}
157 
158 		if (MUL_OVERFLOW(table->num, sizeof(*table->offset), &s) ||
159 		    ADD_OVERFLOW(s, sizeof(struct resource_table), &s) ||
160 		    s > size) {
161 			EMSG("Resource table incomplete");
162 			return TEE_ERROR_BAD_FORMAT;
163 		}
164 
165 		DMSG("Resource table address %#"PRIx32", size %"PRIu32,
166 		     shdr->sh_addr, shdr->sh_size);
167 
168 		*rsc_addr = shdr->sh_addr;
169 		*rsc_size = shdr->sh_size;
170 
171 		return TEE_SUCCESS;
172 	}
173 
174 	return TEE_ERROR_NO_DATA;
175 }
176