1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "devicetree.h"
13 #include "secure-boot.h"
17 /* magic string to find in the binary image */
18 _used_
_section_(".sdmagic") static const char magic
[] = "#### LoaderInfo: systemd-stub " GIT_VERSION
" ####";
20 static EFI_STATUS
combine_initrd(
21 EFI_PHYSICAL_ADDRESS initrd_base
, UINTN initrd_size
,
22 const void *credential_initrd
, UINTN credential_initrd_size
,
23 const void *global_credential_initrd
, UINTN global_credential_initrd_size
,
24 const void *sysext_initrd
, UINTN sysext_initrd_size
,
25 EFI_PHYSICAL_ADDRESS
*ret_initrd_base
, UINTN
*ret_initrd_size
) {
27 EFI_PHYSICAL_ADDRESS base
= UINT32_MAX
; /* allocate an area below the 32bit boundary for this */
32 assert(ret_initrd_base
);
33 assert(ret_initrd_size
);
35 /* Combines four initrds into one, by simple concatenation in memory */
37 n
= ALIGN4(initrd_size
); /* main initrd might not be padded yet */
38 if (credential_initrd
) {
39 if (n
> UINTN_MAX
- credential_initrd_size
)
40 return EFI_OUT_OF_RESOURCES
;
42 n
+= credential_initrd_size
;
44 if (global_credential_initrd
) {
45 if (n
> UINTN_MAX
- global_credential_initrd_size
)
46 return EFI_OUT_OF_RESOURCES
;
48 n
+= global_credential_initrd_size
;
51 if (n
> UINTN_MAX
- sysext_initrd_size
)
52 return EFI_OUT_OF_RESOURCES
;
54 n
+= sysext_initrd_size
;
57 err
= BS
->AllocatePages(
62 if (err
!= EFI_SUCCESS
)
63 return log_error_status_stall(err
, L
"Failed to allocate space for combined initrd: %r", err
);
65 p
= PHYSICAL_ADDRESS_TO_POINTER(base
);
66 if (initrd_base
!= 0) {
69 /* Order matters, the real initrd must come first, since it might include microcode updates
70 * which the kernel only looks for in the first cpio archive */
71 memcpy(p
, PHYSICAL_ADDRESS_TO_POINTER(initrd_base
), initrd_size
);
74 pad
= ALIGN4(initrd_size
) - initrd_size
;
81 if (credential_initrd
) {
82 memcpy(p
, credential_initrd
, credential_initrd_size
);
83 p
+= credential_initrd_size
;
86 if (global_credential_initrd
) {
87 memcpy(p
, global_credential_initrd
, global_credential_initrd_size
);
88 p
+= global_credential_initrd_size
;
92 memcpy(p
, sysext_initrd
, sysext_initrd_size
);
93 p
+= sysext_initrd_size
;
96 assert((uint8_t*) PHYSICAL_ADDRESS_TO_POINTER(base
) + n
== p
);
98 *ret_initrd_base
= base
;
104 static void export_variables(EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
) {
107 assert(loaded_image
);
109 /* Export the device path this image is started from, if it's not set yet */
110 if (efivar_get_raw(LOADER_GUID
, L
"LoaderDevicePartUUID", NULL
, NULL
) != EFI_SUCCESS
)
111 if (disk_get_part_uuid(loaded_image
->DeviceHandle
, uuid
) == EFI_SUCCESS
)
112 efivar_set(LOADER_GUID
, L
"LoaderDevicePartUUID", uuid
, 0);
114 /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the
115 * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note
116 * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us,
117 * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong
118 * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath
119 * is non-NULL explicitly.) */
120 if (efivar_get_raw(LOADER_GUID
, L
"LoaderImageIdentifier", NULL
, NULL
) != EFI_SUCCESS
&&
121 loaded_image
->FilePath
) {
122 _cleanup_free_ char16_t
*s
= NULL
;
124 s
= DevicePathToStr(loaded_image
->FilePath
);
126 efivar_set(LOADER_GUID
, L
"LoaderImageIdentifier", s
, 0);
131 /* if LoaderFirmwareInfo is not set, let's set it */
132 if (efivar_get_raw(LOADER_GUID
, L
"LoaderFirmwareInfo", NULL
, NULL
) != EFI_SUCCESS
) {
133 _cleanup_free_ char16_t
*s
= NULL
;
134 s
= xpool_print(L
"%s %u.%02u", ST
->FirmwareVendor
, ST
->FirmwareRevision
>> 16, ST
->FirmwareRevision
& 0xffff);
135 efivar_set(LOADER_GUID
, L
"LoaderFirmwareInfo", s
, 0);
138 /* ditto for LoaderFirmwareType */
139 if (efivar_get_raw(LOADER_GUID
, L
"LoaderFirmwareType", NULL
, NULL
) != EFI_SUCCESS
) {
140 _cleanup_free_ char16_t
*s
= NULL
;
141 s
= xpool_print(L
"UEFI %u.%02u", ST
->Hdr
.Revision
>> 16, ST
->Hdr
.Revision
& 0xffff);
142 efivar_set(LOADER_GUID
, L
"LoaderFirmwareType", s
, 0);
145 /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
147 (void) efivar_set(LOADER_GUID
, L
"StubInfo", L
"systemd-stub " GIT_VERSION
, 0);
150 EFI_STATUS
efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*sys_table
) {
161 static const char * const sections
[_SECTION_MAX
+ 1] = {
162 [SECTION_CMDLINE
] = ".cmdline",
163 [SECTION_LINUX
] = ".linux",
164 [SECTION_INITRD
] = ".initrd",
165 [SECTION_SPLASH
] = ".splash",
166 [SECTION_DTB
] = ".dtb",
170 UINTN cmdline_len
= 0, linux_size
, initrd_size
, dt_size
;
171 UINTN credential_initrd_size
= 0, global_credential_initrd_size
= 0, sysext_initrd_size
= 0;
172 _cleanup_free_
void *credential_initrd
= NULL
, *global_credential_initrd
= NULL
;
173 _cleanup_free_
void *sysext_initrd
= NULL
;
174 EFI_PHYSICAL_ADDRESS linux_base
, initrd_base
, dt_base
;
175 _cleanup_(devicetree_cleanup
) struct devicetree_state dt_state
= {};
176 EFI_LOADED_IMAGE_PROTOCOL
*loaded_image
;
177 UINTN addrs
[_SECTION_MAX
] = {};
178 UINTN szs
[_SECTION_MAX
] = {};
179 char *cmdline
= NULL
;
180 _cleanup_free_
char *cmdline_owned
= NULL
;
183 InitializeLib(image
, sys_table
);
184 debug_hook(L
"systemd-stub");
185 /* Uncomment the next line if you need to wait for debugger. */
188 err
= BS
->OpenProtocol(
190 &LoadedImageProtocol
,
191 (void **)&loaded_image
,
194 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
195 if (err
!= EFI_SUCCESS
)
196 return log_error_status_stall(err
, L
"Error getting a LoadedImageProtocol handle: %r", err
);
198 err
= pe_memory_locate_sections(loaded_image
->ImageBase
, sections
, addrs
, szs
);
199 if (err
!= EFI_SUCCESS
|| szs
[SECTION_LINUX
] == 0) {
200 if (err
== EFI_SUCCESS
)
202 return log_error_status_stall(err
, L
"Unable to locate embedded .linux section: %r", err
);
205 /* Show splash screen as early as possible */
206 graphics_splash((const uint8_t*) loaded_image
->ImageBase
+ addrs
[SECTION_SPLASH
], szs
[SECTION_SPLASH
], NULL
);
208 if (szs
[SECTION_CMDLINE
] > 0) {
209 cmdline
= (char *) loaded_image
->ImageBase
+ addrs
[SECTION_CMDLINE
];
210 cmdline_len
= szs
[SECTION_CMDLINE
];
213 /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
214 if ((!secure_boot_enabled() || cmdline_len
== 0) && loaded_image
->LoadOptionsSize
> 0 &&
215 *(char16_t
*) loaded_image
->LoadOptions
> 0x1F) {
216 cmdline_len
= (loaded_image
->LoadOptionsSize
/ sizeof(char16_t
)) * sizeof(char);
217 cmdline
= cmdline_owned
= xmalloc(cmdline_len
);
219 for (UINTN i
= 0; i
< cmdline_len
; i
++)
220 cmdline
[i
] = ((char16_t
*) loaded_image
->LoadOptions
)[i
];
222 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
223 * duplicates what we already did in the boot menu, if that was already used. However, since
224 * we want the boot menu to support an EFI binary, and want to this stub to be usable from
225 * any boot menu, let's measure things anyway. */
226 (void) tpm_log_load_options(loaded_image
->LoadOptions
);
229 export_variables(loaded_image
);
231 (void) pack_cpio(loaded_image
,
234 ".extra/credentials",
235 /* dir_mode= */ 0500,
236 /* access_mode= */ 0400,
237 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS
, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT
},
239 L
"Credentials initrd",
241 &credential_initrd_size
);
243 (void) pack_cpio(loaded_image
,
244 L
"\\loader\\credentials",
246 ".extra/global_credentials",
247 /* dir_mode= */ 0500,
248 /* access_mode= */ 0400,
249 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS
, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT
},
251 L
"Global credentials initrd",
252 &global_credential_initrd
,
253 &global_credential_initrd_size
);
255 (void) pack_cpio(loaded_image
,
259 /* dir_mode= */ 0555,
260 /* access_mode= */ 0444,
261 /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD
},
263 L
"System extension initrd",
265 &sysext_initrd_size
);
267 linux_size
= szs
[SECTION_LINUX
];
268 linux_base
= POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[SECTION_LINUX
];
270 initrd_size
= szs
[SECTION_INITRD
];
271 initrd_base
= initrd_size
!= 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[SECTION_INITRD
] : 0;
273 dt_size
= szs
[SECTION_DTB
];
274 dt_base
= dt_size
!= 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image
->ImageBase
) + addrs
[SECTION_DTB
] : 0;
276 if (credential_initrd
|| global_credential_initrd
|| sysext_initrd
) {
277 /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
278 err
= combine_initrd(
279 initrd_base
, initrd_size
,
280 credential_initrd
, credential_initrd_size
,
281 global_credential_initrd
, global_credential_initrd_size
,
282 sysext_initrd
, sysext_initrd_size
,
283 &initrd_base
, &initrd_size
);
284 if (err
!= EFI_SUCCESS
)
287 /* Given these might be large let's free them explicitly, quickly. */
288 credential_initrd
= mfree(credential_initrd
);
289 global_credential_initrd
= mfree(global_credential_initrd
);
290 sysext_initrd
= mfree(sysext_initrd
);
294 err
= devicetree_install_from_memory(
295 &dt_state
, PHYSICAL_ADDRESS_TO_POINTER(dt_base
), dt_size
);
296 if (err
!= EFI_SUCCESS
)
297 log_error_stall(L
"Error loading embedded devicetree: %r", err
);
300 err
= linux_exec(image
, cmdline
, cmdline_len
,
301 PHYSICAL_ADDRESS_TO_POINTER(linux_base
), linux_size
,
302 PHYSICAL_ADDRESS_TO_POINTER(initrd_base
), initrd_size
);
303 graphics_mode(false);
304 return log_error_status_stall(err
, L
"Execution of embedded linux image failed: %r", err
);