// SPDX-License-Identifier: GPL-2.0-only1/* -*- linux-c -*- ------------------------------------------------------- *2*3* Copyright (C) 1991, 1992 Linus Torvalds4* Copyright 2007 rPath, Inc. - All Rights Reserved5* Copyright 2009 Intel Corporation; author H. Peter Anvin6*7* ----------------------------------------------------------------------- */89/*10* Memory detection code11*/1213#include "boot.h"1415#define SMAP 0x534d4150 /* ASCII "SMAP" */1617static void detect_memory_e820(void)18{19int count = 0;20struct biosregs ireg, oreg;21struct boot_e820_entry *desc = boot_params.e820_table;22static struct boot_e820_entry buf; /* static so it is zeroed */2324initregs(&ireg);25ireg.ax = 0xe820;26ireg.cx = sizeof(buf);27ireg.edx = SMAP;28ireg.di = (size_t)&buf;2930/*31* Note: at least one BIOS is known which assumes that the32* buffer pointed to by one e820 call is the same one as33* the previous call, and only changes modified fields. Therefore,34* we use a temporary buffer and copy the results entry by entry.35*36* This routine deliberately does not try to account for37* ACPI 3+ extended attributes. This is because there are38* BIOSes in the field which report zero for the valid bit for39* all ranges, and we don't currently make any use of the40* other attribute bits. Revisit this if we see the extended41* attribute bits deployed in a meaningful way in the future.42*/4344do {45intcall(0x15, &ireg, &oreg);46ireg.ebx = oreg.ebx; /* for next iteration... */4748/* BIOSes which terminate the chain with CF = 1 as opposed49to %ebx = 0 don't always report the SMAP signature on50the final, failing, probe. */51if (oreg.eflags & X86_EFLAGS_CF)52break;5354/* Some BIOSes stop returning SMAP in the middle of55the search loop. We don't know exactly how the BIOS56screwed up the map at that point, we might have a57partial map, the full map, or complete garbage, so58just return failure. */59if (oreg.eax != SMAP) {60count = 0;61break;62}6364*desc++ = buf;65count++;66} while (ireg.ebx && count < ARRAY_SIZE(boot_params.e820_table));6768boot_params.e820_entries = count;69}7071static void detect_memory_e801(void)72{73struct biosregs ireg, oreg;7475initregs(&ireg);76ireg.ax = 0xe801;77intcall(0x15, &ireg, &oreg);7879if (oreg.eflags & X86_EFLAGS_CF)80return;8182/* Do we really need to do this? */83if (oreg.cx || oreg.dx) {84oreg.ax = oreg.cx;85oreg.bx = oreg.dx;86}8788if (oreg.ax > 15*1024) {89return; /* Bogus! */90} else if (oreg.ax == 15*1024) {91boot_params.alt_mem_k = (oreg.bx << 6) + oreg.ax;92} else {93/*94* This ignores memory above 16MB if we have a memory95* hole there. If someone actually finds a machine96* with a memory hole at 16MB and no support for97* 0E820h they should probably generate a fake e82098* map.99*/100boot_params.alt_mem_k = oreg.ax;101}102}103104static void detect_memory_88(void)105{106struct biosregs ireg, oreg;107108initregs(&ireg);109ireg.ah = 0x88;110intcall(0x15, &ireg, &oreg);111112boot_params.screen_info.ext_mem_k = oreg.ax;113}114115void detect_memory(void)116{117detect_memory_e820();118119detect_memory_e801();120121detect_memory_88();122}123124125