1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4#include "nandpart-utils.h" 5 6#include <stdlib.h> 7#include <string.h> 8 9#ifdef TEST 10#include <unittest/unittest.h> 11#define zxlogf(flags, ...) unittest_printf(__VA_ARGS__) 12#else 13#include <ddk/debug.h> 14#endif 15 16#include <fbl/algorithm.h> 17#include <zircon/assert.h> 18 19// Checks that the partition map is valid, sorts it in partition order, and 20// ensures blocks are on erase block boundaries. 21zx_status_t SanitizePartitionMap(zbi_partition_map_t* pmap, const nand_info_t& nand_info) { 22 if (pmap->partition_count == 0) { 23 zxlogf(ERROR, "nandpart: partition count is zero\n"); 24 return ZX_ERR_INTERNAL; 25 } 26 27 auto* const begin = &pmap->partitions[0]; 28 const auto* const end = &pmap->partitions[pmap->partition_count]; 29 30 // 1) Last block must be greater than first for each partition entry. 31 for (auto* part = begin; part != end; part++) { 32 if (part->first_block > part->last_block) { 33 return ZX_ERR_INVALID_ARGS; 34 } 35 } 36 37 // 2) Partitions should be in order. 38 qsort(pmap->partitions, pmap->partition_count, sizeof(zbi_partition_t), 39 [](const void* left, const void* right) { 40 const auto* left_ = static_cast<const zbi_partition_t*>(left); 41 const auto* right_ = static_cast<const zbi_partition_t*>(right); 42 if (left_->first_block < right_->first_block) { 43 return -1; 44 } 45 if (left_->first_block > right_->first_block) { 46 return 1; 47 } 48 return 0; 49 }); 50 51 // 3) Partitions should not be overlapping. 52 for (auto *part = begin, *next = begin + 1; next != end; part++, next++) { 53 if (part->last_block >= next->first_block) { 54 zxlogf(ERROR, "nandpart: partition %s [%lu, %lu] overlaps partition %s [%lu, %lu]\n", 55 part->name, part->first_block, part->last_block, next->name, next->first_block, 56 next->last_block); 57 return ZX_ERR_INTERNAL; 58 } 59 } 60 61 // 4) All partitions must start at an erase block boundary. 62 const size_t erase_block_size = nand_info.page_size * nand_info.pages_per_block; 63 ZX_DEBUG_ASSERT(fbl::is_pow2(erase_block_size)); 64 const int block_shift = ffs(static_cast<int>(erase_block_size)) - 1; 65 66 if (pmap->block_size != erase_block_size) { 67 for (auto* part = begin; part != end; part++) { 68 uint64_t first_byte_offset = part->first_block * pmap->block_size; 69 uint64_t last_byte_offset = (part->last_block + 1) * pmap->block_size; 70 71 if (fbl::round_down(first_byte_offset, erase_block_size) != first_byte_offset || 72 fbl::round_down(last_byte_offset, erase_block_size) != last_byte_offset) { 73 zxlogf(ERROR, "nandpart: partition %s size is not a multiple of erase_block_size\n", 74 part->name); 75 return ZX_ERR_INTERNAL; 76 } 77 part->first_block = first_byte_offset >> block_shift; 78 part->last_block = (last_byte_offset >> block_shift) - 1; 79 } 80 } 81 // 5) Partitions should exist within NAND. 82 if ((end - 1)->last_block >= nand_info.num_blocks) { 83 return ZX_ERR_OUT_OF_RANGE; 84 } 85 return ZX_OK; 86} 87 88