1
2
3
4
5
6
7
8
9
10
11
12#include <common.h>
13#include <efi_api.h>
14#include <efi_load_initrd.h>
15
16#define BUFFER_SIZE 64
17#define ESC 0x17
18
19#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
20
21static struct efi_system_table *systable;
22static struct efi_boot_services *bs;
23static struct efi_simple_text_output_protocol *cerr;
24static struct efi_simple_text_output_protocol *cout;
25static struct efi_simple_text_input_protocol *cin;
26static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
27static const efi_guid_t guid_simple_file_system_protocol =
28 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
29static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
30static efi_handle_t handle;
31static bool nocolor;
32
33
34
35
36
37static const struct efi_initrd_dp initrd_dp = {
38 .vendor = {
39 {
40 DEVICE_PATH_TYPE_MEDIA_DEVICE,
41 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
42 sizeof(initrd_dp.vendor),
43 },
44 EFI_INITRD_MEDIA_GUID,
45 },
46 .end = {
47 DEVICE_PATH_TYPE_END,
48 DEVICE_PATH_SUB_TYPE_END,
49 sizeof(initrd_dp.end),
50 }
51};
52
53
54
55
56
57
58static void color(u8 color)
59{
60 if (!nocolor)
61 cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK);
62}
63
64
65
66
67
68
69static void print(u16 *string)
70{
71 cout->output_string(cout, string);
72}
73
74
75
76
77static void cls(void)
78{
79 if (nocolor)
80 print(u"\r\n");
81 else
82 cout->clear_screen(cout);
83}
84
85
86
87
88
89
90static void error(u16 *string)
91{
92 color(EFI_LIGHTRED);
93 print(string);
94 color(EFI_LIGHTBLUE);
95}
96
97
98
99
100
101
102
103static void printx(u64 val, u32 prec)
104{
105 int i;
106 u16 c;
107 u16 buf[16];
108 u16 *pos = buf;
109
110 for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
111 c = (val >> (4 * i)) & 0x0f;
112 if (c || pos != buf || !i || i < prec) {
113 c += '0';
114 if (c > '9')
115 c += 'a' - '9' - 1;
116 *pos++ = c;
117 }
118 }
119 *pos = 0;
120 print(buf);
121}
122
123
124
125
126static void efi_drain_input(void)
127{
128 cin->reset(cin, true);
129}
130
131
132
133
134
135
136
137
138
139
140
141
142static efi_status_t efi_input_yn(void)
143{
144 struct efi_input_key key = {0};
145 efi_uintn_t index;
146 efi_status_t ret;
147
148 for (;;) {
149 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
150 if (ret != EFI_SUCCESS)
151 continue;
152 ret = cin->read_key_stroke(cin, &key);
153 if (ret != EFI_SUCCESS)
154 continue;
155 switch (key.scan_code) {
156 case 0x17:
157 return EFI_ABORTED;
158 default:
159 break;
160 }
161
162 switch (key.unicode_char | 0x20) {
163 case 'y':
164 return EFI_SUCCESS;
165 case 'n':
166 return EFI_ACCESS_DENIED;
167 default:
168 break;
169 }
170 }
171}
172
173
174
175
176
177
178
179
180static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
181{
182 struct efi_input_key key = {0};
183 efi_uintn_t index;
184 efi_uintn_t pos = 0;
185 u16 outbuf[2] = u" ";
186 efi_status_t ret;
187
188 *buffer = 0;
189 for (;;) {
190 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
191 if (ret != EFI_SUCCESS)
192 continue;
193 ret = cin->read_key_stroke(cin, &key);
194 if (ret != EFI_SUCCESS)
195 continue;
196 switch (key.scan_code) {
197 case 0x17:
198 print(u"\r\nAborted\r\n");
199 return EFI_ABORTED;
200 default:
201 break;
202 }
203 switch (key.unicode_char) {
204 case 0x08:
205 if (pos) {
206 buffer[pos--] = 0;
207 print(u"\b \b");
208 }
209 break;
210 case 0x0a:
211 case 0x0d:
212 print(u"\r\n");
213 return EFI_SUCCESS;
214 default:
215 break;
216 }
217
218 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
219 continue;
220 if (key.unicode_char >= 0x20 &&
221 pos < buffer_size - 1) {
222 *outbuf = key.unicode_char;
223 buffer[pos++] = key.unicode_char;
224 buffer[pos] = 0;
225 print(outbuf);
226 }
227 }
228}
229
230
231
232
233
234
235
236static u16 *skip_whitespace(u16 *pos)
237{
238 for (; *pos && *pos <= 0x20; ++pos)
239 ;
240 return pos;
241}
242
243
244
245
246
247
248
249
250static bool starts_with(u16 *string, u16 *keyword)
251{
252 if (!string || !keyword)
253 return false;
254
255 for (; *keyword; ++string, ++keyword) {
256 if (*string != *keyword)
257 return false;
258 }
259 return true;
260}
261
262
263
264
265static void do_help(void)
266{
267 error(u"load - show length and CRC32 of initial RAM disk\r\n");
268 error(u"save <initrd> - save initial RAM disk to file\r\n");
269 error(u"exit - exit the shell\r\n");
270}
271
272
273
274
275
276
277
278
279static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
280{
281 struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
282 struct efi_load_file_protocol *load_file2_prot;
283 u64 buffer;
284 efi_handle_t handle;
285 efi_status_t ret;
286
287 *initrd = NULL;
288 *initrd_size = 0;
289 ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
290 if (ret != EFI_SUCCESS) {
291 error(u"Load File2 protocol not found\r\n");
292 return ret;
293 }
294 ret = bs->handle_protocol(handle, &load_file2_guid,
295 (void **)&load_file2_prot);
296 ret = load_file2_prot->load_file(load_file2_prot, dp, false,
297 initrd_size, NULL);
298 if (ret != EFI_BUFFER_TOO_SMALL) {
299 error(u"Load File2 protocol does not provide file length\r\n");
300 return EFI_LOAD_ERROR;
301 }
302 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
303 efi_size_in_pages(*initrd_size), &buffer);
304 if (ret != EFI_SUCCESS) {
305 error(u"Out of memory\r\n");
306 return ret;
307 }
308 *initrd = (void *)(uintptr_t)buffer;
309 ret = load_file2_prot->load_file(load_file2_prot, dp, false,
310 initrd_size, *initrd);
311 if (ret != EFI_SUCCESS) {
312 error(u"Load File2 protocol failed to provide file\r\n");
313 bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
314 return EFI_LOAD_ERROR;
315 }
316 return ret;
317}
318
319
320
321
322
323
324
325static efi_status_t do_load(void)
326{
327 void *initrd;
328 efi_uintn_t initrd_size;
329 u32 crc32;
330 efi_uintn_t ret;
331
332 ret = get_initrd(&initrd, &initrd_size);
333 if (ret != EFI_SUCCESS)
334 return ret;
335 print(u"length: 0x");
336 printx(initrd_size, 1);
337 print(u"\r\n");
338
339 ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
340 if (ret != EFI_SUCCESS) {
341 error(u"Calculating CRC32 failed\r\n");
342 return EFI_LOAD_ERROR;
343 }
344 print(u"crc32: 0x");
345 printx(crc32, 8);
346 print(u"\r\n");
347
348 return EFI_SUCCESS;
349}
350
351
352
353
354
355
356
357static efi_status_t do_save(u16 *filename)
358{
359 struct efi_loaded_image *loaded_image;
360 struct efi_simple_file_system_protocol *file_system;
361 struct efi_file_handle *root, *file;
362 void *initrd;
363 efi_uintn_t initrd_size;
364 efi_uintn_t ret;
365
366 ret = get_initrd(&initrd, &initrd_size);
367 if (ret != EFI_SUCCESS)
368 return ret;
369
370 filename = skip_whitespace(filename);
371
372 ret = bs->open_protocol(handle, &loaded_image_guid,
373 (void **)&loaded_image, NULL, NULL,
374 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
375 if (ret != EFI_SUCCESS) {
376 error(u"Loaded image protocol not found\r\n");
377 goto out;
378 }
379
380
381 ret = bs->open_protocol(loaded_image->device_handle,
382 &guid_simple_file_system_protocol,
383 (void **)&file_system, NULL, NULL,
384 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
385 if (ret != EFI_SUCCESS) {
386 error(u"Failed to open simple file system protocol\r\n");
387 goto out;
388 }
389
390
391 ret = file_system->open_volume(file_system, &root);
392 if (ret != EFI_SUCCESS) {
393 error(u"Failed to open volume\r\n");
394 goto out;
395 }
396
397 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
398 if (ret == EFI_SUCCESS) {
399 file->close(file);
400 efi_drain_input();
401 print(u"Overwrite existing file (y/n)? ");
402 ret = efi_input_yn();
403 print(u"\r\n");
404 if (ret != EFI_SUCCESS) {
405 root->close(root);
406 error(u"Aborted by user\r\n");
407 goto out;
408 }
409 }
410
411
412 ret = root->open(root, &file, filename,
413 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
414 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
415 if (ret == EFI_SUCCESS) {
416
417 ret = file->write(file, &initrd_size, initrd);
418 if (ret != EFI_SUCCESS) {
419 error(u"Failed to write file\r\n");
420 } else {
421 print(filename);
422 print(u" written\r\n");
423 }
424 file->close(file);
425 } else {
426 error(u"Failed to open file\r\n");
427 }
428 root->close(root);
429
430out:
431 if (initrd)
432 bs->free_pages((uintptr_t)initrd,
433 efi_size_in_pages(initrd_size));
434 return ret;
435}
436
437
438
439
440
441
442u16 *get_load_options(void)
443{
444 efi_status_t ret;
445 struct efi_loaded_image *loaded_image;
446
447 ret = bs->open_protocol(handle, &loaded_image_guid,
448 (void **)&loaded_image, NULL, NULL,
449 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
450 if (ret != EFI_SUCCESS) {
451 error(u"Loaded image protocol not found\r\n");
452 return NULL;
453 }
454
455 if (!loaded_image->load_options_size || !loaded_image->load_options)
456 return NULL;
457
458 return loaded_image->load_options;
459}
460
461
462
463
464
465
466
467
468efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
469 struct efi_system_table *systab)
470{
471 u16 *load_options;
472
473 handle = image_handle;
474 systable = systab;
475 cerr = systable->std_err;
476 cout = systable->con_out;
477 cin = systable->con_in;
478 bs = systable->boottime;
479 load_options = get_load_options();
480
481 if (starts_with(load_options, u"nocolor"))
482 nocolor = true;
483
484 color(EFI_WHITE);
485 cls();
486 print(u"INITRD Dump\r\n===========\r\n\r\n");
487 color(EFI_LIGHTBLUE);
488
489 for (;;) {
490 u16 command[BUFFER_SIZE];
491 u16 *pos;
492 efi_uintn_t ret;
493
494 efi_drain_input();
495 print(u"=> ");
496 ret = efi_input(command, sizeof(command));
497 if (ret == EFI_ABORTED)
498 break;
499 pos = skip_whitespace(command);
500 if (starts_with(pos, u"exit"))
501 break;
502 else if (starts_with(pos, u"load"))
503 do_load();
504 else if (starts_with(pos, u"save "))
505 do_save(pos + 5);
506 else
507 do_help();
508 }
509
510 color(EFI_LIGHTGRAY);
511 cls();
512
513 return EFI_SUCCESS;
514}
515