int get_recovery_message(struct recovery_message *out) { struct ptentry *ptn; struct ptable *ptable; unsigned offset = 0; unsigned pagesize = flash_page_size(); ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "ERROR: Partition table not found\n"); return -1; } ptn = ptable_find(ptable, "misc"); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No misc partition found\n"); return -1; } offset += (pagesize * MISC_COMMAND_PAGE); if (flash_read(ptn, offset, (void *) buf, pagesize)) { dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n"); return -1; } memcpy(out, buf, sizeof(*out)); return 0; }
static int set_ssd_radio_update (char *name) { struct ptentry *ptn; struct ptable *ptable; unsigned int ssd_cookie[2] = {0x53534443, 0x4F4F4B49}; unsigned pagesize = flash_page_size(); unsigned pagemask = pagesize -1; unsigned n = 0; ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "ERROR: Partition table not found\n"); return -1; } n = (sizeof(ssd_cookie) + pagemask) & (~pagemask); ptn = ptable_find(ptable, name); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No %s partition found\n", name); return -1; } if (flash_write(ptn, 0, ssd_cookie, n)) { dprintf(CRITICAL, "ERROR: flash write fail!\n"); return -1; } dprintf(INFO, "FOTA partition written successfully!"); return 0; }
int read_update_header_for_bootloader(struct update_header *header) { struct ptentry *ptn; struct ptable *ptable; unsigned offset = 0; unsigned pagesize = flash_page_size(); ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "ERROR: Partition table not found\n"); return -1; } ptn = ptable_find(ptable, "cache"); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No cache partition found\n"); return -1; } if (flash_read(ptn, offset, buf, pagesize)) { dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n"); return -1; } memcpy(header, buf, sizeof(*header)); if (strncmp((char *) header->MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE)) { return -1; } return 0; }
int update_firmware_emmc_image (struct update_header *header, char *name) { unsigned offset = 0; unsigned pagesize = flash_page_size(); unsigned pagemask = pagesize -1; unsigned n = 0; unsigned long long ptn = 0; unsigned ret; ptn = emmc_ptn_offset("cache"); if (ptn == 0) { dprintf(CRITICAL, "ERROR: No cache partition found\n"); return -1; } offset += header->image_offset; n = (header->image_length + pagemask) & (~pagemask); if (emmc_read(ptn, n+60, SCRATCH_ADDR)) { dprintf(CRITICAL, "ERROR: Cannot read bootloader image\n"); return -1; } memcpy(SCRATCH_ADDR,SCRATCH_ADDR+0x03c, n); if (!strcmp(name, "bootloader")) FwdnUpdateBootSDFirmware(n); dprintf(INFO, "Partition writen successfully!"); return 0; }
// // Запись одной страницы данных (в режиме мультиблочной записи). // data - указатель на буфер с данными // size - размер буфера // static int do_write_one_page(sdhc_spi_t *m, void *data, unsigned size) { int res; // Дожидаемся освобождения линии res = wait_line_free(m); if (res != FLASH_ERR_OK) return res; // Отправляем маркер начала данных m->msg.word_count = 1; m->databuf[0] = MULTI_START_BLOCK_TOKEN; if (spim_trx(m->spi, &m->msg) != SPI_ERR_OK) return FLASH_ERR_IO; // Выдаём требуемый размер данных m->msg.rx_data = 0; m->msg.tx_data = data; m->msg.word_count = size; if (spim_trx(m->spi, &m->msg) != SPI_ERR_OK) { return FLASH_ERR_IO; } // Обмен с картой всегда происходит блоками по 512 байт. // Поэтому оставшиеся байты всё равно необходимо передать. Для этого // используем вспомогательный буфер databuf size = flash_page_size((flashif_t*)m) - size; m->msg.tx_data = m->databuf; if (size > 0) memset(m->databuf, 0xFF, sizeof(m->databuf)); while (size > 0) { m->msg.word_count = (sizeof(m->databuf) < size) ? sizeof(m->databuf) : size; if (spim_trx(m->spi, &m->msg) != SPI_ERR_OK) return FLASH_ERR_IO; size -= m->msg.word_count; }; // Дожидаемся ответа со статусом завершения операции m->msg.rx_data = m->databuf; m->msg.word_count = 1; uint8_t token; do { m->databuf[0] = 0xFF; if (spim_trx(m->spi, &m->msg) != SPI_ERR_OK) return FLASH_ERR_IO; token = m->databuf[0] & DATA_RESPONSE_MASK; } while (token != DATA_ACCEPTED && token != DATA_WRITE_ERROR); if (token == DATA_WRITE_ERROR) return FLASH_ERR_IO; return FLASH_ERR_OK; }
int update_firmware_image (struct update_header *header, char *name) { struct ptentry *ptn; struct ptable *ptable; unsigned offset = 0; unsigned pagesize = flash_page_size(); unsigned pagemask = pagesize -1; unsigned n = 0; void *scratch_addr = target_get_scratch_address(); ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "ERROR: Partition table not found\n"); return -1; } ptn = ptable_find(ptable, "cache"); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No cache partition found\n"); return -1; } offset += header->image_offset; n = (header->image_length + pagemask) & (~pagemask); if (flash_read(ptn, offset, scratch_addr, n)) { dprintf(CRITICAL, "ERROR: Cannot read radio image\n"); return -1; } ptn = ptable_find(ptable, name); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No %s partition found\n", name); return -1; } if (flash_write(ptn, 0, scratch_addr, n)) { dprintf(CRITICAL, "ERROR: flash write fail!\n"); return -1; } dprintf(INFO, "Partition writen successfully!"); return 0; }
int update_firmware_image_v8(struct update_header *header, char *name) { unsigned offset = 0; unsigned pagesize = flash_page_size(); unsigned pagemask = pagesize -1; unsigned n = 0; unsigned long long ptn = 0; unsigned ret; ptn = flash_ptn_offset("cache"); if (ptn == 0) { dprintf(CRITICAL, "ERROR: No cache partition found\n"); return -1; } offset += header->image_offset; n = (header->image_length + pagemask) & (~pagemask); if (flash_read_tnftl_v8(ptn, n+60, SCRATCH_ADDR)) { dprintf(CRITICAL, "ERROR: Cannot read bootloader image\n"); return -1; } memcpy(SCRATCH_ADDR,SCRATCH_ADDR+0x03c, n); if (!strcmp(name, "bootloader")) { dprintf(ALWAYS, "\n[NAND] Bootloader Update Start !!! \n"); ret = flash_write_tnftl_v8( name, 0, n , SCRATCH_ADDR); if( ret != 0 ) { dprintf(ALWAYS, "\n[NAND] Bootloader Update Fail [ret:0x%08X]!!! \n", ret); return ret; } else dprintf(ALWAYS, "\n[NAND] Bootloader Update Success !!! \n"); } dprintf(INFO, "Partition writen successfully!"); return 0; }
static int read_from_flash(struct ptentry* ptn, int offset, int size, void *dest) { void *buffer = NULL; unsigned page_size = flash_page_size(); unsigned page_mask = page_size - 1; int read_size = (size + page_mask) & (~page_mask); buffer = malloc(read_size); if(!buffer){ dprintf(CRITICAL, "ERROR : Malloc failed for read_from_flash \n"); return -1; } if(flash_read(ptn, offset, buffer, read_size)){ dprintf(CRITICAL, "ERROR : Flash read failed \n"); return -1; } memcpy(dest, buffer, size); free(buffer); return 0; }
// // Чтение одной страницы данных (в режиме мультиблочного чтения). // data - указатель на буфер, в который следует положить принятые данные // size - размер буфера // existing_data - если не 0, то содержит указатель на уже принятую // часть данных (она лежит в буфере databuf). // static int do_read_one_page(sdhc_spi_t *m, void *data, unsigned size, uint8_t *existing_data) { int offset = 0; // Перенос уже принятой части данных в буфер data if (existing_data) { offset = existing_data - m->databuf + m->msg.word_count; memcpy(data, existing_data, offset); data = (uint8_t *)data + offset; } // Приём оставшихся данных с учётом их длины m->msg.rx_data = data; m->msg.tx_data = data; m->msg.word_count = size - offset; memset(data, 0xFF, m->msg.word_count); if (spim_trx(m->spi, &m->msg) != SPI_ERR_OK) return FLASH_ERR_IO; // Обмен с картой всегда происходит блоками по 512 байт. // Даже оставшиеся данные не нужны (size < 512), всё равно // их нужно дочитать из SPI - в вспомогательный буфер databuf. m->msg.rx_data = 0; m->msg.tx_data = m->databuf; size = flash_page_size((flashif_t*)m) - size + 3; memset(m->databuf, 0xFF, sizeof(m->databuf)); do { m->msg.word_count = (sizeof(m->databuf) < size) ? sizeof(m->databuf) : size; if (spim_trx(m->spi, &m->msg) != SPI_ERR_OK) return FLASH_ERR_IO; size -= m->msg.word_count; } while (size > 0); m->msg.rx_data = m->databuf; return FLASH_ERR_OK; }
int set_recovery_message(const struct recovery_message *in) { struct ptentry *ptn; struct ptable *ptable; unsigned offset = 0; unsigned pagesize = flash_page_size(); unsigned n = 0; void *scratch_addr = target_get_scratch_address(); ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "ERROR: Partition table not found\n"); return -1; } ptn = ptable_find(ptable, "misc"); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No misc partition found\n"); return -1; } n = pagesize * (MISC_COMMAND_PAGE + 1); if (flash_read(ptn, offset, scratch_addr, n)) { dprintf(CRITICAL, "ERROR: Cannot read recovery_header\n"); return -1; } offset += (pagesize * MISC_COMMAND_PAGE); offset += (unsigned) scratch_addr; memcpy((void *) offset, in, sizeof(*in)); if (flash_write(ptn, 0, scratch_addr, n)) { dprintf(CRITICAL, "ERROR: flash write fail!\n"); return -1; } return 0; }
int write_misc(unsigned page_offset, void *buf, unsigned size) { const char *ptn_name = "misc"; void *scratch_addr = target_get_scratch_address(); unsigned offset; unsigned aligned_size; if (size == 0 || buf == NULL || scratch_addr == NULL) return -1; if (target_is_emmc_boot()) { int index; unsigned long long ptn; unsigned long long ptn_size; index = partition_get_index(ptn_name); if (index == INVALID_PTN) { dprintf(CRITICAL, "No '%s' partition found\n", ptn_name); return -1; } ptn = partition_get_offset(index); ptn_size = partition_get_size(index); offset = page_offset * BLOCK_SIZE; aligned_size = ROUND_TO_PAGE(size, (unsigned)BLOCK_SIZE - 1); if (ptn_size < offset + aligned_size) { dprintf(CRITICAL, "Write request out of '%s' boundaries\n", ptn_name); return -1; } if (scratch_addr != buf) memcpy(scratch_addr, buf, size); if (mmc_write(ptn + offset, aligned_size, (unsigned int *)scratch_addr)) { dprintf(CRITICAL, "Writing MMC failed\n"); return -1; } } else { struct ptentry *ptn; struct ptable *ptable; unsigned pagesize = flash_page_size(); ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "Partition table not found\n"); return -1; } ptn = ptable_find(ptable, ptn_name); if (ptn == NULL) { dprintf(CRITICAL, "No '%s' partition found\n", ptn_name); return -1; } offset = page_offset * pagesize; aligned_size = ROUND_TO_PAGE(size, pagesize - 1); if (ptn->length < offset + aligned_size) { dprintf(CRITICAL, "Write request out of '%s' boundaries\n", ptn_name); return -1; } if (scratch_addr != buf) memcpy(scratch_addr, buf, size); if (flash_write(ptn, offset, scratch_addr, aligned_size)) { dprintf(CRITICAL, "Writing flash failed\n"); return -1; } } return 0; }
// // Интерфейсная функция записи данных. // size может быть больше, чем размер страницы, и функция должна это // учитывать. // static int sd_write(flashif_t *flash, unsigned page_num, void *data, unsigned size) { int res = FLASH_ERR_OK; sdhc_spi_t *m = (sdhc_spi_t *) flash; int exit = 0; uint8_t *cur_ptr = data; unsigned cur_size; mutex_lock(&flash->lock); do { switch (m->state) { case SDHC_STATE_MULTIREAD: // Карта сейчас в режиме многоблочного чтения, переводим // её сначала в нормальный режим res = stop_multiple_read(m); if (res != FLASH_ERR_OK) { exit = 1; break; } m->state = SDHC_STATE_IDLE; // Intentionally no break case SDHC_STATE_IDLE: // Инициируем режим многоблочной записи res = init_multiple_op(m, page_num, OP_WRITE, 0); if (res != FLASH_ERR_OK) { exit = 1; break; } m->state = SDHC_STATE_MULTIWRITE; m->next_page = page_num; // Intentionally no break case SDHC_STATE_MULTIWRITE: if (page_num == m->next_page) { // Карта в режиме многоблочной записи и требуемый // номер страницы является последовательным по отношению // к предыдущему - выполняем передачу очередного блока. // Дожидаемся начала блока. do { // Выполняем запись одной страницы cur_size = (size <= flash_page_size(flash)) ? size : flash_page_size(flash); res = do_write_one_page(m, cur_ptr, cur_size); if (res != FLASH_ERR_OK) break; m->next_page++; cur_ptr += cur_size; size -= cur_size; } while (size > 0); exit = 1; } else { // Карта в нужном режиме, но адрес не последовательный. // Останавливаем режим многоблочной записи, чтобы // потом заново его инициировать, но уже с нужным // номером страницы. res = stop_multiple_write(m); if (res != FLASH_ERR_OK) { exit = 1; break; } m->state = SDHC_STATE_IDLE; } break; } } while (!exit); mutex_unlock(&flash->lock); return res; }
int update_firmware_image (struct update_header *header, char *name) { struct ptentry *ptn; struct ptable *ptable; unsigned offset = 0; unsigned pagesize = flash_page_size(); unsigned pagemask = pagesize -1; unsigned n = 0; ptable = flash_get_ptable(); if (ptable == NULL) { dprintf(CRITICAL, "ERROR: Partition table not found\n"); return -1; } ptn = ptable_find(ptable, "cache"); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No cache partition found\n"); return -1; } offset += header->image_offset; n = (header->image_length + pagemask) & (~pagemask); if (flash_read(ptn, offset, SCRATCH_ADDR, n)) { dprintf(CRITICAL, "ERROR: Cannot read radio image\n"); return -1; } if (!strcmp(name, "radio")) { ptn = ptable_find(ptable, name); if (ptn == NULL) { dprintf(CRITICAL, "ERROR: No %s partition found\n", name); return -1; } if (flash_write(ptn, 0, SCRATCH_ADDR, n)) { dprintf(CRITICAL, "ERROR: flash write fail!\n"); return -1; } } else if (!strcmp(name, "bootloader")) { #ifdef PLATFORM_TCC struct ptentry ptn = { .name = "bootloader", }; flash_write(&ptn, 0, SCRATCH_ADDR, n); #endif } dprintf(INFO, "Partition writen successfully!"); return 0; } /* Bootloader / Recovery Flow * * On every boot, the bootloader will read the recovery_message * from flash and check the command field. The bootloader should * deal with the command field not having a 0 terminator correctly * (so as to not crash if the block is invalid or corrupt). * * The bootloader will have to publish the partition that contains * the recovery_message to the linux kernel so it can update it. * * if command == "boot-recovery" -> boot recovery.img * else if command == "update-radio" -> update radio image (below) * else -> boot boot.img (normal boot) * * Radio Update Flow * 1. the bootloader will attempt to load and validate the header * 2. if the header is invalid, status="invalid-update", goto #8 * 3. display the busy image on-screen * 4. if the update image is invalid, status="invalid-radio-image", goto #8 * 5. attempt to update the firmware (depending on the command) * 6. if successful, status="okay", goto #8 * 7. if failed, and the old image can still boot, status="failed-update" * 8. write the recovery_message, leaving the recovery field * unchanged, updating status, and setting command to * "boot-recovery" * 9. reboot * * The bootloader will not modify or erase the cache partition. * It is recovery's responsibility to clean up the mess afterwards. */ int recovery_init (void) { struct recovery_message msg; struct update_header header; char partition_name[32]; unsigned valid_command = 0; // get recovery message if(get_recovery_message(&msg)) return -1; if (msg.command[0] != 0 && msg.command[0] != 255) { dprintf(INFO, "Recovery command: %s\n", msg.command); } msg.command[sizeof(msg.command)-1] = '\0'; //Ensure termination if (!strcmp("boot-recovery",msg.command)) { valid_command = 1; strcpy(msg.command, ""); // to safe against multiple reboot into recovery strcpy(msg.status, "OKAY"); set_recovery_message(&msg); // send recovery message boot_into_recovery = 1; // Boot in recovery mode return 0; } if (!strcmp("update-radio",msg.command)) { valid_command = 1; strcpy(partition_name, "AMSS"); } else if (!strcmp("update-bootloader", msg.command)) { valid_command = 1; strcpy(partition_name, "bootloader"); } if(!valid_command) { //We need not to do anything return 0; // Boot in normal mode } if (read_update_header_for_bootloader(&header)) { strcpy(msg.status, "invalid-update"); goto SEND_RECOVERY_MSG; } if (update_firmware_image (&header, partition_name)) { strcpy(msg.status, "failed-update"); goto SEND_RECOVERY_MSG; } strcpy(msg.status, "OKAY"); SEND_RECOVERY_MSG: strcpy(msg.command, "boot-recovery"); set_recovery_message(&msg); // send recovery message boot_into_recovery = 1; // Boot in recovery mode reboot_device(0); return 0; } #elif defined(TNFTL_V8_INCLUDE) static int set_recovery_msg_v8(struct recovery_message *out) { char *ptn_name = "misc"; unsigned long long ptn = 0; unsigned int size = ROUND_TO_PAGE(sizeof(*out),511); unsigned char data[size]; ptn = flash_ptn_offset(ptn_name); if(ptn == 0) { dprintf(CRITICAL,"partition %s doesn't exist\n",ptn_name); return -1; } memcpy(data, out, sizeof(*out)); if (flash_write_tnftl_v8(ptn_name, ptn , size, (unsigned int*)data)) { dprintf(CRITICAL,"write failure %s %d\n",ptn_name, sizeof(*out)); return -1; } return 0; }