xref: /freebsd/contrib/xz/src/common/tuklib_physmem.c (revision 3b35e7ee8de9b0260149a2b77e87a2b9c7a36244)
1*3b35e7eeSXin LI // SPDX-License-Identifier: 0BSD
2*3b35e7eeSXin LI 
381ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
481ad8388SMartin Matuska //
581ad8388SMartin Matuska /// \file       tuklib_physmem.c
681ad8388SMartin Matuska /// \brief      Get the amount of physical memory
781ad8388SMartin Matuska //
881ad8388SMartin Matuska //  Author:     Lasse Collin
981ad8388SMartin Matuska //
1081ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
1181ad8388SMartin Matuska 
1281ad8388SMartin Matuska #include "tuklib_physmem.h"
1381ad8388SMartin Matuska 
1481ad8388SMartin Matuska // We want to use Windows-specific code on Cygwin, which also has memory
1581ad8388SMartin Matuska // information available via sysconf(), but on Cygwin 1.5 and older it
1681ad8388SMartin Matuska // gives wrong results (from our point of view).
1781ad8388SMartin Matuska #if defined(_WIN32) || defined(__CYGWIN__)
1881ad8388SMartin Matuska #	ifndef _WIN32_WINNT
1981ad8388SMartin Matuska #		define _WIN32_WINNT 0x0500
2081ad8388SMartin Matuska #	endif
2181ad8388SMartin Matuska #	include <windows.h>
2281ad8388SMartin Matuska 
2381ad8388SMartin Matuska #elif defined(__OS2__)
2481ad8388SMartin Matuska #	define INCL_DOSMISC
2581ad8388SMartin Matuska #	include <os2.h>
2681ad8388SMartin Matuska 
2781ad8388SMartin Matuska #elif defined(__DJGPP__)
2881ad8388SMartin Matuska #	include <dpmi.h>
2981ad8388SMartin Matuska 
3081ad8388SMartin Matuska #elif defined(__VMS)
3181ad8388SMartin Matuska #	include <lib$routines.h>
3281ad8388SMartin Matuska #	include <syidef.h>
3381ad8388SMartin Matuska #	include <ssdef.h>
3481ad8388SMartin Matuska 
3553200025SRui Paulo #elif defined(AMIGA) || defined(__AROS__)
3653200025SRui Paulo #	define __USE_INLINE__
3753200025SRui Paulo #	include <proto/exec.h>
3853200025SRui Paulo 
39fe50a38eSXin LI #elif defined(__QNX__)
40fe50a38eSXin LI #	include <sys/syspage.h>
41fe50a38eSXin LI #	include <string.h>
42fe50a38eSXin LI 
43e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_AIX)
44e0f0e66dSMartin Matuska #	include <sys/systemcfg.h>
45e0f0e66dSMartin Matuska 
4681ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCONF)
4781ad8388SMartin Matuska #	include <unistd.h>
4881ad8388SMartin Matuska 
4981ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCTL)
5081ad8388SMartin Matuska #	ifdef HAVE_SYS_PARAM_H
5181ad8388SMartin Matuska #		include <sys/param.h>
5281ad8388SMartin Matuska #	endif
5381ad8388SMartin Matuska #	include <sys/sysctl.h>
5481ad8388SMartin Matuska 
55e0f0e66dSMartin Matuska // Tru64
56e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
57e0f0e66dSMartin Matuska #	include <sys/sysinfo.h>
58e0f0e66dSMartin Matuska #	include <machine/hal_sysinfo.h>
59e0f0e66dSMartin Matuska 
60e0f0e66dSMartin Matuska // HP-UX
61e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
62e0f0e66dSMartin Matuska #	include <sys/param.h>
63e0f0e66dSMartin Matuska #	include <sys/pstat.h>
64e0f0e66dSMartin Matuska 
6581ad8388SMartin Matuska // IRIX
6681ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
6781ad8388SMartin Matuska #	include <invent.h>
6881ad8388SMartin Matuska 
6981ad8388SMartin Matuska // This sysinfo() is Linux-specific.
7081ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSINFO)
7181ad8388SMartin Matuska #	include <sys/sysinfo.h>
7281ad8388SMartin Matuska #endif
7381ad8388SMartin Matuska 
7481ad8388SMartin Matuska 
7581ad8388SMartin Matuska extern uint64_t
7681ad8388SMartin Matuska tuklib_physmem(void)
7781ad8388SMartin Matuska {
7881ad8388SMartin Matuska 	uint64_t ret = 0;
7981ad8388SMartin Matuska 
8081ad8388SMartin Matuska #if defined(_WIN32) || defined(__CYGWIN__)
81*3b35e7eeSXin LI 	// This requires Windows 2000 or later.
82*3b35e7eeSXin LI 	MEMORYSTATUSEX meminfo;
83*3b35e7eeSXin LI 	meminfo.dwLength = sizeof(meminfo);
84*3b35e7eeSXin LI 	if (GlobalMemoryStatusEx(&meminfo))
85*3b35e7eeSXin LI 		ret = meminfo.ullTotalPhys;
86*3b35e7eeSXin LI 
87*3b35e7eeSXin LI /*
88*3b35e7eeSXin LI 	// Old version that is compatible with even Win95:
8981ad8388SMartin Matuska 	if ((GetVersion() & 0xFF) >= 5) {
9081ad8388SMartin Matuska 		// Windows 2000 and later have GlobalMemoryStatusEx() which
9181ad8388SMartin Matuska 		// supports reporting values greater than 4 GiB. To keep the
9281ad8388SMartin Matuska 		// code working also on older Windows versions, use
9381ad8388SMartin Matuska 		// GlobalMemoryStatusEx() conditionally.
949e6bbe47SXin LI 		HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
9581ad8388SMartin Matuska 		if (kernel32 != NULL) {
961456f0f9SXin LI 			typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX);
97c917796cSXin LI #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
98c917796cSXin LI #	pragma GCC diagnostic push
99c917796cSXin LI #	pragma GCC diagnostic ignored "-Wcast-function-type"
100c917796cSXin LI #endif
1011456f0f9SXin LI 			gmse_type gmse = (gmse_type)GetProcAddress(
10281ad8388SMartin Matuska 					kernel32, "GlobalMemoryStatusEx");
103c917796cSXin LI #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
104c917796cSXin LI #	pragma GCC diagnostic pop
105c917796cSXin LI #endif
10681ad8388SMartin Matuska 			if (gmse != NULL) {
10781ad8388SMartin Matuska 				MEMORYSTATUSEX meminfo;
10881ad8388SMartin Matuska 				meminfo.dwLength = sizeof(meminfo);
10981ad8388SMartin Matuska 				if (gmse(&meminfo))
11081ad8388SMartin Matuska 					ret = meminfo.ullTotalPhys;
11181ad8388SMartin Matuska 			}
11281ad8388SMartin Matuska 		}
11381ad8388SMartin Matuska 	}
11481ad8388SMartin Matuska 
11581ad8388SMartin Matuska 	if (ret == 0) {
11681ad8388SMartin Matuska 		// GlobalMemoryStatus() is supported by Windows 95 and later,
11781ad8388SMartin Matuska 		// so it is fine to link against it unconditionally. Note that
11881ad8388SMartin Matuska 		// GlobalMemoryStatus() has no return value.
11981ad8388SMartin Matuska 		MEMORYSTATUS meminfo;
12081ad8388SMartin Matuska 		meminfo.dwLength = sizeof(meminfo);
12181ad8388SMartin Matuska 		GlobalMemoryStatus(&meminfo);
12281ad8388SMartin Matuska 		ret = meminfo.dwTotalPhys;
12381ad8388SMartin Matuska 	}
124*3b35e7eeSXin LI */
12581ad8388SMartin Matuska 
12681ad8388SMartin Matuska #elif defined(__OS2__)
12781ad8388SMartin Matuska 	unsigned long mem;
12881ad8388SMartin Matuska 	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
12981ad8388SMartin Matuska 			&mem, sizeof(mem)) == 0)
13081ad8388SMartin Matuska 		ret = mem;
13181ad8388SMartin Matuska 
13281ad8388SMartin Matuska #elif defined(__DJGPP__)
13381ad8388SMartin Matuska 	__dpmi_free_mem_info meminfo;
13481ad8388SMartin Matuska 	if (__dpmi_get_free_memory_information(&meminfo) == 0
13581ad8388SMartin Matuska 			&& meminfo.total_number_of_physical_pages
13681ad8388SMartin Matuska 				!= (unsigned long)-1)
13781ad8388SMartin Matuska 		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
13881ad8388SMartin Matuska 
13981ad8388SMartin Matuska #elif defined(__VMS)
14081ad8388SMartin Matuska 	int vms_mem;
14181ad8388SMartin Matuska 	int val = SYI$_MEMSIZE;
14281ad8388SMartin Matuska 	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
14381ad8388SMartin Matuska 		ret = (uint64_t)vms_mem * 8192;
14481ad8388SMartin Matuska 
14553200025SRui Paulo #elif defined(AMIGA) || defined(__AROS__)
14653200025SRui Paulo 	ret = AvailMem(MEMF_TOTAL);
14753200025SRui Paulo 
148fe50a38eSXin LI #elif defined(__QNX__)
149fe50a38eSXin LI 	const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo);
150fe50a38eSXin LI 	size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry);
151fe50a38eSXin LI 	const char *strings = SYSPAGE_ENTRY(strings)->data;
152fe50a38eSXin LI 
153fe50a38eSXin LI 	for (size_t i = 0; i < count; ++i)
154fe50a38eSXin LI 		if (strcmp(strings + entries[i].name, "ram") == 0)
155fe50a38eSXin LI 			ret += entries[i].end - entries[i].start + 1;
156fe50a38eSXin LI 
157e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_AIX)
158e0f0e66dSMartin Matuska 	ret = _system_configuration.physmem;
159e0f0e66dSMartin Matuska 
16081ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCONF)
16181ad8388SMartin Matuska 	const long pagesize = sysconf(_SC_PAGESIZE);
16281ad8388SMartin Matuska 	const long pages = sysconf(_SC_PHYS_PAGES);
163f99e4a2dSXin LI 	if (pagesize != -1 && pages != -1)
16481ad8388SMartin Matuska 		// According to docs, pagesize * pages can overflow.
16581ad8388SMartin Matuska 		// Simple case is 32-bit box with 4 GiB or more RAM,
16681ad8388SMartin Matuska 		// which may report exactly 4 GiB of RAM, and "long"
16781ad8388SMartin Matuska 		// being 32-bit will overflow. Casting to uint64_t
16881ad8388SMartin Matuska 		// hopefully avoids overflows in the near future.
16981ad8388SMartin Matuska 		ret = (uint64_t)pagesize * (uint64_t)pages;
17081ad8388SMartin Matuska 
17181ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCTL)
17281ad8388SMartin Matuska 	int name[2] = {
17381ad8388SMartin Matuska 		CTL_HW,
17481ad8388SMartin Matuska #ifdef HW_PHYSMEM64
17581ad8388SMartin Matuska 		HW_PHYSMEM64
17681ad8388SMartin Matuska #else
17781ad8388SMartin Matuska 		HW_PHYSMEM
17881ad8388SMartin Matuska #endif
17981ad8388SMartin Matuska 	};
18081ad8388SMartin Matuska 	union {
18181ad8388SMartin Matuska 		uint32_t u32;
18281ad8388SMartin Matuska 		uint64_t u64;
18381ad8388SMartin Matuska 	} mem;
18481ad8388SMartin Matuska 	size_t mem_ptr_size = sizeof(mem.u64);
18581ad8388SMartin Matuska 	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
18681ad8388SMartin Matuska 		// IIRC, 64-bit "return value" is possible on some 64-bit
18781ad8388SMartin Matuska 		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
18881ad8388SMartin Matuska 		// so support both.
18981ad8388SMartin Matuska 		if (mem_ptr_size == sizeof(mem.u64))
19081ad8388SMartin Matuska 			ret = mem.u64;
19181ad8388SMartin Matuska 		else if (mem_ptr_size == sizeof(mem.u32))
19281ad8388SMartin Matuska 			ret = mem.u32;
19381ad8388SMartin Matuska 	}
19481ad8388SMartin Matuska 
195e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
196e0f0e66dSMartin Matuska 	// Docs are unclear if "start" is needed, but it doesn't hurt
197e0f0e66dSMartin Matuska 	// much to have it.
198e0f0e66dSMartin Matuska 	int memkb;
199e0f0e66dSMartin Matuska 	int start = 0;
200e0f0e66dSMartin Matuska 	if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start)
201e0f0e66dSMartin Matuska 			!= -1)
202e0f0e66dSMartin Matuska 		ret = (uint64_t)memkb * 1024;
203e0f0e66dSMartin Matuska 
204e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
205e0f0e66dSMartin Matuska 	struct pst_static pst;
206e0f0e66dSMartin Matuska 	if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1)
207e0f0e66dSMartin Matuska 		ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size;
208e0f0e66dSMartin Matuska 
20981ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
21081ad8388SMartin Matuska 	inv_state_t *st = NULL;
21181ad8388SMartin Matuska 	if (setinvent_r(&st) != -1) {
21281ad8388SMartin Matuska 		inventory_t *i;
21381ad8388SMartin Matuska 		while ((i = getinvent_r(st)) != NULL) {
21481ad8388SMartin Matuska 			if (i->inv_class == INV_MEMORY
21581ad8388SMartin Matuska 					&& i->inv_type == INV_MAIN_MB) {
21681ad8388SMartin Matuska 				ret = (uint64_t)i->inv_state << 20;
21781ad8388SMartin Matuska 				break;
21881ad8388SMartin Matuska 			}
21981ad8388SMartin Matuska 		}
22081ad8388SMartin Matuska 
22181ad8388SMartin Matuska 		endinvent_r(st);
22281ad8388SMartin Matuska 	}
22381ad8388SMartin Matuska 
22481ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSINFO)
22581ad8388SMartin Matuska 	struct sysinfo si;
22681ad8388SMartin Matuska 	if (sysinfo(&si) == 0)
22781ad8388SMartin Matuska 		ret = (uint64_t)si.totalram * si.mem_unit;
22881ad8388SMartin Matuska #endif
22981ad8388SMartin Matuska 
23081ad8388SMartin Matuska 	return ret;
23181ad8388SMartin Matuska }
232