| 1 | ;; ----------------------------------------------------------------------- |
|---|
| 2 | ;; |
|---|
| 3 | ;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved |
|---|
| 4 | ;; |
|---|
| 5 | ;; This program is free software; you can redistribute it and/or modify |
|---|
| 6 | ;; it under the terms of the GNU General Public License as published by |
|---|
| 7 | ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
|---|
| 8 | ;; Boston MA 02111-1307, USA; either version 2 of the License, or |
|---|
| 9 | ;; (at your option) any later version; incorporated herein by reference. |
|---|
| 10 | ;; |
|---|
| 11 | ;; ----------------------------------------------------------------------- |
|---|
| 12 | |
|---|
| 13 | ;; |
|---|
| 14 | ;; highmem.inc |
|---|
| 15 | ;; |
|---|
| 16 | ;; Probe for the size of high memory. This can be overridden by a |
|---|
| 17 | ;; mem= command on the command line while booting a new kernel. |
|---|
| 18 | ;; |
|---|
| 19 | |
|---|
| 20 | section .text16 |
|---|
| 21 | |
|---|
| 22 | ; |
|---|
| 23 | ; This is set up as a subroutine; it will set up the global variable |
|---|
| 24 | ; HighMemSize. All registers are preserved. |
|---|
| 25 | ; |
|---|
| 26 | highmemsize: |
|---|
| 27 | push es |
|---|
| 28 | pushfd |
|---|
| 29 | pushad |
|---|
| 30 | |
|---|
| 31 | push cs |
|---|
| 32 | pop es |
|---|
| 33 | |
|---|
| 34 | ; |
|---|
| 35 | ; First, try INT 15:E820 (get BIOS memory map) |
|---|
| 36 | ; |
|---|
| 37 | ; Note: we may have to scan this multiple times, because some (daft) BIOSes |
|---|
| 38 | ; report main memory as multiple contiguous ranges... |
|---|
| 39 | ; |
|---|
| 40 | get_e820: |
|---|
| 41 | mov dword [E820Max],-(1 << 20) ; Max amount of high memory |
|---|
| 42 | mov dword [E820Mem],(1 << 20) ; End of detected high memory |
|---|
| 43 | .start_over: |
|---|
| 44 | mov di,E820Buf |
|---|
| 45 | xor ax,ax |
|---|
| 46 | mov cx,10 |
|---|
| 47 | rep stosw ; Clear buffer |
|---|
| 48 | xor ebx,ebx ; Start with first record |
|---|
| 49 | jmp short .do_e820 ; Skip "at end" check first time! |
|---|
| 50 | .int_loop: and ebx,ebx ; If we're back at beginning... |
|---|
| 51 | jz .e820_done ; ... we're done |
|---|
| 52 | .do_e820: mov eax,0000E820h |
|---|
| 53 | mov edx,534D4150h ; "SMAP" backwards |
|---|
| 54 | xor ecx,ecx |
|---|
| 55 | mov cl,20 ; ECX <- 20 (size of buffer) |
|---|
| 56 | mov di,E820Buf |
|---|
| 57 | int 15h |
|---|
| 58 | jnc .no_carry |
|---|
| 59 | ; If carry, ebx == 0 means error, ebx != 0 means we're done |
|---|
| 60 | and ebx,ebx |
|---|
| 61 | jnz .e820_done |
|---|
| 62 | jmp no_e820 |
|---|
| 63 | .no_carry: |
|---|
| 64 | cmp eax,534D4150h |
|---|
| 65 | jne no_e820 |
|---|
| 66 | cmp cx,20 |
|---|
| 67 | jb no_e820 |
|---|
| 68 | |
|---|
| 69 | ; |
|---|
| 70 | ; Look for a memory block starting at <= 1 MB and continuing upward |
|---|
| 71 | ; |
|---|
| 72 | cmp dword [E820Buf+4], byte 0 |
|---|
| 73 | ja .int_loop ; Start >= 4 GB? |
|---|
| 74 | mov eax, [E820Buf] |
|---|
| 75 | cmp dword [E820Buf+16],1 |
|---|
| 76 | je .is_ram ; Is it memory? |
|---|
| 77 | ; |
|---|
| 78 | ; Non-memory range. Remember this as a limit; some BIOSes get the length |
|---|
| 79 | ; of primary RAM incorrect! |
|---|
| 80 | ; |
|---|
| 81 | .not_ram: |
|---|
| 82 | cmp eax, (1 << 20) |
|---|
| 83 | jb .int_loop ; Starts in lowmem region |
|---|
| 84 | cmp eax,[E820Max] |
|---|
| 85 | jae .int_loop ; Already above limit |
|---|
| 86 | mov [E820Max],eax ; Set limit |
|---|
| 87 | jmp .int_loop |
|---|
| 88 | |
|---|
| 89 | .is_ram: |
|---|
| 90 | cmp eax,[E820Mem] |
|---|
| 91 | ja .int_loop ; Not contiguous with our starting point |
|---|
| 92 | add eax,[E820Buf+8] |
|---|
| 93 | jc .overflow |
|---|
| 94 | cmp dword [E820Buf+12],0 |
|---|
| 95 | je .nooverflow |
|---|
| 96 | .overflow: |
|---|
| 97 | or eax,-1 |
|---|
| 98 | .nooverflow: |
|---|
| 99 | cmp eax,[E820Mem] |
|---|
| 100 | jbe .int_loop ; All is below our baseline |
|---|
| 101 | mov [E820Mem],eax |
|---|
| 102 | jmp .start_over ; Start over in case we find an adjacent range |
|---|
| 103 | |
|---|
| 104 | .e820_done: |
|---|
| 105 | mov eax,[E820Mem] |
|---|
| 106 | cmp eax,[E820Max] |
|---|
| 107 | jna .not_limited |
|---|
| 108 | mov eax,[E820Max] |
|---|
| 109 | .not_limited: |
|---|
| 110 | cmp eax,(1 << 20) |
|---|
| 111 | ja got_highmem ; Did we actually find memory? |
|---|
| 112 | ; otherwise fall through |
|---|
| 113 | |
|---|
| 114 | ; |
|---|
| 115 | ; INT 15:E820 failed. Try INT 15:E801. |
|---|
| 116 | ; |
|---|
| 117 | no_e820: |
|---|
| 118 | mov ax,0e801h ; Query high memory (semi-recent) |
|---|
| 119 | int 15h |
|---|
| 120 | jc no_e801 |
|---|
| 121 | cmp ax,3c00h |
|---|
| 122 | ja no_e801 ; > 3C00h something's wrong with this call |
|---|
| 123 | jb e801_hole ; If memory hole we can only use low part |
|---|
| 124 | |
|---|
| 125 | mov ax,bx |
|---|
| 126 | shl eax,16 ; 64K chunks |
|---|
| 127 | add eax,(16 << 20) ; Add first 16M |
|---|
| 128 | jmp short got_highmem |
|---|
| 129 | |
|---|
| 130 | ; |
|---|
| 131 | ; INT 15:E801 failed. Try INT 15:88. |
|---|
| 132 | ; |
|---|
| 133 | no_e801: |
|---|
| 134 | mov ah,88h ; Query high memory (oldest) |
|---|
| 135 | int 15h |
|---|
| 136 | cmp ax,14*1024 ; Don't trust memory >15M |
|---|
| 137 | jna e801_hole |
|---|
| 138 | mov ax,14*1024 |
|---|
| 139 | e801_hole: |
|---|
| 140 | and eax,0ffffh |
|---|
| 141 | shl eax,10 ; Convert from kilobytes |
|---|
| 142 | add eax,(1 << 20) ; First megabyte |
|---|
| 143 | got_highmem: |
|---|
| 144 | %if HIGHMEM_SLOP != 0 |
|---|
| 145 | sub eax,HIGHMEM_SLOP |
|---|
| 146 | %endif |
|---|
| 147 | mov [HighMemSize],eax |
|---|
| 148 | popad |
|---|
| 149 | popfd |
|---|
| 150 | pop es |
|---|
| 151 | ret ; Done! |
|---|
| 152 | |
|---|
| 153 | section .bss16 |
|---|
| 154 | alignb 4 |
|---|
| 155 | E820Buf resd 5 ; INT 15:E820 data buffer |
|---|
| 156 | E820Mem resd 1 ; Memory detected by E820 |
|---|
| 157 | E820Max resd 1 ; Is E820 memory capped? |
|---|
| 158 | ; HighMemSize is defined in com32.inc |
|---|