aboutsummaryrefslogtreecommitdiffstats
path: root/com32/gpllib/memory.c
blob: 6c6e351c797a32a404058026ae6a212c289aee35 (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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* ----------------------------------------------------------------------- *
 *
 *   Copyright 2009 Pierre-Alexandre Meyer
 *
 *   Some parts borrowed from meminfo.c32:
 *
 *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
 *
 *   Some parts borrowed from Linux:
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *   Copyright 2009 Intel Corporation; author H. Peter Anvin
 *
 *   Interrupt list from Ralf Brown (http://www.cs.cmu.edu/~ralf/files.html)
 *
 *   This file is part of Syslinux, and is made available under
 *   the terms of the GNU General Public License version 2.
 *
 * ----------------------------------------------------------------------- */

#include <stdint.h>
#include <com32.h>
#include <string.h>
#include <memory.h>

const char * const e820_types[] = {
	"usable",
	"reserved",
	"ACPI reclaim",
	"ACPI NVS",
	"unusable",
};

struct e820_ext_entry {
	struct e820entry std;
	uint32_t ext_flags;
} __attribute__((packed));

#define SMAP	0x534d4150	/* ASCII "SMAP" */

void get_type(int type, char *type_ptr, int type_ptr_sz)
{
	unsigned int real_type = type - 1;
	if (real_type < sizeof(e820_types)/sizeof(e820_types[0]))
		strncpy(type_ptr, e820_types[real_type], type_ptr_sz);
}

/**
 *INT 15 - newer BIOSes - GET SYSTEM MEMORY MAP
 *	AX = E820h
 *	EAX = 0000E820h
 *	EDX = 534D4150h ('SMAP')
 *	EBX = continuation value or 00000000h to start at beginning of map
 *	ECX = size of buffer for result, in bytes (should be >= 20 bytes)
 *	ES:DI -> buffer for result (see #00581)
 *
 * Return: CF clear if successful
 *	    EAX = 534D4150h ('SMAP')
 *	    ES:DI buffer filled
 *	    EBX = next offset from which to copy or 00000000h if all done
 *	    ECX = actual length returned in bytes
 *	CF set on error
 *	    AH = error code (86h) (see #00496 at INT 15/AH=80h)
 *
 * Notes: originally introduced with the Phoenix BIOS v4.0, this function is
 *	  now supported by most newer BIOSes, since various versions of Windows
 *	  call it to find out about the system memory
 *	a maximum of 20 bytes will be transferred at one time, even if ECX is
 *	  higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the
 *	  value of ECX on entry, and always copy 20 bytes
 *	some BIOSes expect the high word of EAX to be clear on entry, i.e.
 *	  EAX=0000E820h
 *	if this function is not supported, an application should fall back
 *	  to AX=E802h, AX=E801h, and then AH=88h
 *	the BIOS is permitted to return a nonzero continuation value in EBX
 *	  and indicate that the end of the list has already been reached by
 *	  returning with CF set on the next iteration
 *	this function will return base memory and ISA/PCI memory contiguous
 *	  with base memory as normal memory ranges; it will indicate
 *	  chipset-defined address holes which are not in use and motherboard
 *	  memory-mapped devices, and all occurrences of the system BIOS as
 *	  reserved; standard PC address ranges will not be reported
 **/
void detect_memory_e820(struct e820entry *desc, int size_map, int *size_found)
{
	int count = 0;
	static struct e820_ext_entry buf; /* static so it is zeroed */

	com32sys_t ireg, oreg;
	memset(&ireg, 0, sizeof ireg);

	ireg.eax.w[0] = 0xe820;
	ireg.edx.l    = SMAP;
	ireg.ecx.l    = sizeof(struct e820_ext_entry);
	ireg.edi.w[0] = OFFS(__com32.cs_bounce);
	ireg.es       = SEG(__com32.cs_bounce);

	/*
	 * Set this here so that if the BIOS doesn't change this field
	 * but still doesn't change %ecx, we're still okay...
	 */
	memset(&buf, 0, sizeof buf);
	buf.ext_flags = 1;

	do {
		memcpy(__com32.cs_bounce, &buf, sizeof buf);

		/* Important: %edx and %esi are clobbered by some BIOSes,
		   so they must be either used for the error output
		   or explicitly marked clobbered.  Given that, assume there
		   is something out there clobbering %ebp and %edi, too. */
		__intcall(0x15, &ireg, &oreg);

		/* Some BIOSes stop returning SMAP in the middle of
		   the search loop.  We don't know exactly how the BIOS
		   screwed up the map at that point, we might have a
		   partial map, the full map, or complete garbage, so
		   just return failure. */
		if (oreg.eax.l != SMAP) {
			count = 0;
			break;
		}

		if (oreg.eflags.l & EFLAGS_CF ||
		    oreg.ecx.l < 20)
			break;

		memcpy(&buf, __com32.cs_bounce, sizeof buf);

		/*
		 * ACPI 3.0 added the extended flags support.  If bit 0
		 * in the extended flags is zero, we're supposed to simply
		 * ignore the entry -- a backwards incompatible change!
		 */
		if (oreg.ecx.l > 20 && !(buf.ext_flags & 1))
			continue;

		memcpy(&desc[count], &buf, sizeof buf);
		count++;

		/* Set continuation value */
		ireg.ebx.l = oreg.ebx.l;
	} while (ireg.ebx.l && count < size_map);

	*size_found = count;
}

/**
 * detect_memory_e801
 *
 *INT 15 - Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
 *	AX = E801h
 *
 * Return: CF clear if successful
 *	    AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
 *	    BX = extended memory above 16M, in 64K blocks
 *	    CX = configured memory 1M to 16M, in K
 *	    DX = configured memory above 16M, in 64K blocks
 *	CF set on error
 *
 * Notes: supported by the A03 level (6/14/94) and later XPS P90 BIOSes, as well
 *	as the Compaq Contura, 3/8/93 DESKPRO/i, and 7/26/93 LTE Lite 386 ROM
 *	BIOS
 *	supported by AMI BIOSes dated 8/23/94 or later
 *	on some systems, the BIOS returns AX=BX=0000h; in this case, use CX
 *	  and DX instead of AX and BX
 *	this interface is used by Windows NT 3.1, OS/2 v2.11/2.20, and is
 *	  used as a fall-back by newer versions if AX=E820h is not supported
 *	this function is not used by MS-DOS 6.0 HIMEM.SYS when an EISA machine
 *	  (for example with parameter /EISA) (see also MEM F000h:FFD9h), or no
 *	  Compaq machine was detected, or parameter /NOABOVE16 was given.
 **/
int detect_memory_e801(int* mem_size_below_16, int* mem_size_above_16)
{
	com32sys_t ireg, oreg;
	memset(&ireg, 0, sizeof ireg);

	ireg.eax.w[0] = 0xe801;

	__intcall(0x15, &ireg, &oreg);

	if (oreg.eflags.l & EFLAGS_CF)
		return -1;

	if (oreg.eax.w[0] > 0x3c00)
		return -1;	/* Bogus! */

	/* Linux seems to use ecx and edx by default if they are defined */
	if (oreg.eax.w[0] || oreg.eax.w[0]) {
		oreg.eax.w[0] = oreg.ecx.w[0];
		oreg.ebx.w[0] = oreg.edx.w[0];
	}

	*mem_size_below_16 = oreg.eax.w[0]; /* 1K blocks */
	*mem_size_above_16 = oreg.ebx.w[0]; /* 64K blocks */

	return 0;
}

int detect_memory_88(int* mem_size)
{
	com32sys_t ireg, oreg;
	memset(&ireg, 0, sizeof ireg);

	ireg.eax.w[0] = 0x8800;

	__intcall(0x15, &ireg, &oreg);

	if (oreg.eflags.l & EFLAGS_CF)
		return -1;

	*mem_size = oreg.eax.w[0];
	return 0;
}