Initial commi
[nyanefivars.git] / main.c
blob18d84861db9e2c5c392f48759f685b0d67855927
1 /*
2 * Protected by a GNU AFFERO GPLv3
3 * Copyright (C) 2021
4 * Sylvain Bertrand <sylvain.bertrand@legeek.net>
5 */
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <endian.h>
11 #include <string.h>
13 #define loop for(;;)
14 #define strtou64 strtoul
15 #define u16 uint16_t
16 #define u32 uint32_t
17 #define u8 uint8_t
18 #define u64 uint64_t
20 #define EFI_VARIABLE_NON_VOLATILE 0x00000001
21 #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
22 #define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
24 #define LOAD_OPTION_ACTIVE 0x00000001
26 #define PARTITION_UUID 1
27 #define PARTITION_NUMBER 2
28 #define PARTITION_START_LBA 3
29 #define PARTITION_LBAS_N 4
30 #define DESCRIPTION 5
31 #define LOADER_FILE_PATH 6
32 #define LOADER_ARGS 7
34 struct guid_t {
35 u32 blk_0;
36 u16 blk_1;
37 u16 blk_2;
38 u16 blk_3;
39 u8 blk_4[6];
41 /* brain damaged mixed-endian guid */
42 static void guid_write(void *dest, struct guid_t *src)
44 u8 *d;
45 u32 *p32;
46 u16 *p16;
48 d = (u8*)dest;
50 p32 = (u32*)d;
51 *p32 = htole32(src->blk_0); /* little endian */
52 d += 4;
53 p16 = (u16*)d;
54 *p16 = htole16(src->blk_1); /* little endian */
55 d += 2;
56 p16 = (u16*)d;
57 *p16 = htole16(src->blk_2); /* little endian */
58 d += 2;
59 p16 = (u16*)d;
60 *p16 = htobe16(src->blk_3); /* big endian */
61 d += 2;
62 d[0] = src->blk_4[0];
63 d[1] = src->blk_4[1];
64 d[2] = src->blk_4[2];
65 d[3] = src->blk_4[3];
66 d[4] = src->blk_4[4];
67 d[5] = src->blk_4[5];
69 /* best effort */
70 static void guid_read_from_args(struct guid_t *guid, void *arg)
72 u8 *blk_start;
73 u8 *blk_end;
74 u8 save;
75 u8 *end;
77 memset(guid, 0, sizeof(*guid));
78 end = arg + strlen(arg);
80 blk_start = arg;
81 blk_end = strchr(blk_start, '-');
82 if (blk_end == 0)
83 return;
84 *blk_end = 0;
85 guid->blk_0 = (u32)strtou64(blk_start, 0, 16);
86 blk_start = blk_end + 1;
87 if (blk_start >= end)
88 return;
89 blk_end = strchr(blk_start, '-');
90 if (blk_end == 0)
91 return;
92 *blk_end = 0;
93 guid->blk_1 = (u16)strtou64(blk_start, 0, 16);
94 blk_start = blk_end + 1;
95 if (blk_start >= end)
96 return;
97 blk_end = strchr(blk_start, '-');
98 if (blk_end == 0)
99 return;
100 *blk_end = 0;
101 guid->blk_2 = (u16)strtou64(blk_start, 0, 16);
102 blk_start = blk_end + 1;
103 if (blk_start >= end)
104 return;
105 blk_end = strchr(blk_start, '-');
106 if (blk_end == 0)
107 return;
108 *blk_end = 0;
109 guid->blk_3 = (u16)strtou64(blk_start, 0, 16);
110 /* blk_4 */
111 blk_start = blk_end + 1;
112 if (blk_start >= end)
113 return;
114 blk_end = blk_start + 12; /* 6 bytes */
115 if (blk_end > end)
116 return;
117 save = blk_start[2];
118 blk_start[2] = 0;
119 guid->blk_4[0] = (u8)strtou64(&blk_start[0], 0, 16);
120 blk_start[2] = save;
122 save = blk_start[4];
123 blk_start[4] = 0;
124 guid->blk_4[1] = (u8)strtou64(&blk_start[2], 0, 16);
125 blk_start[4] = save;
127 save = blk_start[6];
128 blk_start[6] = 0;
129 guid->blk_4[2] = (u8)strtou64(&blk_start[4], 0, 16);
130 blk_start[6] = save;
132 save = blk_start[8];
133 blk_start[8] = 0;
134 guid->blk_4[3] = (u8)strtou64(&blk_start[6], 0, 16);
135 blk_start[8] = save;
137 save = blk_start[10];
138 blk_start[10] = 0;
139 guid->blk_4[4] = (u8)strtou64(&blk_start[8], 0, 16);
140 blk_start[10] = save;
142 guid->blk_4[5] = (u8)strtou64(&blk_start[10], 0, 16);
145 static void usage(void)
147 printf("\
148 Generate a UEFI load option variable on the standard output intended for linux\n\
149 efivarfs: /sys/firmware/efi/efivars/BootXXXX-8be4df61-93ca-11d2-aa0d-00e098032b8c\n\
150 Use a command like \"nyangpt\" in order to get the partition UUID (not the\n\
151 filesystem UUID), number (starting from 1), starting LBA, size in cound of LBAs.\n\
152 Don't forget to create/update\n\
153 /sys/firmware/efi/efivars/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c.\n\
155 nyanefivars partition_uuid partition_number partition_starting_lba partition_size_lbas description_ascii loader_file_path_ascii [loader_arguments_ascii]\n"
157 exit(1);
159 int main(int args_n, u8 **args)
161 u8 *buf;
162 ssize_t buf_bytes_n;
164 u32 *p32;
165 u8 *p8;
166 u8 *p8_src;
167 u8 *p8_dst;
168 u16 *p16;
169 u64 *p64;
171 u32 UEFIVarAttrs;
172 u32 LoadOptionAttrs;
173 u16 FilePathListLength;
174 u16 desc_bytes_n;
176 u16 HardDriveMediaDevicePath_Length;
177 struct guid_t partition_guid;
178 u32 partition_number;
179 u64 partition_start_lba;
180 u64 partition_lbas_n;
181 u16 FilePathMediaDevicePath_Length;
183 ssize_t written_bytes_n;
185 if (args_n != 7 && args_n != 8)
186 usage();
187 buf = 0;
188 buf_bytes_n = 0;
189 /* linux specific, the variable attrs goes as a header */
190 UEFIVarAttrs = EFI_VARIABLE_NON_VOLATILE
191 | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
192 buf_bytes_n = sizeof(UEFIVarAttrs);
193 buf = realloc(buf, (size_t)buf_bytes_n);
194 p32 = (u32*)buf;
195 *p32 = htole32(UEFIVarAttrs);
197 LoadOptionAttrs = LOAD_OPTION_ACTIVE;
198 buf_bytes_n += sizeof(LoadOptionAttrs);
199 buf = realloc(buf, (size_t)buf_bytes_n);
200 p32 = (u32*)(buf + sizeof(UEFIVarAttrs));
201 *p32 = htole32(LoadOptionAttrs);
202 /* we don't know FilePathListLength yet */
203 buf_bytes_n += sizeof(FilePathListLength);
204 buf = realloc(buf, (size_t)buf_bytes_n);
205 /* description */
206 desc_bytes_n = (strlen(args[DESCRIPTION]) + 1) * 2; /* account for the 0 terminating ucs2 char */
207 buf_bytes_n += desc_bytes_n;
208 buf = realloc(buf, (size_t)buf_bytes_n);
209 p8_src = args[DESCRIPTION];
210 p8_dst = buf + sizeof(UEFIVarAttrs)
211 + sizeof(LoadOptionAttrs)
212 + sizeof(FilePathListLength);
213 loop { /* ascii to ucs2 */
214 p8_dst[0] = p8_src[0];
215 p8_dst[1] = 0;
216 if (p8_src[0] == 0)
217 break;
218 ++p8_src;
219 p8_dst += 2;
221 /* Hard Drive Media Device Path -- START */
222 HardDriveMediaDevicePath_Length = 42;
223 buf_bytes_n += HardDriveMediaDevicePath_Length;
224 buf = realloc(buf, (size_t)buf_bytes_n);
225 p8 = buf + sizeof(UEFIVarAttrs)
226 + sizeof(LoadOptionAttrs)
227 + sizeof(FilePathListLength)
228 + desc_bytes_n;
229 p8[0] = 0x04; /* Media Device Path */
230 p8[1] = 0x01; /* Hard Drive */
231 p8[2] = (u8)HardDriveMediaDevicePath_Length; /* length */
232 p8[3] = 0x00;
233 partition_number = (u32)strtou64(args[PARTITION_NUMBER], 0, 10);
234 p32 = (u32*)(p8 + 4);
235 *p32 = htole32(partition_number);
236 partition_start_lba = strtou64(args[PARTITION_START_LBA], 0, 10);
237 p64 = (u64*)(p8 + 8);
238 *p64 = htole64(partition_start_lba);
239 partition_lbas_n = strtou64(args[PARTITION_LBAS_N], 0, 10);
240 p64 = (u64*)(p8 + 16);
241 *p64 = htole64(partition_lbas_n);
242 guid_read_from_args(&partition_guid, args[PARTITION_UUID]);
243 guid_write(p8 + 24, &partition_guid);
244 p8[40] = 0x02; /* GUID partition table */
245 p8[41] = 0x02; /* GUID signature */
246 /* Hard Drive Media Device Path -- END */
247 /* File Path Media Device Path -- START */
248 FilePathMediaDevicePath_Length = 4 + (strlen(args[LOADER_FILE_PATH])
249 + 1) * 2;
250 buf_bytes_n += FilePathMediaDevicePath_Length;
251 buf = realloc(buf, (size_t)buf_bytes_n);
252 p8 = buf + sizeof(UEFIVarAttrs)
253 + sizeof(LoadOptionAttrs)
254 + sizeof(FilePathListLength)
255 + desc_bytes_n
256 + HardDriveMediaDevicePath_Length;
257 p8[0] = 0x04; /* Media Device Path */
258 p8[1] = 0x04; /* File Path */
259 p16 = (u16*)(p8 + 2);
260 *p16 = htole16(FilePathMediaDevicePath_Length);
261 p8_src = args[LOADER_FILE_PATH];
262 p8_dst = p8 + 4;
263 loop { /* ascii to ucs2 */
264 p8_dst[0] = p8_src[0];
265 p8_dst[1] = 0;
266 if (p8_src[0] == 0)
267 break;
268 ++p8_src;
269 p8_dst += 2;
271 /* File Path Media Device Path -- END */
272 /* Device Path End Structure */
273 buf_bytes_n += 4;
274 buf = realloc(buf, (size_t)buf_bytes_n);
275 p8 = buf + sizeof(UEFIVarAttrs)
276 + sizeof(LoadOptionAttrs)
277 + sizeof(FilePathListLength)
278 + desc_bytes_n
279 + HardDriveMediaDevicePath_Length
280 + FilePathMediaDevicePath_Length;
281 p8[0] = 0x7f; /* End of Hardware Device Path */
282 p8[1] = 0xff; /* End Entire Device Path */
283 p8[2] = 0x04; /* u16 0x004, the bytes count of this structure */
284 p8[3] = 0x00;
285 /* we don't know FilePathListLength now */
286 FilePathListLength = HardDriveMediaDevicePath_Length
287 + FilePathMediaDevicePath_Length + 4;
288 p16 = (u16*)(buf + sizeof(UEFIVarAttrs)
289 + sizeof(LoadOptionAttrs));
290 *p16 = htole16(FilePathListLength);
291 /* loader arguments */
292 if (args_n == 5) {
293 buf_bytes_n += strlen(args[LOADER_ARGS]) * 2; /* no terminating 0 */
294 buf = realloc(buf, (size_t)buf_bytes_n);
295 p8_src = args[LOADER_ARGS];
296 p8_dst = buf + sizeof(UEFIVarAttrs)
297 + sizeof(LoadOptionAttrs)
298 + sizeof(FilePathListLength)
299 + desc_bytes_n
300 + FilePathListLength;
301 loop { /* ascii to ucs2 */
302 if (p8_src[0] == 0)
303 break;
305 p8_dst[0] = p8_src[0];
306 p8_dst[1] = 0;
308 ++p8_src;
309 p8_dst += 2;
312 written_bytes_n = write(1, buf, buf_bytes_n);
313 if (written_bytes_n != buf_bytes_n) {
314 fprintf(stderr, "ERROR:unable to write properly on the standard output the load option variable data\n");
315 return 1;
317 return 0;
319 #undef loop
320 #undef strtoul
321 #undef u16
322 #undef u32
323 #undef u8
324 #undef u64
326 #undef EFI_VARIABLE_NON_VOLATILE
327 #undef EFI_VARIABLE_BOOTSERVICE_ACCESS
328 #undef EFI_VARIABLE_RUNTIME_ACCESS
330 #undef LOAD_OPTION_ACTIVE
332 #undef PARTITION_UUID
333 #undef PARTITION_NUMBER
334 #undef PARTITION_START_LBA
335 #undef PARTITION_LBAS_N
336 #undef DESCRIPTION
337 #undef LOADER_FILE_PATH
338 #undef LOADER_ARGS