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