]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/pe.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / boot / efi / pe.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 #include "pe.h"
7 #include "util.h"
8
9 #define DOS_FILE_MAGIC "MZ"
10 #define PE_FILE_MAGIC "PE\0\0"
11 #define MAX_SECTIONS 96
12
13 #if defined(__i386__)
14 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
15 #elif defined(__x86_64__)
16 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
17 #elif defined(__aarch64__)
18 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
19 #elif defined(__arm__)
20 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
21 #elif defined(__riscv) && __riscv_xlen == 64
22 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
23 #else
24 #error Unknown EFI arch
25 #endif
26
27 struct DosFileHeader {
28 UINT8 Magic[2];
29 UINT16 LastSize;
30 UINT16 nBlocks;
31 UINT16 nReloc;
32 UINT16 HdrSize;
33 UINT16 MinAlloc;
34 UINT16 MaxAlloc;
35 UINT16 ss;
36 UINT16 sp;
37 UINT16 Checksum;
38 UINT16 ip;
39 UINT16 cs;
40 UINT16 RelocPos;
41 UINT16 nOverlay;
42 UINT16 reserved[4];
43 UINT16 OEMId;
44 UINT16 OEMInfo;
45 UINT16 reserved2[10];
46 UINT32 ExeHeader;
47 } _packed_;
48
49 struct CoffFileHeader {
50 UINT16 Machine;
51 UINT16 NumberOfSections;
52 UINT32 TimeDateStamp;
53 UINT32 PointerToSymbolTable;
54 UINT32 NumberOfSymbols;
55 UINT16 SizeOfOptionalHeader;
56 UINT16 Characteristics;
57 } _packed_;
58
59 struct PeFileHeader {
60 UINT8 Magic[4];
61 struct CoffFileHeader FileHeader;
62 /* OptionalHeader omitted */
63 } _packed_;
64
65 struct PeSectionHeader {
66 UINT8 Name[8];
67 UINT32 VirtualSize;
68 UINT32 VirtualAddress;
69 UINT32 SizeOfRawData;
70 UINT32 PointerToRawData;
71 UINT32 PointerToRelocations;
72 UINT32 PointerToLinenumbers;
73 UINT16 NumberOfRelocations;
74 UINT16 NumberOfLinenumbers;
75 UINT32 Characteristics;
76 } _packed_;
77
78 static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
79 assert(dos);
80 return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
81 }
82
83 static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
84 assert(pe);
85 return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
86 pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
87 pe->FileHeader.NumberOfSections > 0 &&
88 pe->FileHeader.NumberOfSections <= MAX_SECTIONS;
89 }
90
91 static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
92 assert(dos);
93 assert(pe);
94 return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader;
95 }
96
97 static VOID locate_sections(
98 const struct PeSectionHeader section_table[],
99 UINTN n_table,
100 const CHAR8 **sections,
101 UINTN *addrs,
102 UINTN *offsets,
103 UINTN *sizes) {
104
105 assert(section_table);
106 assert(sections);
107 assert(sizes);
108
109 for (UINTN i = 0; i < n_table; i++) {
110 const struct PeSectionHeader *sect = section_table + i;
111
112 for (UINTN j = 0; sections[j]; j++) {
113 if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
114 continue;
115
116 if (addrs)
117 addrs[j] = sect->VirtualAddress;
118 if (offsets)
119 offsets[j] = sect->PointerToRawData;
120 sizes[j] = sect->VirtualSize;
121 }
122 }
123 }
124
125 EFI_STATUS pe_memory_locate_sections(
126 const CHAR8 *base,
127 const CHAR8 **sections,
128 UINTN *addrs,
129 UINTN *sizes) {
130 const struct DosFileHeader *dos;
131 const struct PeFileHeader *pe;
132 UINTN offset;
133
134 assert(base);
135 assert(sections);
136 assert(addrs);
137 assert(sizes);
138
139 dos = (const struct DosFileHeader*)base;
140 if (!verify_dos(dos))
141 return EFI_LOAD_ERROR;
142
143 pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
144 if (!verify_pe(pe))
145 return EFI_LOAD_ERROR;
146
147 offset = section_table_offset(dos, pe);
148 locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections,
149 sections, addrs, NULL, sizes);
150
151 return EFI_SUCCESS;
152 }
153
154 EFI_STATUS pe_file_locate_sections(
155 EFI_FILE *dir,
156 const CHAR16 *path,
157 const CHAR8 **sections,
158 UINTN *offsets,
159 UINTN *sizes) {
160 _cleanup_freepool_ struct PeSectionHeader *section_table = NULL;
161 _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
162 struct DosFileHeader dos;
163 struct PeFileHeader pe;
164 UINTN len, section_table_len;
165 EFI_STATUS err;
166
167 assert(dir);
168 assert(path);
169 assert(sections);
170 assert(offsets);
171 assert(sizes);
172
173 err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL);
174 if (EFI_ERROR(err))
175 return err;
176
177 len = sizeof(dos);
178 err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos);
179 if (EFI_ERROR(err))
180 return err;
181 if (len != sizeof(dos) || !verify_dos(&dos))
182 return EFI_LOAD_ERROR;
183
184 err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
185 if (EFI_ERROR(err))
186 return err;
187
188 len = sizeof(pe);
189 err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
190 if (EFI_ERROR(err))
191 return err;
192 if (len != sizeof(pe) || !verify_pe(&pe))
193 return EFI_LOAD_ERROR;
194
195 section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
196 section_table = AllocatePool(section_table_len);
197 if (!section_table)
198 return EFI_OUT_OF_RESOURCES;
199
200 err = uefi_call_wrapper(handle->SetPosition, 2, handle, section_table_offset(&dos, &pe));
201 if (EFI_ERROR(err))
202 return err;
203
204 len = section_table_len;
205 err = uefi_call_wrapper(handle->Read, 3, handle, &len, section_table);
206 if (EFI_ERROR(err))
207 return err;
208 if (len != section_table_len)
209 return EFI_LOAD_ERROR;
210
211 locate_sections(section_table, pe.FileHeader.NumberOfSections,
212 sections, NULL, offsets, sizes);
213
214 return EFI_SUCCESS;
215 }