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
5#pragma once
6
7#include <stdbool.h>
8
9#include <fbl/function.h>
10#include <fbl/string.h>
11#include <fbl/unique_fd.h>
12#include <fbl/unique_ptr.h>
13#include <fbl/vector.h>
14#include <gpt/gpt.h>
15#include <zircon/types.h>
16
17namespace paver {
18
19enum class Partition {
20    kBootloader,
21    kKernelC,
22    kEfi,
23    kZirconA,
24    kZirconB,
25    kZirconR,
26    kFuchsiaVolumeManager,
27    // The following are only valid for WipePartition.
28    kInstallType,
29    kSystem,
30    kBlob,
31    kData,
32};
33
34// A special filter for test injection.
35// API should return true if device passed in should be filtered out.
36extern bool (*TestBlockFilter)(const fbl::unique_fd&);
37extern bool (*TestSkipBlockFilter)(const fbl::unique_fd&);
38
39// Abstract device partitioner definition.
40// This class defines common APIs for interacting with a device partitioner.
41class DevicePartitioner {
42public:
43    // Factory method which automatically returns the correct DevicePartitioner
44    // implementation. Returns nullptr on failure.
45    static fbl::unique_ptr<DevicePartitioner> Create();
46
47    virtual ~DevicePartitioner() = default;
48
49    virtual bool IsCros() const = 0;
50
51    // Whether to use skip block interface or block interface for non-FVM
52    // partitions.
53    virtual bool UseSkipBlockInterface() const = 0;
54
55    // Returns a file descriptor to a partition of type |partition_type|, creating it.
56    // Assumes that the partition does not already exist.
57    virtual zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) = 0;
58
59    // Returns a file descriptor to a partition of type |partition_type| if one exists.
60    virtual zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const = 0;
61
62    // Finalizes the partition of type |partition_type| after it has been
63    // written.
64    virtual zx_status_t FinalizePartition(Partition partition_type) = 0;
65
66    // Wipes partition list specified.
67    virtual zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) = 0;
68
69    // Returns block size in bytes for specified device.
70    virtual zx_status_t GetBlockSize(const fbl::unique_fd& device_fd,
71                                     uint32_t* block_size) const = 0;
72};
73
74// Useful for when a GPT table is available (e.g. x86 devices). Provides common
75// utility functions.
76class GptDevicePartitioner {
77public:
78    using FilterCallback = fbl::Function<bool(const gpt_partition_t&)>;
79
80    // Find and initialize a GPT based device.
81    static zx_status_t InitializeGpt(fbl::unique_ptr<GptDevicePartitioner>* gpt_out);
82
83    virtual ~GptDevicePartitioner() {
84        if (gpt_) {
85            gpt_device_release(gpt_);
86        }
87    }
88
89    // Returns block info for a specified block device.
90    zx_status_t GetBlockInfo(block_info_t* block_info) const {
91        memcpy(block_info, &block_info_, sizeof(*block_info));
92        return ZX_OK;
93    }
94
95    gpt_device_t* GetGpt() const { return gpt_; }
96    int GetFd() const { return fd_.get(); }
97
98    // Find the first spot that has at least |bytes_requested| of space.
99    //
100    // Returns the |start_out| block and |length_out| blocks, indicating
101    // how much space was found, on success. This may be larger than
102    // the number of bytes requested.
103    zx_status_t FindFirstFit(size_t bytes_requested, size_t* start_out, size_t* length_out) const;
104
105    // Creates a partition, adds an entry to the GPT, and returns a file descriptor to it.
106    // Assumes that the partition does not already exist.
107    zx_status_t AddPartition(const char* name, uint8_t* type, size_t minimum_size_bytes,
108                             size_t optional_reserve_bytes, fbl::unique_fd* out_fd);
109
110    // Returns a file descriptor to a partition which can be paved,
111    // if one exists.
112    zx_status_t FindPartition(FilterCallback filter, gpt_partition_t** out,
113                              fbl::unique_fd* out_fd);
114    zx_status_t FindPartition(FilterCallback filter, fbl::unique_fd* out_fd) const;
115
116    // Wipes a specified partition from the GPT, and ovewrites first 8KiB with
117    // nonsense.
118    zx_status_t WipePartitions(FilterCallback filter);
119
120private:
121    // Find and return the topological path of the GPT which we will pave.
122    static bool FindTargetGptPath(fbl::String* out);
123
124    GptDevicePartitioner(fbl::unique_fd fd, gpt_device_t* gpt, block_info_t block_info)
125        : fd_(fbl::move(fd)), gpt_(gpt), block_info_(block_info) {}
126
127    zx_status_t CreateGptPartition(const char* name, uint8_t* type, uint64_t offset,
128                                   uint64_t blocks, uint8_t* out_guid);
129
130    fbl::unique_fd fd_;
131    gpt_device_t* gpt_;
132    block_info_t block_info_;
133};
134
135// DevicePartitioner implementation for EFI based devices.
136class EfiDevicePartitioner : public DevicePartitioner {
137public:
138    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
139
140    bool IsCros() const override { return false; }
141
142    bool UseSkipBlockInterface() const override { return false; }
143
144    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override;
145
146    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
147
148    zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
149
150    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
151
152    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
153
154private:
155    EfiDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)
156        : gpt_(fbl::move(gpt)) {}
157
158    static bool FilterZirconPartition(const block_info_t& info, const gpt_partition_t& part);
159
160    fbl::unique_ptr<GptDevicePartitioner> gpt_;
161};
162
163// DevicePartitioner implementation for ChromeOS devices.
164class CrosDevicePartitioner : public DevicePartitioner {
165public:
166    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
167
168    bool IsCros() const override { return true; }
169
170    bool UseSkipBlockInterface() const override { return false; }
171
172    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override;
173
174    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
175
176    zx_status_t FinalizePartition(Partition unused) override;
177
178    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
179
180    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
181
182private:
183    CrosDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)
184        : gpt_(fbl::move(gpt)) {}
185
186    fbl::unique_ptr<GptDevicePartitioner> gpt_;
187};
188
189// DevicePartitioner implementation for devices which have fixed partition maps (e.g. ARM
190// devices). It will not attempt to write a partition map of any kind to the device.
191// Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B,
192// ZIRCON-R).
193class FixedDevicePartitioner : public DevicePartitioner {
194public:
195    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
196
197    bool IsCros() const override { return false; }
198
199    bool UseSkipBlockInterface() const override { return false; }
200
201    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override {
202        return ZX_ERR_NOT_SUPPORTED;
203    }
204
205    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
206
207    zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
208
209    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
210
211    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
212
213private:
214    FixedDevicePartitioner() {}
215};
216
217// DevicePartitioner implementation for devices which have fixed partition maps, but do not expose a
218// block device interface. Instead they expose devices with skip-block IOCTL interfaces. Like the
219// FixedDevicePartitioner, it will not attempt to write a partition map of any kind to the device.
220// Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B,
221// ZIRCON-R).
222class SkipBlockDevicePartitioner : public DevicePartitioner {
223public:
224    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
225
226    bool IsCros() const override { return false; }
227
228    bool UseSkipBlockInterface() const override { return true; }
229
230    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override {
231        return ZX_ERR_NOT_SUPPORTED;
232    }
233
234    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
235
236    zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
237
238    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
239
240    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
241
242private:
243    SkipBlockDevicePartitioner() {}
244};
245} // namespace paver
246