1///////////////////////////////////////////////////////////////////////////////
2//
3/// \file       tuklib_physmem.c
4/// \brief      Get the amount of physical memory
5//
6//  Author:     Lasse Collin
7//
8//  This file has been put into the public domain.
9//  You can do whatever you want with this file.
10//
11///////////////////////////////////////////////////////////////////////////////
12
13#include "tuklib_physmem.h"
14
15// We want to use Windows-specific code on Cygwin, which also has memory
16// information available via sysconf(), but on Cygwin 1.5 and older it
17// gives wrong results (from our point of view).
18#if defined(_WIN32) || defined(__CYGWIN__)
19#	ifndef _WIN32_WINNT
20#		define _WIN32_WINNT 0x0500
21#	endif
22#	include <windows.h>
23
24#elif defined(__OS2__)
25#	define INCL_DOSMISC
26#	include <os2.h>
27
28#elif defined(__DJGPP__)
29#	include <dpmi.h>
30
31#elif defined(__VMS)
32#	include <lib$routines.h>
33#	include <syidef.h>
34#	include <ssdef.h>
35
36// AIX
37#elif defined(TUKLIB_PHYSMEM_AIX)
38#	include <sys/systemcfg.h>
39
40#elif defined(TUKLIB_PHYSMEM_SYSCONF)
41#	include <unistd.h>
42
43#elif defined(TUKLIB_PHYSMEM_SYSCTL)
44#	ifdef HAVE_SYS_PARAM_H
45#		include <sys/param.h>
46#	endif
47#	include <sys/sysctl.h>
48
49// Tru64
50#elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
51#	include <sys/sysinfo.h>
52#	include <machine/hal_sysinfo.h>
53
54// HP-UX
55#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
56#	include <sys/param.h>
57#	include <sys/pstat.h>
58
59// IRIX
60#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
61#	include <invent.h>
62
63// This sysinfo() is Linux-specific.
64#elif defined(TUKLIB_PHYSMEM_SYSINFO)
65#	include <sys/sysinfo.h>
66#endif
67
68
69extern uint64_t
70tuklib_physmem(void)
71{
72	uint64_t ret = 0;
73
74#if defined(_WIN32) || defined(__CYGWIN__)
75	if ((GetVersion() & 0xFF) >= 5) {
76		// Windows 2000 and later have GlobalMemoryStatusEx() which
77		// supports reporting values greater than 4 GiB. To keep the
78		// code working also on older Windows versions, use
79		// GlobalMemoryStatusEx() conditionally.
80		HMODULE kernel32 = GetModuleHandle("kernel32.dll");
81		if (kernel32 != NULL) {
82			BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress(
83					kernel32, "GlobalMemoryStatusEx");
84			if (gmse != NULL) {
85				MEMORYSTATUSEX meminfo;
86				meminfo.dwLength = sizeof(meminfo);
87				if (gmse(&meminfo))
88					ret = meminfo.ullTotalPhys;
89			}
90		}
91	}
92
93	if (ret == 0) {
94		// GlobalMemoryStatus() is supported by Windows 95 and later,
95		// so it is fine to link against it unconditionally. Note that
96		// GlobalMemoryStatus() has no return value.
97		MEMORYSTATUS meminfo;
98		meminfo.dwLength = sizeof(meminfo);
99		GlobalMemoryStatus(&meminfo);
100		ret = meminfo.dwTotalPhys;
101	}
102
103#elif defined(__OS2__)
104	unsigned long mem;
105	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
106			&mem, sizeof(mem)) == 0)
107		ret = mem;
108
109#elif defined(__DJGPP__)
110	__dpmi_free_mem_info meminfo;
111	if (__dpmi_get_free_memory_information(&meminfo) == 0
112			&& meminfo.total_number_of_physical_pages
113				!= (unsigned long)-1)
114		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
115
116#elif defined(__VMS)
117	int vms_mem;
118	int val = SYI$_MEMSIZE;
119	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
120		ret = (uint64_t)vms_mem * 8192;
121
122#elif defined(TUKLIB_PHYSMEM_AIX)
123	ret = _system_configuration.physmem;
124
125#elif defined(TUKLIB_PHYSMEM_SYSCONF)
126	const long pagesize = sysconf(_SC_PAGESIZE);
127	const long pages = sysconf(_SC_PHYS_PAGES);
128	if (pagesize != -1 && pages != -1)
129		// According to docs, pagesize * pages can overflow.
130		// Simple case is 32-bit box with 4 GiB or more RAM,
131		// which may report exactly 4 GiB of RAM, and "long"
132		// being 32-bit will overflow. Casting to uint64_t
133		// hopefully avoids overflows in the near future.
134		ret = (uint64_t)pagesize * (uint64_t)pages;
135
136#elif defined(TUKLIB_PHYSMEM_SYSCTL)
137	int name[2] = {
138		CTL_HW,
139#ifdef HW_PHYSMEM64
140		HW_PHYSMEM64
141#else
142		HW_PHYSMEM
143#endif
144	};
145	union {
146		uint32_t u32;
147		uint64_t u64;
148	} mem;
149	size_t mem_ptr_size = sizeof(mem.u64);
150	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
151		// IIRC, 64-bit "return value" is possible on some 64-bit
152		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
153		// so support both.
154		if (mem_ptr_size == sizeof(mem.u64))
155			ret = mem.u64;
156		else if (mem_ptr_size == sizeof(mem.u32))
157			ret = mem.u32;
158	}
159
160#elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
161	// Docs are unclear if "start" is needed, but it doesn't hurt
162	// much to have it.
163	int memkb;
164	int start = 0;
165	if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start)
166			!= -1)
167		ret = (uint64_t)memkb * 1024;
168
169#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
170	struct pst_static pst;
171	if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1)
172		ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size;
173
174#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
175	inv_state_t *st = NULL;
176	if (setinvent_r(&st) != -1) {
177		inventory_t *i;
178		while ((i = getinvent_r(st)) != NULL) {
179			if (i->inv_class == INV_MEMORY
180					&& i->inv_type == INV_MAIN_MB) {
181				ret = (uint64_t)i->inv_state << 20;
182				break;
183			}
184		}
185
186		endinvent_r(st);
187	}
188
189#elif defined(TUKLIB_PHYSMEM_SYSINFO)
190	struct sysinfo si;
191	if (sysinfo(&si) == 0)
192		ret = (uint64_t)si.totalram * si.mem_unit;
193#endif
194
195	return ret;
196}
197