99ca519bee37f7281ae228bafeeb3e464e4a0637
[people/pcmattman/gpxe.git] / src / arch / i386 / firmware / pcbios / e820mangler.S
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
19 FILE_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
69 memory_windows:
70 base_memory_window:     .long 0x00000000, 0x00000000 /* Start of memory */
71
72 hidemem_base:           .long 0x000a0000, 0x00000000 /* Changes at runtime */
73 ext_memory_window:      .long 0x000a0000, 0x00000000 /* 640kB mark */
74
75 hidemem_umalloc:        .long 0xffffffff, 0xffffffff /* Changes at runtime */
76                         .long 0xffffffff, 0xffffffff /* Changes at runtime */
77
78 hidemem_textdata:       .long 0xffffffff, 0xffffffff /* Changes at runtime */
79                         .long 0xffffffff, 0xffffffff /* Changes at runtime */
80
81                         .long 0xffffffff, 0xffffffff /* End of memory */
82 memory_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
97 window_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
105 1:      jae     2f
106         movl    4(%si), %edx
107         movl    0(%si), %eax
108 2:      /* Truncate to window end */
109         cmpl    12(%si), %ecx
110         jne     1f
111         cmpl    8(%si), %ebx
112 1:      jbe     2f
113         movl    12(%si), %ecx
114         movl    8(%si), %ebx
115 2:      /* 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
135 patch_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
165 patch_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
196 patch_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
203 1:      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
222 get_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
233 1:      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
242 2:      
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
249 1:
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
254 1:      
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
265 1:      
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
281 1:      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 */
293 2:      /* 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
297 3:      /* 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
312 underlying_e820_index:
313         .word   0xffff /* Initialise to an invalid value */
314         .size underlying_e820_index, . - underlying_e820_index
315
316         .section ".bss16", "aw", @nobits
317 underlying_e820_ebx:
318         .long   0
319         .size underlying_e820_ebx, . - underlying_e820_ebx
320
321         .section ".bss16", "aw", @nobits
322 underlying_e820_cache:
323         .space  E820MAXSIZE
324         .size underlying_e820_cache, . - underlying_e820_cache
325
326         .section ".bss16", "aw", @nobits
327 underlying_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
346 get_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
361 1:      
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
375 1:      /* 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
390 1:      /* More memory windows to go: restore original %bx */
391         movw    %bp, %bx
392 2:      /* Construct %ebx from %si:%bx */
393         pushw   %si
394         pushw   %bx
395         popl    %ebx
396
397 98:     /* Clear CF */
398         clc
399 99:     /* 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
421 get_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
444 98:     /* Clear CF */
445         clc
446 99:     /* 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
466 get_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         
491 99:     /* 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  */
500 patch_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
512 int15_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
527 int15_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
551 int15_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
571 int15:
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
580 1:      cmpw    $0xe801, %ax
581         jne     2f
582         popfw
583         jmp     int15_e801
584 2:      cmpb    $0x88, %ah
585         jne     3f
586         popfw
587         jmp     int15_88
588 3:      popfw
589         ljmp    *%cs:int15_vector
590         .size int15, . - int15
591         
592         .section ".text16.data", "aw", @progbits
593         .globl int15_vector
594 int15_vector:
595         .long 0
596         .size int15_vector, . - int15_vector