189580Smsmith// Copyright 2018 The Fuchsia Authors. All rights reserved.
289580Smsmith// Use of this source code is governed by a BSD-style license that can be
389580Smsmith// found in the LICENSE file.
489580Smsmith
589580Smsmith#pragma once
689580Smsmith
789580Smsmith#include <stdbool.h>
889580Smsmith
989580Smsmith#include <fbl/function.h>
1089580Smsmith#include <fbl/string.h>
1189580Smsmith#include <fbl/unique_fd.h>
1289580Smsmith#include <fbl/unique_ptr.h>
1389580Smsmith#include <fbl/vector.h>
1489580Smsmith#include <gpt/gpt.h>
1589580Smsmith#include <zircon/types.h>
1689580Smsmith
1789580Smsmithnamespace paver {
1889580Smsmith
1989580Smsmithenum class Partition {
2089580Smsmith    kBootloader,
2189580Smsmith    kKernelC,
2289580Smsmith    kEfi,
2389580Smsmith    kZirconA,
2489580Smsmith    kZirconB,
2589580Smsmith    kZirconR,
2689580Smsmith    kFuchsiaVolumeManager,
2789580Smsmith    // The following are only valid for WipePartition.
2889580Smsmith    kInstallType,
2989580Smsmith    kSystem,
3089580Smsmith    kBlob,
3189580Smsmith    kData,
3289580Smsmith};
3389580Smsmith
3489580Smsmith// A special filter for test injection.
3589580Smsmith// API should return true if device passed in should be filtered out.
3689580Smsmithextern bool (*TestBlockFilter)(const fbl::unique_fd&);
3789580Smsmithextern bool (*TestSkipBlockFilter)(const fbl::unique_fd&);
3889580Smsmith
3989580Smsmith// Abstract device partitioner definition.
4089580Smsmith// This class defines common APIs for interacting with a device partitioner.
4189580Smsmithclass DevicePartitioner {
4289580Smsmithpublic:
4389580Smsmith    // Factory method which automatically returns the correct DevicePartitioner
4495533Smike    // implementation. Returns nullptr on failure.
4589580Smsmith    static fbl::unique_ptr<DevicePartitioner> Create();
4689580Smsmith
4789580Smsmith    virtual ~DevicePartitioner() = default;
4889580Smsmith
4989580Smsmith    virtual bool IsCros() const = 0;
5089580Smsmith
5189580Smsmith    // Whether to use skip block interface or block interface for non-FVM
5289580Smsmith    // partitions.
5389580Smsmith    virtual bool UseSkipBlockInterface() const = 0;
5489580Smsmith
5589580Smsmith    // Returns a file descriptor to a partition of type |partition_type|, creating it.
5689580Smsmith    // Assumes that the partition does not already exist.
5789580Smsmith    virtual zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) = 0;
5889580Smsmith
5989580Smsmith    // Returns a file descriptor to a partition of type |partition_type| if one exists.
6089580Smsmith    virtual zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const = 0;
6189580Smsmith
6289580Smsmith    // Finalizes the partition of type |partition_type| after it has been
6389580Smsmith    // written.
6489580Smsmith    virtual zx_status_t FinalizePartition(Partition partition_type) = 0;
6589580Smsmith
6689580Smsmith    // Wipes partition list specified.
6789580Smsmith    virtual zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) = 0;
6889580Smsmith
6989580Smsmith    // Returns block size in bytes for specified device.
7089580Smsmith    virtual zx_status_t GetBlockSize(const fbl::unique_fd& device_fd,
7189580Smsmith                                     uint32_t* block_size) const = 0;
7289580Smsmith};
7389580Smsmith
7489580Smsmith// Useful for when a GPT table is available (e.g. x86 devices). Provides common
7589580Smsmith// utility functions.
7689580Smsmithclass GptDevicePartitioner {
7789580Smsmithpublic:
7889580Smsmith    using FilterCallback = fbl::Function<bool(const gpt_partition_t&)>;
7989580Smsmith
8089580Smsmith    // Find and initialize a GPT based device.
8189580Smsmith    static zx_status_t InitializeGpt(fbl::unique_ptr<GptDevicePartitioner>* gpt_out);
8289580Smsmith
8389580Smsmith    virtual ~GptDevicePartitioner() {
8489580Smsmith        if (gpt_) {
8589580Smsmith            gpt_device_release(gpt_);
8689580Smsmith        }
8789580Smsmith    }
8889580Smsmith
8989580Smsmith    // Returns block info for a specified block device.
9089580Smsmith    zx_status_t GetBlockInfo(block_info_t* block_info) const {
9189580Smsmith        memcpy(block_info, &block_info_, sizeof(*block_info));
9289580Smsmith        return ZX_OK;
9389580Smsmith    }
9489580Smsmith
9589580Smsmith    gpt_device_t* GetGpt() const { return gpt_; }
9689580Smsmith    int GetFd() const { return fd_.get(); }
9789580Smsmith
9889580Smsmith    // Find the first spot that has at least |bytes_requested| of space.
9989580Smsmith    //
10089580Smsmith    // Returns the |start_out| block and |length_out| blocks, indicating
10189580Smsmith    // how much space was found, on success. This may be larger than
10289580Smsmith    // the number of bytes requested.
10389580Smsmith    zx_status_t FindFirstFit(size_t bytes_requested, size_t* start_out, size_t* length_out) const;
10489580Smsmith
10589580Smsmith    // Creates a partition, adds an entry to the GPT, and returns a file descriptor to it.
10689580Smsmith    // Assumes that the partition does not already exist.
10789580Smsmith    zx_status_t AddPartition(const char* name, uint8_t* type, size_t minimum_size_bytes,
10889580Smsmith                             size_t optional_reserve_bytes, fbl::unique_fd* out_fd);
10989580Smsmith
11089580Smsmith    // Returns a file descriptor to a partition which can be paved,
11189580Smsmith    // if one exists.
11289580Smsmith    zx_status_t FindPartition(FilterCallback filter, gpt_partition_t** out,
11389580Smsmith                              fbl::unique_fd* out_fd);
11489580Smsmith    zx_status_t FindPartition(FilterCallback filter, fbl::unique_fd* out_fd) const;
11589580Smsmith
11689580Smsmith    // Wipes a specified partition from the GPT, and ovewrites first 8KiB with
11789580Smsmith    // nonsense.
11889580Smsmith    zx_status_t WipePartitions(FilterCallback filter);
11989580Smsmith
12089580Smsmithprivate:
12189580Smsmith    // Find and return the topological path of the GPT which we will pave.
12289580Smsmith    static bool FindTargetGptPath(fbl::String* out);
12389580Smsmith
12489580Smsmith    GptDevicePartitioner(fbl::unique_fd fd, gpt_device_t* gpt, block_info_t block_info)
12589580Smsmith        : fd_(fbl::move(fd)), gpt_(gpt), block_info_(block_info) {}
12689580Smsmith
12789580Smsmith    zx_status_t CreateGptPartition(const char* name, uint8_t* type, uint64_t offset,
12889580Smsmith                                   uint64_t blocks, uint8_t* out_guid);
12989580Smsmith
13089580Smsmith    fbl::unique_fd fd_;
13189580Smsmith    gpt_device_t* gpt_;
13289580Smsmith    block_info_t block_info_;
13389580Smsmith};
13489580Smsmith
13589580Smsmith// DevicePartitioner implementation for EFI based devices.
13689580Smsmithclass EfiDevicePartitioner : public DevicePartitioner {
13789580Smsmithpublic:
13889580Smsmith    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
13989580Smsmith
14089580Smsmith    bool IsCros() const override { return false; }
14189580Smsmith
14289580Smsmith    bool UseSkipBlockInterface() const override { return false; }
14389580Smsmith
14489580Smsmith    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override;
14589580Smsmith
14689580Smsmith    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
14789580Smsmith
14889580Smsmith    zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
14989580Smsmith
15089580Smsmith    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
15189580Smsmith
15289580Smsmith    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
15389580Smsmith
15489580Smsmithprivate:
15589580Smsmith    EfiDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)
15689580Smsmith        : gpt_(fbl::move(gpt)) {}
15789580Smsmith
15889580Smsmith    static bool FilterZirconPartition(const block_info_t& info, const gpt_partition_t& part);
15989580Smsmith
16089580Smsmith    fbl::unique_ptr<GptDevicePartitioner> gpt_;
16189580Smsmith};
16289580Smsmith
16389580Smsmith// DevicePartitioner implementation for ChromeOS devices.
16489580Smsmithclass CrosDevicePartitioner : public DevicePartitioner {
16589580Smsmithpublic:
16689580Smsmith    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
16789580Smsmith
16889580Smsmith    bool IsCros() const override { return true; }
16989580Smsmith
17089580Smsmith    bool UseSkipBlockInterface() const override { return false; }
17189580Smsmith
17289580Smsmith    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override;
17389580Smsmith
17489580Smsmith    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
17589580Smsmith
17689580Smsmith    zx_status_t FinalizePartition(Partition unused) override;
17789580Smsmith
17889580Smsmith    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
17989580Smsmith
18089580Smsmith    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
18189580Smsmith
18289580Smsmithprivate:
18389580Smsmith    CrosDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)
18489580Smsmith        : gpt_(fbl::move(gpt)) {}
18589580Smsmith
18689580Smsmith    fbl::unique_ptr<GptDevicePartitioner> gpt_;
18789580Smsmith};
18889580Smsmith
18989580Smsmith// DevicePartitioner implementation for devices which have fixed partition maps (e.g. ARM
19089580Smsmith// devices). It will not attempt to write a partition map of any kind to the device.
19189580Smsmith// Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B,
19289580Smsmith// ZIRCON-R).
19389580Smsmithclass FixedDevicePartitioner : public DevicePartitioner {
19489580Smsmithpublic:
19589580Smsmith    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
19689580Smsmith
19789580Smsmith    bool IsCros() const override { return false; }
19889580Smsmith
19989580Smsmith    bool UseSkipBlockInterface() const override { return false; }
20089580Smsmith
20189580Smsmith    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override {
20289580Smsmith        return ZX_ERR_NOT_SUPPORTED;
20389580Smsmith    }
20489580Smsmith
20589580Smsmith    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
20689580Smsmith
20789580Smsmith    zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
20889580Smsmith
20989580Smsmith    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
21089580Smsmith
21189580Smsmith    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
21289580Smsmith
21389580Smsmithprivate:
21489580Smsmith    FixedDevicePartitioner() {}
21589580Smsmith};
21689580Smsmith
21789580Smsmith// DevicePartitioner implementation for devices which have fixed partition maps, but do not expose a
21889580Smsmith// block device interface. Instead they expose devices with skip-block IOCTL interfaces. Like the
21989580Smsmith// FixedDevicePartitioner, it will not attempt to write a partition map of any kind to the device.
22089580Smsmith// Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B,
22189580Smsmith// ZIRCON-R).
22289580Smsmithclass SkipBlockDevicePartitioner : public DevicePartitioner {
22389580Smsmithpublic:
22489580Smsmith    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
22589580Smsmith
22689580Smsmith    bool IsCros() const override { return false; }
22789580Smsmith
22889580Smsmith    bool UseSkipBlockInterface() const override { return true; }
22989580Smsmith
23089580Smsmith    zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override {
23189580Smsmith        return ZX_ERR_NOT_SUPPORTED;
23289580Smsmith    }
23389580Smsmith
23489580Smsmith    zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
23589580Smsmith
23689580Smsmith    zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
23789580Smsmith
23889580Smsmith    zx_status_t WipePartitions(const fbl::Vector<Partition>& partitions) override;
23989580Smsmith
24089580Smsmith    zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
24189580Smsmith
24289580Smsmithprivate:
24389580Smsmith    SkipBlockDevicePartitioner() {}
24489580Smsmith};
24589580Smsmith} // namespace paver
24689580Smsmith