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