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) {
89312517Sdelphij			typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX);
90312517Sdelphij			gmse_type gmse = (gmse_type)GetProcAddress(
91207753Smm					kernel32, "GlobalMemoryStatusEx");
92207753Smm			if (gmse != NULL) {
93207753Smm				MEMORYSTATUSEX meminfo;
94207753Smm				meminfo.dwLength = sizeof(meminfo);
95207753Smm				if (gmse(&meminfo))
96207753Smm					ret = meminfo.ullTotalPhys;
97207753Smm			}
98207753Smm		}
99207753Smm	}
100207753Smm
101207753Smm	if (ret == 0) {
102207753Smm		// GlobalMemoryStatus() is supported by Windows 95 and later,
103207753Smm		// so it is fine to link against it unconditionally. Note that
104207753Smm		// GlobalMemoryStatus() has no return value.
105207753Smm		MEMORYSTATUS meminfo;
106207753Smm		meminfo.dwLength = sizeof(meminfo);
107207753Smm		GlobalMemoryStatus(&meminfo);
108207753Smm		ret = meminfo.dwTotalPhys;
109207753Smm	}
110207753Smm
111207753Smm#elif defined(__OS2__)
112207753Smm	unsigned long mem;
113207753Smm	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
114207753Smm			&mem, sizeof(mem)) == 0)
115207753Smm		ret = mem;
116207753Smm
117207753Smm#elif defined(__DJGPP__)
118207753Smm	__dpmi_free_mem_info meminfo;
119207753Smm	if (__dpmi_get_free_memory_information(&meminfo) == 0
120207753Smm			&& meminfo.total_number_of_physical_pages
121207753Smm				!= (unsigned long)-1)
122207753Smm		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
123207753Smm
124207753Smm#elif defined(__VMS)
125207753Smm	int vms_mem;
126207753Smm	int val = SYI$_MEMSIZE;
127207753Smm	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
128207753Smm		ret = (uint64_t)vms_mem * 8192;
129207753Smm
130278433Srpaulo#elif defined(AMIGA) || defined(__AROS__)
131278433Srpaulo	ret = AvailMem(MEMF_TOTAL);
132278433Srpaulo
133291125Sdelphij#elif defined(__QNX__)
134291125Sdelphij	const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo);
135291125Sdelphij	size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry);
136291125Sdelphij	const char *strings = SYSPAGE_ENTRY(strings)->data;
137291125Sdelphij
138291125Sdelphij	for (size_t i = 0; i < count; ++i)
139291125Sdelphij		if (strcmp(strings + entries[i].name, "ram") == 0)
140291125Sdelphij			ret += entries[i].end - entries[i].start + 1;
141291125Sdelphij
142213700Smm#elif defined(TUKLIB_PHYSMEM_AIX)
143213700Smm	ret = _system_configuration.physmem;
144213700Smm
145207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF)
146207753Smm	const long pagesize = sysconf(_SC_PAGESIZE);
147207753Smm	const long pages = sysconf(_SC_PHYS_PAGES);
148360523Sdelphij	if (pagesize != -1 && pages != -1)
149207753Smm		// According to docs, pagesize * pages can overflow.
150207753Smm		// Simple case is 32-bit box with 4 GiB or more RAM,
151207753Smm		// which may report exactly 4 GiB of RAM, and "long"
152207753Smm		// being 32-bit will overflow. Casting to uint64_t
153207753Smm		// hopefully avoids overflows in the near future.
154207753Smm		ret = (uint64_t)pagesize * (uint64_t)pages;
155207753Smm
156207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL)
157207753Smm	int name[2] = {
158207753Smm		CTL_HW,
159207753Smm#ifdef HW_PHYSMEM64
160207753Smm		HW_PHYSMEM64
161207753Smm#else
162207753Smm		HW_PHYSMEM
163207753Smm#endif
164207753Smm	};
165207753Smm	union {
166207753Smm		uint32_t u32;
167207753Smm		uint64_t u64;
168207753Smm	} mem;
169207753Smm	size_t mem_ptr_size = sizeof(mem.u64);
170207753Smm	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
171207753Smm		// IIRC, 64-bit "return value" is possible on some 64-bit
172207753Smm		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
173207753Smm		// so support both.
174207753Smm		if (mem_ptr_size == sizeof(mem.u64))
175207753Smm			ret = mem.u64;
176207753Smm		else if (mem_ptr_size == sizeof(mem.u32))
177207753Smm			ret = mem.u32;
178207753Smm	}
179207753Smm
180213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
181213700Smm	// Docs are unclear if "start" is needed, but it doesn't hurt
182213700Smm	// much to have it.
183213700Smm	int memkb;
184213700Smm	int start = 0;
185213700Smm	if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start)
186213700Smm			!= -1)
187213700Smm		ret = (uint64_t)memkb * 1024;
188213700Smm
189213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
190213700Smm	struct pst_static pst;
191213700Smm	if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1)
192213700Smm		ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size;
193213700Smm
194207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
195207753Smm	inv_state_t *st = NULL;
196207753Smm	if (setinvent_r(&st) != -1) {
197207753Smm		inventory_t *i;
198207753Smm		while ((i = getinvent_r(st)) != NULL) {
199207753Smm			if (i->inv_class == INV_MEMORY
200207753Smm					&& i->inv_type == INV_MAIN_MB) {
201207753Smm				ret = (uint64_t)i->inv_state << 20;
202207753Smm				break;
203207753Smm			}
204207753Smm		}
205207753Smm
206207753Smm		endinvent_r(st);
207207753Smm	}
208207753Smm
209207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO)
210207753Smm	struct sysinfo si;
211207753Smm	if (sysinfo(&si) == 0)
212207753Smm		ret = (uint64_t)si.totalram * si.mem_unit;
213207753Smm#endif
214207753Smm
215207753Smm	return ret;
216207753Smm}
217