/* * (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard * and is covered by GPLv3 license found in COPYING. * * sflash.c -- talk to the serial flash * */ #include "sflash.h" #include #include "delay.h" #include "stm32l4xx_hal.h" #include "sigheader.h" #include "verify.h" #include "sha256.h" #include "oled.h" #include "dispatch.h" #include "storage.h" #include "gpio.h" #include "assets/screens.h" // Connections: // - SPI2 port // - all port B // // SF_CS => PB9 // SF_SCLK => PB10 // SF_MISO => PC2 // SF_MOSI => PC3 #define SF_CS_PIN GPIO_PIN_9 // port B #define SF_SPI_SCK GPIO_PIN_10 // port B #define SF_SPI_MISO GPIO_PIN_2 // port C #define SF_SPI_MOSI GPIO_PIN_3 // port C #define CMD_WRSR 0x01 #define CMD_WRITE 0x02 #define CMD_READ 0x03 #define CMD_FAST_READ 0x0b #define CMD_RDSR 0x05 #define CMD_WREN 0x06 #define CMD_SEC_ERASE 0x20 #define CMD_RDCR 0x35 #define CMD_RD_DEVID 0x9f #define CMD_CHIP_ERASE 0xc7 // active-low chip-select line #define CS_LOW() HAL_GPIO_WritePin(GPIOB, SF_CS_PIN, 0) #define CS_HIGH() HAL_GPIO_WritePin(GPIOB, SF_CS_PIN, 1) static SPI_HandleTypeDef sf_spi_port; uint32_t sf_completed_upgrade; // sf_read_bytes() // static HAL_StatusTypeDef sf_read(uint32_t addr, int len, uint8_t *buf) { // send via SPI(1) uint8_t pkt[5] = { CMD_FAST_READ, (addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff, 0x0 }; // for fast-read case CS_LOW(); HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, pkt, sizeof(pkt), HAL_MAX_DELAY); if(rv == HAL_OK) { rv = HAL_SPI_Receive(&sf_spi_port, buf, len, HAL_MAX_DELAY); } CS_HIGH(); return rv; } // sf_wait_wip_done() // static HAL_StatusTypeDef sf_wait_wip_done() { // read RDSR (status register) and busy-wait until // the write operation is done while(1) { uint8_t pkt = CMD_RDSR, stat = 0; CS_LOW(); HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, &pkt, 1, HAL_MAX_DELAY); if(rv == HAL_OK) { rv = HAL_SPI_Receive(&sf_spi_port, &stat, 1, HAL_MAX_DELAY); } CS_HIGH(); if(rv != HAL_OK) return rv; if(stat & 0x01) continue; return HAL_OK; } } // sf_write_enable() // static HAL_StatusTypeDef sf_write_enable(void) { uint8_t pkt = CMD_WREN; CS_LOW(); HAL_StatusTypeDef rv = HAL_SPI_Transmit(&sf_spi_port, &pkt, 1, HAL_MAX_DELAY); CS_HIGH(); return rv; } // sf_write() // static HAL_StatusTypeDef sf_write(uint32_t addr, int len, const uint8_t *buf) { // enable writing HAL_StatusTypeDef rv = sf_write_enable(); if(rv) return rv; // do a "PAGE Program" aka. write uint8_t pkt[4] = { CMD_WRITE, (addr>>16) & 0xff, (addr >> 8) & 0xff, addr & 0xff }; CS_LOW(); rv = HAL_SPI_Transmit(&sf_spi_port, pkt, sizeof(pkt), HAL_MAX_DELAY); if(rv == HAL_OK) { rv = HAL_SPI_Transmit(&sf_spi_port, (uint8_t *)buf, len, HAL_MAX_DELAY); } CS_HIGH(); if(rv == HAL_OK) { rv = sf_wait_wip_done(); } return rv; } #if 0 // sf_sector_erase() // // Erase 4k of data (smallest possible amount). // static HAL_StatusTypeDef sf_sector_erase(uint32_t addr) { sf_write_enable(); /* self.cmd(CMD_SEC_ERASE, address) def is_busy(self): # return status of WIP = Write In Progress bit r = self.read_reg(CMD_RDSR, 1) return bool(r[0] & 0x01) */ } #endif // sf_setup() // // Ok to call this lots. // void sf_setup(void) { HAL_StatusTypeDef rv; // enable some internal clocks __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_SPI2_CLK_ENABLE(); // simple pins GPIO_InitTypeDef setup = { .Pin = SF_CS_PIN, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FREQ_MEDIUM, .Alternate = 0, }; HAL_GPIO_Init(GPIOB, &setup); // starting value: high HAL_GPIO_WritePin(GPIOB, SF_CS_PIN, 1); // SPI pins, on various ports setup.Pin = SF_SPI_SCK; setup.Mode = GPIO_MODE_AF_PP; setup.Alternate = GPIO_AF5_SPI2; HAL_GPIO_Init(GPIOB, &setup); setup.Pin = SF_SPI_MOSI | SF_SPI_MISO; HAL_GPIO_Init(GPIOC, &setup); memset(&sf_spi_port, 0, sizeof(sf_spi_port)); sf_spi_port.Instance = SPI2; // see SPI_InitTypeDef sf_spi_port.Init.Mode = SPI_MODE_MASTER; sf_spi_port.Init.Direction = SPI_DIRECTION_2LINES; sf_spi_port.Init.DataSize = SPI_DATASIZE_8BIT; sf_spi_port.Init.CLKPolarity = SPI_POLARITY_LOW; sf_spi_port.Init.CLKPhase = SPI_PHASE_1EDGE; sf_spi_port.Init.NSS = SPI_NSS_SOFT; sf_spi_port.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // conservative sf_spi_port.Init.FirstBit = SPI_FIRSTBIT_MSB; sf_spi_port.Init.TIMode = SPI_TIMODE_DISABLED; sf_spi_port.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; rv = HAL_SPI_Init(&sf_spi_port); ASSERT(!rv); } // sf_do_upgrade() // // Copy from SPI flash to real flash, at final executable location. // static void sf_do_upgrade(uint32_t size) { ASSERT(size >= FW_MIN_LENGTH); flash_setup0(); flash_unlock(); uint8_t tmp[256] __attribute__((aligned(8))); for(uint32_t pos=0; posfirmware_length; sha256_init(&ctx); uint32_t pos = 0; uint8_t buf[128]; STATIC_ASSERT(FW_HEADER_OFFSET % sizeof(buf) == 0); oled_show_progress(screen_verify, 1); // do part up to header. for(; pos < FW_HEADER_OFFSET; pos += sizeof(buf)) { if(sf_read(pos, sizeof(buf), buf) != HAL_OK) { fail: // fail for sure with bad signature; user can try again memset(fw_digest, 0, 32); return; } sha256_update(&ctx, buf, sizeof(buf)); } // include file header (but not the signature) ASSERT(pos == FW_HEADER_OFFSET); sha256_update(&ctx, (const uint8_t *)hdr, FW_HEADER_SIZE - 64); // then the rest after the 'header' ... the useful firmware pos += FW_HEADER_SIZE; for(int count=0; pos < total_len; pos += sizeof(buf), count++) { if(sf_read(pos, sizeof(buf), buf) != HAL_OK) { goto fail; } sha256_update(&ctx, buf, sizeof(buf)); if((count % 16) == 0) { int percent = (pos * 100) / total_len; oled_show_progress(screen_verify, percent); } } ASSERT(pos == hdr->firmware_length); sha256_final(&ctx, fw_digest); // double SHA256 sha256_init(&ctx); sha256_update(&ctx, fw_digest, 32); sha256_final(&ctx, fw_digest); } // sf_firmware_upgrade() // // maybe upgrade to a firmware image found in sflash // void sf_firmware_upgrade(void) { coldcardFirmwareHeader_t hdr = {}; // simple: just read in right spot to see header. sf_setup(); if(sf_read(FW_HEADER_OFFSET, sizeof(hdr), (void *)&hdr) != HAL_OK) { // hardware issues, keep going return; } if(!verify_header(&hdr)) { // something wrong with it. might be noise, blank or otherwise. Not an error. return; } // We have a good header so we can assume whole file there properly, right? (We could // check the signature first, but would be super slow.) And yet, // if you unpluged during the 'upload' process, after first part written, but before // you get to the end, we'd be bricked. Plus that seems really likely to happen. // // Solution: Look for a duplicated header at end of file. Will always write that last, // and even do a checksum over the data uploaded into the sflash before writing final // header out. // uint32_t off = hdr.firmware_length; coldcardFirmwareHeader_t hdr2 = {}; if(sf_read(off, sizeof(hdr2), (void *)&hdr2) != HAL_OK) { // Huh??? Hardware issue? return; } if(memcmp(&hdr, &hdr2, sizeof(hdr)) != 0) { // mismatch? -- erase stuff to recover? Or just leave it? return; } // We might upgrade now ... but only want to try once, so wipe the // second header to assure that we won't get stuck in an upgrade loop. // // LATER: if they unplug power part way thru, they land in fully-bricked mode, // even tho we have enough data (from SPI) to complete upgrade successfully. // So only clear flash once we've comlpeted successfully, or determined it // cannot work (bad signature, etc). // Check for downgrade attack: show warning and stop. if(check_is_downgrade(hdr.timestamp)) { oled_show(screen_downgrade); fail:{ // prevent second attempts. pointless uint8_t zeros[128] = { 0 }; sf_write(off, sizeof(zeros), zeros); } LOCKUP_FOREVER(); } // Check the firmware signature before changing main flash at all. uint8_t fw_digest[32]; sf_calc_checksum(&hdr, fw_digest); bool ok = verify_signature(&hdr, fw_digest); if(!ok) { // Bad signature over SPI contents; might be corruption or bad signature // We would not run the resulting firmware in main flash, so don't erase // what we have there now and abort. oled_show(screen_corrupt); goto fail; } // Start the upgrade ... takes about a minute. sf_do_upgrade(hdr.firmware_length); if(hdr.install_flags & FWHIF_HIGH_WATER) { // Maybe set a new high-waterlevel for future versions. // Ignore failures, since we can't recover anyway. record_highwater_version(hdr.timestamp); } // We're done, so clear header uint8_t zeros[128] = { 0 }; sf_write(off, sizeof(zeros), zeros); // Tell python, ultimately, that it worked. sf_completed_upgrade = SF_COMPLETED_UPGRADE; } // EOF