| /* |
| * Copyright (c) 2020 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <amlogic/storage.h> |
| #include <common.h> |
| #include <config.h> |
| #include <fastboot.h> |
| #include <image-sparse.h> |
| #include <zircon/partition.h> |
| |
| #include "lz4_wrapper.h" |
| |
| static uint8_t lz4_image_decompress_buffer[4 * 1024 * 1024]; |
| |
| void fb_zircon_sparse_mssg(const char *message, char *response) |
| { |
| /* b/239740938: Use the response string */ |
| fb_fail(message); |
| } |
| |
| static lbaint_t fb_zircon_sparse_write(struct sparse_storage *info, |
| lbaint_t blk, lbaint_t blkcnt, |
| const void *buffer) |
| { |
| zircon_partition *part = (zircon_partition *)info->priv; |
| uint64_t offset = blk * info->blksz; |
| size_t size = blkcnt * info->blksz; |
| return part->write(part, offset, buffer, size) ? 0 : blkcnt; |
| } |
| |
| static lbaint_t fb_zircon_sparse_reserve(struct sparse_storage *info, |
| lbaint_t blk, lbaint_t blkcnt) |
| { |
| return blkcnt; |
| } |
| |
| static int write_lz4_compressed_image(zircon_partition *part, |
| void *download_buffer, |
| unsigned int download_bytes) |
| { |
| int ret = 0; |
| struct lz4_frame_header lz4_header; |
| size_t lz4_data_offset; |
| if ((ret = check_and_extract_lz4_frame_header( |
| download_buffer, download_bytes, &lz4_header, |
| &lz4_data_offset))) { |
| printf("Failed to extract lz4 frame header\n"); |
| return -1; |
| } |
| |
| const uint8_t *compressed_end = download_buffer + download_bytes; |
| const uint8_t *compressed_curr = |
| (uint8_t *)download_buffer + lz4_data_offset; |
| |
| printf("Flashing lz4 compressed image to partition\n"); |
| |
| size_t write_offset = 0; |
| bool has_more_data = true; |
| while (has_more_data) { |
| size_t decompressed_size = sizeof(lz4_image_decompress_buffer); |
| size_t compressed_size = compressed_end - compressed_curr; |
| if ((ret = lz4_decompress( |
| &lz4_header, compressed_curr, &compressed_size, |
| lz4_image_decompress_buffer, &decompressed_size, |
| &has_more_data))) { |
| printf("Failed to decompress, %d\n", ret); |
| return -1; |
| } |
| compressed_curr += compressed_size; |
| |
| if ((ret = part->write(part, write_offset, |
| lz4_image_decompress_buffer, |
| decompressed_size))) { |
| printf("Failed to write to storage\n"); |
| return -1; |
| } |
| write_offset += decompressed_size; |
| } |
| return 0; |
| } |
| |
| void fb_zircon_flash_write(const char *part_name, void *download_buffer, |
| unsigned int download_bytes) |
| { |
| // `write_sparse_image` sets fb_fail() on error, so start with |
| // fb_okay() to avoid clobbering the return value. |
| fb_okay(NULL); |
| |
| zircon_partition *part = zircon_get_fastboot_partition(part_name); |
| if (!part) { |
| fb_fail("partition not found"); |
| return; |
| } |
| |
| if (!part->write) { |
| zircon_free_partition(part); |
| fb_fail("Device must be unlocked to run this command"); |
| return; |
| } |
| |
| printf("Flashing '%s'\n", part_name); |
| if (is_sparse_image(download_buffer)) { |
| struct sparse_storage sparse = { |
| .blksz = 1, |
| .start = 0, |
| .size = part->size, |
| .priv = (void *)part, |
| .write = fb_zircon_sparse_write, |
| .reserve = fb_zircon_sparse_reserve, |
| .mssg = fb_zircon_sparse_mssg, |
| }; |
| |
| printf("sparse image detected\n"); |
| if (write_sparse_image(&sparse, part_name, download_buffer, |
| download_bytes, "")) { |
| // The mssg function will be called by write_sparse_image on failure, |
| // so no need to call fb_fail here. |
| printf("sparse image write failed\n"); |
| } |
| } else { |
| if (is_data_lz4_compressed(download_buffer, download_bytes)) { |
| printf("lz4 flat image detected\n"); |
| |
| if ((write_lz4_compressed_image(part, download_buffer, |
| download_bytes))) { |
| fb_fail("failed to write image"); |
| } |
| } else { |
| printf("uncompressed flat image detected\n"); |
| if (part->write(part, 0, download_buffer, |
| download_bytes)) { |
| fb_fail("failed to write image"); |
| } |
| } |
| } |
| printf("Flashing '%s' done\n", part_name); |
| zircon_free_partition(part); |
| } |
| |
| void fb_zircon_erase(const char *part_name) |
| { |
| zircon_partition *part = zircon_get_fastboot_partition(part_name); |
| if (!part) { |
| fb_fail("partition not found"); |
| return; |
| } |
| |
| if (!part->erase) { |
| zircon_free_partition(part); |
| fb_fail("Device must be unlocked to run this command"); |
| return; |
| } |
| |
| printf("Erasing '%s'\n", part_name); |
| |
| if (part->erase(part)) { |
| printf("partition erase failed: %s\n", part_name); |
| fb_fail("failed erasing from device"); |
| } else { |
| fb_okay(NULL); |
| } |
| |
| zircon_free_partition(part); |
| } |