您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

e820mangler.S 15KB

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