1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file tuklib_physmem.c
4 /// \brief Get the amount of physical memory
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
13 #include "tuklib_physmem.h"
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__)
20 # define _WIN32_WINNT 0x0500
24 #elif defined(__OS2__)
28 #elif defined(__DJGPP__)
32 # include <lib$routines.h>
36 #elif defined(AMIGA) || defined(__AROS__)
37 # define __USE_INLINE__
38 # include <proto/exec.h>
40 #elif defined(__QNX__)
41 # include <sys/syspage.h>
44 #elif defined(TUKLIB_PHYSMEM_AIX)
45 # include <sys/systemcfg.h>
47 #elif defined(TUKLIB_PHYSMEM_SYSCONF)
50 #elif defined(TUKLIB_PHYSMEM_SYSCTL)
51 # ifdef HAVE_SYS_PARAM_H
52 # include <sys/param.h>
54 # include <sys/sysctl.h>
57 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
58 # include <sys/sysinfo.h>
59 # include <machine/hal_sysinfo.h>
62 #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
63 # include <sys/param.h>
64 # include <sys/pstat.h>
67 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
70 // This sysinfo() is Linux-specific.
71 #elif defined(TUKLIB_PHYSMEM_SYSINFO)
72 # include <sys/sysinfo.h>
76 // With GCC >= 8.1 with -Wextra and Clang >= 13 with -Wcast-function-type
77 // will warn about the Windows-specific code.
78 #if defined(__has_warning)
79 # if __has_warning("-Wcast-function-type")
80 # define CAN_DISABLE_WCAST_FUNCTION_TYPE 1
82 #elif TUKLIB_GNUC_REQ(8,1)
83 # define CAN_DISABLE_WCAST_FUNCTION_TYPE 1
92 #if defined(_WIN32) || defined(__CYGWIN__)
93 if ((GetVersion() & 0xFF) >= 5) {
94 // Windows 2000 and later have GlobalMemoryStatusEx() which
95 // supports reporting values greater than 4 GiB. To keep the
96 // code working also on older Windows versions, use
97 // GlobalMemoryStatusEx() conditionally.
98 HMODULE kernel32
= GetModuleHandle(TEXT("kernel32.dll"));
99 if (kernel32
!= NULL
) {
100 typedef BOOL (WINAPI
*gmse_type
)(LPMEMORYSTATUSEX
);
101 #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
102 # pragma GCC diagnostic push
103 # pragma GCC diagnostic ignored "-Wcast-function-type"
105 gmse_type gmse
= (gmse_type
)GetProcAddress(
106 kernel32
, "GlobalMemoryStatusEx");
107 #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
108 # pragma GCC diagnostic pop
111 MEMORYSTATUSEX meminfo
;
112 meminfo
.dwLength
= sizeof(meminfo
);
114 ret
= meminfo
.ullTotalPhys
;
120 // GlobalMemoryStatus() is supported by Windows 95 and later,
121 // so it is fine to link against it unconditionally. Note that
122 // GlobalMemoryStatus() has no return value.
123 MEMORYSTATUS meminfo
;
124 meminfo
.dwLength
= sizeof(meminfo
);
125 GlobalMemoryStatus(&meminfo
);
126 ret
= meminfo
.dwTotalPhys
;
129 #elif defined(__OS2__)
131 if (DosQuerySysInfo(QSV_TOTPHYSMEM
, QSV_TOTPHYSMEM
,
132 &mem
, sizeof(mem
)) == 0)
135 #elif defined(__DJGPP__)
136 __dpmi_free_mem_info meminfo
;
137 if (__dpmi_get_free_memory_information(&meminfo
) == 0
138 && meminfo
.total_number_of_physical_pages
139 != (unsigned long)-1)
140 ret
= (uint64_t)meminfo
.total_number_of_physical_pages
* 4096;
144 int val
= SYI$_MEMSIZE
;
145 if (LIB$
GETSYI(&val
, &vms_mem
, 0, 0, 0, 0) == SS$_NORMAL
)
146 ret
= (uint64_t)vms_mem
* 8192;
148 #elif defined(AMIGA) || defined(__AROS__)
149 ret
= AvailMem(MEMF_TOTAL
);
151 #elif defined(__QNX__)
152 const struct asinfo_entry
*entries
= SYSPAGE_ENTRY(asinfo
);
153 size_t count
= SYSPAGE_ENTRY_SIZE(asinfo
) / sizeof(struct asinfo_entry
);
154 const char *strings
= SYSPAGE_ENTRY(strings
)->data
;
156 for (size_t i
= 0; i
< count
; ++i
)
157 if (strcmp(strings
+ entries
[i
].name
, "ram") == 0)
158 ret
+= entries
[i
].end
- entries
[i
].start
+ 1;
160 #elif defined(TUKLIB_PHYSMEM_AIX)
161 ret
= _system_configuration
.physmem
;
163 #elif defined(TUKLIB_PHYSMEM_SYSCONF)
164 const long pagesize
= sysconf(_SC_PAGESIZE
);
165 const long pages
= sysconf(_SC_PHYS_PAGES
);
166 if (pagesize
!= -1 && pages
!= -1)
167 // According to docs, pagesize * pages can overflow.
168 // Simple case is 32-bit box with 4 GiB or more RAM,
169 // which may report exactly 4 GiB of RAM, and "long"
170 // being 32-bit will overflow. Casting to uint64_t
171 // hopefully avoids overflows in the near future.
172 ret
= (uint64_t)pagesize
* (uint64_t)pages
;
174 #elif defined(TUKLIB_PHYSMEM_SYSCTL)
187 size_t mem_ptr_size
= sizeof(mem
.u64
);
188 if (sysctl(name
, 2, &mem
.u64
, &mem_ptr_size
, NULL
, 0) != -1) {
189 // IIRC, 64-bit "return value" is possible on some 64-bit
190 // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
192 if (mem_ptr_size
== sizeof(mem
.u64
))
194 else if (mem_ptr_size
== sizeof(mem
.u32
))
198 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
199 // Docs are unclear if "start" is needed, but it doesn't hurt
203 if (getsysinfo(GSI_PHYSMEM
, (caddr_t
)&memkb
, sizeof(memkb
), &start
)
205 ret
= (uint64_t)memkb
* 1024;
207 #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
208 struct pst_static pst
;
209 if (pstat_getstatic(&pst
, sizeof(pst
), 1, 0) != -1)
210 ret
= (uint64_t)pst
.physical_memory
* (uint64_t)pst
.page_size
;
212 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
213 inv_state_t
*st
= NULL
;
214 if (setinvent_r(&st
) != -1) {
216 while ((i
= getinvent_r(st
)) != NULL
) {
217 if (i
->inv_class
== INV_MEMORY
218 && i
->inv_type
== INV_MAIN_MB
) {
219 ret
= (uint64_t)i
->inv_state
<< 20;
227 #elif defined(TUKLIB_PHYSMEM_SYSINFO)
229 if (sysinfo(&si
) == 0)
230 ret
= (uint64_t)si
.totalram
* si
.mem_unit
;