tuklib_physmem.c revision 291125
1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       tuklib_physmem.c
4207753Smm/// \brief      Get the amount of physical memory
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "tuklib_physmem.h"
14207753Smm
15207753Smm// We want to use Windows-specific code on Cygwin, which also has memory
16207753Smm// information available via sysconf(), but on Cygwin 1.5 and older it
17207753Smm// gives wrong results (from our point of view).
18207753Smm#if defined(_WIN32) || defined(__CYGWIN__)
19207753Smm#	ifndef _WIN32_WINNT
20207753Smm#		define _WIN32_WINNT 0x0500
21207753Smm#	endif
22207753Smm#	include <windows.h>
23207753Smm
24207753Smm#elif defined(__OS2__)
25207753Smm#	define INCL_DOSMISC
26207753Smm#	include <os2.h>
27207753Smm
28207753Smm#elif defined(__DJGPP__)
29207753Smm#	include <dpmi.h>
30207753Smm
31207753Smm#elif defined(__VMS)
32207753Smm#	include <lib$routines.h>
33207753Smm#	include <syidef.h>
34207753Smm#	include <ssdef.h>
35207753Smm
36278433Srpaulo#elif defined(AMIGA) || defined(__AROS__)
37278433Srpaulo#	define __USE_INLINE__
38278433Srpaulo#	include <proto/exec.h>
39278433Srpaulo
40291125Sdelphij#elif defined(__QNX__)
41291125Sdelphij#	include <sys/syspage.h>
42291125Sdelphij#	include <string.h>
43291125Sdelphij
44213700Smm#elif defined(TUKLIB_PHYSMEM_AIX)
45213700Smm#	include <sys/systemcfg.h>
46213700Smm
47207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF)
48207753Smm#	include <unistd.h>
49207753Smm
50207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL)
51207753Smm#	ifdef HAVE_SYS_PARAM_H
52207753Smm#		include <sys/param.h>
53207753Smm#	endif
54207753Smm#	include <sys/sysctl.h>
55207753Smm
56213700Smm// Tru64
57213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
58213700Smm#	include <sys/sysinfo.h>
59213700Smm#	include <machine/hal_sysinfo.h>
60213700Smm
61213700Smm// HP-UX
62213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
63213700Smm#	include <sys/param.h>
64213700Smm#	include <sys/pstat.h>
65213700Smm
66207753Smm// IRIX
67207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
68207753Smm#	include <invent.h>
69207753Smm
70207753Smm// This sysinfo() is Linux-specific.
71207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO)
72207753Smm#	include <sys/sysinfo.h>
73207753Smm#endif
74207753Smm
75207753Smm
76207753Smmextern uint64_t
77207753Smmtuklib_physmem(void)
78207753Smm{
79207753Smm	uint64_t ret = 0;
80207753Smm
81207753Smm#if defined(_WIN32) || defined(__CYGWIN__)
82207753Smm	if ((GetVersion() & 0xFF) >= 5) {
83207753Smm		// Windows 2000 and later have GlobalMemoryStatusEx() which
84207753Smm		// supports reporting values greater than 4 GiB. To keep the
85207753Smm		// code working also on older Windows versions, use
86207753Smm		// GlobalMemoryStatusEx() conditionally.
87207753Smm		HMODULE kernel32 = GetModuleHandle("kernel32.dll");
88207753Smm		if (kernel32 != NULL) {
89207753Smm			BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress(
90207753Smm					kernel32, "GlobalMemoryStatusEx");
91207753Smm			if (gmse != NULL) {
92207753Smm				MEMORYSTATUSEX meminfo;
93207753Smm				meminfo.dwLength = sizeof(meminfo);
94207753Smm				if (gmse(&meminfo))
95207753Smm					ret = meminfo.ullTotalPhys;
96207753Smm			}
97207753Smm		}
98207753Smm	}
99207753Smm
100207753Smm	if (ret == 0) {
101207753Smm		// GlobalMemoryStatus() is supported by Windows 95 and later,
102207753Smm		// so it is fine to link against it unconditionally. Note that
103207753Smm		// GlobalMemoryStatus() has no return value.
104207753Smm		MEMORYSTATUS meminfo;
105207753Smm		meminfo.dwLength = sizeof(meminfo);
106207753Smm		GlobalMemoryStatus(&meminfo);
107207753Smm		ret = meminfo.dwTotalPhys;
108207753Smm	}
109207753Smm
110207753Smm#elif defined(__OS2__)
111207753Smm	unsigned long mem;
112207753Smm	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
113207753Smm			&mem, sizeof(mem)) == 0)
114207753Smm		ret = mem;
115207753Smm
116207753Smm#elif defined(__DJGPP__)
117207753Smm	__dpmi_free_mem_info meminfo;
118207753Smm	if (__dpmi_get_free_memory_information(&meminfo) == 0
119207753Smm			&& meminfo.total_number_of_physical_pages
120207753Smm				!= (unsigned long)-1)
121207753Smm		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
122207753Smm
123207753Smm#elif defined(__VMS)
124207753Smm	int vms_mem;
125207753Smm	int val = SYI$_MEMSIZE;
126207753Smm	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
127207753Smm		ret = (uint64_t)vms_mem * 8192;
128207753Smm
129278433Srpaulo#elif defined(AMIGA) || defined(__AROS__)
130278433Srpaulo	ret = AvailMem(MEMF_TOTAL);
131278433Srpaulo
132291125Sdelphij#elif defined(__QNX__)
133291125Sdelphij	const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo);
134291125Sdelphij	size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry);
135291125Sdelphij	const char *strings = SYSPAGE_ENTRY(strings)->data;
136291125Sdelphij
137291125Sdelphij	for (size_t i = 0; i < count; ++i)
138291125Sdelphij		if (strcmp(strings + entries[i].name, "ram") == 0)
139291125Sdelphij			ret += entries[i].end - entries[i].start + 1;
140291125Sdelphij
141213700Smm#elif defined(TUKLIB_PHYSMEM_AIX)
142213700Smm	ret = _system_configuration.physmem;
143213700Smm
144207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF)
145207753Smm	const long pagesize = sysconf(_SC_PAGESIZE);
146207753Smm	const long pages = sysconf(_SC_PHYS_PAGES);
147213700Smm	if (pagesize != -1 && pages != -1)
148207753Smm		// According to docs, pagesize * pages can overflow.
149207753Smm		// Simple case is 32-bit box with 4 GiB or more RAM,
150207753Smm		// which may report exactly 4 GiB of RAM, and "long"
151207753Smm		// being 32-bit will overflow. Casting to uint64_t
152207753Smm		// hopefully avoids overflows in the near future.
153207753Smm		ret = (uint64_t)pagesize * (uint64_t)pages;
154207753Smm
155207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL)
156207753Smm	int name[2] = {
157207753Smm		CTL_HW,
158207753Smm#ifdef HW_PHYSMEM64
159207753Smm		HW_PHYSMEM64
160207753Smm#else
161207753Smm		HW_PHYSMEM
162207753Smm#endif
163207753Smm	};
164207753Smm	union {
165207753Smm		uint32_t u32;
166207753Smm		uint64_t u64;
167207753Smm	} mem;
168207753Smm	size_t mem_ptr_size = sizeof(mem.u64);
169207753Smm	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
170207753Smm		// IIRC, 64-bit "return value" is possible on some 64-bit
171207753Smm		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
172207753Smm		// so support both.
173207753Smm		if (mem_ptr_size == sizeof(mem.u64))
174207753Smm			ret = mem.u64;
175207753Smm		else if (mem_ptr_size == sizeof(mem.u32))
176207753Smm			ret = mem.u32;
177207753Smm	}
178207753Smm
179213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
180213700Smm	// Docs are unclear if "start" is needed, but it doesn't hurt
181213700Smm	// much to have it.
182213700Smm	int memkb;
183213700Smm	int start = 0;
184213700Smm	if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start)
185213700Smm			!= -1)
186213700Smm		ret = (uint64_t)memkb * 1024;
187213700Smm
188213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
189213700Smm	struct pst_static pst;
190213700Smm	if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1)
191213700Smm		ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size;
192213700Smm
193207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
194207753Smm	inv_state_t *st = NULL;
195207753Smm	if (setinvent_r(&st) != -1) {
196207753Smm		inventory_t *i;
197207753Smm		while ((i = getinvent_r(st)) != NULL) {
198207753Smm			if (i->inv_class == INV_MEMORY
199207753Smm					&& i->inv_type == INV_MAIN_MB) {
200207753Smm				ret = (uint64_t)i->inv_state << 20;
201207753Smm				break;
202207753Smm			}
203207753Smm		}
204207753Smm
205207753Smm		endinvent_r(st);
206207753Smm	}
207207753Smm
208207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO)
209207753Smm	struct sysinfo si;
210207753Smm	if (sysinfo(&si) == 0)
211207753Smm		ret = (uint64_t)si.totalram * si.mem_unit;
212207753Smm#endif
213207753Smm
214207753Smm	return ret;
215207753Smm}
216