source: bootcd/isolinux/syslinux-6.03/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 15.5 KB
Line 
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
Note: See TracBrowser for help on using the repository browser.