Skip to content

Commit 6e86342

Browse files
esmilyuwata
authored andcommitted
sd-boot: Support installing new devicetree
The Bootloader Specification says "devicetree refers to the binary device tree to use when executing the kernel..", but systemd-boot didn't actually do anything when encountering this stanza until now. Add support for loading, applying fixups if relevant, and installing the new device tree before executing the kernel.
1 parent 7c5b995 commit 6e86342

File tree

5 files changed

+200
-1
lines changed

5 files changed

+200
-1
lines changed

src/boot/efi/boot.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <efilib.h>
66

77
#include "console.h"
8+
#include "devicetree.h"
89
#include "disk.h"
910
#include "efi-loader-features.h"
1011
#include "graphics.h"
@@ -40,6 +41,7 @@ typedef struct {
4041
EFI_HANDLE *device;
4142
enum loader_type type;
4243
CHAR16 *loader;
44+
CHAR16 *devicetree;
4345
CHAR16 *options;
4446
CHAR16 key;
4547
EFI_STATUS (*call)(VOID);
@@ -475,6 +477,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
475477
}
476478
if (entry->loader)
477479
Print(L"loader '%s'\n", entry->loader);
480+
if (entry->devicetree)
481+
Print(L"devicetree '%s'\n", entry->devicetree);
478482
if (entry->options)
479483
Print(L"options '%s'\n", entry->options);
480484
Print(L"auto-select %s\n", yes_no(!entry->no_autoselect));
@@ -893,6 +897,7 @@ static VOID config_entry_free(ConfigEntry *entry) {
893897
FreePool(entry->version);
894898
FreePool(entry->machine_id);
895899
FreePool(entry->loader);
900+
FreePool(entry->devicetree);
896901
FreePool(entry->options);
897902
FreePool(entry->path);
898903
FreePool(entry->current_name);
@@ -1330,6 +1335,12 @@ static VOID config_entry_add_from_file(
13301335
continue;
13311336
}
13321337

1338+
if (strcmpa((CHAR8 *)"devicetree", key) == 0) {
1339+
FreePool(entry->devicetree);
1340+
entry->devicetree = stra_to_path(value);
1341+
continue;
1342+
}
1343+
13331344
if (strcmpa((CHAR8 *)"initrd", key) == 0) {
13341345
_cleanup_freepool_ CHAR16 *new = NULL;
13351346

@@ -2270,10 +2281,12 @@ static VOID config_load_xbootldr(
22702281
}
22712282

22722283
static EFI_STATUS image_start(
2284+
EFI_FILE_HANDLE root_dir,
22732285
EFI_HANDLE parent_image,
22742286
const Config *config,
22752287
const ConfigEntry *entry) {
22762288

2289+
_cleanup_(devicetree_cleanup) struct devicetree_state dtstate = {};
22772290
EFI_HANDLE image;
22782291
_cleanup_freepool_ EFI_DEVICE_PATH *path = NULL;
22792292
CHAR16 *options;
@@ -2290,6 +2303,12 @@ static EFI_STATUS image_start(
22902303
if (EFI_ERROR(err))
22912304
return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
22922305

2306+
if (entry->devicetree) {
2307+
err = devicetree_install(&dtstate, root_dir, entry->devicetree);
2308+
if (EFI_ERROR(err))
2309+
return log_error_status_stall(err, L"Error loading %s: %r", entry->devicetree, err);
2310+
}
2311+
22932312
if (config->options_edit)
22942313
options = config->options_edit;
22952314
else if (entry->options)
@@ -2533,7 +2552,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
25332552
(VOID) process_random_seed(root_dir, config.random_seed_mode);
25342553

25352554
uefi_call_wrapper(BS->SetWatchdogTimer, 4, 5 * 60, 0x10000, 0, NULL);
2536-
err = image_start(image, &config, entry);
2555+
err = image_start(root_dir, image, &config, entry);
25372556
if (EFI_ERROR(err)) {
25382557
graphics_mode(FALSE);
25392558
log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err);

src/boot/efi/devicetree.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <efi.h>
4+
5+
#include "devicetree.h"
6+
#include "missing_efi.h"
7+
#include "util.h"
8+
9+
#define FDT_V1_SIZE (7*4)
10+
11+
static EFI_STATUS devicetree_allocate(struct devicetree_state *state, UINTN size) {
12+
UINTN pages = DIV_ROUND_UP(size, EFI_PAGE_SIZE);
13+
EFI_STATUS err;
14+
15+
assert(state);
16+
17+
err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateAnyPages, EfiACPIReclaimMemory, pages,
18+
&state->addr);
19+
if (EFI_ERROR(err))
20+
return err;
21+
22+
state->pages = pages;
23+
return err;
24+
}
25+
26+
static UINTN devicetree_allocated(const struct devicetree_state *state) {
27+
assert(state);
28+
return state->pages * EFI_PAGE_SIZE;
29+
}
30+
31+
static VOID *devicetree_ptr(const struct devicetree_state *state) {
32+
assert(state);
33+
assert(state->addr <= UINTN_MAX);
34+
return (VOID *)(UINTN)state->addr;
35+
}
36+
37+
static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) {
38+
EFI_DT_FIXUP_PROTOCOL *fixup;
39+
UINTN size;
40+
EFI_STATUS err;
41+
42+
assert(state);
43+
44+
err = LibLocateProtocol(&EfiDtFixupProtocol, (VOID **)&fixup);
45+
if (EFI_ERROR(err))
46+
return log_error_status_stall(EFI_SUCCESS,
47+
L"Could not locate device tree fixup protocol, skipping.");
48+
49+
size = devicetree_allocated(state);
50+
err = uefi_call_wrapper(fixup->Fixup, 4, fixup, devicetree_ptr(state), &size,
51+
EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY);
52+
if (err == EFI_BUFFER_TOO_SMALL) {
53+
EFI_PHYSICAL_ADDRESS oldaddr = state->addr;
54+
UINTN oldpages = state->pages;
55+
VOID *oldptr = devicetree_ptr(state);
56+
57+
err = devicetree_allocate(state, size);
58+
if (EFI_ERROR(err))
59+
return err;
60+
61+
CopyMem(devicetree_ptr(state), oldptr, len);
62+
err = uefi_call_wrapper(BS->FreePages, 2, oldaddr, oldpages);
63+
if (EFI_ERROR(err))
64+
return err;
65+
66+
size = devicetree_allocated(state);
67+
err = uefi_call_wrapper(fixup->Fixup, 4, fixup, devicetree_ptr(state), &size,
68+
EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY);
69+
}
70+
71+
return err;
72+
}
73+
74+
EFI_STATUS devicetree_install(struct devicetree_state *state,
75+
EFI_FILE_HANDLE root_dir, CHAR16 *name) {
76+
_cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
77+
_cleanup_freepool_ EFI_FILE_INFO *info = NULL;
78+
UINTN len;
79+
EFI_STATUS err;
80+
81+
assert(state);
82+
assert(root_dir);
83+
assert(name);
84+
85+
err = LibGetSystemConfigurationTable(&EfiDtbTableGuid, &state->orig);
86+
if (EFI_ERROR(err))
87+
return EFI_UNSUPPORTED;
88+
89+
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, name, EFI_FILE_MODE_READ,
90+
EFI_FILE_READ_ONLY);
91+
if (EFI_ERROR(err))
92+
return err;
93+
94+
info = LibFileInfo(handle);
95+
if (!info)
96+
return EFI_OUT_OF_RESOURCES;
97+
if (info->FileSize < FDT_V1_SIZE || info->FileSize > 32 * 1024 * 1024)
98+
/* 32MB device tree blob doesn't seem right */
99+
return EFI_INVALID_PARAMETER;
100+
101+
len = info->FileSize;
102+
103+
err = devicetree_allocate(state, len);
104+
if (EFI_ERROR(err))
105+
return err;
106+
107+
err = uefi_call_wrapper(handle->Read, 3, handle, &len, devicetree_ptr(state));
108+
if (EFI_ERROR(err))
109+
return err;
110+
111+
err = devicetree_fixup(state, len);
112+
if (EFI_ERROR(err))
113+
return err;
114+
115+
return uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, devicetree_ptr(state));
116+
}
117+
118+
void devicetree_cleanup(struct devicetree_state *state) {
119+
EFI_STATUS err;
120+
121+
if (!state->pages)
122+
return;
123+
124+
err = uefi_call_wrapper(BS->InstallConfigurationTable, 2, &EfiDtbTableGuid, state->orig);
125+
/* don't free the current device tree if we can't reinstate the old one */
126+
if (EFI_ERROR(err))
127+
return;
128+
129+
uefi_call_wrapper(BS->FreePages, 2, state->addr, state->pages);
130+
state->pages = 0;
131+
}

src/boot/efi/devicetree.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
#pragma once
3+
4+
struct devicetree_state {
5+
EFI_PHYSICAL_ADDRESS addr;
6+
UINTN pages;
7+
VOID *orig;
8+
};
9+
10+
EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE_HANDLE root_dir, CHAR16 *name);
11+
void devicetree_cleanup(struct devicetree_state *state);

src/boot/efi/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
efi_headers = files('''
44
console.h
5+
devicetree.h
56
disk.h
67
graphics.h
78
linux.h
@@ -28,6 +29,7 @@ common_sources = '''
2829
systemd_boot_sources = '''
2930
boot.c
3031
console.c
32+
devicetree.c
3133
random-seed.c
3234
sha256.c
3335
shim.c

src/boot/efi/missing_efi.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,39 @@ typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
124124
#ifndef EFI_IMAGE_MACHINE_RISCV64
125125
#define EFI_IMAGE_MACHINE_RISCV64 0x5064
126126
#endif
127+
128+
#ifndef EFI_DTB_TABLE_GUID
129+
#define EFI_DTB_TABLE_GUID \
130+
{ 0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0} }
131+
#define EfiDtbTableGuid ((EFI_GUID)EFI_DTB_TABLE_GUID)
132+
#endif
133+
134+
#ifndef EFI_DT_FIXUP_PROTOCOL_GUID
135+
#define EFI_DT_FIXUP_PROTOCOL_GUID \
136+
{ 0xe617d64c, 0xfe08, 0x46da, {0xf4, 0xdc, 0xbb, 0xd5, 0x87, 0x0c, 0x73, 0x00} }
137+
#define EfiDtFixupProtocol ((EFI_GUID)EFI_DT_FIXUP_PROTOCOL_GUID)
138+
139+
#define EFI_DT_FIXUP_PROTOCOL_REVISION 0x00010000
140+
141+
/* Add nodes and update properties */
142+
#define EFI_DT_APPLY_FIXUPS 0x00000001
143+
/*
144+
* Reserve memory according to the /reserved-memory node
145+
* and the memory reservation block
146+
*/
147+
#define EFI_DT_RESERVE_MEMORY 0x00000002
148+
149+
typedef struct _EFI_DT_FIXUP_PROTOCOL EFI_DT_FIXUP_PROTOCOL;
150+
151+
typedef EFI_STATUS (EFIAPI *EFI_DT_FIXUP) (
152+
IN EFI_DT_FIXUP_PROTOCOL *This,
153+
IN VOID *Fdt,
154+
IN OUT UINTN *BufferSize,
155+
IN UINT32 Flags);
156+
157+
struct _EFI_DT_FIXUP_PROTOCOL {
158+
UINT64 Revision;
159+
EFI_DT_FIXUP Fixup;
160+
};
161+
162+
#endif

0 commit comments

Comments
 (0)