| ;; ----------------------------------------------------------------------- |
| ;; |
| ;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved |
| ;; |
| ;; This program is free software; you can redistribute it and/or modify |
| ;; it under the terms of the GNU General Public License as published by |
| ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| ;; Boston MA 02111-1307, USA; either version 2 of the License, or |
| ;; (at your option) any later version; incorporated herein by reference. |
| ;; |
| ;; ----------------------------------------------------------------------- |
| |
| ;; |
| ;; highmem.inc |
| ;; |
| ;; Probe for the size of high memory. This can be overridden by a |
| ;; mem= command on the command line while booting a new kernel. |
| ;; |
| |
| section .text |
| |
| ; |
| ; This is set up as a subroutine; it will set up the global variable |
| ; HighMemSize. All registers are preserved. |
| ; |
| highmemsize: |
| push es |
| pushad |
| |
| push cs |
| pop es |
| |
| ; |
| ; First, try INT 15:E820 (get BIOS memory map) |
| ; |
| ; Note: we may have to scan this multiple times, because some (daft) BIOSes |
| ; report main memory as multiple contiguous ranges... |
| ; |
| get_e820: |
| mov dword [E820Max],-(1 << 20) ; Max amount of high memory |
| mov dword [E820Mem],(1 << 20) ; End of detected high memory |
| .start_over: |
| xor ebx,ebx ; Start with first record |
| jmp short .do_e820 ; Skip "at end" check first time! |
| .int_loop: and ebx,ebx ; If we're back at beginning... |
| jz .e820_done ; ... we're done |
| .do_e820: mov eax,0000E820h |
| mov edx,534D4150h ; "SMAP" backwards |
| xor ecx,ecx |
| mov cl,20 ; ECX <- 20 |
| mov di,E820Buf |
| int 15h |
| jnc .no_carry |
| ; If carry, ebx == 0 means error, ebx != 0 means we're done |
| and ebx,ebx |
| jnz .e820_done |
| jmp no_e820 |
| .no_carry: |
| cmp eax,534D4150h |
| jne no_e820 |
| ; |
| ; Look for a memory block starting at <= 1 MB and continuing upward |
| ; |
| cmp dword [E820Buf+4], byte 0 |
| ja .int_loop ; Start >= 4 GB? |
| mov eax, [E820Buf] |
| cmp dword [E820Buf+16],1 |
| je .is_ram ; Is it memory? |
| ; |
| ; Non-memory range. Remember this as a limit; some BIOSes get the length |
| ; of primary RAM incorrect! |
| ; |
| cmp eax, (1 << 20) |
| jb .int_loop ; Starts in lowmem region |
| cmp eax,[E820Max] |
| jae .int_loop ; Already above limit |
| mov [E820Max],eax ; Set limit |
| jmp .int_loop |
| |
| .is_ram: |
| cmp eax,[E820Mem] |
| ja .int_loop ; Not contiguous with our starting point |
| add eax,[E820Buf+8] |
| jc .overflow |
| cmp dword [E820Buf+12],0 |
| je .nooverflow |
| .overflow: |
| or eax,-1 |
| .nooverflow: |
| cmp eax,[E820Mem] |
| jbe .int_loop ; All is below our baseline |
| mov [E820Mem],eax |
| jmp .start_over ; Start over in case we find an adjacent range |
| |
| .e820_done: |
| mov eax,[E820Mem] |
| cmp eax,[E820Max] |
| jna .not_limited |
| mov eax,[E820Max] |
| .not_limited: |
| cmp eax,(1 << 20) |
| ja got_highmem ; Did we actually find memory? |
| ; otherwise fall through |
| |
| ; |
| ; INT 15:E820 failed. Try INT 15:E801. |
| ; |
| no_e820: |
| mov ax,0e801h ; Query high memory (semi-recent) |
| int 15h |
| jc no_e801 |
| cmp ax,3c00h |
| ja no_e801 ; > 3C00h something's wrong with this call |
| jb e801_hole ; If memory hole we can only use low part |
| |
| mov ax,bx |
| shl eax,16 ; 64K chunks |
| add eax,(16 << 20) ; Add first 16M |
| jmp short got_highmem |
| |
| ; |
| ; INT 15:E801 failed. Try INT 15:88. |
| ; |
| no_e801: |
| mov ah,88h ; Query high memory (oldest) |
| int 15h |
| cmp ax,14*1024 ; Don't trust memory >15M |
| jna e801_hole |
| mov ax,14*1024 |
| e801_hole: |
| and eax,0ffffh |
| shl eax,10 ; Convert from kilobytes |
| add eax,(1 << 20) ; First megabyte |
| got_highmem: |
| %if HIGHMEM_SLOP != 0 |
| sub eax,HIGHMEM_SLOP |
| %endif |
| mov [HighMemSize],eax |
| popad |
| pop es |
| ret ; Done! |
| |
| section .bss |
| alignb 4 |
| E820Buf resd 5 ; INT 15:E820 data buffer |
| E820Mem resd 1 ; Memory detected by E820 |
| E820Max resd 1 ; Is E820 memory capped? |
| HighMemSize resd 1 ; End of memory pointer (bytes) |