1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file tuklib_physmem.c 4207753Smm/// \brief Get the amount of physical memory 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "tuklib_physmem.h" 14207753Smm 15207753Smm// We want to use Windows-specific code on Cygwin, which also has memory 16207753Smm// information available via sysconf(), but on Cygwin 1.5 and older it 17207753Smm// gives wrong results (from our point of view). 18207753Smm#if defined(_WIN32) || defined(__CYGWIN__) 19207753Smm# ifndef _WIN32_WINNT 20207753Smm# define _WIN32_WINNT 0x0500 21207753Smm# endif 22207753Smm# include <windows.h> 23207753Smm 24207753Smm#elif defined(__OS2__) 25207753Smm# define INCL_DOSMISC 26207753Smm# include <os2.h> 27207753Smm 28207753Smm#elif defined(__DJGPP__) 29207753Smm# include <dpmi.h> 30207753Smm 31207753Smm#elif defined(__VMS) 32207753Smm# include <lib$routines.h> 33207753Smm# include <syidef.h> 34207753Smm# include <ssdef.h> 35207753Smm 36278433Srpaulo#elif defined(AMIGA) || defined(__AROS__) 37278433Srpaulo# define __USE_INLINE__ 38278433Srpaulo# include <proto/exec.h> 39278433Srpaulo 40291125Sdelphij#elif defined(__QNX__) 41291125Sdelphij# include <sys/syspage.h> 42291125Sdelphij# include <string.h> 43291125Sdelphij 44213700Smm#elif defined(TUKLIB_PHYSMEM_AIX) 45213700Smm# include <sys/systemcfg.h> 46213700Smm 47207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF) 48207753Smm# include <unistd.h> 49207753Smm 50207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL) 51207753Smm# ifdef HAVE_SYS_PARAM_H 52207753Smm# include <sys/param.h> 53207753Smm# endif 54207753Smm# include <sys/sysctl.h> 55207753Smm 56213700Smm// Tru64 57213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 58213700Smm# include <sys/sysinfo.h> 59213700Smm# include <machine/hal_sysinfo.h> 60213700Smm 61213700Smm// HP-UX 62213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 63213700Smm# include <sys/param.h> 64213700Smm# include <sys/pstat.h> 65213700Smm 66207753Smm// IRIX 67207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 68207753Smm# include <invent.h> 69207753Smm 70207753Smm// This sysinfo() is Linux-specific. 71207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO) 72207753Smm# include <sys/sysinfo.h> 73207753Smm#endif 74207753Smm 75207753Smm 76207753Smmextern uint64_t 77207753Smmtuklib_physmem(void) 78207753Smm{ 79207753Smm uint64_t ret = 0; 80207753Smm 81207753Smm#if defined(_WIN32) || defined(__CYGWIN__) 82207753Smm if ((GetVersion() & 0xFF) >= 5) { 83207753Smm // Windows 2000 and later have GlobalMemoryStatusEx() which 84207753Smm // supports reporting values greater than 4 GiB. To keep the 85207753Smm // code working also on older Windows versions, use 86207753Smm // GlobalMemoryStatusEx() conditionally. 87207753Smm HMODULE kernel32 = GetModuleHandle("kernel32.dll"); 88207753Smm if (kernel32 != NULL) { 89312517Sdelphij typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX); 90312517Sdelphij gmse_type gmse = (gmse_type)GetProcAddress( 91207753Smm kernel32, "GlobalMemoryStatusEx"); 92207753Smm if (gmse != NULL) { 93207753Smm MEMORYSTATUSEX meminfo; 94207753Smm meminfo.dwLength = sizeof(meminfo); 95207753Smm if (gmse(&meminfo)) 96207753Smm ret = meminfo.ullTotalPhys; 97207753Smm } 98207753Smm } 99207753Smm } 100207753Smm 101207753Smm if (ret == 0) { 102207753Smm // GlobalMemoryStatus() is supported by Windows 95 and later, 103207753Smm // so it is fine to link against it unconditionally. Note that 104207753Smm // GlobalMemoryStatus() has no return value. 105207753Smm MEMORYSTATUS meminfo; 106207753Smm meminfo.dwLength = sizeof(meminfo); 107207753Smm GlobalMemoryStatus(&meminfo); 108207753Smm ret = meminfo.dwTotalPhys; 109207753Smm } 110207753Smm 111207753Smm#elif defined(__OS2__) 112207753Smm unsigned long mem; 113207753Smm if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, 114207753Smm &mem, sizeof(mem)) == 0) 115207753Smm ret = mem; 116207753Smm 117207753Smm#elif defined(__DJGPP__) 118207753Smm __dpmi_free_mem_info meminfo; 119207753Smm if (__dpmi_get_free_memory_information(&meminfo) == 0 120207753Smm && meminfo.total_number_of_physical_pages 121207753Smm != (unsigned long)-1) 122207753Smm ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; 123207753Smm 124207753Smm#elif defined(__VMS) 125207753Smm int vms_mem; 126207753Smm int val = SYI$_MEMSIZE; 127207753Smm if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) 128207753Smm ret = (uint64_t)vms_mem * 8192; 129207753Smm 130278433Srpaulo#elif defined(AMIGA) || defined(__AROS__) 131278433Srpaulo ret = AvailMem(MEMF_TOTAL); 132278433Srpaulo 133291125Sdelphij#elif defined(__QNX__) 134291125Sdelphij const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo); 135291125Sdelphij size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry); 136291125Sdelphij const char *strings = SYSPAGE_ENTRY(strings)->data; 137291125Sdelphij 138291125Sdelphij for (size_t i = 0; i < count; ++i) 139291125Sdelphij if (strcmp(strings + entries[i].name, "ram") == 0) 140291125Sdelphij ret += entries[i].end - entries[i].start + 1; 141291125Sdelphij 142213700Smm#elif defined(TUKLIB_PHYSMEM_AIX) 143213700Smm ret = _system_configuration.physmem; 144213700Smm 145207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF) 146207753Smm const long pagesize = sysconf(_SC_PAGESIZE); 147207753Smm const long pages = sysconf(_SC_PHYS_PAGES); 148360523Sdelphij if (pagesize != -1 && pages != -1) 149207753Smm // According to docs, pagesize * pages can overflow. 150207753Smm // Simple case is 32-bit box with 4 GiB or more RAM, 151207753Smm // which may report exactly 4 GiB of RAM, and "long" 152207753Smm // being 32-bit will overflow. Casting to uint64_t 153207753Smm // hopefully avoids overflows in the near future. 154207753Smm ret = (uint64_t)pagesize * (uint64_t)pages; 155207753Smm 156207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL) 157207753Smm int name[2] = { 158207753Smm CTL_HW, 159207753Smm#ifdef HW_PHYSMEM64 160207753Smm HW_PHYSMEM64 161207753Smm#else 162207753Smm HW_PHYSMEM 163207753Smm#endif 164207753Smm }; 165207753Smm union { 166207753Smm uint32_t u32; 167207753Smm uint64_t u64; 168207753Smm } mem; 169207753Smm size_t mem_ptr_size = sizeof(mem.u64); 170207753Smm if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { 171207753Smm // IIRC, 64-bit "return value" is possible on some 64-bit 172207753Smm // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), 173207753Smm // so support both. 174207753Smm if (mem_ptr_size == sizeof(mem.u64)) 175207753Smm ret = mem.u64; 176207753Smm else if (mem_ptr_size == sizeof(mem.u32)) 177207753Smm ret = mem.u32; 178207753Smm } 179207753Smm 180213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 181213700Smm // Docs are unclear if "start" is needed, but it doesn't hurt 182213700Smm // much to have it. 183213700Smm int memkb; 184213700Smm int start = 0; 185213700Smm if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) 186213700Smm != -1) 187213700Smm ret = (uint64_t)memkb * 1024; 188213700Smm 189213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 190213700Smm struct pst_static pst; 191213700Smm if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) 192213700Smm ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; 193213700Smm 194207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 195207753Smm inv_state_t *st = NULL; 196207753Smm if (setinvent_r(&st) != -1) { 197207753Smm inventory_t *i; 198207753Smm while ((i = getinvent_r(st)) != NULL) { 199207753Smm if (i->inv_class == INV_MEMORY 200207753Smm && i->inv_type == INV_MAIN_MB) { 201207753Smm ret = (uint64_t)i->inv_state << 20; 202207753Smm break; 203207753Smm } 204207753Smm } 205207753Smm 206207753Smm endinvent_r(st); 207207753Smm } 208207753Smm 209207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO) 210207753Smm struct sysinfo si; 211207753Smm if (sysinfo(&si) == 0) 212207753Smm ret = (uint64_t)si.totalram * si.mem_unit; 213207753Smm#endif 214207753Smm 215207753Smm return ret; 216207753Smm} 217