logo

live-bootstrap

Mirror of <https://github.com/fosslinux/live-bootstrap>

kexec-fiwix.c (9716B)


  1. /* SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com> */
  2. /* SPDX-License-Identifier: MIT */
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include "multiboot1.h"
  7. #define MULTIBOOT_MAGIC 0x2BADB002
  8. multiboot_uint32_t get_memmap(char *filename, void *memmap_addr) {
  9. if (!filename) {
  10. return 0;
  11. }
  12. multiboot_uint32_t memmap_length = 0;
  13. FILE *memmap_file = fopen(filename, "r");
  14. if (!memmap_file) {
  15. return 0;
  16. }
  17. fseek(memmap_file, 0, SEEK_END);
  18. memmap_length = ftell(memmap_file);
  19. if (!memmap_length) {
  20. fclose(memmap_file);
  21. return 0;
  22. }
  23. printf("kexec-fiwix: Memory map file length: %d\n", memmap_length);
  24. puts("kexec-fiwix: Reading memory map file...");
  25. fseek(memmap_file, 0, SEEK_SET);
  26. int memmap_read_len = fread(memmap_addr, memmap_length, 1, memmap_file);
  27. fclose(memmap_file);
  28. if (memmap_read_len < 1) {
  29. printf("kexec-fiwix: memory map fread error: %d\n", memmap_read_len);
  30. return 0;
  31. }
  32. return memmap_length;
  33. }
  34. int main(int argc, char **argv) {
  35. char *fiwix_filename = "/boot/fiwix";
  36. char *initrd_filename = "/boot/fiwix.ext2";
  37. char *cmdline = NULL;
  38. char *memmap = NULL;
  39. int i;
  40. for (i = 1; i < argc; i++) {
  41. if (!strcmp(argv[i], "-c")) {
  42. i++;
  43. cmdline = argv[i];
  44. } else if (!strcmp(argv[i], "-i")) {
  45. i++;
  46. initrd_filename = argv[i];
  47. } else if (!strcmp(argv[i], "-m")) {
  48. i++;
  49. memmap = argv[i];
  50. } else {
  51. fiwix_filename = argv[i];
  52. }
  53. }
  54. /* Read the kernel */
  55. printf("kexec-fiwix: starting kernel %s...\n\n", fiwix_filename);
  56. FILE *fiwix_file = fopen(fiwix_filename, "r");
  57. fseek(fiwix_file, 0, SEEK_END);
  58. int fiwix_len = ftell(fiwix_file);
  59. printf("kexec-fiwix: Fiwix kernel file length: %d\n", fiwix_len);
  60. puts("kexec-fiwix: Reading kernel...");
  61. fseek(fiwix_file, 0, SEEK_SET);
  62. char * fiwix_mem = malloc(fiwix_len);
  63. int read_len = fread(fiwix_mem, fiwix_len, 1, fiwix_file);
  64. fclose(fiwix_file);
  65. if (read_len < 1) {
  66. printf("kexec-fiwix: kernel fread error: %d\n", read_len);
  67. return EXIT_FAILURE;
  68. }
  69. /* Display info from ELF header */
  70. unsigned int e_entry = *((unsigned int *) (&fiwix_mem[0x18]));
  71. printf("ELF virtual entry point : 0x%x\n", e_entry);
  72. unsigned int e_phoff = *((unsigned int *) (&fiwix_mem[0x1C]));
  73. printf("ELF program header offset : 0x%x\n", e_phoff);
  74. unsigned int e_phnum = *((unsigned int *) (&fiwix_mem[0x2C]));
  75. e_phnum &= 0xFFFF;
  76. printf("ELF number of program headers: %d\n", e_phnum);
  77. unsigned int e_phentsize = *((unsigned int *) (&fiwix_mem[0x2A]));
  78. e_phentsize &= 0xFFFF;
  79. printf("ELF size of program headers : %d\n", e_phentsize);
  80. /* Load the kernel */
  81. puts("kexec-fiwix: Placing kernel in memory...");
  82. int header_num;
  83. for (header_num = 0; header_num < e_phnum; header_num++) {
  84. char * fiwix_prog_header = &fiwix_mem[e_phoff + header_num * e_phentsize];
  85. unsigned int p_offset = *((unsigned int *) (&fiwix_prog_header[0x04]));
  86. unsigned int p_vaddr = *((unsigned int *) (&fiwix_prog_header[0x08]));
  87. unsigned int p_paddr = *((unsigned int *) (&fiwix_prog_header[0x0C]));
  88. unsigned int p_filesz = *((unsigned int *) (&fiwix_prog_header[0x10]));
  89. unsigned int p_memsz = *((unsigned int *) (&fiwix_prog_header[0x14]));
  90. if (header_num == 0) {
  91. e_entry -= (p_vaddr - p_paddr);
  92. printf("ELF physical entry point : 0x%x\n", e_entry);
  93. }
  94. printf("header %d:\n", header_num);
  95. printf(" p_offset: 0x%08x\n", p_offset);
  96. printf(" p_paddr : 0x%08x\n", p_paddr);
  97. printf(" p_filesz: 0x%08x\n", p_filesz);
  98. printf(" p_memsz : 0x%08x\n", p_memsz);
  99. memset((void *)p_paddr, 0, p_memsz + 0x10000);
  100. memcpy((void *)p_paddr, &fiwix_mem[p_offset], p_filesz);
  101. }
  102. puts("Preparing multiboot info for kernel...");
  103. char * boot_loader_name = "kexec-fiwix";
  104. unsigned int next_avail_mem = 0x9800;
  105. multiboot_info_t * pmultiboot_info = (multiboot_info_t *) next_avail_mem;
  106. memset(pmultiboot_info, 0, sizeof(multiboot_info_t));
  107. pmultiboot_info->flags = MULTIBOOT_INFO_BOOT_LOADER_NAME
  108. | MULTIBOOT_INFO_MEMORY
  109. | MULTIBOOT_INFO_MODS
  110. | MULTIBOOT_INFO_MEM_MAP;
  111. next_avail_mem += sizeof(multiboot_info_t);
  112. pmultiboot_info->mem_lower = 0x0000027F;
  113. pmultiboot_info->mem_upper = 0x002FFB80;
  114. /* Set command line */
  115. if (cmdline != NULL) {
  116. pmultiboot_info->flags = pmultiboot_info->flags | MULTIBOOT_INFO_CMDLINE;
  117. pmultiboot_info->cmdline = next_avail_mem;
  118. strcpy((char *) next_avail_mem, cmdline);
  119. next_avail_mem += (strlen(cmdline) + 1);
  120. }
  121. int filenum;
  122. unsigned int filename_addr;
  123. for (filenum = 4, filename_addr = 0x201000; filenum <= 14335; filenum++, filename_addr += 1024) {
  124. if (!strcmp((char *) filename_addr, initrd_filename)) {
  125. printf("Found image at filenum %d\n", filenum);
  126. break;
  127. }
  128. }
  129. unsigned int initrd_src = *((unsigned int *) (0x01000000 + (16 * filenum) + 4));
  130. unsigned int initrd_len = *((unsigned int *) (0x01000000 + (16 * filenum) + 8));
  131. printf("initrd_src: 0x%08x\n", initrd_src);
  132. printf("initrd_len: 0x%08x\n", initrd_len);
  133. /* Set ramdrive info */
  134. pmultiboot_info->mods_count = 1;
  135. pmultiboot_info->mods_addr = next_avail_mem;
  136. multiboot_module_t *pmultiboot_module = (multiboot_module_t *) next_avail_mem;
  137. pmultiboot_module->mod_start = 0x1C6000;
  138. pmultiboot_module->mod_end = 0x1C6000 + initrd_len;
  139. next_avail_mem += sizeof(multiboot_module_t);
  140. pmultiboot_module->cmdline = next_avail_mem;
  141. strcpy((char *) next_avail_mem, "fiwix.ext2");
  142. next_avail_mem += (strlen("fiwix.ext2") + 1);
  143. /* Set memory map info */
  144. pmultiboot_info->mmap_addr = next_avail_mem;
  145. pmultiboot_info->mmap_length = get_memmap(memmap, (void *) pmultiboot_info->mmap_addr);
  146. /* if we have no memory map, fall back to a safe default */
  147. if (pmultiboot_info->mmap_length == 0) {
  148. pmultiboot_info->mmap_length = 7 * sizeof(multiboot_memory_map_t);
  149. multiboot_memory_map_t *pmultiboot_memory_map = (multiboot_memory_map_t *) next_avail_mem;
  150. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  151. pmultiboot_memory_map->addr = 0x00000000;
  152. pmultiboot_memory_map->len = 0x00080000;
  153. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_AVAILABLE;
  154. pmultiboot_memory_map++;
  155. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  156. pmultiboot_memory_map->addr = 0x00080000;
  157. pmultiboot_memory_map->len = 0x00020000;
  158. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_RESERVED;
  159. pmultiboot_memory_map++;
  160. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  161. pmultiboot_memory_map->addr = 0x000F0000;
  162. pmultiboot_memory_map->len = 0x00010000;
  163. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_RESERVED;
  164. pmultiboot_memory_map++;
  165. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  166. pmultiboot_memory_map->addr = 0x00100000;
  167. pmultiboot_memory_map->len = 0xBBF00000;
  168. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_AVAILABLE;
  169. pmultiboot_memory_map++;
  170. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  171. pmultiboot_memory_map->addr = 0XBC000000;
  172. pmultiboot_memory_map->len = 0x04000000;
  173. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_RESERVED;
  174. pmultiboot_memory_map++;
  175. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  176. pmultiboot_memory_map->addr = 0XFEFFC000;
  177. pmultiboot_memory_map->len = 0x00004000;
  178. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_RESERVED;
  179. pmultiboot_memory_map++;
  180. pmultiboot_memory_map->size = sizeof(multiboot_memory_map_t) - sizeof(multiboot_uint32_t);
  181. pmultiboot_memory_map->addr = 0XFFFC0000;
  182. pmultiboot_memory_map->len = 0x00040000;
  183. pmultiboot_memory_map->type = MULTIBOOT_MEMORY_RESERVED;
  184. pmultiboot_memory_map++;
  185. }
  186. next_avail_mem += pmultiboot_info->mmap_length;
  187. /* Set boot loader name */
  188. pmultiboot_info->boot_loader_name = next_avail_mem;
  189. strcpy((char *) next_avail_mem, boot_loader_name);
  190. /* next_avail_mem += (strlen(boot_loader_name) + 1); */
  191. /* Jump to kernel entry point */
  192. unsigned int magic = MULTIBOOT_BOOTLOADER_MAGIC;
  193. unsigned int dummy = 0;
  194. unsigned int multiboot_info_num = (unsigned int) pmultiboot_info;
  195. printf("Preparing trampoline...\n");
  196. /* The ramdrive needs to be written to a location that would overwrite this program.
  197. * Therfore, the code that copies the ram drive and jumps to the kernel needs to be
  198. * run from a safe location. So, we put that code into an array variable and
  199. * copy the code (called a "trampoline") to a safe location and then jump to it.
  200. * The 0x00000000 values below are place holders which are set below
  201. */
  202. char trampoline[] = {
  203. 0xBE, 0x00, 0x00, 0x00, 0x00, /* mov esi, 0x00000000 */
  204. 0xBF, 0x00, 0x00, 0x00, 0x00, /* mov edi, 0x00000000 */
  205. 0xB9, 0x00, 0x00, 0x00, 0x00, /* mov ecx, 0x00000000 */
  206. 0xFC, /* cld */
  207. 0xF3, 0xA4, /* rep movsb */
  208. 0xB8, 0x00, 0x00, 0x00, 0x00, /* mov eax, 0x00000000 */
  209. 0xBB, 0x00, 0x00, 0x00, 0x00, /* mov ebx, 0x00000000 */
  210. 0xFB, /* sti */
  211. 0xEA, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 /* jmp far 0x0008:0x00000000 */
  212. };
  213. /* Set place holder values */
  214. *((unsigned int *) &trampoline[1]) = initrd_src;
  215. *((unsigned int *) &trampoline[6]) = 0x001C6000;
  216. *((unsigned int *) &trampoline[11]) = initrd_len;
  217. *((unsigned int *) &trampoline[19]) = magic;
  218. *((unsigned int *) &trampoline[24]) = multiboot_info_num;
  219. *((unsigned int *) &trampoline[30]) = e_entry;
  220. memcpy((void *)0x4000, trampoline, sizeof(trampoline));
  221. printf("kexec-fiwix: jumping to trampoline...\n");
  222. __asm__ __volatile__ (
  223. "ljmp $0x8, $0x00004000\n\t"
  224. );
  225. }