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