nbi.c 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include "image.h"
  2. #include "memsizes.h"
  3. #include "realmode.h"
  4. #include "gateA20.h"
  5. #include "osloader.h"
  6. #include "etherboot.h"
  7. /* An NBI image header */
  8. struct imgheader {
  9. unsigned long magic;
  10. union {
  11. unsigned char length;
  12. unsigned long flags;
  13. };
  14. segoff_t location;
  15. union {
  16. segoff_t segoff;
  17. unsigned long linear;
  18. } execaddr;
  19. } __attribute__ (( packed ));
  20. /* NBI magic number */
  21. #define NBI_MAGIC 0x1B031336UL
  22. /* Interpretation of the "length" fields */
  23. #define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 )
  24. #define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 )
  25. #define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) )
  26. /* Interpretation of the "flags" fields */
  27. #define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) )
  28. #define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) )
  29. /* NBI header length */
  30. #define NBI_HEADER_LENGTH 512
  31. /* An NBI segment header */
  32. struct segheader {
  33. unsigned char length;
  34. unsigned char vendortag;
  35. unsigned char reserved;
  36. unsigned char flags;
  37. unsigned long loadaddr;
  38. unsigned long imglength;
  39. unsigned long memlength;
  40. };
  41. /* Interpretation of the "flags" fields */
  42. #define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 )
  43. #define NBI_LOADADDR_ABS 0x00
  44. #define NBI_LOADADDR_AFTER 0x01
  45. #define NBI_LOADADDR_END 0x02
  46. #define NBI_LOADADDR_BEFORE 0x03
  47. #define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) )
  48. /* Info passed to NBI image */
  49. static struct ebinfo loaderinfo = {
  50. VERSION_MAJOR, VERSION_MINOR,
  51. 0
  52. };
  53. /*
  54. * Determine whether or not this is a valid NBI image
  55. *
  56. */
  57. static int nbi_probe ( physaddr_t start, off_t len, void **context ) {
  58. static struct imgheader imgheader;
  59. if ( (unsigned)len < sizeof ( imgheader ) ) {
  60. DBG ( "NBI image too small\n" );
  61. return 0;
  62. }
  63. copy_from_phys ( &imgheader, start, sizeof ( imgheader ) );
  64. if ( imgheader.magic != NBI_MAGIC )
  65. return 0;
  66. /* Record image context */
  67. DBG ( "NBI found valid image\n" );
  68. *context = &imgheader;
  69. return 1;
  70. }
  71. /*
  72. * Prepare a segment for an NBI image
  73. *
  74. */
  75. static int nbi_prepare_segment ( physaddr_t dest, off_t imglen, off_t memlen,
  76. physaddr_t src __unused ) {
  77. DBG ( "NBI preparing segment [%x,%x) (imglen %d memlen %d)\n",
  78. dest, dest + memlen, imglen, memlen );
  79. return prep_segment ( dest, dest + imglen, dest + memlen );
  80. }
  81. /*
  82. * Load a segment for an NBI image
  83. *
  84. */
  85. static int nbi_load_segment ( physaddr_t dest, off_t imglen,
  86. off_t memlen __unused, physaddr_t src ) {
  87. DBG ( "NBI loading segment [%x,%x)\n", dest, dest + imglen );
  88. copy_phys_to_phys ( dest, src, imglen );
  89. return 1;
  90. }
  91. /*
  92. * Process segments of an NBI image
  93. *
  94. */
  95. static int nbi_process_segments ( physaddr_t start, off_t len,
  96. struct imgheader *imgheader,
  97. int ( * process ) ( physaddr_t dest,
  98. off_t imglen,
  99. off_t memlen,
  100. physaddr_t src ) ) {
  101. struct segheader sh;
  102. off_t offset = 0;
  103. off_t sh_off;
  104. physaddr_t dest;
  105. off_t dest_imglen, dest_memlen;
  106. /* Copy header to target location */
  107. dest = ( ( imgheader->location.segment << 4 ) +
  108. imgheader->location.offset );
  109. dest_imglen = dest_memlen = NBI_HEADER_LENGTH;
  110. if ( ! process ( dest, dest_imglen, dest_memlen, start + offset ) )
  111. return 0;
  112. offset += dest_imglen;
  113. /* Process segments in turn */
  114. sh_off = NBI_LENGTH ( imgheader->length );
  115. do {
  116. /* Read segment header */
  117. copy_from_phys ( &sh, start + sh_off, sizeof ( sh ) );
  118. if ( sh.length == 0 ) {
  119. /* Avoid infinite loop? */
  120. DBG ( "NBI invalid segheader length 0\n" );
  121. return 0;
  122. }
  123. /* Calculate segment load address */
  124. switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) {
  125. case NBI_LOADADDR_ABS:
  126. dest = sh.loadaddr;
  127. break;
  128. case NBI_LOADADDR_AFTER:
  129. dest = dest + dest_memlen + sh.loadaddr;
  130. break;
  131. case NBI_LOADADDR_BEFORE:
  132. dest = dest - sh.loadaddr;
  133. break;
  134. case NBI_LOADADDR_END:
  135. /* Not correct according to the spec, but
  136. * maintains backwards compatibility with
  137. * previous versions of Etherboot.
  138. */
  139. dest = ( meminfo.memsize * 1024 + 0x100000UL )
  140. - sh.loadaddr;
  141. break;
  142. default:
  143. DBG ( "NBI can't count up to three\n" );
  144. return 0;
  145. }
  146. /* Process this segment */
  147. dest_imglen = sh.imglength;
  148. dest_memlen = sh.memlength;
  149. if ( ! process ( dest, dest_imglen, dest_memlen,
  150. start + offset ) )
  151. return 0;
  152. offset += dest_imglen;
  153. /* Next segheader */
  154. sh_off += NBI_LENGTH ( sh.length );
  155. if ( sh_off >= NBI_HEADER_LENGTH ) {
  156. DBG ( "NBI header overflow\n" );
  157. return 0;
  158. }
  159. } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) );
  160. if ( offset != len ) {
  161. DBG ( "NBI length mismatch (file %d, metadata %d)\n",
  162. len, offset );
  163. return 0;
  164. }
  165. return 1;
  166. }
  167. /*
  168. * Load an NBI image into memory
  169. *
  170. */
  171. static int nbi_load ( physaddr_t start, off_t len, void *context ) {
  172. struct imgheader *imgheader = context;
  173. /* If we don't have enough data give up */
  174. if ( len < NBI_HEADER_LENGTH )
  175. return 0;
  176. DBG ( "NBI placing header at %hx:%hx\n",
  177. imgheader->location.segment, imgheader->location.offset );
  178. /* NBI files can have overlaps between segments; the bss of
  179. * one segment may overlap the initialised data of another. I
  180. * assume this is a design flaw, but there are images out
  181. * there that we need to work with. We therefore do two
  182. * passes: first to initialise the segments, then to copy the
  183. * data. This avoids zeroing out already-copied data.
  184. */
  185. if ( ! nbi_process_segments ( start, len, imgheader,
  186. nbi_prepare_segment ) )
  187. return 0;
  188. if ( ! nbi_process_segments ( start, len, imgheader,
  189. nbi_load_segment ) )
  190. return 0;
  191. return 1;
  192. }
  193. /*
  194. * Boot a 16-bit NBI image
  195. *
  196. */
  197. static int nbi_boot16 ( struct imgheader *imgheader ) {
  198. uint16_t basemem_bootp;
  199. int discard_D, discard_S, discard_b;
  200. DBG ( "NBI executing 16-bit image at %hx:%hx\n",
  201. imgheader->execaddr.segoff.segment,
  202. imgheader->execaddr.segoff.offset );
  203. gateA20_unset();
  204. basemem_bootp = BASEMEM_PARAMETER_INIT ( bootp_data );
  205. REAL_EXEC ( rm_xstart16,
  206. "pushw %%ds\n\t" /* far pointer to bootp data copy */
  207. "pushw %%bx\n\t"
  208. "pushl %%esi\n\t" /* location */
  209. "pushw %%cs\n\t" /* lcall execaddr */
  210. "call 1f\n\t"
  211. "jmp 2f\n\t"
  212. "\n1:\n\t"
  213. "pushl %%edi\n\t"
  214. "lret\n\t"
  215. "\n2:\n\t"
  216. "addw $8,%%sp\n\t", /* pop location and bootp ptr */
  217. 3,
  218. OUT_CONSTRAINTS ( "=D" ( discard_D ), "=S" ( discard_S ),
  219. "=b" ( discard_b ) ),
  220. IN_CONSTRAINTS ( "D" ( imgheader->execaddr.segoff ),
  221. "S" ( imgheader->location ),
  222. "b" ( basemem_bootp ) ),
  223. CLOBBER ( "eax", "ecx", "edx", "ebp" ) );
  224. BASEMEM_PARAMETER_DONE ( bootp_data );
  225. return 0;
  226. }
  227. /*
  228. * Boot a 32-bit NBI image
  229. *
  230. */
  231. static int nbi_boot32 ( struct imgheader *imgheader ) {
  232. int rc = 0;
  233. DBG ( "NBI executing 32-bit image at %x\n",
  234. imgheader->execaddr.linear );
  235. /* no gateA20_unset for PM call */
  236. rc = xstart32 ( imgheader->execaddr.linear,
  237. virt_to_phys ( &loaderinfo ),
  238. ( ( imgheader->location.segment << 4 ) +
  239. imgheader->location.offset ),
  240. virt_to_phys ( &bootp_data ) );
  241. printf ( "Secondary program returned %d\n", rc );
  242. if ( ! NBI_PROGRAM_RETURNS ( imgheader->flags ) ) {
  243. /* We shouldn't have returned */
  244. rc = 0;
  245. }
  246. return rc;
  247. }
  248. /*
  249. * Boot a loaded NBI image
  250. *
  251. */
  252. static int nbi_boot ( void *context ) {
  253. struct imgheader *imgheader = context;
  254. if ( NBI_LINEAR_EXEC_ADDR ( imgheader->flags ) ) {
  255. return nbi_boot32 ( imgheader );
  256. } else {
  257. return nbi_boot16 ( imgheader );
  258. }
  259. }
  260. static struct image nbi_image __image = {
  261. .name = "NBI",
  262. .probe = nbi_probe,
  263. .load = nbi_load,
  264. .boot = nbi_boot,
  265. };