Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 66 additions & 3 deletions common/openipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,10 +693,62 @@ static int nor_split_layout(struct spi_flash *flash, ulong kernel_off, ulong roo
return 0;
if (!kernel_off || !rootfs_off ||
spi_flash_read(flash, kernel_off, sizeof(hdr), &hdr) ||
!image_check_magic(&hdr))
!image_check_magic(&hdr) || !image_check_hcrc(&hdr))
return 0;
return rootfs_off == kernel_off + ALIGN(image_get_image_size(&hdr), 0x10000);
}

/*
* Whether the split layout's kernel is whole: its header and its data CRC,
* read back from the flash. The scan clears bootfail for a split image, and
* a kernel with a sound header but a damaged payload would then fail bootm
* with nothing asking for recovery -- the boot-limit fallback retries the
* same image. Checked here, it is handed to recovery like a missing one.
* Without the memory to check, the kernel is taken as it is; bootm still
* checks it.
*/
static int nor_split_kernel_ok(struct spi_flash *flash, ulong kernel_off)
{
const ulong chunk = 0x10000;
image_header_t hdr;
ulong len, off, n;
u32 crc = 0;
u8 *buf;
int ok = 1;

if (spi_flash_read(flash, kernel_off, sizeof(hdr), &hdr) ||
!image_check_hcrc(&hdr))
return 0;
len = image_get_data_size(&hdr);
if (kernel_off + sizeof(hdr) > flash->size ||
len > flash->size - kernel_off - sizeof(hdr))
return 0;
buf = malloc_cache_aligned(chunk);
if (!buf)
return 1;
for (off = 0; ok && off < len; off += n) {
n = min(len - off, chunk);
if (spi_flash_read(flash, kernel_off + sizeof(hdr) + off, n, buf))
ok = 0;
else
crc = crc32(crc, buf, n);
}
free(buf);
return ok && crc == image_get_dcrc(&hdr);
}

/*
* A uImage whose header fails its CRC: bootm refuses it whatever verify
* says, so it boots nothing in either layout. Its size field cannot be
* trusted either, which is why nor_split_layout does not take it as split.
*/
static int nor_split_header_bad(struct spi_flash *flash, ulong kernel_off)
{
image_header_t hdr;

return kernel_off && !spi_flash_read(flash, kernel_off, sizeof(hdr), &hdr) &&
image_check_magic(&hdr) && !image_check_hcrc(&hdr);
}
#endif

int firmware_scan(void) {
Expand Down Expand Up @@ -752,6 +804,7 @@ int firmware_scan(void) {
ulong rootfs_off = 0;
uint64_t kernel_size = 0;
uint64_t rootfs_size = 0;
int bad_header = 0;

printf("Automatic flash layout detection...\n");

Expand Down Expand Up @@ -822,6 +875,7 @@ int firmware_scan(void) {
char str[32], mtds[128], mtdparts[160];
const char *mtdids = env_get("mtdids");
ulong kern_slot, root_slot;
int kernel_ok = nor_split_kernel_ok(flash, kernel_off);
Comment thread
widgetii marked this conversation as resolved.

/*
* kernsize is what loadkernel reads, and also what uk erases; rootaddr
Expand Down Expand Up @@ -865,9 +919,15 @@ int firmware_scan(void) {
* bootloader must not skip it. RAM only, like the rest of the scan. */
env_set("verify", NULL);
nor_split_bootargs();
env_set("bootfail", NULL);
if (kernel_ok) {
env_set("bootfail", NULL);
} else {
printf("Firmware is absent/corrupt (kernel at 0x%lx fails its CRC)\n", kernel_off);
env_set("bootfail", "1");
Comment thread
widgetii marked this conversation as resolved.
}
return 0;
}
bad_header = nor_split_header_bad(flash, kernel_off);
#endif

spi_flash_free(flash);
Expand Down Expand Up @@ -929,7 +989,10 @@ int firmware_scan(void) {
#endif
}

if (kernel_off && rootfs_off) {
if (bad_header) {
printf("Firmware is absent/corrupt (kernel header at 0x%lx fails its CRC)\n", kernel_off);
env_set("bootfail", "1");
} else if (kernel_off && rootfs_off) {
env_set("bootfail", NULL);
} else {
printf("Firmware is absent/corrupt (kernel=0x%lx, rootfs=0x%lx)\n", kernel_off, rootfs_off);
Expand Down
Loading