]>
Commit | Line | Data |
---|---|---|
a131c1f4 SG |
1 | /* |
2 | * (C) Copyright 2015 Google, Inc | |
3 | * Written by Simon Glass <sjg@chromium.org> | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | * | |
7 | * Helper functions for Rockchip images | |
8 | */ | |
9 | ||
10 | #include "imagetool.h" | |
11 | #include <image.h> | |
12 | #include <rc4.h> | |
13 | #include "mkimage.h" | |
14 | #include "rkcommon.h" | |
15 | ||
a1a2dfb8 PT |
16 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) |
17 | ||
a131c1f4 SG |
18 | enum { |
19 | RK_SIGNATURE = 0x0ff0aa55, | |
20 | }; | |
21 | ||
22 | /** | |
23 | * struct header0_info - header block for boot ROM | |
24 | * | |
25 | * This is stored at SD card block 64 (where each block is 512 bytes, or at | |
26 | * the start of SPI flash. It is encoded with RC4. | |
27 | * | |
28 | * @signature: Signature (must be RKSD_SIGNATURE) | |
29 | * @disable_rc4: 0 to use rc4 for boot image, 1 to use plain binary | |
3641339e | 30 | * @init_offset: Offset in blocks of the SPL code from this header |
a131c1f4 SG |
31 | * block. E.g. 4 means 2KB after the start of this header. |
32 | * Other fields are not used by U-Boot | |
33 | */ | |
34 | struct header0_info { | |
35 | uint32_t signature; | |
36 | uint8_t reserved[4]; | |
37 | uint32_t disable_rc4; | |
3641339e JC |
38 | uint16_t init_offset; |
39 | uint8_t reserved1[492]; | |
40 | uint16_t init_size; | |
41 | uint16_t init_boot_size; | |
a131c1f4 SG |
42 | uint8_t reserved2[2]; |
43 | }; | |
44 | ||
111bcc4f PT |
45 | /** |
46 | * struct header1_info | |
47 | */ | |
48 | struct header1_info { | |
49 | uint32_t magic; | |
111bcc4f PT |
50 | }; |
51 | ||
7bf274b9 JC |
52 | /** |
53 | * struct spl_info - spl info for each chip | |
54 | * | |
55 | * @imagename: Image name(passed by "mkimage -n") | |
56 | * @spl_hdr: Boot ROM requires a 4-bytes spl header | |
57 | * @spl_size: Spl size(include extra 4-bytes spl header) | |
cfbcdade | 58 | * @spl_rc4: RC4 encode the SPL binary (same key as header) |
30827756 PT |
59 | * @spl_boot0: A new-style (ARM_SOC_BOOT0_HOOK) image that should |
60 | * have the boot magic (e.g. 'RK33') written to its first | |
61 | * word. | |
7bf274b9 | 62 | */ |
111bcc4f | 63 | |
7bf274b9 JC |
64 | struct spl_info { |
65 | const char *imagename; | |
66 | const char *spl_hdr; | |
67 | const uint32_t spl_size; | |
cfbcdade | 68 | const bool spl_rc4; |
30827756 | 69 | const bool spl_boot0; |
7bf274b9 JC |
70 | }; |
71 | ||
72 | static struct spl_info spl_infos[] = { | |
111bcc4f PT |
73 | { "rk3036", "RK30", 0x1000, false, false }, |
74 | { "rk3188", "RK31", 0x8000 - 0x800, true, false }, | |
75 | { "rk3288", "RK32", 0x8000, false, false }, | |
68d12600 | 76 | { "rk3328", "RK32", 0x8000 - 0x1000, false, false }, |
111bcc4f | 77 | { "rk3399", "RK33", 0x20000, false, true }, |
7bf274b9 JC |
78 | }; |
79 | ||
a131c1f4 SG |
80 | static unsigned char rc4_key[16] = { |
81 | 124, 78, 3, 4, 85, 5, 9, 7, | |
82 | 45, 44, 123, 56, 23, 13, 23, 17 | |
83 | }; | |
84 | ||
7bf274b9 JC |
85 | static struct spl_info *rkcommon_get_spl_info(char *imagename) |
86 | { | |
87 | int i; | |
88 | ||
24aae93f PT |
89 | if (!imagename) |
90 | return NULL; | |
91 | ||
7bf274b9 JC |
92 | for (i = 0; i < ARRAY_SIZE(spl_infos); i++) |
93 | if (!strncmp(imagename, spl_infos[i].imagename, 6)) | |
94 | return spl_infos + i; | |
95 | ||
96 | return NULL; | |
97 | } | |
98 | ||
99 | int rkcommon_check_params(struct image_tool_params *params) | |
100 | { | |
101 | int i; | |
102 | ||
103 | if (rkcommon_get_spl_info(params->imagename) != NULL) | |
24aae93f PT |
104 | return EXIT_SUCCESS; |
105 | ||
106 | /* | |
107 | * If this is a operation (list or extract), the don't require | |
108 | * imagename to be set. | |
109 | */ | |
110 | if (params->lflag || params->iflag) | |
111 | return EXIT_SUCCESS; | |
7bf274b9 JC |
112 | |
113 | fprintf(stderr, "ERROR: imagename (%s) is not supported!\n", | |
24aae93f | 114 | params->imagename ? params->imagename : "NULL"); |
7bf274b9 JC |
115 | |
116 | fprintf(stderr, "Available imagename:"); | |
117 | for (i = 0; i < ARRAY_SIZE(spl_infos); i++) | |
118 | fprintf(stderr, "\t%s", spl_infos[i].imagename); | |
119 | fprintf(stderr, "\n"); | |
120 | ||
24aae93f | 121 | return EXIT_FAILURE; |
7bf274b9 JC |
122 | } |
123 | ||
124 | const char *rkcommon_get_spl_hdr(struct image_tool_params *params) | |
125 | { | |
126 | struct spl_info *info = rkcommon_get_spl_info(params->imagename); | |
127 | ||
128 | /* | |
129 | * info would not be NULL, because of we checked params before. | |
130 | */ | |
131 | return info->spl_hdr; | |
132 | } | |
133 | ||
30827756 PT |
134 | |
135 | int rkcommon_get_spl_size(struct image_tool_params *params) | |
111bcc4f PT |
136 | { |
137 | struct spl_info *info = rkcommon_get_spl_info(params->imagename); | |
138 | ||
139 | /* | |
140 | * info would not be NULL, because of we checked params before. | |
141 | */ | |
30827756 | 142 | return info->spl_size; |
111bcc4f PT |
143 | } |
144 | ||
30827756 | 145 | bool rkcommon_need_rc4_spl(struct image_tool_params *params) |
7bf274b9 JC |
146 | { |
147 | struct spl_info *info = rkcommon_get_spl_info(params->imagename); | |
148 | ||
149 | /* | |
150 | * info would not be NULL, because of we checked params before. | |
151 | */ | |
30827756 | 152 | return info->spl_rc4; |
7bf274b9 JC |
153 | } |
154 | ||
30827756 | 155 | bool rkcommon_spl_is_boot0(struct image_tool_params *params) |
cfbcdade HS |
156 | { |
157 | struct spl_info *info = rkcommon_get_spl_info(params->imagename); | |
158 | ||
159 | /* | |
160 | * info would not be NULL, because of we checked params before. | |
161 | */ | |
30827756 | 162 | return info->spl_boot0; |
cfbcdade HS |
163 | } |
164 | ||
111bcc4f PT |
165 | static void rkcommon_set_header0(void *buf, uint file_size, |
166 | struct image_tool_params *params) | |
a131c1f4 | 167 | { |
111bcc4f | 168 | struct header0_info *hdr = buf; |
a131c1f4 | 169 | |
111bcc4f | 170 | memset(buf, '\0', RK_INIT_OFFSET * RK_BLK_SIZE); |
a131c1f4 | 171 | hdr->signature = RK_SIGNATURE; |
cfbcdade | 172 | hdr->disable_rc4 = !rkcommon_need_rc4_spl(params); |
3641339e | 173 | hdr->init_offset = RK_INIT_OFFSET; |
a131c1f4 | 174 | |
a1a2dfb8 PT |
175 | hdr->init_size = DIV_ROUND_UP(file_size, RK_BLK_SIZE); |
176 | /* | |
177 | * The init_size has to be a multiple of 4 blocks (i.e. of 2K) | |
178 | * or the BootROM will not boot the image. | |
179 | * | |
180 | * Note: To verify that this is not a legacy constraint, we | |
181 | * rechecked this against the RK3399 BootROM. | |
182 | */ | |
183 | hdr->init_size = ROUND(hdr->init_size, 4); | |
184 | /* | |
185 | * The images we create do not contain the stage following the SPL as | |
186 | * part of the SPL image, so the init_boot_size (which might have been | |
187 | * read by Rockchip's miniloder) should be the same as the init_size. | |
188 | */ | |
189 | hdr->init_boot_size = hdr->init_size; | |
a131c1f4 SG |
190 | |
191 | rc4_encode(buf, RK_BLK_SIZE, rc4_key); | |
111bcc4f PT |
192 | } |
193 | ||
194 | int rkcommon_set_header(void *buf, uint file_size, | |
195 | struct image_tool_params *params) | |
196 | { | |
197 | struct header1_info *hdr = buf + RK_SPL_HDR_START; | |
198 | ||
199 | if (file_size > rkcommon_get_spl_size(params)) | |
200 | return -ENOSPC; | |
201 | ||
202 | rkcommon_set_header0(buf, file_size, params); | |
203 | ||
ea3729e2 | 204 | /* Set up the SPL name */ |
111bcc4f PT |
205 | memcpy(&hdr->magic, rkcommon_get_spl_hdr(params), RK_SPL_HDR_SIZE); |
206 | ||
111bcc4f PT |
207 | if (rkcommon_need_rc4_spl(params)) |
208 | rkcommon_rc4_encode_spl(buf, RK_SPL_HDR_START, | |
209 | params->file_size - RK_SPL_HDR_START); | |
a131c1f4 SG |
210 | |
211 | return 0; | |
212 | } | |
cfbcdade HS |
213 | |
214 | void rkcommon_rc4_encode_spl(void *buf, unsigned int offset, unsigned int size) | |
215 | { | |
216 | unsigned int remaining = size; | |
217 | ||
218 | while (remaining > 0) { | |
219 | int step = (remaining > RK_BLK_SIZE) ? RK_BLK_SIZE : remaining; | |
220 | ||
221 | rc4_encode(buf + offset, step, rc4_key); | |
222 | offset += RK_BLK_SIZE; | |
223 | remaining -= step; | |
224 | } | |
225 | } | |
111bcc4f | 226 | |
366aad4d PT |
227 | int rkcommon_vrec_header(struct image_tool_params *params, |
228 | struct image_type_params *tparams, | |
229 | unsigned int alignment) | |
111bcc4f | 230 | { |
366aad4d PT |
231 | unsigned int unpadded_size; |
232 | unsigned int padded_size; | |
233 | ||
111bcc4f PT |
234 | /* |
235 | * The SPL image looks as follows: | |
236 | * | |
237 | * 0x0 header0 (see rkcommon.c) | |
238 | * 0x800 spl_name ('RK30', ..., 'RK33') | |
ea3729e2 PT |
239 | * (start of the payload for AArch64 payloads: we expect the |
240 | * first 4 bytes to be available for overwriting with our | |
241 | * spl_name) | |
111bcc4f | 242 | * 0x804 first instruction to be executed |
ea3729e2 | 243 | * (start of the image/payload for 32bit payloads) |
111bcc4f | 244 | * |
ea3729e2 PT |
245 | * For AArch64 (ARMv8) payloads, natural alignment (8-bytes) is |
246 | * required for its sections (so the image we receive needs to | |
247 | * have the first 4 bytes reserved for the spl_name). Reserving | |
248 | * these 4 bytes is done using the BOOT0_HOOK infrastructure. | |
111bcc4f | 249 | * |
ea3729e2 PT |
250 | * Depending on this, the header is either 0x800 (if this is a |
251 | * 'boot0'-style payload, which has reserved 4 bytes at the | |
252 | * beginning for the 'spl_name' and expects us to overwrite | |
253 | * its first 4 bytes) or 0x804 bytes in length. | |
111bcc4f | 254 | */ |
30827756 PT |
255 | if (rkcommon_spl_is_boot0(params)) |
256 | tparams->header_size = RK_SPL_HDR_START; | |
111bcc4f PT |
257 | else |
258 | tparams->header_size = RK_SPL_HDR_START + 4; | |
259 | ||
260 | /* Allocate, clear and install the header */ | |
261 | tparams->hdr = malloc(tparams->header_size); | |
262 | memset(tparams->hdr, 0, tparams->header_size); | |
366aad4d PT |
263 | tparams->header_size = tparams->header_size; |
264 | ||
265 | /* | |
266 | * If someone passed in 0 for the alignment, we'd better handle | |
267 | * it correctly... | |
268 | */ | |
269 | if (!alignment) | |
270 | alignment = 1; | |
271 | ||
272 | unpadded_size = tparams->header_size + params->file_size; | |
273 | padded_size = ROUND(unpadded_size, alignment); | |
274 | ||
275 | return padded_size - unpadded_size; | |
111bcc4f | 276 | } |