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