kernel.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. "use strict";
  2. // https://www.kernel.org/doc/Documentation/x86/boot.txt
  3. const LINUX_BOOT_HDR_SETUP_SECTS = 0x1F1;
  4. const LINUX_BOOT_HDR_SYSSIZE = 0x1F4;
  5. const LINUX_BOOT_HDR_VIDMODE = 0x1FA;
  6. const LINUX_BOOT_HDR_BOOT_FLAG = 0x1FE;
  7. const LINUX_BOOT_HDR_HEADER = 0x202;
  8. const LINUX_BOOT_HDR_VERSION = 0x206;
  9. const LINUX_BOOT_HDR_TYPE_OF_LOADER = 0x210;
  10. const LINUX_BOOT_HDR_LOADFLAGS = 0x211;
  11. const LINUX_BOOT_HDR_CODE32_START = 0x214;
  12. const LINUX_BOOT_HDR_RAMDISK_IMAGE = 0x218;
  13. const LINUX_BOOT_HDR_RAMDISK_SIZE = 0x21C;
  14. const LINUX_BOOT_HDR_HEAP_END_PTR = 0x224;
  15. const LINUX_BOOT_HDR_CMD_LINE_PTR = 0x228;
  16. const LINUX_BOOT_HDR_INITRD_ADDR_MAX = 0x22C;
  17. const LINUX_BOOT_HDR_KERNEL_ALIGNMENT = 0x230;
  18. const LINUX_BOOT_HDR_RELOCATABLE_KERNEL = 0x234;
  19. const LINUX_BOOT_HDR_MIN_ALIGNMENT = 0x235;
  20. const LINUX_BOOT_HDR_XLOADFLAGS = 0x236;
  21. const LINUX_BOOT_HDR_CMDLINE_SIZE = 0x238;
  22. const LINUX_BOOT_HDR_PAYLOAD_OFFSET = 0x248;
  23. const LINUX_BOOT_HDR_PAYLOAD_LENGTH = 0x24C;
  24. const LINUX_BOOT_HDR_PREF_ADDRESS = 0x258;
  25. const LINUX_BOOT_HDR_INIT_SIZE = 0x260;
  26. const LINUX_BOOT_HDR_CHECKSUM1 = 0xAA55;
  27. const LINUX_BOOT_HDR_CHECKSUM2 = 0x53726448;
  28. const LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED = 0xFF;
  29. const LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH = 1 << 0;
  30. const LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG = 1 << 5;
  31. const LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS = 1 << 6;
  32. const LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS = 1 << 7;
  33. function load_kernel(mem8, bzimage, initrd, cmdline)
  34. {
  35. dbg_log("Trying to load kernel of size " + bzimage.byteLength);
  36. const KERNEL_HIGH_ADDRESS = 0x100000;
  37. // Put the initrd at the 64 MB boundary. This means the minimum memory size
  38. // is 64 MB plus the size of the initrd.
  39. // Note: If set too low, kernel may fail to load the initrd with "invalid magic at start of compressed archive"
  40. const INITRD_ADDRESS = 64 << 20;
  41. const quiet = false;
  42. const bzimage8 = new Uint8Array(bzimage);
  43. const bzimage16 = new Uint16Array(bzimage);
  44. const bzimage32 = new Uint32Array(bzimage);
  45. const setup_sects = bzimage8[LINUX_BOOT_HDR_SETUP_SECTS] || 4;
  46. const syssize = bzimage32[LINUX_BOOT_HDR_SYSSIZE >> 2] << 4;
  47. const vidmode = bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1];
  48. const checksum1 = bzimage16[LINUX_BOOT_HDR_BOOT_FLAG >> 1];
  49. if(checksum1 !== LINUX_BOOT_HDR_CHECKSUM1)
  50. {
  51. dbg_log("Bad checksum1: " + h(checksum1));
  52. return;
  53. }
  54. // Not aligned, so split into two 16-bit reads
  55. const checksum2 =
  56. bzimage16[LINUX_BOOT_HDR_HEADER >> 1] |
  57. bzimage16[LINUX_BOOT_HDR_HEADER + 2 >> 1] << 16;
  58. if(checksum2 !== LINUX_BOOT_HDR_CHECKSUM2)
  59. {
  60. dbg_log("Bad checksum2: " + h(checksum2));
  61. return;
  62. }
  63. const protocol = bzimage16[LINUX_BOOT_HDR_VERSION >> 1];
  64. dbg_assert(protocol >= 0x202); // older not supported by us
  65. const flags = bzimage8[LINUX_BOOT_HDR_LOADFLAGS];
  66. dbg_assert(flags & LINUX_BOOT_HDR_LOADFLAGS_LOADED_HIGH); // low kernels not supported by us
  67. // we don't relocate the kernel, so we don't care much about most of these
  68. const flags2 = bzimage16[LINUX_BOOT_HDR_XLOADFLAGS >> 1];
  69. const initrd_addr_max = bzimage32[LINUX_BOOT_HDR_INITRD_ADDR_MAX >> 2];
  70. const kernel_alignment = bzimage32[LINUX_BOOT_HDR_KERNEL_ALIGNMENT >> 2];
  71. const relocatable_kernel = bzimage8[LINUX_BOOT_HDR_RELOCATABLE_KERNEL];
  72. const min_alignment = bzimage8[LINUX_BOOT_HDR_MIN_ALIGNMENT];
  73. const cmdline_size = protocol >= 0x206 ? bzimage32[LINUX_BOOT_HDR_CMDLINE_SIZE >> 2] : 255;
  74. const payload_offset = bzimage32[LINUX_BOOT_HDR_PAYLOAD_OFFSET >> 2];
  75. const payload_length = bzimage32[LINUX_BOOT_HDR_PAYLOAD_LENGTH >> 2];
  76. const pref_address = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS >> 2];
  77. const pref_address_high = bzimage32[LINUX_BOOT_HDR_PREF_ADDRESS + 4 >> 2];
  78. const init_size = bzimage32[LINUX_BOOT_HDR_INIT_SIZE >> 2];
  79. dbg_log("kernel boot protocol version: " + h(protocol));
  80. dbg_log("flags=" + h(flags) + " xflags=" + h(flags2));
  81. dbg_log("code32_start=" + h(bzimage32[LINUX_BOOT_HDR_CODE32_START >> 2]));
  82. dbg_log("initrd_addr_max=" + h(initrd_addr_max));
  83. dbg_log("kernel_alignment=" + h(kernel_alignment));
  84. dbg_log("relocatable=" + relocatable_kernel);
  85. dbg_log("min_alignment=" + h(min_alignment));
  86. dbg_log("cmdline max=" + h(cmdline_size));
  87. dbg_log("payload offset=" + h(payload_offset) + " size=" + h(payload_length));
  88. dbg_log("pref_address=" + h(pref_address_high) + ":" + h(pref_address));
  89. dbg_log("init_size=" + h(init_size));
  90. const real_mode_segment = 0x8000;
  91. const base_ptr = real_mode_segment << 4;
  92. const heap_end = 0xE000;
  93. const heap_end_ptr = heap_end - 0x200;
  94. // fill in the kernel boot header with infos the kernel needs to know
  95. bzimage8[LINUX_BOOT_HDR_TYPE_OF_LOADER] = LINUX_BOOT_HDR_TYPE_OF_LOADER_NOT_ASSIGNED;
  96. const new_flags =
  97. (quiet ? flags | LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG : flags & ~LINUX_BOOT_HDR_LOADFLAGS_QUIET_FLAG)
  98. & ~LINUX_BOOT_HDR_LOADFLAGS_KEEP_SEGMENTS
  99. | LINUX_BOOT_HDR_LOADFLAGS_CAN_USE_HEAPS;
  100. bzimage8[LINUX_BOOT_HDR_LOADFLAGS] = new_flags;
  101. bzimage16[LINUX_BOOT_HDR_HEAP_END_PTR >> 1] = heap_end_ptr;
  102. // should parse the vga=... paramter from cmdline here, but we don't really care
  103. bzimage16[LINUX_BOOT_HDR_VIDMODE >> 1] = 0xFFFF; // normal
  104. dbg_log("heap_end_ptr=" + h(heap_end_ptr));
  105. cmdline += "\x00";
  106. dbg_assert(cmdline.length < cmdline_size);
  107. const cmd_line_ptr = base_ptr + heap_end;
  108. dbg_log("cmd_line_ptr=" + h(cmd_line_ptr));
  109. bzimage32[LINUX_BOOT_HDR_CMD_LINE_PTR >> 2] = cmd_line_ptr;
  110. for(let i = 0; i < cmdline.length; i++)
  111. {
  112. mem8[cmd_line_ptr + i] = cmdline.charCodeAt(i);
  113. }
  114. const prot_mode_kernel_start = (setup_sects + 1) * 512;
  115. dbg_log("prot_mode_kernel_start=" + h(prot_mode_kernel_start));
  116. const real_mode_kernel = new Uint8Array(bzimage, 0, prot_mode_kernel_start);
  117. const protected_mode_kernel = new Uint8Array(bzimage, prot_mode_kernel_start);
  118. let ramdisk_address = 0;
  119. let ramdisk_size = 0;
  120. if(initrd)
  121. {
  122. ramdisk_address = INITRD_ADDRESS;
  123. ramdisk_size = initrd.byteLength;
  124. dbg_assert(KERNEL_HIGH_ADDRESS + protected_mode_kernel.length < ramdisk_address);
  125. mem8.set(new Uint8Array(initrd), ramdisk_address);
  126. }
  127. bzimage32[LINUX_BOOT_HDR_RAMDISK_IMAGE >> 2] = ramdisk_address;
  128. bzimage32[LINUX_BOOT_HDR_RAMDISK_SIZE >> 2] = ramdisk_size;
  129. dbg_assert(base_ptr + real_mode_kernel.length < 0xA0000);
  130. mem8.set(real_mode_kernel, base_ptr);
  131. mem8.set(protected_mode_kernel, KERNEL_HIGH_ADDRESS);
  132. return {
  133. name: "genroms/kernel.bin",
  134. data: make_linux_boot_rom(real_mode_segment, heap_end),
  135. };
  136. }
  137. function make_linux_boot_rom(real_mode_segment, heap_end)
  138. {
  139. // This rom will be executed by seabios after its initialisation
  140. // It sets up segment registers, the stack and calls the kernel real mode entry point
  141. const SIZE = 0x200;
  142. const data8 = new Uint8Array(SIZE);
  143. const data16 = new Uint16Array(data8.buffer);
  144. data16[0] = 0xAA55;
  145. data8[2] = SIZE / 0x200;
  146. let i = 3;
  147. data8[i++] = 0xFA; // cli
  148. data8[i++] = 0xB8; // mov ax, real_mode_segment
  149. data8[i++] = real_mode_segment >> 0;
  150. data8[i++] = real_mode_segment >> 8;
  151. data8[i++] = 0x8E; // mov es, ax
  152. data8[i++] = 0xC0;
  153. data8[i++] = 0x8E; // mov ds, ax
  154. data8[i++] = 0xD8;
  155. data8[i++] = 0x8E; // mov fs, ax
  156. data8[i++] = 0xE0;
  157. data8[i++] = 0x8E; // mov gs, ax
  158. data8[i++] = 0xE8;
  159. data8[i++] = 0x8E; // mov ss, ax
  160. data8[i++] = 0xD0;
  161. data8[i++] = 0xBC; // mov sp, heap_end
  162. data8[i++] = heap_end >> 0;
  163. data8[i++] = heap_end >> 8;
  164. data8[i++] = 0xEA; // jmp (real_mode_segment+0x20):0x0
  165. data8[i++] = 0x00;
  166. data8[i++] = 0x00;
  167. data8[i++] = real_mode_segment + 0x20 >> 0;
  168. data8[i++] = real_mode_segment + 0x20 >> 8;
  169. dbg_assert(i < SIZE);
  170. const checksum_index = i;
  171. data8[checksum_index] = 0;
  172. let checksum = 0;
  173. for(let i = 0; i < data8.length; i++)
  174. {
  175. checksum += data8[i];
  176. }
  177. data8[checksum_index] = -checksum;
  178. return data8;
  179. }