efiffs.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. * utils/efiffs.c
  3. *
  4. * Copyright (C) 2022 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * This file is part of the POSIX-UEFI package.
  27. * @brief small tool to convert an .efi file to an .ffs file
  28. *
  29. */
  30. #include <stdint.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <time.h>
  35. typedef struct {
  36. uint32_t Data1;
  37. uint16_t Data2;
  38. uint16_t Data3;
  39. uint8_t Data4[8];
  40. } __attribute__((packed)) guid_t;
  41. /**
  42. * Print usage
  43. */
  44. void usage(char *cmd)
  45. {
  46. printf("POSIX-UEFI utils - efiffs by bztsrc@gitlab MIT\r\n\r\n");
  47. printf("%s [-g <guid>] [-n <name>] [-v <ver>] [-t <type>] [-p <type>] infile [outfile]\r\n\r\n", cmd);
  48. printf(" -g <guid> specify the GUID (defaults to random)\r\n");
  49. printf(" -n <name> specify the driver's name (eg 'FAT')\r\n");
  50. printf(" -v <ver> specify the driver's version (eg '1.0')\r\n");
  51. printf(" -t <type> specify the ffs type (defaults to 7, EFI_FV_FILETYPE_DRIVER)\r\n");
  52. printf(" -p <type> specify the pe section type (defaults to 16, EFI_SECTION_PE32)\r\n");
  53. printf(" infile input .efi file\r\n");
  54. printf(" outfile output file name (default generated from infile)\r\n");
  55. exit(1);
  56. }
  57. /**
  58. * Convert hex string into integer
  59. */
  60. unsigned int gethex(char *ptr, int len)
  61. {
  62. unsigned int ret = 0;
  63. for(;len--;ptr++) {
  64. if(*ptr>='0' && *ptr<='9') { ret <<= 4; ret += (unsigned int)(*ptr-'0'); }
  65. else if(*ptr >= 'a' && *ptr <= 'f') { ret <<= 4; ret += (unsigned int)(*ptr-'a'+10); }
  66. else if(*ptr >= 'A' && *ptr <= 'F') { ret <<= 4; ret += (unsigned int)(*ptr-'A'+10); }
  67. else break;
  68. }
  69. return ret;
  70. }
  71. /**
  72. * Parse a GUID in string into binary representation
  73. */
  74. void getguid(char *ptr, guid_t *guid)
  75. {
  76. int i;
  77. if(!ptr || !*ptr || ptr[8] != '-' || ptr[13] != '-' || ptr[18] != '-') {
  78. fprintf(stderr, "efiffs: bad GUID format\r\n");
  79. return;
  80. }
  81. guid->Data1 = gethex(ptr, 8); ptr += 9;
  82. guid->Data2 = gethex(ptr, 4); ptr += 5;
  83. guid->Data3 = gethex(ptr, 4); ptr += 5;
  84. guid->Data4[0] = gethex(ptr, 2); ptr += 2;
  85. guid->Data4[1] = gethex(ptr, 2); ptr += 2; if(*ptr == '-') ptr++;
  86. for(i = 2; i < 8; i++, ptr += 2) guid->Data4[i] = gethex(ptr, 2);
  87. }
  88. /**
  89. * Parse ffs type
  90. */
  91. int gettype(char *str)
  92. {
  93. static const char *types[] = {
  94. NULL, "RAW", "FREEFORM", "SECURITY_CORE", "PEI_CORE", "DXE_CORE", "PEIM", "DRIVER", "COMBINED_PEIM_DRIVER",
  95. "APPLICATION", "SMM", "FIRMWARE_VOLUME_IMAGE", "COMBINED_SMM_DXE", "SMM_CORE", NULL };
  96. int i;
  97. i = atoi(str);
  98. if(i > 0 && i < (int)(sizeof(types)/sizeof(types[0]))) return i;
  99. if(!memcmp(str, "EFI_FV_FILETYPE_", 16)) str += 16;
  100. for(i = 1; types[i] && strcmp(str, types[i]); i++);
  101. if(!types[i]) {
  102. fprintf(stderr, "efiffs: invalid ffs type, available values:\r\n");
  103. for(i = 1; types[i]; i++)
  104. fprintf(stderr, " EFI_FV_FILETYPE_%s\r\n", types[i]);
  105. return 7; /* EFI_FV_FILETYPE_DRIVER */
  106. }
  107. return i;
  108. }
  109. /**
  110. * Parse section type
  111. */
  112. int getsec(char *str)
  113. {
  114. static const char *types[] = {
  115. "PE32", "PIC", "TE", "DXE_DEPEX", "VERSION", "USER_INTERFACE", "COMPATIBILITY16",
  116. "FIRMWARE_VOLUME_IMAGE", "FREEFORM_SUBTYPE_GUID", "RAW", "RESERVED", "PEI_DEPEX", "SMM_DEPEX", NULL };
  117. int i;
  118. i = atoi(str);
  119. if(i == 1 || i == 2 || i >= 16) return i;
  120. if(!memcmp(str, "EFI_SECTION_", 12)) str += 12;
  121. if(!strcmp(str, "COMPRESSION")) return 1;
  122. if(!strcmp(str, "GUID_DEFINED")) return 2;
  123. for(i = 0; types[i] && strcmp(str, types[i]); i++);
  124. if(!types[i]) {
  125. fprintf(stderr, "efiffs: invalid section type, available values:\r\n");
  126. fprintf(stderr, " EFI_SECTION_COMPRESSION\r\n EFI_SECTION_GUID_DEFINED\r\n");
  127. for(i = 0; types[i]; i++)
  128. fprintf(stderr, " EFI_SECTION_%s\r\n", types[i]);
  129. return 16; /* EFI_SECTION_PE32 */
  130. }
  131. return i + 16;
  132. }
  133. /**
  134. * Calculate checksum
  135. */
  136. uint8_t checksum8(uint8_t *buff, int len)
  137. {
  138. uint8_t ret = 0;
  139. int i;
  140. for(i = 0; i < len; i++)
  141. ret = (uint8_t)(ret + buff[i]);
  142. return (uint8_t)(0x100 - ret);
  143. }
  144. /**
  145. * Main function
  146. */
  147. int main(int argc, char **argv)
  148. {
  149. FILE *f;
  150. int i, size, len;
  151. int filetype = 7; /* EFI_FV_FILETYPE_DRIVER */
  152. int sectype = 16; /* EFI_SECTION_PE32 */
  153. char *in = NULL, *out = NULL, *name = NULL, *ver = NULL;
  154. uint8_t *buff = NULL, *sec = NULL;
  155. guid_t guid;
  156. /* get a random GUID */
  157. srand(time(NULL));
  158. i = rand(); memcpy(&((uint8_t*)&guid)[0], &i, 4);
  159. i = rand(); memcpy(&((uint8_t*)&guid)[4], &i, 4);
  160. i = rand(); memcpy(&((uint8_t*)&guid)[8], &i, 4);
  161. i = rand(); memcpy(&((uint8_t*)&guid)[12], &i, 4);
  162. /* parse command line */
  163. for(i = 1; i < argc && argv[i]; i++)
  164. if(argv[i][0] == '-') {
  165. switch(argv[i][1]) {
  166. case 'g': getguid(argv[++i], &guid); break;
  167. case 'n': name = argv[++i]; break;
  168. case 'v': ver = argv[++i]; break;
  169. case 't': filetype = gettype(argv[++i]); break;
  170. case 'p': sectype = getsec(argv[++i]); break;
  171. default: usage(argv[0]); break;
  172. }
  173. } else
  174. if(!in) in = argv[i]; else
  175. if(!out) out = argv[i]; else
  176. usage(argv[0]);
  177. if(!in) usage(argv[0]);
  178. /* get input data */
  179. f = fopen(in, "rb");
  180. if(!f) {
  181. fprintf(stderr, "efiffs: unable to read '%s'\r\n", in);
  182. return 2;
  183. }
  184. fseek(f, 0L, SEEK_END);
  185. size = (int)ftell(f);
  186. fseek(f, 0L, SEEK_SET);
  187. len = 24 + 12 + 4 + size + (name ? 6 + 2 * strlen(name) : 0) + (ver ? 6 + 2 * strlen(ver) : 0);
  188. buff = (uint8_t*)malloc(len);
  189. if(!buff) { fprintf(stderr, "efiffs: unable to allocate memory\r\n"); return 2; }
  190. memset(buff, 0, len);
  191. sec = buff + 24;
  192. /* add the PE section */
  193. sec[0] = size & 0xff;
  194. sec[1] = (size >> 8) & 0xff;
  195. sec[2] = (size >> 16) & 0xff;
  196. sec[3] = sectype;
  197. fread(sec + 4, 1, size, f);
  198. fclose(f);
  199. sec += 4 + ((size + 3) & ~3);
  200. /* add the name section */
  201. if(name) {
  202. size = 6 + 2 * strlen(name);
  203. sec[0] = size & 0xff;
  204. sec[1] = (size >> 8) & 0xff;
  205. sec[2] = (size >> 16) & 0xff;
  206. sec[3] = 0x15; /* EFI_SECTION_USER_INTERFACE */
  207. for(i = 0; name[i]; i++)
  208. sec[4 + 2 * i] = name[i];
  209. sec += ((size + 3) & ~3);
  210. }
  211. /* add the version section */
  212. if(ver) {
  213. size = 6 + 2 * strlen(ver);
  214. sec[0] = size & 0xff;
  215. sec[1] = (size >> 8) & 0xff;
  216. sec[2] = (size >> 16) & 0xff;
  217. sec[3] = 0x14; /* EFI_SECTION_VERSION */
  218. for(i = 0; ver[i]; i++)
  219. sec[4 + 2 * i] = ver[i];
  220. sec += ((size + 3) & ~3);
  221. }
  222. /* calculate ffs header fields */
  223. len = (int)((uintptr_t)sec - (uintptr_t)buff);
  224. memcpy(buff, &guid, 16);
  225. buff[0x12] = filetype;
  226. buff[0x14] = len & 0xff;
  227. buff[0x15] = (len >> 8) & 0xff;
  228. buff[0x16] = (len >> 16) & 0xff;
  229. buff[0x11] = 0xAA;
  230. buff[0x10] = checksum8(buff, 24);
  231. /* write out image */
  232. if(!out) {
  233. i = strlen(in);
  234. out = (char*)malloc(i + 5);
  235. if(!out) { fprintf(stderr, "efiffs: unable to allocate memory\r\n"); return 2; }
  236. strcpy(out, in);
  237. strcpy(!strcmp(out + i - 4, ".efi") ? out + i - 4 : out + i, ".ffs");
  238. }
  239. f = fopen(out, "wb");
  240. if(!f) {
  241. fprintf(stderr, "efiffs: unable to write '%s'\r\n", out);
  242. return 2;
  243. }
  244. fwrite(buff, 1, len, f);
  245. fclose(f);
  246. free(buff);
  247. return 0;
  248. }