tuklib_physmem.c revision 207753
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
36207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF)
37207753Smm#	include <unistd.h>
38207753Smm
39207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL)
40207753Smm#	ifdef HAVE_SYS_PARAM_H
41207753Smm#		include <sys/param.h>
42207753Smm#	endif
43207753Smm#	include <sys/sysctl.h>
44207753Smm
45207753Smm// IRIX
46207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
47207753Smm#	include <invent.h>
48207753Smm
49207753Smm// This sysinfo() is Linux-specific.
50207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO)
51207753Smm#	include <sys/sysinfo.h>
52207753Smm#endif
53207753Smm
54207753Smm
55207753Smmextern uint64_t
56207753Smmtuklib_physmem(void)
57207753Smm{
58207753Smm	uint64_t ret = 0;
59207753Smm
60207753Smm#if defined(_WIN32) || defined(__CYGWIN__)
61207753Smm	if ((GetVersion() & 0xFF) >= 5) {
62207753Smm		// Windows 2000 and later have GlobalMemoryStatusEx() which
63207753Smm		// supports reporting values greater than 4 GiB. To keep the
64207753Smm		// code working also on older Windows versions, use
65207753Smm		// GlobalMemoryStatusEx() conditionally.
66207753Smm		HMODULE kernel32 = GetModuleHandle("kernel32.dll");
67207753Smm		if (kernel32 != NULL) {
68207753Smm			BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress(
69207753Smm					kernel32, "GlobalMemoryStatusEx");
70207753Smm			if (gmse != NULL) {
71207753Smm				MEMORYSTATUSEX meminfo;
72207753Smm				meminfo.dwLength = sizeof(meminfo);
73207753Smm				if (gmse(&meminfo))
74207753Smm					ret = meminfo.ullTotalPhys;
75207753Smm			}
76207753Smm		}
77207753Smm	}
78207753Smm
79207753Smm	if (ret == 0) {
80207753Smm		// GlobalMemoryStatus() is supported by Windows 95 and later,
81207753Smm		// so it is fine to link against it unconditionally. Note that
82207753Smm		// GlobalMemoryStatus() has no return value.
83207753Smm		MEMORYSTATUS meminfo;
84207753Smm		meminfo.dwLength = sizeof(meminfo);
85207753Smm		GlobalMemoryStatus(&meminfo);
86207753Smm		ret = meminfo.dwTotalPhys;
87207753Smm	}
88207753Smm
89207753Smm#elif defined(__OS2__)
90207753Smm	unsigned long mem;
91207753Smm	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
92207753Smm			&mem, sizeof(mem)) == 0)
93207753Smm		ret = mem;
94207753Smm
95207753Smm#elif defined(__DJGPP__)
96207753Smm	__dpmi_free_mem_info meminfo;
97207753Smm	if (__dpmi_get_free_memory_information(&meminfo) == 0
98207753Smm			&& meminfo.total_number_of_physical_pages
99207753Smm				!= (unsigned long)-1)
100207753Smm		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
101207753Smm
102207753Smm#elif defined(__VMS)
103207753Smm	int vms_mem;
104207753Smm	int val = SYI$_MEMSIZE;
105207753Smm	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
106207753Smm		ret = (uint64_t)vms_mem * 8192;
107207753Smm
108207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF)
109207753Smm	const long pagesize = sysconf(_SC_PAGESIZE);
110207753Smm	const long pages = sysconf(_SC_PHYS_PAGES);
111207753Smm	if (pagesize != -1 || pages != -1)
112207753Smm		// According to docs, pagesize * pages can overflow.
113207753Smm		// Simple case is 32-bit box with 4 GiB or more RAM,
114207753Smm		// which may report exactly 4 GiB of RAM, and "long"
115207753Smm		// being 32-bit will overflow. Casting to uint64_t
116207753Smm		// hopefully avoids overflows in the near future.
117207753Smm		ret = (uint64_t)pagesize * (uint64_t)pages;
118207753Smm
119207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL)
120207753Smm	int name[2] = {
121207753Smm		CTL_HW,
122207753Smm#ifdef HW_PHYSMEM64
123207753Smm		HW_PHYSMEM64
124207753Smm#else
125207753Smm		HW_PHYSMEM
126207753Smm#endif
127207753Smm	};
128207753Smm	union {
129207753Smm		uint32_t u32;
130207753Smm		uint64_t u64;
131207753Smm	} mem;
132207753Smm	size_t mem_ptr_size = sizeof(mem.u64);
133207753Smm	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
134207753Smm		// IIRC, 64-bit "return value" is possible on some 64-bit
135207753Smm		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
136207753Smm		// so support both.
137207753Smm		if (mem_ptr_size == sizeof(mem.u64))
138207753Smm			ret = mem.u64;
139207753Smm		else if (mem_ptr_size == sizeof(mem.u32))
140207753Smm			ret = mem.u32;
141207753Smm	}
142207753Smm
143207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
144207753Smm	inv_state_t *st = NULL;
145207753Smm	if (setinvent_r(&st) != -1) {
146207753Smm		inventory_t *i;
147207753Smm		while ((i = getinvent_r(st)) != NULL) {
148207753Smm			if (i->inv_class == INV_MEMORY
149207753Smm					&& i->inv_type == INV_MAIN_MB) {
150207753Smm				ret = (uint64_t)i->inv_state << 20;
151207753Smm				break;
152207753Smm			}
153207753Smm		}
154207753Smm
155207753Smm		endinvent_r(st);
156207753Smm	}
157207753Smm
158207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO)
159207753Smm	struct sysinfo si;
160207753Smm	if (sysinfo(&si) == 0)
161207753Smm		ret = (uint64_t)si.totalram * si.mem_unit;
162207753Smm#endif
163207753Smm
164207753Smm	return ret;
165207753Smm}
166