summaryrefslogtreecommitdiff
path: root/core/highmem.inc
blob: 69652b7fa701ceee87074daf0381849d1e7c155a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
;; -----------------------------------------------------------------------
;;
;;   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:
		mov di,E820Buf
		xor ax,ax
		mov cx,12
		rep stosw			; Clear buffer
		mov byte [di-4],01h		; Initial extattr value
		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,24			; ECX <- 24 (size of buffer)
		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
		cmp cl,24
		jb .no_ext_attr
		;
		; Some blithering idiot added a whole new field to E820,
		; completely without regard for its implications...
		;
		test byte [E820Buf+20],1	; AddressRangeEnabled
		jz .not_ram
.no_ext_attr:		
;
; 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!
		;
.not_ram:
		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 6			; 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)