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.
SanitizePartitionMap(zbi_partition_map_t * pmap,const fuchsia_hardware_nand_Info & nand_info)21 zx_status_t SanitizePartitionMap(zbi_partition_map_t* pmap,
22                                  const fuchsia_hardware_nand_Info& nand_info) {
23     if (pmap->partition_count == 0) {
24         zxlogf(ERROR, "nandpart: partition count is zero\n");
25         return ZX_ERR_INTERNAL;
26     }
27 
28     auto* const begin = &pmap->partitions[0];
29     const auto* const end = &pmap->partitions[pmap->partition_count];
30 
31     // 1) Last block must be greater than first for each partition entry.
32     for (auto* part = begin; part != end; part++) {
33         if (part->first_block > part->last_block) {
34             return ZX_ERR_INVALID_ARGS;
35         }
36     }
37 
38     // 2) Partitions should be in order.
39     qsort(pmap->partitions, pmap->partition_count, sizeof(zbi_partition_t),
40           [](const void* left, const void* right) {
41               const auto* left_ = static_cast<const zbi_partition_t*>(left);
42               const auto* right_ = static_cast<const zbi_partition_t*>(right);
43               if (left_->first_block < right_->first_block) {
44                   return -1;
45               }
46               if (left_->first_block > right_->first_block) {
47                   return 1;
48               }
49               return 0;
50           });
51 
52     // 3) Partitions should not be overlapping.
53     for (auto *part = begin, *next = begin + 1; next != end; part++, next++) {
54         if (part->last_block >= next->first_block) {
55             zxlogf(ERROR, "nandpart: partition %s [%lu, %lu] overlaps partition %s [%lu, %lu]\n",
56                    part->name, part->first_block, part->last_block, next->name, next->first_block,
57                    next->last_block);
58             return ZX_ERR_INTERNAL;
59         }
60     }
61 
62     // 4) All partitions must start at an erase block boundary.
63     const size_t erase_block_size = nand_info.page_size * nand_info.pages_per_block;
64     ZX_DEBUG_ASSERT(fbl::is_pow2(erase_block_size));
65     const int block_shift = ffs(static_cast<int>(erase_block_size)) - 1;
66 
67     if (pmap->block_size != erase_block_size) {
68         for (auto* part = begin; part != end; part++) {
69             uint64_t first_byte_offset = part->first_block * pmap->block_size;
70             uint64_t last_byte_offset = (part->last_block + 1) * pmap->block_size;
71 
72             if (fbl::round_down(first_byte_offset, erase_block_size) != first_byte_offset ||
73                 fbl::round_down(last_byte_offset, erase_block_size) != last_byte_offset) {
74                 zxlogf(ERROR, "nandpart: partition %s size is not a multiple of erase_block_size\n",
75                        part->name);
76                 return ZX_ERR_INTERNAL;
77             }
78             part->first_block = first_byte_offset >> block_shift;
79             part->last_block = (last_byte_offset >> block_shift) - 1;
80         }
81     }
82     // 5) Partitions should exist within NAND.
83     if ((end - 1)->last_block >= nand_info.num_blocks) {
84         return ZX_ERR_OUT_OF_RANGE;
85     }
86     return ZX_OK;
87 }
88