1*3b35e7eeSXin LI // SPDX-License-Identifier: 0BSD
2*3b35e7eeSXin LI
381ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
481ad8388SMartin Matuska //
581ad8388SMartin Matuska /// \file tuklib_physmem.c
681ad8388SMartin Matuska /// \brief Get the amount of physical memory
781ad8388SMartin Matuska //
881ad8388SMartin Matuska // Author: Lasse Collin
981ad8388SMartin Matuska //
1081ad8388SMartin Matuska ///////////////////////////////////////////////////////////////////////////////
1181ad8388SMartin Matuska
1281ad8388SMartin Matuska #include "tuklib_physmem.h"
1381ad8388SMartin Matuska
1481ad8388SMartin Matuska // We want to use Windows-specific code on Cygwin, which also has memory
1581ad8388SMartin Matuska // information available via sysconf(), but on Cygwin 1.5 and older it
1681ad8388SMartin Matuska // gives wrong results (from our point of view).
1781ad8388SMartin Matuska #if defined(_WIN32) || defined(__CYGWIN__)
1881ad8388SMartin Matuska # ifndef _WIN32_WINNT
1981ad8388SMartin Matuska # define _WIN32_WINNT 0x0500
2081ad8388SMartin Matuska # endif
2181ad8388SMartin Matuska # include <windows.h>
2281ad8388SMartin Matuska
2381ad8388SMartin Matuska #elif defined(__OS2__)
2481ad8388SMartin Matuska # define INCL_DOSMISC
2581ad8388SMartin Matuska # include <os2.h>
2681ad8388SMartin Matuska
2781ad8388SMartin Matuska #elif defined(__DJGPP__)
2881ad8388SMartin Matuska # include <dpmi.h>
2981ad8388SMartin Matuska
3081ad8388SMartin Matuska #elif defined(__VMS)
3181ad8388SMartin Matuska # include <lib$routines.h>
3281ad8388SMartin Matuska # include <syidef.h>
3381ad8388SMartin Matuska # include <ssdef.h>
3481ad8388SMartin Matuska
3553200025SRui Paulo #elif defined(AMIGA) || defined(__AROS__)
3653200025SRui Paulo # define __USE_INLINE__
3753200025SRui Paulo # include <proto/exec.h>
3853200025SRui Paulo
39fe50a38eSXin LI #elif defined(__QNX__)
40fe50a38eSXin LI # include <sys/syspage.h>
41fe50a38eSXin LI # include <string.h>
42fe50a38eSXin LI
43e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_AIX)
44e0f0e66dSMartin Matuska # include <sys/systemcfg.h>
45e0f0e66dSMartin Matuska
4681ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCONF)
4781ad8388SMartin Matuska # include <unistd.h>
4881ad8388SMartin Matuska
4981ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCTL)
5081ad8388SMartin Matuska # ifdef HAVE_SYS_PARAM_H
5181ad8388SMartin Matuska # include <sys/param.h>
5281ad8388SMartin Matuska # endif
5381ad8388SMartin Matuska # include <sys/sysctl.h>
5481ad8388SMartin Matuska
55e0f0e66dSMartin Matuska // Tru64
56e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
57e0f0e66dSMartin Matuska # include <sys/sysinfo.h>
58e0f0e66dSMartin Matuska # include <machine/hal_sysinfo.h>
59e0f0e66dSMartin Matuska
60e0f0e66dSMartin Matuska // HP-UX
61e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
62e0f0e66dSMartin Matuska # include <sys/param.h>
63e0f0e66dSMartin Matuska # include <sys/pstat.h>
64e0f0e66dSMartin Matuska
6581ad8388SMartin Matuska // IRIX
6681ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
6781ad8388SMartin Matuska # include <invent.h>
6881ad8388SMartin Matuska
6981ad8388SMartin Matuska // This sysinfo() is Linux-specific.
7081ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSINFO)
7181ad8388SMartin Matuska # include <sys/sysinfo.h>
7281ad8388SMartin Matuska #endif
7381ad8388SMartin Matuska
7481ad8388SMartin Matuska
7581ad8388SMartin Matuska extern uint64_t
tuklib_physmem(void)7681ad8388SMartin Matuska tuklib_physmem(void)
7781ad8388SMartin Matuska {
7881ad8388SMartin Matuska uint64_t ret = 0;
7981ad8388SMartin Matuska
8081ad8388SMartin Matuska #if defined(_WIN32) || defined(__CYGWIN__)
81*3b35e7eeSXin LI // This requires Windows 2000 or later.
82*3b35e7eeSXin LI MEMORYSTATUSEX meminfo;
83*3b35e7eeSXin LI meminfo.dwLength = sizeof(meminfo);
84*3b35e7eeSXin LI if (GlobalMemoryStatusEx(&meminfo))
85*3b35e7eeSXin LI ret = meminfo.ullTotalPhys;
86*3b35e7eeSXin LI
87*3b35e7eeSXin LI /*
88*3b35e7eeSXin LI // Old version that is compatible with even Win95:
8981ad8388SMartin Matuska if ((GetVersion() & 0xFF) >= 5) {
9081ad8388SMartin Matuska // Windows 2000 and later have GlobalMemoryStatusEx() which
9181ad8388SMartin Matuska // supports reporting values greater than 4 GiB. To keep the
9281ad8388SMartin Matuska // code working also on older Windows versions, use
9381ad8388SMartin Matuska // GlobalMemoryStatusEx() conditionally.
949e6bbe47SXin LI HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
9581ad8388SMartin Matuska if (kernel32 != NULL) {
961456f0f9SXin LI typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX);
97c917796cSXin LI #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
98c917796cSXin LI # pragma GCC diagnostic push
99c917796cSXin LI # pragma GCC diagnostic ignored "-Wcast-function-type"
100c917796cSXin LI #endif
1011456f0f9SXin LI gmse_type gmse = (gmse_type)GetProcAddress(
10281ad8388SMartin Matuska kernel32, "GlobalMemoryStatusEx");
103c917796cSXin LI #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE
104c917796cSXin LI # pragma GCC diagnostic pop
105c917796cSXin LI #endif
10681ad8388SMartin Matuska if (gmse != NULL) {
10781ad8388SMartin Matuska MEMORYSTATUSEX meminfo;
10881ad8388SMartin Matuska meminfo.dwLength = sizeof(meminfo);
10981ad8388SMartin Matuska if (gmse(&meminfo))
11081ad8388SMartin Matuska ret = meminfo.ullTotalPhys;
11181ad8388SMartin Matuska }
11281ad8388SMartin Matuska }
11381ad8388SMartin Matuska }
11481ad8388SMartin Matuska
11581ad8388SMartin Matuska if (ret == 0) {
11681ad8388SMartin Matuska // GlobalMemoryStatus() is supported by Windows 95 and later,
11781ad8388SMartin Matuska // so it is fine to link against it unconditionally. Note that
11881ad8388SMartin Matuska // GlobalMemoryStatus() has no return value.
11981ad8388SMartin Matuska MEMORYSTATUS meminfo;
12081ad8388SMartin Matuska meminfo.dwLength = sizeof(meminfo);
12181ad8388SMartin Matuska GlobalMemoryStatus(&meminfo);
12281ad8388SMartin Matuska ret = meminfo.dwTotalPhys;
12381ad8388SMartin Matuska }
124*3b35e7eeSXin LI */
12581ad8388SMartin Matuska
12681ad8388SMartin Matuska #elif defined(__OS2__)
12781ad8388SMartin Matuska unsigned long mem;
12881ad8388SMartin Matuska if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
12981ad8388SMartin Matuska &mem, sizeof(mem)) == 0)
13081ad8388SMartin Matuska ret = mem;
13181ad8388SMartin Matuska
13281ad8388SMartin Matuska #elif defined(__DJGPP__)
13381ad8388SMartin Matuska __dpmi_free_mem_info meminfo;
13481ad8388SMartin Matuska if (__dpmi_get_free_memory_information(&meminfo) == 0
13581ad8388SMartin Matuska && meminfo.total_number_of_physical_pages
13681ad8388SMartin Matuska != (unsigned long)-1)
13781ad8388SMartin Matuska ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
13881ad8388SMartin Matuska
13981ad8388SMartin Matuska #elif defined(__VMS)
14081ad8388SMartin Matuska int vms_mem;
14181ad8388SMartin Matuska int val = SYI$_MEMSIZE;
14281ad8388SMartin Matuska if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
14381ad8388SMartin Matuska ret = (uint64_t)vms_mem * 8192;
14481ad8388SMartin Matuska
14553200025SRui Paulo #elif defined(AMIGA) || defined(__AROS__)
14653200025SRui Paulo ret = AvailMem(MEMF_TOTAL);
14753200025SRui Paulo
148fe50a38eSXin LI #elif defined(__QNX__)
149fe50a38eSXin LI const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo);
150fe50a38eSXin LI size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry);
151fe50a38eSXin LI const char *strings = SYSPAGE_ENTRY(strings)->data;
152fe50a38eSXin LI
153fe50a38eSXin LI for (size_t i = 0; i < count; ++i)
154fe50a38eSXin LI if (strcmp(strings + entries[i].name, "ram") == 0)
155fe50a38eSXin LI ret += entries[i].end - entries[i].start + 1;
156fe50a38eSXin LI
157e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_AIX)
158e0f0e66dSMartin Matuska ret = _system_configuration.physmem;
159e0f0e66dSMartin Matuska
16081ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCONF)
16181ad8388SMartin Matuska const long pagesize = sysconf(_SC_PAGESIZE);
16281ad8388SMartin Matuska const long pages = sysconf(_SC_PHYS_PAGES);
163f99e4a2dSXin LI if (pagesize != -1 && pages != -1)
16481ad8388SMartin Matuska // According to docs, pagesize * pages can overflow.
16581ad8388SMartin Matuska // Simple case is 32-bit box with 4 GiB or more RAM,
16681ad8388SMartin Matuska // which may report exactly 4 GiB of RAM, and "long"
16781ad8388SMartin Matuska // being 32-bit will overflow. Casting to uint64_t
16881ad8388SMartin Matuska // hopefully avoids overflows in the near future.
16981ad8388SMartin Matuska ret = (uint64_t)pagesize * (uint64_t)pages;
17081ad8388SMartin Matuska
17181ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSCTL)
17281ad8388SMartin Matuska int name[2] = {
17381ad8388SMartin Matuska CTL_HW,
17481ad8388SMartin Matuska #ifdef HW_PHYSMEM64
17581ad8388SMartin Matuska HW_PHYSMEM64
17681ad8388SMartin Matuska #else
17781ad8388SMartin Matuska HW_PHYSMEM
17881ad8388SMartin Matuska #endif
17981ad8388SMartin Matuska };
18081ad8388SMartin Matuska union {
18181ad8388SMartin Matuska uint32_t u32;
18281ad8388SMartin Matuska uint64_t u64;
18381ad8388SMartin Matuska } mem;
18481ad8388SMartin Matuska size_t mem_ptr_size = sizeof(mem.u64);
18581ad8388SMartin Matuska if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
18681ad8388SMartin Matuska // IIRC, 64-bit "return value" is possible on some 64-bit
18781ad8388SMartin Matuska // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
18881ad8388SMartin Matuska // so support both.
18981ad8388SMartin Matuska if (mem_ptr_size == sizeof(mem.u64))
19081ad8388SMartin Matuska ret = mem.u64;
19181ad8388SMartin Matuska else if (mem_ptr_size == sizeof(mem.u32))
19281ad8388SMartin Matuska ret = mem.u32;
19381ad8388SMartin Matuska }
19481ad8388SMartin Matuska
195e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
196e0f0e66dSMartin Matuska // Docs are unclear if "start" is needed, but it doesn't hurt
197e0f0e66dSMartin Matuska // much to have it.
198e0f0e66dSMartin Matuska int memkb;
199e0f0e66dSMartin Matuska int start = 0;
200e0f0e66dSMartin Matuska if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start)
201e0f0e66dSMartin Matuska != -1)
202e0f0e66dSMartin Matuska ret = (uint64_t)memkb * 1024;
203e0f0e66dSMartin Matuska
204e0f0e66dSMartin Matuska #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
205e0f0e66dSMartin Matuska struct pst_static pst;
206e0f0e66dSMartin Matuska if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1)
207e0f0e66dSMartin Matuska ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size;
208e0f0e66dSMartin Matuska
20981ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
21081ad8388SMartin Matuska inv_state_t *st = NULL;
21181ad8388SMartin Matuska if (setinvent_r(&st) != -1) {
21281ad8388SMartin Matuska inventory_t *i;
21381ad8388SMartin Matuska while ((i = getinvent_r(st)) != NULL) {
21481ad8388SMartin Matuska if (i->inv_class == INV_MEMORY
21581ad8388SMartin Matuska && i->inv_type == INV_MAIN_MB) {
21681ad8388SMartin Matuska ret = (uint64_t)i->inv_state << 20;
21781ad8388SMartin Matuska break;
21881ad8388SMartin Matuska }
21981ad8388SMartin Matuska }
22081ad8388SMartin Matuska
22181ad8388SMartin Matuska endinvent_r(st);
22281ad8388SMartin Matuska }
22381ad8388SMartin Matuska
22481ad8388SMartin Matuska #elif defined(TUKLIB_PHYSMEM_SYSINFO)
22581ad8388SMartin Matuska struct sysinfo si;
22681ad8388SMartin Matuska if (sysinfo(&si) == 0)
22781ad8388SMartin Matuska ret = (uint64_t)si.totalram * si.mem_unit;
22881ad8388SMartin Matuska #endif
22981ad8388SMartin Matuska
23081ad8388SMartin Matuska return ret;
23181ad8388SMartin Matuska }
232