L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
partition.h
1/*
2 * Copyright (C) 2018, 2020-2025 Kernkonzept GmbH.
3 * Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
4 * Jakub Jermar <jakub.jermar@kernkonzept.com>
5 *
6 * License: see LICENSE.spdx (in this directory or the directories above)
7 */
8#pragma once
9
10#include <cstring>
11#include <string>
12#include <locale>
13#include <codecvt>
14#include <cassert>
15#include <map>
16
17#include <l4/cxx/ref_ptr>
18
19#include <l4/l4virtio/virtio_block.h>
20
21#include <l4/libblock-device/debug.h>
22#include <l4/libblock-device/errand.h>
23#include <l4/libblock-device/inout_memory.h>
24#include <l4/libblock-device/gpt.h>
25#include <l4/libblock-device/mbr.h>
26
27#include <l4/sys/cache.h>
28
29namespace Block_device {
30
31enum Partition_type
32{
33 Gpt_part = 1,
34 Mbr_part = 2
35};
36
41{
42 Partition_type type;
43 char uuid[37];
44 std::u16string name;
48};
49
53class Reader : public cxx::Ref_obj
54{
55public:
56 virtual ~Reader() = default;
57
58 virtual void read(Errand::Callback const &callback) = 0;
59 virtual l4_size_t table_size() const = 0;
60 virtual int get_partition(l4_size_t idx, Partition_info *inf) const = 0;
61};
62
66template <typename DERIVED, typename DEV>
67class Base_reader : public Reader
68{
69protected:
70 enum
71 {
73 };
74
75public:
76 using Device_type = DEV;
77
78 Base_reader(Device_type *dev)
79 : _dev(dev)
80 {}
81
82protected:
83 void invoke_callback()
84 {
85 assert(_callback);
86 auto cb = _callback;
87 // Reset the callback to drop potential transitive self-references
88 _callback = nullptr;
89 cb();
90 }
91
92 void read_sectors(l4_uint64_t sector,
93 void (DERIVED::*func)(int, l4_size_t))
94 {
95 using namespace std::placeholders;
96 auto next = std::bind(func, static_cast<DERIVED *>(this), _1, _2);
97
98 l4_addr_t vstart = reinterpret_cast<l4_addr_t>(_db.virt_addr);
99 l4_addr_t vend = vstart + _db.num_sectors * _dev->sector_size();
100 l4_cache_inv_data(vstart, vend);
101
102 Errand::poll(10, 10000,
103 [=]()
104 {
105 int ret = _dev->inout_data(
106 sector, _db,
107 [next, vstart, vend](int error, l4_size_t size)
108 {
109 l4_cache_inv_data(vstart, vend);
110 next(error, size);
111 },
113 if (ret < 0 && ret != -L4_EBUSY)
114 invoke_callback();
115 return ret != -L4_EBUSY;
116 },
117 [=](bool ret) { if (!ret) invoke_callback(); }
118 );
119 }
120
121 Inout_block _db;
122 Device_type *_dev;
123 Errand::Callback _callback;
124};
125
129template <typename DEV>
130class Gpt_reader : public Base_reader<Gpt_reader<DEV>, DEV>
131{
132 using Device_type = DEV;
133 using Base = Base_reader<Gpt_reader<DEV>, Device_type>;
134
135public:
136 Gpt_reader(Device_type *dev)
137 : Base_reader<Gpt_reader<Device_type>, Device_type>(dev),
139 {}
140
141 l4_size_t table_size() const override
142 { return _num_partitions; }
143
144 void read(Errand::Callback const &callback) override
145 {
146 _num_partitions = 0;
147 Base::_callback = callback;
148
149 // preparation: read the first two sectors
150 Base::_db = _header.inout_block();
151 Base::read_sectors(0, &Gpt_reader<Device_type>::get_gpt);
152 }
153
154 int get_partition(l4_size_t idx, Partition_info *inf) const override
155 {
156 if (idx == 0 || idx > _num_partitions)
157 return -L4_ERANGE;
158
159 unsigned secsz = Base::_dev->sector_size();
160 auto *header = _header.template get<Gpt::Header const>(secsz);
161
162 Gpt::Entry *e =
163 _parray.template get<Gpt::Entry>((idx - 1) * header->entry_size);
164
165 if (*((l4_uint64_t *) &e->partition_guid) == 0ULL)
166 return -L4_ENODEV;
167
168 render_guid(e->partition_guid, inf->uuid);
169
170 auto name =
171 std::u16string((char16_t *)e->name, sizeof(e->name) / sizeof(e->name[0]));
172 inf->name = name.substr(0, name.find((char16_t) 0));
173
174 inf->type = Gpt_part;
175 inf->first = e->first;
176 inf->last = e->last;
177 inf->flags = e->flags;
178
179 auto info = Dbg::info();
180 if (info.is_active())
181 {
182 info.printf("%3zu: %10lld %10lld %5gMiB [%.37s]\n",
183 idx, e->first, e->last,
184 (e->last - e->first + 1.0) * secsz / (1 << 20),
185 inf->uuid);
186
187 char buf[37];
188 _Pragma("GCC diagnostic push");
189 _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
190 std::string pname = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
191 .to_bytes(inf->name);
192 _Pragma("GCC diagnostic pop");
193 info.printf(" : [%s] Type: %s\n",
194 pname.c_str(), render_guid(e->type_guid, buf));
195 }
196
197 auto warn = Dbg::warn();
198 if (inf->last < inf->first)
199 {
200 warn.printf(
201 "Invalid settings of %3zu. Last lba before first lba. Will ignore.\n",
202 idx);
203 // Errors in the GPT shall not crash any service -- just ignore the
204 // corresponding partition.
205 return -L4_ENODEV;
206 }
207
208 return L4_EOK;
209 }
210
211private:
212 void get_gpt(int error, l4_size_t)
213 {
214 _header.unmap();
215
216 if (error < 0)
217 {
218 // can't read from device, we are done
219 Base::invoke_callback();
220 return;
221 }
222
223 // prepare reading of the table from disk
224 unsigned secsz = Base::_dev->sector_size();
225 auto *header = _header.template get<Gpt::Header const>(secsz);
226
227 auto info = Dbg::info();
228 auto trace = Dbg::trace();
229
230 if (strncmp(header->signature, "EFI PART", 8) != 0)
231 {
232 info.printf("No GUID partition header found.\n");
233 Base::invoke_callback();
234 return;
235 }
236
237 // XXX check CRC32 of header
238
239 info.printf("GUID partition header found with up to %d partitions.\n",
240 header->partition_array_size);
241 char buf[37];
242 info.printf("GUID: %s\n", render_guid(header->disk_guid, buf));
243 trace.printf("Header positions: %llx (Backup: %llx)\n",
244 header->current_lba, header->backup_lba);
245 trace.printf("First + last: %llx and %llx\n",
246 header->first_lba, header->last_lba);
247 trace.printf("Partition table at %llx\n",
248 header->partition_array_lba);
249 trace.printf("Size of a partition entry: %d\n",
250 header->entry_size);
251
252 info.printf("GUID partition header found with %d partitions.\n",
253 header->partition_array_size);
254
255 _num_partitions =
256 cxx::min<l4_uint32_t>(header->partition_array_size, Base::Max_partitions);
257
258 l4_size_t arraysz = _num_partitions * header->entry_size;
259 l4_size_t numsec = (arraysz - 1 + secsz) / secsz;
260
261 _parray =
262 Inout_memory<Device_type>(numsec, Base::_dev,
264 trace.printf("Reading GPT table @ 0x%p\n", _parray.template get<void>(0));
265
266 Base::_db = _parray.inout_block();
267 Base::read_sectors(header->partition_array_lba, &Gpt_reader<DEV>::done_gpt);
268 }
269
270 void done_gpt(int error, l4_size_t)
271 {
272 _parray.unmap();
273
274 // XXX check CRC32 of table
275
276 if (error < 0)
277 _num_partitions = 0;
278
279 Base::invoke_callback();
280 }
281
282 static char const *render_guid(void const *guid_p, char buf[])
283 {
284 auto *p = static_cast<unsigned char const *>(guid_p);
285 snprintf(buf, 37,
286 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
287 p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
288 p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
289
290 return buf;
291 }
292
293 l4_size_t _num_partitions;
296};
297
310template <typename DEV, typename DERIVED = void>
311class Mbr_reader
312: public Base_reader<typename std::conditional<std::is_class<DERIVED>::value,
313 DERIVED, Mbr_reader<DEV>>::type,
314 DEV>
315{
316 using Device_type = DEV;
317 using Derived_type =
318 typename std::conditional<std::is_class<DERIVED>::value, DERIVED,
319 Mbr_reader<DEV>>::type;
320 using Base = Base_reader<Derived_type, Device_type>;
321
322 enum : unsigned
323 {
324 // Limit the number of MBR partitions to 255 so that they all can have
325 // a unique UUID.
326 Max_partitions = cxx::min<unsigned>(Base::Max_partitions, 255u)
327 };
328
329public:
330 Mbr_reader(Device_type *dev)
331 : Base(dev),
332 _mbr(nullptr),
334 _partitions(Mbr::Primary_partitions),
335 _extended(nullptr),
336 _lba_offset_ext(0)
337 {}
338
339 l4_size_t table_size() const override
340 {
341 return _num_partitions;
342 }
343
344 void read(Errand::Callback const &callback) override
345 {
346 _num_partitions = 0;
347 Base::_callback = callback;
348
349 // preparation: read the first sector
350 Base::_db = _header.inout_block();
351 Base::read_sectors(0, &Derived_type::get_mbr);
352 }
353
354 int get_partition(l4_size_t idx, Partition_info *inf) const override
355 {
356 if (idx == 0 || idx > _num_partitions)
357 return -L4_ERANGE;
358
359 *inf = _partitions[idx - 1];
360
361 if (inf->first > inf->last)
362 return -L4_ENODEV;
363
364 return L4_EOK;
365 }
366
367private:
368 bool register_mbr_partition(Mbr::Entry const *p, l4_uint32_t disk_id,
369 unsigned n)
370 {
371 _partitions.resize(n + 1);
372
373 auto info = Dbg::info();
374
375 bool valid = false;
376 if (p->type && p->lba_start <= p->lba_start + p->lba_num - 1)
377 {
378 valid = true;
379 snprintf(_partitions[n].uuid, sizeof(_partitions[n].uuid), "%08X-%02X",
380 disk_id, n + 1);
381 _partitions[n].type = Mbr_part;
382 _partitions[n].first = _lba_offset_ext + p->lba_start;
383 _partitions[n].last = _lba_offset_ext + p->lba_start + p->lba_num - 1;
384 _partitions[n].flags = p->type;
385
386 info.printf("Partition %u, UUID=%s: start=%llu size=%zu, type=%x\n",
387 n + 1, _partitions[n].uuid, _partitions[n].first,
388 p->lba_num * Base::_dev->sector_size(), p->type);
389
390 if (p->type == Mbr::Partition_type::Extended && !_extended)
391 _extended = p;
392 }
393 else
394 {
395 _partitions[n].type = Mbr_part;
396 _partitions[n].uuid[0] = 0;
397 _partitions[n].first = -1ULL;
398 _partitions[n].last = 0ULL;
399 _partitions[n].flags = 0ULL;
400 }
401
402 return valid;
403 }
404
405 void get_mbr(int error, l4_size_t)
406 {
407 _header.unmap();
408
409 if (error < 0)
410 {
411 // can't read from device, we are done
412 Base::invoke_callback();
413 return;
414 }
415
416 _mbr = _header.template get<Mbr::Mbr const>(0);
417
418 auto info = Dbg::info();
419
420 if (_mbr->signature[0] != Mbr::Magic_lo
421 || _mbr->signature[1] != Mbr::Magic_hi)
422 {
423 info.printf("No MBR found.\n");
424 Base::invoke_callback();
425 return;
426 }
427
428 info.printf("MBR found.\n");
429
430 for (unsigned i = 0; i < Mbr::Primary_partitions; i++)
431 (void)register_mbr_partition(&_mbr->partition[i], _mbr->disk_id, i);
432
433 _num_partitions = Mbr::Primary_partitions;
434 if (_extended)
435 {
436 _ep =
437 Inout_memory<Device_type>(1, Base::_dev,
439 Base::_db = _ep.inout_block();
440 _lba_offset_ext += _extended->lba_start;
441 Base::read_sectors(_extended->lba_start, &Derived_type::get_ext);
442 }
443 else
444 Base::invoke_callback();
445 }
446
447 void get_ext(int error, l4_size_t)
448 {
449 _ep.unmap();
450
451 if (error < 0)
452 {
453 // can't read from device, we are done
454 Base::invoke_callback();
455 return;
456 }
457
458 auto *ext = _ep.template get<Mbr::Mbr const>(0);
459
460 auto info = Dbg::info();
461 if (ext->signature[0] != Mbr::Magic_lo
462 || ext->signature[1] != Mbr::Magic_hi)
463 {
464 info.printf("No extended MBR found.\n");
465 Base::invoke_callback();
466 return;
467 }
468
469 info.printf("Extended MBR found at LBA %u.\n", _lba_offset_ext);
470
471 if (register_mbr_partition(&ext->partition[0], _mbr->disk_id,
472 _num_partitions))
473 {
474 _num_partitions++;
475 l4_uint32_t next = ext->partition[1].lba_start;
476 if (next && _num_partitions < Max_partitions)
477 {
480 Base::_db = _ep.inout_block();
481 _lba_offset_ext = _extended->lba_start + next;
482 Base::read_sectors(_extended->lba_start + next,
483 &Derived_type::get_ext);
484 return;
485 }
486 }
487 Base::invoke_callback();
488 }
489
490 l4_size_t _num_partitions;
491 Mbr::Mbr const *_mbr;
493 std::vector<Partition_info> _partitions;
494 Mbr::Entry const *_extended; // Pointer to the first extended MBR
496 l4_uint32_t _lba_offset_ext; // Extended partition offset
497};
498
510template <typename DEV, typename BASE>
511class Labeling_reader : public BASE
512{
513 using Device_type = DEV;
514 using Base = BASE;
515
516public:
517 Labeling_reader(Device_type *dev) : Base(dev), _cur(0)
518 {}
519
520 void read(Errand::Callback const &callback) override
521 {
522 Base::read([=](){ label(callback); });
523 }
524
525 int get_partition(l4_size_t idx, Partition_info *inf) const override
526 {
527 int ret = Base::get_partition(idx, inf);
528 if (ret == L4_EOK)
529 {
530 if (inf->name.empty())
531 {
532 auto it = _labels.find(idx);
533 if (it != _labels.end())
534 {
535 _Pragma("GCC diagnostic push");
536 _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
537 inf->name =
538 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,
539 char16_t>{}
540 .from_bytes(it->second);
541 _Pragma("GCC diagnostic pop");
542 }
543 }
544 }
545 return ret;
546 }
547
548private:
549 bool next(Partition_info *info)
550 {
551 for (; _cur < Base::table_size(); _cur++)
552 {
553 if (Base::get_partition(_cur + 1, info) == L4_EOK)
554 {
555 if (info->type == Mbr_part
556 && info->flags == Mbr::Partition_type::Linux)
557 {
558 return true;
559 }
560 }
561 }
562 return false;
563 }
564
565 void label(Errand::Callback const &callback)
566 {
567 Base::_callback = callback;
568 Partition_info inf;
569 if (!next(&inf))
570 {
571 Base::invoke_callback();
572 return;
573 }
574 _sb = Inout_memory<Device_type>(1, Base::_dev,
576 Base::_db = _sb.inout_block();
577 Base::read_sectors(
578 inf.first + (Ext2::Superblock_offset / Base::_dev->sector_size()),
579 &Labeling_reader<Device_type, Base>::get_label);
580 }
581
582 void get_label(int error, l4_size_t)
583 {
584 _sb.unmap();
585
586 if (error < 0)
587 {
588 // can't read from device, we are done
589 Base::invoke_callback();
590 return;
591 }
592
593 Partition_info inf;
594
595 auto *sb = _sb.template get<Ext2::Superblock const>(
596 Ext2::Superblock_offset % Base::_dev->sector_size());
597
598 auto info = Dbg::info();
599 if (sb->magic == Ext2::Superblock_magic
600 && sb->major >= Ext2::Superblock_version)
601 {
602 std::string label = std::string(
603 reinterpret_cast<const char *>(sb->name),
604 cxx::min(strlen(reinterpret_cast<const char *>(sb->name)),
605 sizeof(sb->name)));
606 label.erase(0, label.find_first_not_of(" "));
607 label.erase(label.find_last_not_of(" ") + 1);
608 info.printf("Found Ext2 superblock on partition %u, label=%s\n",
609 _cur + 1, label.c_str());
610 _labels[_cur + 1] = label;
611 }
612 else
613 info.printf("No label found for partition %u.\n", _cur + 1);
614
615 _cur++;
616 if (!next(&inf))
617 {
618 Base::invoke_callback();
619 return;
620 }
621
622 _sb = Inout_memory<Device_type>(1, Base::_dev,
624 Base::_db = _sb.inout_block();
625 Base::read_sectors(
626 inf.first + (Ext2::Superblock_offset / Base::_dev->sector_size()),
627 &Labeling_reader<Device_type, Base>::get_label);
628 }
629
630 unsigned _cur;
631 std::map<unsigned, std::string> _labels;
633};
634
638template <typename DEV>
639class Labeling_mbr_reader
640: public Labeling_reader<DEV, Mbr_reader<DEV, Labeling_mbr_reader<DEV>>>
641{
642public:
643 Labeling_mbr_reader(DEV *dev)
644 : Labeling_reader<DEV, Mbr_reader<DEV, Labeling_mbr_reader<DEV>>>(dev)
645 {}
646};
647
652template <typename DEV>
653class Partition_reader : public Base_reader<Partition_reader<DEV>, DEV>
654{
655 using Device_type = DEV;
656 using Base = Base_reader<Partition_reader<DEV>, Device_type>;
657
658public:
659 Partition_reader(Device_type *dev)
660 : Base_reader<Partition_reader<Device_type>, Device_type>(dev),
661 _reader(cxx::make_ref_obj<Gpt_reader<Device_type>>(dev))
662 {}
663
664 l4_size_t table_size() const override
665 {
666 return _reader->table_size();
667 }
668
669 void read(Errand::Callback const &callback) override
670 {
671 Base::_callback = callback; // hold ref to the original callback
672 _reader->read([=]()
673 {
674 // If the GPT reader fails to discover any partitions, we carefully
675 // switch to the MBR reader. When the MBR reader's callback is called
676 // we are done whether it found any partitions or not. In any case
677 // this ends by invoking the original callback.
678 if (_reader->table_size() == 0)
679 {
680 // Hold ref to the old reader as we are still executing its lambda.
681 auto old = _reader;
682 _reader =
683 cxx::make_ref_obj<Labeling_mbr_reader<Device_type>>(Base::_dev);
684 _reader->read([=](){ Base::invoke_callback();});
685 }
686 else
687 Base::invoke_callback();
688 });
689 }
690
691 int get_partition(l4_size_t idx, Partition_info *inf) const override
692 {
693 return _reader->get_partition(idx, inf);
694 }
695
696private:
697 cxx::Ref_ptr<Reader> _reader;
698};
699
700}
Class providing common functionality for partition table readers.
Definition partition.h:68
GPT partition table reader.
Definition partition.h:131
Helper class that temporarily allocates memory that can be used for in/out operations with the device...
Interface of a partition table reader for block devices.
Definition partition.h:54
@ From_device
device writes to the memory
Definition dma_space:68
A reference-counting pointer with automatic cleanup.
Definition ref_ptr:71
unsigned int l4_size_t
Unsigned size type.
Definition l4int.h:24
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
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
Cache-consistency functions.
Information about a single partition.
Definition partition.h:41
l4_uint64_t flags
Additional flags, depending on partition type.
Definition partition.h:47
Partition_type type
What kind of partition table scheme is used.
Definition partition.h:42
l4_uint64_t last
Last valid sector.
Definition partition.h:46
l4_uint64_t first
First valid sector.
Definition partition.h:45
std::u16string name
UTF16 name of the partition.
Definition partition.h:44
char uuid[37]
ID of the partition.
Definition partition.h:43