You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
3.2 KiB
148 lines
3.2 KiB
// SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
//
|
|
/*
|
|
* (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
|
|
* and is covered by GPLv3 license found in COPYING.
|
|
*
|
|
* update.c -- firmware update processing
|
|
*
|
|
*/
|
|
#include <string.h>
|
|
|
|
#include "fwheader.h"
|
|
#include "sha256.h"
|
|
#include "spiflash.h"
|
|
#include "utils.h"
|
|
|
|
#include "verify.h"
|
|
#include "flash.h"
|
|
#include "update.h"
|
|
|
|
static void calculate_spi_hash(
|
|
passport_firmware_header_t *hdr,
|
|
uint8_t *hash,
|
|
uint8_t hashlen
|
|
)
|
|
{
|
|
SHA256_CTX ctx;
|
|
uint32_t pos = FW_HEADER_SIZE;
|
|
uint32_t remaining = hdr->info.fwlength;
|
|
uint8_t *buf = (uint8_t *)D1_AXISRAM_BASE; /* Working memory */
|
|
|
|
sha256_init(&ctx);
|
|
|
|
sha256_update(&ctx, (uint8_t *)&hdr->info, sizeof(fw_info_t));
|
|
|
|
while (remaining > 0)
|
|
{
|
|
size_t bufsize;
|
|
|
|
if (remaining >= 8192)
|
|
bufsize = 8192;
|
|
else
|
|
bufsize = remaining;
|
|
|
|
if (spi_read(pos, bufsize, buf) != HAL_OK)
|
|
goto out;
|
|
|
|
sha256_update(&ctx, buf, bufsize);
|
|
remaining -= bufsize;
|
|
pos += bufsize;
|
|
}
|
|
|
|
sha256_final(&ctx, hash);
|
|
|
|
/* double SHA256 */
|
|
sha256_init(&ctx);
|
|
sha256_update(&ctx, hash, hashlen);
|
|
sha256_final(&ctx, hash);
|
|
|
|
out:
|
|
return;
|
|
}
|
|
|
|
static int do_update(uint32_t size)
|
|
{
|
|
int rc;
|
|
uint8_t flash_word_len = sizeof(uint32_t) * FLASH_NB_32BITWORD_IN_FLASHWORD;
|
|
uint32_t pos;
|
|
uint32_t addr;
|
|
uint32_t data[FLASH_NB_32BITWORD_IN_FLASHWORD] __attribute__((aligned(8)));
|
|
|
|
flash_unlock();
|
|
|
|
for (pos = 0, addr = FW_START; pos < size; pos += flash_word_len, addr += flash_word_len)
|
|
{
|
|
if (spi_read(pos, sizeof(data), (uint8_t *)data) != HAL_OK)
|
|
{
|
|
rc = -1;
|
|
break;
|
|
}
|
|
|
|
if (addr % FLASH_SECTOR_SIZE == 0)
|
|
{
|
|
rc = flash_sector_erase(addr);
|
|
if (rc < 0)
|
|
break;
|
|
}
|
|
|
|
rc = flash_burn(addr, (uint32_t)data);
|
|
if (rc < 0)
|
|
break;
|
|
}
|
|
|
|
flash_lock();
|
|
return rc;
|
|
}
|
|
|
|
bool is_firmware_update_present(void)
|
|
{
|
|
passport_firmware_header_t hdr = {};
|
|
|
|
if (spi_setup() != HAL_OK)
|
|
return false;
|
|
|
|
if (spi_read(0, sizeof(hdr), (void *)&hdr) != HAL_OK)
|
|
return false;
|
|
|
|
if (!verify_header(&hdr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void update_firmware(void)
|
|
{
|
|
int rc;
|
|
passport_firmware_header_t *internalhdr = FW_HDR;
|
|
passport_firmware_header_t spihdr = {0};
|
|
uint8_t fw_hash[HASH_LEN] = {0};
|
|
uint8_t zeros[FW_HEADER_SIZE] = {0};
|
|
|
|
if (spi_setup() != HAL_OK)
|
|
return;
|
|
|
|
if (spi_read(0, sizeof(spihdr), (void *)&spihdr) != HAL_OK)
|
|
return;
|
|
|
|
|
|
if (!verify_header(&spihdr))
|
|
goto out;
|
|
|
|
/* Don't allow downgrades */
|
|
if (spihdr.info.timestamp <= internalhdr->info.timestamp)
|
|
goto out;
|
|
|
|
calculate_spi_hash(&spihdr, fw_hash, sizeof(fw_hash));
|
|
|
|
if (!verify_signature(&spihdr, fw_hash, sizeof(fw_hash)))
|
|
goto out;
|
|
|
|
rc = do_update(FW_HEADER_SIZE + spihdr.info.fwlength);
|
|
if (rc < 0)
|
|
return; /* Don't erase SPI...maybe it will work next time */
|
|
|
|
out:
|
|
spi_write(0, sizeof(zeros), zeros);
|
|
}
|
|
|