1 // SPDX-License-Identifier: 0BSD 2 3 /////////////////////////////////////////////////////////////////////////////// 4 // 5 /// \file tuklib_physmem.c 6 /// \brief Get the amount of physical memory 7 // 8 // Author: Lasse Collin 9 // 10 /////////////////////////////////////////////////////////////////////////////// 11 12 #include "tuklib_physmem.h" 13 14 // We want to use Windows-specific code on Cygwin, which also has memory 15 // information available via sysconf(), but on Cygwin 1.5 and older it 16 // gives wrong results (from our point of view). 17 #if defined(_WIN32) || defined(__CYGWIN__) 18 # ifndef _WIN32_WINNT 19 # define _WIN32_WINNT 0x0500 20 # endif 21 # include <windows.h> 22 23 #elif defined(__OS2__) 24 # define INCL_DOSMISC 25 # include <os2.h> 26 27 #elif defined(__DJGPP__) 28 # include <dpmi.h> 29 30 #elif defined(__VMS) 31 # include <lib$routines.h> 32 # include <syidef.h> 33 # include <ssdef.h> 34 35 #elif defined(AMIGA) || defined(__AROS__) 36 # define __USE_INLINE__ 37 # include <proto/exec.h> 38 39 #elif defined(__QNX__) 40 # include <sys/syspage.h> 41 # include <string.h> 42 43 #elif defined(TUKLIB_PHYSMEM_AIX) 44 # include <sys/systemcfg.h> 45 46 #elif defined(TUKLIB_PHYSMEM_SYSCONF) 47 # include <unistd.h> 48 49 #elif defined(TUKLIB_PHYSMEM_SYSCTL) 50 # ifdef HAVE_SYS_PARAM_H 51 # include <sys/param.h> 52 # endif 53 # include <sys/sysctl.h> 54 55 // Tru64 56 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 57 # include <sys/sysinfo.h> 58 # include <machine/hal_sysinfo.h> 59 60 // HP-UX 61 #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 62 # include <sys/param.h> 63 # include <sys/pstat.h> 64 65 // IRIX 66 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 67 # include <invent.h> 68 69 // This sysinfo() is Linux-specific. 70 #elif defined(TUKLIB_PHYSMEM_SYSINFO) 71 # include <sys/sysinfo.h> 72 #endif 73 74 75 extern uint64_t 76 tuklib_physmem(void) 77 { 78 uint64_t ret = 0; 79 80 #if defined(_WIN32) || defined(__CYGWIN__) 81 // This requires Windows 2000 or later. 82 MEMORYSTATUSEX meminfo; 83 meminfo.dwLength = sizeof(meminfo); 84 if (GlobalMemoryStatusEx(&meminfo)) 85 ret = meminfo.ullTotalPhys; 86 87 /* 88 // Old version that is compatible with even Win95: 89 if ((GetVersion() & 0xFF) >= 5) { 90 // Windows 2000 and later have GlobalMemoryStatusEx() which 91 // supports reporting values greater than 4 GiB. To keep the 92 // code working also on older Windows versions, use 93 // GlobalMemoryStatusEx() conditionally. 94 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); 95 if (kernel32 != NULL) { 96 typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX); 97 #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE 98 # pragma GCC diagnostic push 99 # pragma GCC diagnostic ignored "-Wcast-function-type" 100 #endif 101 gmse_type gmse = (gmse_type)GetProcAddress( 102 kernel32, "GlobalMemoryStatusEx"); 103 #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE 104 # pragma GCC diagnostic pop 105 #endif 106 if (gmse != NULL) { 107 MEMORYSTATUSEX meminfo; 108 meminfo.dwLength = sizeof(meminfo); 109 if (gmse(&meminfo)) 110 ret = meminfo.ullTotalPhys; 111 } 112 } 113 } 114 115 if (ret == 0) { 116 // GlobalMemoryStatus() is supported by Windows 95 and later, 117 // so it is fine to link against it unconditionally. Note that 118 // GlobalMemoryStatus() has no return value. 119 MEMORYSTATUS meminfo; 120 meminfo.dwLength = sizeof(meminfo); 121 GlobalMemoryStatus(&meminfo); 122 ret = meminfo.dwTotalPhys; 123 } 124 */ 125 126 #elif defined(__OS2__) 127 unsigned long mem; 128 if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, 129 &mem, sizeof(mem)) == 0) 130 ret = mem; 131 132 #elif defined(__DJGPP__) 133 __dpmi_free_mem_info meminfo; 134 if (__dpmi_get_free_memory_information(&meminfo) == 0 135 && meminfo.total_number_of_physical_pages 136 != (unsigned long)-1) 137 ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; 138 139 #elif defined(__VMS) 140 int vms_mem; 141 int val = SYI$_MEMSIZE; 142 if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) 143 ret = (uint64_t)vms_mem * 8192; 144 145 #elif defined(AMIGA) || defined(__AROS__) 146 ret = AvailMem(MEMF_TOTAL); 147 148 #elif defined(__QNX__) 149 const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo); 150 size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry); 151 const char *strings = SYSPAGE_ENTRY(strings)->data; 152 153 for (size_t i = 0; i < count; ++i) 154 if (strcmp(strings + entries[i].name, "ram") == 0) 155 ret += entries[i].end - entries[i].start + 1; 156 157 #elif defined(TUKLIB_PHYSMEM_AIX) 158 ret = _system_configuration.physmem; 159 160 #elif defined(TUKLIB_PHYSMEM_SYSCONF) 161 const long pagesize = sysconf(_SC_PAGESIZE); 162 const long pages = sysconf(_SC_PHYS_PAGES); 163 if (pagesize != -1 && pages != -1) 164 // According to docs, pagesize * pages can overflow. 165 // Simple case is 32-bit box with 4 GiB or more RAM, 166 // which may report exactly 4 GiB of RAM, and "long" 167 // being 32-bit will overflow. Casting to uint64_t 168 // hopefully avoids overflows in the near future. 169 ret = (uint64_t)pagesize * (uint64_t)pages; 170 171 #elif defined(TUKLIB_PHYSMEM_SYSCTL) 172 int name[2] = { 173 CTL_HW, 174 #ifdef HW_PHYSMEM64 175 HW_PHYSMEM64 176 #else 177 HW_PHYSMEM 178 #endif 179 }; 180 union { 181 uint32_t u32; 182 uint64_t u64; 183 } mem; 184 size_t mem_ptr_size = sizeof(mem.u64); 185 if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { 186 // IIRC, 64-bit "return value" is possible on some 64-bit 187 // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), 188 // so support both. 189 if (mem_ptr_size == sizeof(mem.u64)) 190 ret = mem.u64; 191 else if (mem_ptr_size == sizeof(mem.u32)) 192 ret = mem.u32; 193 } 194 195 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 196 // Docs are unclear if "start" is needed, but it doesn't hurt 197 // much to have it. 198 int memkb; 199 int start = 0; 200 if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) 201 != -1) 202 ret = (uint64_t)memkb * 1024; 203 204 #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 205 struct pst_static pst; 206 if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) 207 ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; 208 209 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 210 inv_state_t *st = NULL; 211 if (setinvent_r(&st) != -1) { 212 inventory_t *i; 213 while ((i = getinvent_r(st)) != NULL) { 214 if (i->inv_class == INV_MEMORY 215 && i->inv_type == INV_MAIN_MB) { 216 ret = (uint64_t)i->inv_state << 20; 217 break; 218 } 219 } 220 221 endinvent_r(st); 222 } 223 224 #elif defined(TUKLIB_PHYSMEM_SYSINFO) 225 struct sysinfo si; 226 if (sysinfo(&si) == 0) 227 ret = (uint64_t)si.totalram * si.mem_unit; 228 #endif 229 230 return ret; 231 } 232