You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

e820mangler.S 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. */
  19. FILE_LICENCE ( GPL2_OR_LATER )
  20. .text
  21. .arch i386
  22. .code16
  23. #define SMAP 0x534d4150
  24. /* Most documentation refers to the E820 buffer as being 20 bytes, and
  25. * the API makes it perfectly legitimate to pass only a 20-byte buffer
  26. * and expect to get valid data. However, some morons at ACPI decided
  27. * to extend the data structure by adding an extra "extended
  28. * attributes" field and by including critical information within this
  29. * field, such as whether or not the region is enabled. A caller who
  30. * passes in only a 20-byte buffer therefore risks getting very, very
  31. * misleading information.
  32. *
  33. * I have personally witnessed an HP BIOS that returns a value of
  34. * 0x0009 in the extended attributes field. If we don't pass this
  35. * value through to the caller, 32-bit WinPE will die, usually with a
  36. * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death.
  37. *
  38. * Allow a ridiculously large maximum value (64 bytes) for the E820
  39. * buffer as a guard against insufficiently creative idiots in the
  40. * future.
  41. */
  42. #define E820MAXSIZE 64
  43. /****************************************************************************
  44. *
  45. * Allowed memory windows
  46. *
  47. * There are two ways to view this list. The first is as a list of
  48. * (non-overlapping) allowed memory regions, sorted by increasing
  49. * address. The second is as a list of (non-overlapping) hidden
  50. * memory regions, again sorted by increasing address. The second
  51. * view is offset by half an entry from the first: think about this
  52. * for a moment and it should make sense.
  53. *
  54. * xxx_memory_window is used to indicate an "allowed region"
  55. * structure, hidden_xxx_memory is used to indicate a "hidden region"
  56. * structure. Each structure is 16 bytes in length.
  57. *
  58. ****************************************************************************
  59. */
  60. .section ".data16", "aw", @progbits
  61. .align 16
  62. .globl hidemem_base
  63. .globl hidemem_umalloc
  64. .globl hidemem_textdata
  65. memory_windows:
  66. base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */
  67. hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */
  68. ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */
  69. hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */
  70. .long 0xffffffff, 0xffffffff /* Changes at runtime */
  71. hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */
  72. .long 0xffffffff, 0xffffffff /* Changes at runtime */
  73. .long 0xffffffff, 0xffffffff /* End of memory */
  74. memory_windows_end:
  75. /****************************************************************************
  76. * Truncate region to memory window
  77. *
  78. * Parameters:
  79. * %edx:%eax Start of region
  80. * %ecx:%ebx Length of region
  81. * %si Memory window
  82. * Returns:
  83. * %edx:%eax Start of windowed region
  84. * %ecx:%ebx Length of windowed region
  85. ****************************************************************************
  86. */
  87. .section ".text16", "ax", @progbits
  88. window_region:
  89. /* Convert (start,len) to (start, end) */
  90. addl %eax, %ebx
  91. adcl %edx, %ecx
  92. /* Truncate to window start */
  93. cmpl 4(%si), %edx
  94. jne 1f
  95. cmpl 0(%si), %eax
  96. 1: jae 2f
  97. movl 4(%si), %edx
  98. movl 0(%si), %eax
  99. 2: /* Truncate to window end */
  100. cmpl 12(%si), %ecx
  101. jne 1f
  102. cmpl 8(%si), %ebx
  103. 1: jbe 2f
  104. movl 12(%si), %ecx
  105. movl 8(%si), %ebx
  106. 2: /* Convert (start, end) back to (start, len) */
  107. subl %eax, %ebx
  108. sbbl %edx, %ecx
  109. /* If length is <0, set length to 0 */
  110. jae 1f
  111. xorl %ebx, %ebx
  112. xorl %ecx, %ecx
  113. ret
  114. .size window_region, . - window_region
  115. /****************************************************************************
  116. * Patch "memory above 1MB" figure
  117. *
  118. * Parameters:
  119. * %ax Memory above 1MB, in 1kB blocks
  120. * Returns:
  121. * %ax Modified memory above 1M in 1kB blocks
  122. ****************************************************************************
  123. */
  124. .section ".text16", "ax", @progbits
  125. patch_1m:
  126. pushal
  127. /* Convert to (start,len) format and call truncate */
  128. xorl %ecx, %ecx
  129. movzwl %ax, %ebx
  130. shll $10, %ebx
  131. xorl %edx, %edx
  132. movl $0x100000, %eax
  133. movw $ext_memory_window, %si
  134. call window_region
  135. /* Convert back to "memory above 1MB" format and return via %ax */
  136. pushfw
  137. shrl $10, %ebx
  138. popfw
  139. movw %sp, %bp
  140. movw %bx, 28(%bp)
  141. popal
  142. ret
  143. .size patch_1m, . - patch_1m
  144. /****************************************************************************
  145. * Patch "memory above 16MB" figure
  146. *
  147. * Parameters:
  148. * %bx Memory above 16MB, in 64kB blocks
  149. * Returns:
  150. * %bx Modified memory above 16M in 64kB blocks
  151. ****************************************************************************
  152. */
  153. .section ".text16", "ax", @progbits
  154. patch_16m:
  155. pushal
  156. /* Convert to (start,len) format and call truncate */
  157. xorl %ecx, %ecx
  158. shll $16, %ebx
  159. xorl %edx, %edx
  160. movl $0x1000000, %eax
  161. movw $ext_memory_window, %si
  162. call window_region
  163. /* Convert back to "memory above 16MB" format and return via %bx */
  164. pushfw
  165. shrl $16, %ebx
  166. popfw
  167. movw %sp, %bp
  168. movw %bx, 16(%bp)
  169. popal
  170. ret
  171. .size patch_16m, . - patch_16m
  172. /****************************************************************************
  173. * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures
  174. *
  175. * Parameters:
  176. * %ax Memory between 1MB and 16MB, in 1kB blocks
  177. * %bx Memory above 16MB, in 64kB blocks
  178. * Returns:
  179. * %ax Modified memory between 1MB and 16MB, in 1kB blocks
  180. * %bx Modified memory above 16MB, in 64kB blocks
  181. ****************************************************************************
  182. */
  183. .section ".text16", "ax", @progbits
  184. patch_1m_16m:
  185. call patch_1m
  186. call patch_16m
  187. /* If 1M region is no longer full-length, kill off the 16M region */
  188. cmpw $( 15 * 1024 ), %ax
  189. je 1f
  190. xorw %bx, %bx
  191. 1: ret
  192. .size patch_1m_16m, . - patch_1m_16m
  193. /****************************************************************************
  194. * Get underlying e820 memory region to underlying_e820 buffer
  195. *
  196. * Parameters:
  197. * As for INT 15,e820
  198. * Returns:
  199. * As for INT 15,e820
  200. *
  201. * Wraps the underlying INT 15,e820 call so that the continuation
  202. * value (%ebx) is a 16-bit simple sequence counter (with the high 16
  203. * bits ignored), and termination is always via CF=1 rather than
  204. * %ebx=0.
  205. *
  206. ****************************************************************************
  207. */
  208. .section ".text16", "ax", @progbits
  209. get_underlying_e820:
  210. /* If the requested region is in the cache, return it */
  211. cmpw %bx, underlying_e820_index
  212. jne 2f
  213. pushw %di
  214. pushw %si
  215. movw $underlying_e820_cache, %si
  216. cmpl underlying_e820_cache_size, %ecx
  217. jbe 1f
  218. movl underlying_e820_cache_size, %ecx
  219. 1: pushl %ecx
  220. rep movsb
  221. popl %ecx
  222. popw %si
  223. popw %di
  224. incw %bx
  225. movl %edx, %eax
  226. clc
  227. ret
  228. 2:
  229. /* If the requested region is earlier than the cached region,
  230. * invalidate the cache.
  231. */
  232. cmpw %bx, underlying_e820_index
  233. jbe 1f
  234. movw $0xffff, underlying_e820_index
  235. 1:
  236. /* If the cache is invalid, reset the underlying %ebx */
  237. cmpw $0xffff, underlying_e820_index
  238. jne 1f
  239. andl $0, underlying_e820_ebx
  240. 1:
  241. /* If the cache is valid but the continuation value is zero,
  242. * this means that the previous underlying call returned with
  243. * %ebx=0. Return with CF=1 in this case.
  244. */
  245. cmpw $0xffff, underlying_e820_index
  246. je 1f
  247. cmpl $0, underlying_e820_ebx
  248. jne 1f
  249. stc
  250. ret
  251. 1:
  252. /* Get the next region into the cache */
  253. pushl %eax
  254. pushl %ebx
  255. pushl %ecx
  256. pushl %edx
  257. pushl %esi /* Some implementations corrupt %esi, so we */
  258. pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */
  259. pushl %ebp
  260. pushw %es
  261. pushw %ds
  262. popw %es
  263. movw $underlying_e820_cache, %di
  264. cmpl $E820MAXSIZE, %ecx
  265. jbe 1f
  266. movl $E820MAXSIZE, %ecx
  267. 1: movl underlying_e820_ebx, %ebx
  268. stc
  269. pushfw
  270. lcall *%cs:int15_vector
  271. popw %es
  272. popl %ebp
  273. popl %edi
  274. popl %esi
  275. /* Check for error return from underlying e820 call */
  276. jc 2f /* CF set: error */
  277. cmpl $SMAP, %eax
  278. je 3f /* 'SMAP' missing: error */
  279. 2: /* An error occurred: return values returned by underlying e820 call */
  280. stc /* Force CF set if SMAP was missing */
  281. addr32 leal 16(%esp), %esp /* avoid changing other flags */
  282. ret
  283. 3: /* No error occurred */
  284. movl %ebx, underlying_e820_ebx
  285. movl %ecx, underlying_e820_cache_size
  286. popl %edx
  287. popl %ecx
  288. popl %ebx
  289. popl %eax
  290. /* Mark cache as containing this result */
  291. incw underlying_e820_index
  292. /* Loop until found */
  293. jmp get_underlying_e820
  294. .size get_underlying_e820, . - get_underlying_e820
  295. .section ".data16", "aw", @progbits
  296. underlying_e820_index:
  297. .word 0xffff /* Initialise to an invalid value */
  298. .size underlying_e820_index, . - underlying_e820_index
  299. .section ".bss16", "aw", @nobits
  300. underlying_e820_ebx:
  301. .long 0
  302. .size underlying_e820_ebx, . - underlying_e820_ebx
  303. .section ".bss16", "aw", @nobits
  304. underlying_e820_cache:
  305. .space E820MAXSIZE
  306. .size underlying_e820_cache, . - underlying_e820_cache
  307. .section ".bss16", "aw", @nobits
  308. underlying_e820_cache_size:
  309. .long 0
  310. .size underlying_e820_cache_size, . - underlying_e820_cache_size
  311. /****************************************************************************
  312. * Get windowed e820 region, without empty region stripping
  313. *
  314. * Parameters:
  315. * As for INT 15,e820
  316. * Returns:
  317. * As for INT 15,e820
  318. *
  319. * Wraps the underlying INT 15,e820 call so that each underlying
  320. * region is returned N times, windowed to fit within N visible-memory
  321. * windows. Termination is always via CF=1.
  322. *
  323. ****************************************************************************
  324. */
  325. .section ".text16", "ax", @progbits
  326. get_windowed_e820:
  327. /* Preserve registers */
  328. pushl %esi
  329. pushw %bp
  330. /* Split %ebx into %si:%bx, store original %bx in %bp */
  331. pushl %ebx
  332. popw %bp
  333. popw %si
  334. /* %si == 0 => start of memory_windows list */
  335. testw %si, %si
  336. jne 1f
  337. movw $memory_windows, %si
  338. 1:
  339. /* Get (cached) underlying e820 region to buffer */
  340. call get_underlying_e820
  341. jc 99f /* Abort on error */
  342. /* Preserve registers */
  343. pushal
  344. /* start => %edx:%eax, len => %ecx:%ebx */
  345. movl %es:0(%di), %eax
  346. movl %es:4(%di), %edx
  347. movl %es:8(%di), %ebx
  348. movl %es:12(%di), %ecx
  349. /* Truncate region to current window */
  350. call window_region
  351. 1: /* Store modified values in e820 map entry */
  352. movl %eax, %es:0(%di)
  353. movl %edx, %es:4(%di)
  354. movl %ebx, %es:8(%di)
  355. movl %ecx, %es:12(%di)
  356. /* Restore registers */
  357. popal
  358. /* Derive continuation value for next call */
  359. addw $16, %si
  360. cmpw $memory_windows_end, %si
  361. jne 1f
  362. /* End of memory windows: reset %si and allow %bx to continue */
  363. xorw %si, %si
  364. jmp 2f
  365. 1: /* More memory windows to go: restore original %bx */
  366. movw %bp, %bx
  367. 2: /* Construct %ebx from %si:%bx */
  368. pushw %si
  369. pushw %bx
  370. popl %ebx
  371. 98: /* Clear CF */
  372. clc
  373. 99: /* Restore registers and return */
  374. popw %bp
  375. popl %esi
  376. ret
  377. .size get_windowed_e820, . - get_windowed_e820
  378. /****************************************************************************
  379. * Get windowed e820 region, with empty region stripping
  380. *
  381. * Parameters:
  382. * As for INT 15,e820
  383. * Returns:
  384. * As for INT 15,e820
  385. *
  386. * Wraps the underlying INT 15,e820 call so that each underlying
  387. * region is returned up to N times, windowed to fit within N
  388. * visible-memory windows. Empty windows are never returned.
  389. * Termination is always via CF=1.
  390. *
  391. ****************************************************************************
  392. */
  393. .section ".text16", "ax", @progbits
  394. get_nonempty_e820:
  395. /* Record entry parameters */
  396. pushl %eax
  397. pushl %ecx
  398. pushl %edx
  399. /* Get next windowed region */
  400. call get_windowed_e820
  401. jc 99f /* abort on error */
  402. /* If region is non-empty, finish here */
  403. cmpl $0, %es:8(%di)
  404. jne 98f
  405. cmpl $0, %es:12(%di)
  406. jne 98f
  407. /* Region was empty: restore entry parameters and go to next region */
  408. popl %edx
  409. popl %ecx
  410. popl %eax
  411. jmp get_nonempty_e820
  412. 98: /* Clear CF */
  413. clc
  414. 99: /* Return values from underlying call */
  415. addr32 leal 12(%esp), %esp /* avoid changing flags */
  416. ret
  417. .size get_nonempty_e820, . - get_nonempty_e820
  418. /****************************************************************************
  419. * Get mangled e820 region, with empty region stripping
  420. *
  421. * Parameters:
  422. * As for INT 15,e820
  423. * Returns:
  424. * As for INT 15,e820
  425. *
  426. * Wraps the underlying INT 15,e820 call so that underlying regions
  427. * are windowed to the allowed memory regions. Empty regions are
  428. * stripped from the map. Termination is always via %ebx=0.
  429. *
  430. ****************************************************************************
  431. */
  432. .section ".text16", "ax", @progbits
  433. get_mangled_e820:
  434. /* Get a nonempty region */
  435. call get_nonempty_e820
  436. jc 99f /* Abort on error */
  437. /* Peek ahead to see if there are any further nonempty regions */
  438. pushal
  439. pushw %es
  440. movw %sp, %bp
  441. subw %cx, %sp
  442. movl $0xe820, %eax
  443. movl $SMAP, %edx
  444. pushw %ss
  445. popw %es
  446. movw %sp, %di
  447. call get_nonempty_e820
  448. movw %bp, %sp
  449. popw %es
  450. popal
  451. jnc 99f /* There are further nonempty regions */
  452. /* No futher nonempty regions: zero %ebx and clear CF */
  453. xorl %ebx, %ebx
  454. 99: /* Return */
  455. ret
  456. .size get_mangled_e820, . - get_mangled_e820
  457. /****************************************************************************
  458. * INT 15,e820 handler
  459. ****************************************************************************
  460. */
  461. .section ".text16", "ax", @progbits
  462. int15_e820:
  463. pushw %ds
  464. pushw %cs:rm_ds
  465. popw %ds
  466. call get_mangled_e820
  467. popw %ds
  468. call patch_cf
  469. iret
  470. .size int15_e820, . - int15_e820
  471. /****************************************************************************
  472. * INT 15,e801 handler
  473. ****************************************************************************
  474. */
  475. .section ".text16", "ax", @progbits
  476. int15_e801:
  477. /* Call previous handler */
  478. pushfw
  479. lcall *%cs:int15_vector
  480. call patch_cf
  481. /* Edit result */
  482. pushw %ds
  483. pushw %cs:rm_ds
  484. popw %ds
  485. call patch_1m_16m
  486. xchgw %ax, %cx
  487. xchgw %bx, %dx
  488. call patch_1m_16m
  489. xchgw %ax, %cx
  490. xchgw %bx, %dx
  491. popw %ds
  492. iret
  493. .size int15_e801, . - int15_e801
  494. /****************************************************************************
  495. * INT 15,88 handler
  496. ****************************************************************************
  497. */
  498. .section ".text16", "ax", @progbits
  499. int15_88:
  500. /* Call previous handler */
  501. pushfw
  502. lcall *%cs:int15_vector
  503. call patch_cf
  504. /* Edit result */
  505. pushw %ds
  506. pushw %cs:rm_ds
  507. popw %ds
  508. call patch_1m
  509. popw %ds
  510. iret
  511. .size int15_88, . - int15_88
  512. /****************************************************************************
  513. * INT 15 handler
  514. ****************************************************************************
  515. */
  516. .section ".text16", "ax", @progbits
  517. .globl int15
  518. int15:
  519. /* See if we want to intercept this call */
  520. pushfw
  521. cmpw $0xe820, %ax
  522. jne 1f
  523. cmpl $SMAP, %edx
  524. jne 1f
  525. popfw
  526. jmp int15_e820
  527. 1: cmpw $0xe801, %ax
  528. jne 2f
  529. popfw
  530. jmp int15_e801
  531. 2: cmpb $0x88, %ah
  532. jne 3f
  533. popfw
  534. jmp int15_88
  535. 3: popfw
  536. ljmp *%cs:int15_vector
  537. .size int15, . - int15
  538. .section ".text16.data", "aw", @progbits
  539. .globl int15_vector
  540. int15_vector:
  541. .long 0
  542. .size int15_vector, . - int15_vector