162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- *
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   Copyright (C) 1991, 1992 Linus Torvalds
562306a36Sopenharmony_ci *   Copyright 2007 rPath, Inc. - All Rights Reserved
662306a36Sopenharmony_ci *   Copyright 2009 Intel Corporation; author H. Peter Anvin
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * ----------------------------------------------------------------------- */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * Memory detection code
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "boot.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define SMAP	0x534d4150	/* ASCII "SMAP" */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void detect_memory_e820(void)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	int count = 0;
2162306a36Sopenharmony_ci	struct biosregs ireg, oreg;
2262306a36Sopenharmony_ci	struct boot_e820_entry *desc = boot_params.e820_table;
2362306a36Sopenharmony_ci	static struct boot_e820_entry buf; /* static so it is zeroed */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	initregs(&ireg);
2662306a36Sopenharmony_ci	ireg.ax  = 0xe820;
2762306a36Sopenharmony_ci	ireg.cx  = sizeof(buf);
2862306a36Sopenharmony_ci	ireg.edx = SMAP;
2962306a36Sopenharmony_ci	ireg.di  = (size_t)&buf;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/*
3262306a36Sopenharmony_ci	 * Note: at least one BIOS is known which assumes that the
3362306a36Sopenharmony_ci	 * buffer pointed to by one e820 call is the same one as
3462306a36Sopenharmony_ci	 * the previous call, and only changes modified fields.  Therefore,
3562306a36Sopenharmony_ci	 * we use a temporary buffer and copy the results entry by entry.
3662306a36Sopenharmony_ci	 *
3762306a36Sopenharmony_ci	 * This routine deliberately does not try to account for
3862306a36Sopenharmony_ci	 * ACPI 3+ extended attributes.  This is because there are
3962306a36Sopenharmony_ci	 * BIOSes in the field which report zero for the valid bit for
4062306a36Sopenharmony_ci	 * all ranges, and we don't currently make any use of the
4162306a36Sopenharmony_ci	 * other attribute bits.  Revisit this if we see the extended
4262306a36Sopenharmony_ci	 * attribute bits deployed in a meaningful way in the future.
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	do {
4662306a36Sopenharmony_ci		intcall(0x15, &ireg, &oreg);
4762306a36Sopenharmony_ci		ireg.ebx = oreg.ebx; /* for next iteration... */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		/* BIOSes which terminate the chain with CF = 1 as opposed
5062306a36Sopenharmony_ci		   to %ebx = 0 don't always report the SMAP signature on
5162306a36Sopenharmony_ci		   the final, failing, probe. */
5262306a36Sopenharmony_ci		if (oreg.eflags & X86_EFLAGS_CF)
5362306a36Sopenharmony_ci			break;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		/* Some BIOSes stop returning SMAP in the middle of
5662306a36Sopenharmony_ci		   the search loop.  We don't know exactly how the BIOS
5762306a36Sopenharmony_ci		   screwed up the map at that point, we might have a
5862306a36Sopenharmony_ci		   partial map, the full map, or complete garbage, so
5962306a36Sopenharmony_ci		   just return failure. */
6062306a36Sopenharmony_ci		if (oreg.eax != SMAP) {
6162306a36Sopenharmony_ci			count = 0;
6262306a36Sopenharmony_ci			break;
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		*desc++ = buf;
6662306a36Sopenharmony_ci		count++;
6762306a36Sopenharmony_ci	} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_table));
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	boot_params.e820_entries = count;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void detect_memory_e801(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct biosregs ireg, oreg;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	initregs(&ireg);
7762306a36Sopenharmony_ci	ireg.ax = 0xe801;
7862306a36Sopenharmony_ci	intcall(0x15, &ireg, &oreg);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (oreg.eflags & X86_EFLAGS_CF)
8162306a36Sopenharmony_ci		return;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Do we really need to do this? */
8462306a36Sopenharmony_ci	if (oreg.cx || oreg.dx) {
8562306a36Sopenharmony_ci		oreg.ax = oreg.cx;
8662306a36Sopenharmony_ci		oreg.bx = oreg.dx;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (oreg.ax > 15*1024) {
9062306a36Sopenharmony_ci		return;	/* Bogus! */
9162306a36Sopenharmony_ci	} else if (oreg.ax == 15*1024) {
9262306a36Sopenharmony_ci		boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;
9362306a36Sopenharmony_ci	} else {
9462306a36Sopenharmony_ci		/*
9562306a36Sopenharmony_ci		 * This ignores memory above 16MB if we have a memory
9662306a36Sopenharmony_ci		 * hole there.  If someone actually finds a machine
9762306a36Sopenharmony_ci		 * with a memory hole at 16MB and no support for
9862306a36Sopenharmony_ci		 * 0E820h they should probably generate a fake e820
9962306a36Sopenharmony_ci		 * map.
10062306a36Sopenharmony_ci		 */
10162306a36Sopenharmony_ci		boot_params.alt_mem_k = oreg.ax;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void detect_memory_88(void)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct biosregs ireg, oreg;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	initregs(&ireg);
11062306a36Sopenharmony_ci	ireg.ah = 0x88;
11162306a36Sopenharmony_ci	intcall(0x15, &ireg, &oreg);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	boot_params.screen_info.ext_mem_k = oreg.ax;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_civoid detect_memory(void)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	detect_memory_e820();
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	detect_memory_e801();
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	detect_memory_88();
12362306a36Sopenharmony_ci}
124