blob: 118b274414ad4b1f93fd5c2e086b5c64befe9f80 [file] [log] [blame]
Kyle Swenson3c6e30f2021-03-29 09:52:45 -06001/*
2 * Copyright (C) 2019 Cradlepoint <kswenson@cradlepoint.com>
3 *
4 * Commands for reading/writing/updating bootconfig paritions
5 *
6 * Probably GPLv2 cause everything else in here is too
7 */
8
9#include <common.h>
10#include <command.h>
11#include <environment.h>
12#include <malloc.h>
13
14#include <asm/arch-qca-common/smem.h>
15
16#include <mmc.h>
17#include <part.h>
18#include <part_efi.h>
19
20#include <asm/byteorder.h>
21#include <linux/compiler.h>
22#include <linux/ctype.h>
23#include <linux/err.h>
24
25#include <bootconfig.h>
26#include <blkdev.h>
27#include <cp_board.h>
28DECLARE_GLOBAL_DATA_PTR;
29
30static struct bootconfig* gBootconfig0Ptr = NULL;
31static struct bootconfig* gBootconfig1Ptr = NULL;
32static struct bootconfig* gCurrentBootconfig= NULL;
33
34struct bootconfig * init_bootconfig()
35{
36 if (gBootconfig0Ptr && gBootconfig1Ptr && gCurrentBootconfig) {
37 return gCurrentBootconfig;
38 }
39 printf("Bootconfig init begin\n");
40 if (!gBootconfig0Ptr) {
41 gBootconfig0Ptr = (struct bootconfig *) malloc(sizeof(struct bootconfig));
42 if (gBootconfig0Ptr == NULL) {
43 printf("Failed to malloc a struct bootconfig during bootconfig init\n");
44 goto init_malloc_fail;
45 }
46 }
47 if (!gBootconfig1Ptr) {
48 gBootconfig1Ptr = (struct bootconfig *) malloc(sizeof(struct bootconfig));
49 if (gBootconfig1Ptr == NULL) {
50 printf("Failed to malloc a struct bootconfig during bootconfig init\n");
51 goto init_malloc_fail;
52 }
53 }
54 gBootconfig0Ptr = read_bootconfig(gBootconfig0Ptr, 0);
55 if (!gBootconfig0Ptr) {
56 printf("Failed to read the bootconfig\n");
57 goto init_malloc_fail;
58 }
59 gBootconfig1Ptr = read_bootconfig(gBootconfig1Ptr, 1);
60 if (!gBootconfig1Ptr){
61 printf("Failed to read the bootconfig1\n");
62 goto init_malloc_fail;
63 }
64
65 if (gBootconfig0Ptr->age >= gBootconfig1Ptr->age) {
66 gCurrentBootconfig = gBootconfig0Ptr;
67 } else {
68 printf("Bootconfig1 Age is older than Bootconfig0\n");
69 gCurrentBootconfig = gBootconfig1Ptr;
70 }
71
72 return gCurrentBootconfig;
73init_malloc_fail:
74 if (gBootconfig0Ptr) {
75 free(gBootconfig0Ptr);
76 gBootconfig0Ptr = NULL;
77 }
78 if (gBootconfig1Ptr) {
79 free(gBootconfig1Ptr);
80 gBootconfig1Ptr = NULL;
81 }
82 return NULL;
83}
84
85struct cp_part* get_active_partition_info(char * name)
86{
87 debug("%s(name=%s)\n", __func__, name);
88 char * real_name = (char*) malloc(sizeof(char) * MAX_PART_NAME_LENGTH );
89 if (real_name == NULL){
90 printf("Failed to allocate the real partition name\n");
91 return NULL;
92 }
93 real_name = memset(real_name, 0, sizeof(char)* MAX_PART_NAME_LENGTH);
94 strcpy(real_name, name);
95
96 if (smem_bootconfig_info() == 0) {
97 if (get_active_partition(name)) {
98 strcat(real_name, "_1");
99 }
100 } else {
101 printf("Failed to get smem_bootconfig_info()\n");
102 }
103 debug("Returning partition info for %s\n", real_name);
104 struct cp_part * p = get_partition(real_name);
105 free(real_name);
106 return p;
107
108}
109
110void deinit_bootconfig()
111{
112 gCurrentBootconfig = NULL;
113 if (gBootconfig0Ptr){
114 free(gBootconfig0Ptr);
115 gBootconfig0Ptr = NULL;
116 }
117 if (gBootconfig1Ptr){
118 free(gBootconfig1Ptr);
119 gBootconfig1Ptr = NULL;
120 }
121}
122int switch_primary_boot(const char * partition_name)
123{
124
125 struct bootconfig * bootconfig_buffer = init_bootconfig();
126 if (!bootconfig_buffer){
127 printf("Failed to init the bootconfig \n");
128 return -2;
129 }
130 for (int i = 0; i < bootconfig_buffer->num_alt_parts; i++) {
131
132 /* We want things like APPSBL_1 to be the same as APPSBL below, hence the strncmp */
133 ssize_t ncompare = strlen(bootconfig_buffer->partitions[i].name)-1;
134 if (ncompare <= 0) {
135 printf("Warning ncompare is %d\n", ncompare);
136 }
137 printf("comparing the first %zu bytes of %s and %s\n", ncompare, partition_name, bootconfig_buffer->partitions[i].name);
138 if (strncmp(bootconfig_buffer->partitions[i].name, partition_name, ncompare) == 0) {
139 printf("Match found, primary_boot value for %s is %u\n", partition_name, bootconfig_buffer->partitions[i].primary_boot);
140 if(bootconfig_buffer->partitions[i].primary_boot){
141 bootconfig_buffer->partitions[i].primary_boot = 0;
142 } else {
143 bootconfig_buffer->partitions[i].primary_boot = 1;
144 }
145 printf("primary_boot value for %s has been updated to %u\n", partition_name, bootconfig_buffer->partitions[i].primary_boot);
146 return 0;
147 }
148 }
149 printf("Failed to find partition %s\n", partition_name);
150 return -1;
151}
152
153
154/* Take a partition name as input, return the disk_partition_t for the right partition to upgrade */
155int upgrade_failsafe_partition(const char * partition_name, void* upgrade_data, uint32_t upgrade_length)
156{
157 struct cp_part * upgrade_part = NULL;
158 struct bootconfig * bc = init_bootconfig();
159 if (!bc) {
160 printf("Failed to init bootconfig\n");
161 return -EINVAL;
162 }
163 upgrade_part = find_upgrade_partition(partition_name);
164 if (upgrade_part == NULL) {
165 printf("Failed to find upgrade partition for %s\n", partition_name);
166 return -ENOENT;
167 }
168
169 printf("Upgrading partition %s\n", upgrade_part->name);
170 if (upgrade_length > (PARTITION_SIZE(upgrade_part))) {
171 printf("Upgrade length (0x%X) is too large to fit into the partition of size %u bytes (0x%X)\n", upgrade_length, PARTITION_SIZE(upgrade_part),PARTITION_SIZE(upgrade_part) );
172 return -ENOMEM;
173 }
174 partition_erase(upgrade_part);
175 partition_write(upgrade_part, 0, upgrade_length, upgrade_data);
176
177 if(switch_primary_boot(partition_name)) {
178 printf("We might've failed to siwtch the primary boot partition for %s\n", partition_name);
179 }
180 return 0;
181}
182
183int upgrade_complete(void)
184{
185 struct bootconfig * bc = init_bootconfig();
186 if (!bc) {
187 printf("Failed to initalize the bootconfig\n");
188 return -1;
189 }
190 bc->age += 1;
191 return write_bootconfig(bc);
192}
193int write_bootconfig(struct bootconfig * bootconfig )
194{
195 struct cp_part * bc = get_partition("0:BOOTCONFIG");
196 struct cp_part * bc1 = get_partition("0:BOOTCONFIG1");
197
198
199
200 uint32_t buffer_len= PAD_SIZE(sizeof(struct bootconfig), bc->blkdev->sector_size);
201 printf("About to allocate 0x%X bytes for a bootconfig buffer", buffer_len);
202 uint8_t * buffer = (uint8_t*) malloc(sizeof(uint8_t)*buffer_len);
203 if (buffer == NULL) {
204 printf("Failed to allcoate buffer to serialize the bootcofnig\n");
205 return -1;
206 }
207 buffer = memset(buffer, 0, buffer_len);
208 if (!buffer) {
209 printf("Failed to memset buffer to write the bootconfigs!\n");
210 return -1;
211 }
212 buffer = serialize_bootconfig(buffer, gCurrentBootconfig);
213 if (!buffer) {
214 printf("Failed to serialize the bootconfig buffer for writing\n");
215 return -1;
216 }
217
218 printf("Writing %d bytes out to partition %s\n", buffer_len, bc->name);
219 partition_write(bc, 0, buffer_len, buffer);
220 printf("Writing %d bytes out to partition %s\n", buffer_len, bc1->name);
221 partition_write(bc1, 0, buffer_len, buffer);
222
223 if (buffer) {
224 free(buffer);
225 }
226 deinit_bootconfig();
227 gCurrentBootconfig = init_bootconfig();
228 return 0;
229}
230struct bootconfig * read_bootconfig(struct bootconfig * bootconfig, int primary)
231{
232 struct cp_part * part = NULL;
233
234 if (primary) {
235 part = get_partition("0:BOOTCONFIG");
236 } else {
237 part = get_partition("0:BOOTCONFIG1");
238 }
239
240 if (!part) {
241 printf("Failed to get the bootconfig parition!\n");
242 return NULL;
243 }
244 uint8_t * buffer = (uint8_t*) malloc(PARTITION_SIZE(part));
245 if (!buffer) {
246 printf("Failed to allocate buffer to read the bootconfigs!\n");
247 return NULL;
248 }
249
250 partition_read(part, 0, PARTITION_SIZE(part), buffer);
251 struct bootconfig * res = parse_bootconfig(bootconfig, buffer);
252 if (buffer) {
253 free(buffer);
254 }
255 return res;
256}
257
258void print_bootconfig_partition(struct bootconfig_partition * p)
259{
260 if (!p) {
261 return ;
262 }
263 printf("%s\t\t\tPrimary Boot: %d\n", p->name, p->primary_boot);
264 return;
265}
266void print_bootconfig(struct bootconfig * bc)
267{
268 if (!bc) {
269 printf("NULL BOOTCONFIG\n");
270 return;
271 }
272
273 printf("BOOTCONFIG: (Magic is 0x%X)\n", bc->start_magic);
274 printf("\tAGE: %d\n", bc->age);
275 printf("\tNum Parts: %d\n", bc->num_alt_parts);
276 printf("\tpartiions: %d\n", bc->num_alt_parts);
277 printf("\tNum Parts: %d\n", bc->num_alt_parts);
278 for (int i = 0; i < bc->num_alt_parts; i++) {
279 printf("\t");
280 print_bootconfig_partition(&bc->partitions[i]);
281 }
282 printf("BOOTCONFIG: (End Magic is 0x%X)\n", bc->end_magic);
283}
284
285
286uint8_t * serialize_bootconfig(uint8_t * buffer, struct bootconfig * bootconfig)
287{
288 printf("Serilizing a bootconfig structure into buffer at 0x%p from struct bootconfig at 0x%p\n", (void*)buffer, (void*)bootconfig);
289 return (uint8_t*) memcpy(buffer, bootconfig, sizeof(struct bootconfig));
290}
291
292struct bootconfig * parse_bootconfig(struct bootconfig * bootconfig, uint8_t * buffer)
293{
294 printf("deserilizing a bootconfig structure into struct bootconfig at 0x%p from buffer at 0x%p\n", (void*)bootconfig, (void*)buffer);
295 struct bootconfig * setbc = memset(bootconfig, 0, sizeof(struct bootconfig));
296 return (struct bootconfig *) memcpy(setbc, buffer, sizeof(struct bootconfig));
297
298}
299static void display_bootconfig_info(void)
300{
301 struct bootconfig* bc = init_bootconfig();
302 if (!bc) {
303 printf("Failed to init the bootconfig\n");
304 return;
305 }
306
307 printf("Active bootconfig is ");
308 if(bc == gBootconfig0Ptr) {
309 printf("primary");
310 }
311 if(bc == gBootconfig1Ptr) {
312 printf("secondary");
313 }
314 printf("\n");
315 print_bootconfig(bc);
316 printf("\n\nPrimary bootconfig: \n");
317 print_bootconfig(gBootconfig0Ptr);
318 printf("\n\nSecondary bootconfig: \n");
319 print_bootconfig(gBootconfig1Ptr);
320
321}
322/**
323 * do_bootconfig() - Handle the "bootconfig" command-line command
324 * @cmdtp: Command data struct pointer
325 * @flag: Command flag
326 * @argc: Command-line argument count
327 * @argv: Array of command-line arguments
328 *
329 * Returns zero on success, CMD_RET_USAGE in case of misuse and negative
330 * on error.
331 */
332static int do_bootconfig_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
333{
334 struct cp_part * part = NULL;
335
336 switch(argc) {
337 case 3:
338 printf("Switching the primary boot of %s\n", argv[2]);
339 if (switch_primary_boot(argv[2]) != 0) {
340 printf("Failed\n");
341 return CMD_RET_FAILURE;
342 }
343 upgrade_complete();
344 break;
345 case 2:
346 printf("Active partition for %s\n", argv[1]);
347 part = get_active_partition_info(argv[1]);
348 if (part == NULL) {
349 printf("Partition %s not found\n", argv[1]);
350 return CMD_RET_FAILURE;
351 }
352 printf("Active partition for %s is %s\n", argv[1], part->name);
353 break;
354 case 1:
355 display_bootconfig_info();
356 break;
357 default:
358 return CMD_RET_USAGE;
359 }
360 return CMD_RET_SUCCESS;
361
362
363 return 0;
364
365}
366
367/***************************************************/
368
369U_BOOT_CMD(
370 bootconfig, 6, 1, do_bootconfig_cmd,
371 "Bootconfig Information",
372 "bootconfig"
373);