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 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