L4Re Operating System Framework
Interface and Usage Documentation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
partition.h
1/*
2 * Copyright (C) 2018, 2020-2024 Kernkonzept GmbH.
3 * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
4 *
5 * License: see LICENSE.spdx (in this directory or the directories above)
6 */
7#pragma once
8
9#include <cstring>
10#include <string>
11#include <cassert>
12
13#include <l4/cxx/ref_ptr>
14
15#include <l4/l4virtio/virtio_block.h>
16
17#include <l4/libblock-device/debug.h>
18#include <l4/libblock-device/errand.h>
19#include <l4/libblock-device/inout_memory.h>
20#include <l4/libblock-device/gpt.h>
21
22#include <l4/sys/cache.h>
23
24namespace Block_device {
25
37
38
42template <typename DEV>
43class Partition_reader : public cxx::Ref_obj
44{
45 enum
46 {
47 Max_partitions = 1024
48 };
49
50public:
51 using Device_type = DEV;
52
53 Partition_reader(Device_type *dev)
54 : _num_partitions(0),
55 _dev(dev),
57 {}
58
59 void read(Errand::Callback const &callback)
60 {
61 _num_partitions = 0;
62 _callback = callback;
63
64 // preparation: read the first two sectors
65 _db = _header.inout_block();
66 read_sectors(0, &Partition_reader::get_gpt);
67 }
68
69 l4_size_t table_size() const
70 { return _num_partitions; }
71
72 int get_partition(l4_size_t idx, Partition_info *inf) const
73 {
74 if (idx == 0 || idx > _num_partitions)
75 return -L4_ERANGE;
76
77 unsigned secsz = _dev->sector_size();
78 auto *header = _header.template get<Gpt::Header const>(secsz);
79
80 Gpt::Entry *e = _parray.template get<Gpt::Entry>((idx - 1) * header->entry_size);
81
82 if (*((l4_uint64_t *) &e->partition_guid) == 0ULL)
83 return -L4_ENODEV;
84
85 render_guid(e->partition_guid, inf->guid);
86
87 auto name =
88 std::u16string((char16_t *)e->name, sizeof(e->name) / sizeof(e->name[0]));
89 inf->name = name.substr(0, name.find((char16_t) 0));
90
91 inf->first = e->first;
92 inf->last = e->last;
93 inf->flags = e->flags;
94
95 auto info = Dbg::info();
96 if (info.is_active())
97 {
98 info.printf("%3zu: %10lld %10lld %5gMiB [%.37s]\n",
99 idx, e->first, e->last,
100 (e->last - e->first + 1.0) * secsz / (1 << 20),
101 inf->guid);
102
103 char buf[37];
104 info.printf(" : Type: %s\n", render_guid(e->type_guid, buf));
105 }
106
107 auto warn = Dbg::warn();
108 if (inf->last < inf->first)
109 {
110 warn.printf(
111 "Invalid settings of %3zu. Last lba before first lba. Will ignore.\n",
112 idx);
113 // Errors in the GPT shall not crash any service -- just ignore the
114 // corresponding partition.
115 return -L4_ENODEV;
116 }
117
118 return L4_EOK;
119 }
120
121private:
122 void invoke_callback()
123 {
124 assert(_callback);
125 _callback();
126 // Reset the callback to drop potential transitive self-references
127 _callback = nullptr;
128 }
129
130 void get_gpt(int error, l4_size_t)
131 {
132 _header.unmap();
133
134 if (error < 0)
135 {
136 // can't read from device, we are done
137 invoke_callback();
138 return;
139 }
140
141 // prepare reading of the table from disk
142 unsigned secsz = _dev->sector_size();
143 auto *header = _header.template get<Gpt::Header const>(secsz);
144
145 auto info = Dbg::info();
146 auto trace = Dbg::trace();
147
148 if (strncmp(header->signature, "EFI PART", 8) != 0)
149 {
150 info.printf("No GUID partition header found.\n");
151 invoke_callback();
152 return;
153 }
154
155 // XXX check CRC32 of header
156
157 info.printf("GUID partition header found with up to %d partitions.\n",
158 header->partition_array_size);
159 char buf[37];
160 info.printf("GUID: %s\n", render_guid(header->disk_guid, buf));
161 trace.printf("Header positions: %llx (Backup: %llx)\n",
162 header->current_lba, header->backup_lba);
163 trace.printf("First + last: %llx and %llx\n",
164 header->first_lba, header->last_lba);
165 trace.printf("Partition table at %llx\n",
166 header->partition_array_lba);
167 trace.printf("Size of a partition entry: %d\n",
168 header->entry_size);
169
170 info.printf("GUID partition header found with %d partitions.\n",
171 header->partition_array_size);
172
173 _num_partitions = cxx::min<l4_uint32_t>(header->partition_array_size,
174 Max_partitions);
175
176 l4_size_t arraysz = _num_partitions * header->entry_size;
177 l4_size_t numsec = (arraysz - 1 + secsz) / secsz;
178
180 trace.printf("Reading GPT table @ 0x%p\n", _parray.template get<void>(0));
181
182 _db = _parray.inout_block();
183 read_sectors(header->partition_array_lba, &Partition_reader::done_gpt);
184 }
185
186
187 void done_gpt(int error, l4_size_t)
188 {
189 _parray.unmap();
190
191 // XXX check CRC32 of table
192
193 if (error < 0)
194 _num_partitions = 0;
195
196 invoke_callback();
197 }
198
199 void read_sectors(l4_uint64_t sector,
200 void (Partition_reader::*func)(int, l4_size_t))
201 {
202 using namespace std::placeholders;
203 auto next = std::bind(func, this, _1, _2);
204
205 l4_addr_t vstart = (l4_addr_t)_db.virt_addr;
206 l4_addr_t vend = vstart + _db.num_sectors * _dev->sector_size();
207 l4_cache_inv_data(vstart, vend);
208
209 Errand::poll(10, 10000,
210 [=]()
211 {
212 int ret = _dev->inout_data(
213 sector, _db,
214 [next, vstart, vend](int error, l4_size_t size)
215 {
216 l4_cache_inv_data(vstart, vend);
217 next(error, size);
218 },
220 if (ret < 0 && ret != -L4_EBUSY)
221 invoke_callback();
222 return ret != -L4_EBUSY;
223 },
224 [=](bool ret) { if (!ret) invoke_callback(); }
225 );
226 }
227
228 static char const *render_guid(void const *guid_p, char buf[])
229 {
230 auto *p = static_cast<unsigned char const *>(guid_p);
231 snprintf(buf, 37,
232 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
233 p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
234 p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
235
236 return buf;
237 }
238
239 l4_size_t _num_partitions;
240 Inout_block _db;
241 Device_type *_dev;
244 Errand::Callback _callback;
245};
246
247
248
249}
Helper class that temporarily allocates memory that can be used for in/out operations with the device...
Partition table reader for block devices.
Definition partition.h:44
@ From_device
device writes to the memory
Definition dma_space:68
unsigned int l4_size_t
Unsigned size type.
Definition l4int.h:24
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
int l4_cache_inv_data(unsigned long start, unsigned long end) L4_NOTHROW
Cache invalidate a range; might write back to PoC.
Definition cache.h:86
@ L4_ERANGE
Range error.
Definition err.h:48
@ L4_EBUSY
Object currently busy, try later.
Definition err.h:42
@ L4_ENODEV
No such thing.
Definition err.h:44
@ L4_EOK
Ok.
Definition err.h:32
Description of an inout block to be sent to the device.
Definition types.h:67
Information about a single partition.
Definition partition.h:30
l4_uint64_t flags
Additional flags, depending on partition type.
Definition partition.h:35
char guid[37]
ID of the partition.
Definition partition.h:31
l4_uint64_t last
Last valid sector.
Definition partition.h:34
l4_uint64_t first
First valid sector.
Definition partition.h:33
std::u16string name
UTF16 name of the partition.
Definition partition.h:32