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