1 /////////////////////////////////////////////////////////////////////////////// 2 // 3 /// \file tuklib_physmem.c 4 /// \brief Get the amount of physical memory 5 // 6 // Author: Lasse Collin 7 // 8 // This file has been put into the public domain. 9 // You can do whatever you want with this file. 10 // 11 /////////////////////////////////////////////////////////////////////////////// 12 13 #include "tuklib_physmem.h" 14 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__) 19 # ifndef _WIN32_WINNT 20 # define _WIN32_WINNT 0x0500 21 # endif 22 # include <windows.h> 23 24 #elif defined(__OS2__) 25 # define INCL_DOSMISC 26 # include <os2.h> 27 28 #elif defined(__DJGPP__) 29 # include <dpmi.h> 30 31 #elif defined(__VMS) 32 # include <lib$routines.h> 33 # include <syidef.h> 34 # include <ssdef.h> 35 36 #elif defined(AMIGA) || defined(__AROS__) 37 # define __USE_INLINE__ 38 # include <proto/exec.h> 39 40 #elif defined(__QNX__) 41 # include <sys/syspage.h> 42 # include <string.h> 43 44 #elif defined(TUKLIB_PHYSMEM_AIX) 45 # include <sys/systemcfg.h> 46 47 #elif defined(TUKLIB_PHYSMEM_SYSCONF) 48 # include <unistd.h> 49 50 #elif defined(TUKLIB_PHYSMEM_SYSCTL) 51 # ifdef HAVE_SYS_PARAM_H 52 # include <sys/param.h> 53 # endif 54 # include <sys/sysctl.h> 55 56 // Tru64 57 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 58 # include <sys/sysinfo.h> 59 # include <machine/hal_sysinfo.h> 60 61 // HP-UX 62 #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 63 # include <sys/param.h> 64 # include <sys/pstat.h> 65 66 // IRIX 67 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 68 # include <invent.h> 69 70 // This sysinfo() is Linux-specific. 71 #elif defined(TUKLIB_PHYSMEM_SYSINFO) 72 # include <sys/sysinfo.h> 73 #endif 74 75 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 81 # endif 82 #elif TUKLIB_GNUC_REQ(8,1) 83 # define CAN_DISABLE_WCAST_FUNCTION_TYPE 1 84 #endif 85 86 87 extern uint64_t 88 tuklib_physmem(void) 89 { 90 uint64_t ret = 0; 91 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" 104 #endif 105 gmse_type gmse = (gmse_type)GetProcAddress( 106 kernel32, "GlobalMemoryStatusEx"); 107 #ifdef CAN_DISABLE_WCAST_FUNCTION_TYPE 108 # pragma GCC diagnostic pop 109 #endif 110 if (gmse != NULL) { 111 MEMORYSTATUSEX meminfo; 112 meminfo.dwLength = sizeof(meminfo); 113 if (gmse(&meminfo)) 114 ret = meminfo.ullTotalPhys; 115 } 116 } 117 } 118 119 if (ret == 0) { 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; 127 } 128 129 #elif defined(__OS2__) 130 unsigned long mem; 131 if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, 132 &mem, sizeof(mem)) == 0) 133 ret = mem; 134 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; 141 142 #elif defined(__VMS) 143 int vms_mem; 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; 147 148 #elif defined(AMIGA) || defined(__AROS__) 149 ret = AvailMem(MEMF_TOTAL); 150 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; 155 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; 159 160 #elif defined(TUKLIB_PHYSMEM_AIX) 161 ret = _system_configuration.physmem; 162 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; 173 174 #elif defined(TUKLIB_PHYSMEM_SYSCTL) 175 int name[2] = { 176 CTL_HW, 177 #ifdef HW_PHYSMEM64 178 HW_PHYSMEM64 179 #else 180 HW_PHYSMEM 181 #endif 182 }; 183 union { 184 uint32_t u32; 185 uint64_t u64; 186 } mem; 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), 191 // so support both. 192 if (mem_ptr_size == sizeof(mem.u64)) 193 ret = mem.u64; 194 else if (mem_ptr_size == sizeof(mem.u32)) 195 ret = mem.u32; 196 } 197 198 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 199 // Docs are unclear if "start" is needed, but it doesn't hurt 200 // much to have it. 201 int memkb; 202 int start = 0; 203 if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) 204 != -1) 205 ret = (uint64_t)memkb * 1024; 206 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; 211 212 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 213 inv_state_t *st = NULL; 214 if (setinvent_r(&st) != -1) { 215 inventory_t *i; 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; 220 break; 221 } 222 } 223 224 endinvent_r(st); 225 } 226 227 #elif defined(TUKLIB_PHYSMEM_SYSINFO) 228 struct sysinfo si; 229 if (sysinfo(&si) == 0) 230 ret = (uint64_t)si.totalram * si.mem_unit; 231 #endif 232 233 return ret; 234 } 235