initrddump.c 11 KB

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