1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "gptdisk.hpp"
4
5#include "blockdev.h"
6#include "layer-gpt.hpp"
7#include "proto/blockdev.pb.h"
8#include "uuid.hpp"
9
10#include <cassert>
11#include <iostream>
12#include <librpc/rpc.h>
13#include <optional>
14#include <pb_decode.h>
15#include <string>
16
17static std::optional<GPT::Header> gpt_read_header(const void *data)
18{
19 const auto header = static_cast<const GPT::Header *>(data);
20
21 if (strncmp(a: (const char *) header, b: "EFI PART", max_size: 8) != 0)
22 {
23 std::cout << " (invalid GPT header)" << std::endl;
24 return std::nullopt;
25 }
26
27 std::cout << " (GPT signature is valid)" << std::endl;
28 std::cout << " Revision: " << header->revision << std::endl;
29 std::cout << " Header size: " << header->header_size << std::endl;
30 std::cout << " Header CRC32: " << header->header_crc32 << std::endl;
31 std::cout << " Current LBA: " << header->current_lba << std::endl;
32 std::cout << " Backup LBA: " << header->backup_lba << std::endl;
33 std::cout << " First usable LBA: " << header->first_usable_lba << std::endl;
34 std::cout << " Last usable LBA: " << header->last_usable_lba << std::endl;
35 std::cout << " Disk GUID: " << UUID{ header->disk_guid } << std::endl;
36 std::cout << " Partition table LBA: " << header->partition_table_lba << std::endl;
37 std::cout << " Partition count: " << header->partition_count << std::endl;
38 std::cout << " Partition entry size: " << header->partition_entry_size << std::endl;
39 std::cout << " Partition table CRC32: " << header->partition_table_crc32 << std::endl;
40
41 return *header;
42}
43
44GPTDisk::GPTDisk(const blockdev_handle handle, const std::string &disk_name) : device_handle(handle), disk_name(disk_name)
45{
46}
47
48bool GPTDisk::initialise_gpt()
49{
50 if (ready)
51 throw std::runtime_error("GPTDisk already initialised");
52
53 const auto has_header = disk_read_header();
54 if (!has_header)
55 return false;
56
57 const auto has_partitions = disk_read_partitions();
58 if (!has_partitions)
59 return false;
60
61 ready = true;
62 return true;
63}
64
65GPT::PartitionEntry GPTDisk::get_partition(size_t index) const
66{
67 assert(ready);
68 return partitions.at(n: index);
69}
70
71u32 GPTDisk::get_partition_count() const
72{
73 assert(ready);
74 return partitions.size();
75}
76
77bool GPTDisk::disk_read_header()
78{
79 const read_block::request read_request = {
80 .device = device_handle,
81 .n_boffset = 1,
82 .n_blocks = 1,
83 };
84
85 read_block::response read_resp;
86 const auto result = manager->read_block(request: &read_request, response: &read_resp);
87 if (result != RPC_RESULT_OK || !read_resp.result.success)
88 {
89 std::cout << " (failed to read block)" << std::endl;
90 if (read_resp.result.error)
91 std::cout << " " << read_resp.result.error << std::endl;
92 return {};
93 }
94
95 const auto header = gpt_read_header(data: read_resp.data->bytes);
96 pb_release(mosrpc_blockdev_read_block_response_fields, dest_struct: &read_resp);
97
98 if (header)
99 this->header = header.value();
100
101 return header.has_value();
102}
103
104bool GPTDisk::disk_read_partitions()
105{
106 // continue reading the partition table
107 const read_block::request read_request{
108 .device = device_handle,
109 .n_boffset = header.partition_table_lba,
110 .n_blocks = header.partition_count * header.partition_entry_size / 512,
111 };
112 read_block::response read_resp;
113
114 const auto result3 = manager->read_block(request: &read_request, response: &read_resp);
115 if (result3 != RPC_RESULT_OK || !read_resp.result.success)
116 {
117 std::cout << " (failed to read partition table)" << std::endl;
118 if (read_resp.result.error)
119 std::cout << " " << read_resp.result.error << std::endl;
120 return false;
121 }
122
123 std::cout << " Partition table:" << std::endl;
124 for (size_t i = 0; i < header.partition_count; i++)
125 {
126 const auto ptr = (const char *) read_resp.data->bytes + i * header.partition_entry_size;
127 const auto entry = (const GPT::PartitionEntry *) ptr;
128
129 if (entry->type_guid[0] == 0 && entry->type_guid[1] == 0)
130 continue; // skip empty entries
131
132 std::cout << std::resetiosflags(mask: std::ios::hex);
133 std::cout << " Partition " << i << ":" << std::endl;
134 std::cout << " Type GUID: " << UUID{ entry->type_guid } << std::endl;
135 std::cout << " Partition GUID: " << UUID{ entry->partition_guid } << std::endl;
136 std::cout << " First LBA: " << entry->first_lba << std::endl;
137 std::cout << " Last LBA: " << entry->last_lba << std::endl;
138 std::cout << " Attributes: " << entry->attributes << std::endl;
139 // std::cout << " Name: " << name << std::endl;
140
141 partitions.push_back(x: *entry);
142 }
143
144 pb_release(mosrpc_blockdev_read_block_response_fields, dest_struct: &read_resp);
145 return true;
146}
147
148size_t GPTDisk::read_partition_block(size_t partition_index, u64 blockoffset, u8 *buffer, u32 nblocks)
149{
150 assert(ready);
151 const auto &partition = partitions.at(n: partition_index);
152
153 const read_block::request read_request = {
154 .device = device_handle,
155 .n_boffset = partition.first_lba + blockoffset,
156 .n_blocks = nblocks,
157 };
158
159 read_block::response read_resp;
160 const auto result = manager->read_block(request: &read_request, response: &read_resp);
161 if (result != RPC_RESULT_OK || !read_resp.result.success)
162 {
163 std::cout << " (failed to read partition block)" << std::endl;
164 if (read_resp.result.error)
165 std::cout << " " << read_resp.result.error << std::endl;
166 return 0;
167 }
168
169 const auto datasize = read_resp.data->size;
170 memcpy(dest: buffer, src: read_resp.data->bytes, size: datasize);
171 pb_release(mosrpc_blockdev_read_block_response_fields, dest_struct: &read_resp);
172
173 return datasize;
174}
175
176size_t GPTDisk::write_partition_block(size_t partition_index, u64 blockoffset, const u8 *buffer, u32 nblocks)
177{
178 MOS_UNUSED(partition_index);
179 MOS_UNUSED(blockoffset);
180 MOS_UNUSED(buffer);
181 MOS_UNUSED(nblocks);
182 assert(ready);
183 return 0;
184}
185