tuklib_physmem.c revision 213700
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 36213700Smm// AIX 37213700Smm#elif defined(TUKLIB_PHYSMEM_AIX) 38213700Smm# include <sys/systemcfg.h> 39213700Smm 40207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF) 41207753Smm# include <unistd.h> 42207753Smm 43207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL) 44207753Smm# ifdef HAVE_SYS_PARAM_H 45207753Smm# include <sys/param.h> 46207753Smm# endif 47207753Smm# include <sys/sysctl.h> 48207753Smm 49213700Smm// Tru64 50213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 51213700Smm# include <sys/sysinfo.h> 52213700Smm# include <machine/hal_sysinfo.h> 53213700Smm 54213700Smm// HP-UX 55213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 56213700Smm# include <sys/param.h> 57213700Smm# include <sys/pstat.h> 58213700Smm 59207753Smm// IRIX 60207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 61207753Smm# include <invent.h> 62207753Smm 63207753Smm// This sysinfo() is Linux-specific. 64207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO) 65207753Smm# include <sys/sysinfo.h> 66207753Smm#endif 67207753Smm 68207753Smm 69207753Smmextern uint64_t 70207753Smmtuklib_physmem(void) 71207753Smm{ 72207753Smm uint64_t ret = 0; 73207753Smm 74207753Smm#if defined(_WIN32) || defined(__CYGWIN__) 75207753Smm if ((GetVersion() & 0xFF) >= 5) { 76207753Smm // Windows 2000 and later have GlobalMemoryStatusEx() which 77207753Smm // supports reporting values greater than 4 GiB. To keep the 78207753Smm // code working also on older Windows versions, use 79207753Smm // GlobalMemoryStatusEx() conditionally. 80207753Smm HMODULE kernel32 = GetModuleHandle("kernel32.dll"); 81207753Smm if (kernel32 != NULL) { 82207753Smm BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress( 83207753Smm kernel32, "GlobalMemoryStatusEx"); 84207753Smm if (gmse != NULL) { 85207753Smm MEMORYSTATUSEX meminfo; 86207753Smm meminfo.dwLength = sizeof(meminfo); 87207753Smm if (gmse(&meminfo)) 88207753Smm ret = meminfo.ullTotalPhys; 89207753Smm } 90207753Smm } 91207753Smm } 92207753Smm 93207753Smm if (ret == 0) { 94207753Smm // GlobalMemoryStatus() is supported by Windows 95 and later, 95207753Smm // so it is fine to link against it unconditionally. Note that 96207753Smm // GlobalMemoryStatus() has no return value. 97207753Smm MEMORYSTATUS meminfo; 98207753Smm meminfo.dwLength = sizeof(meminfo); 99207753Smm GlobalMemoryStatus(&meminfo); 100207753Smm ret = meminfo.dwTotalPhys; 101207753Smm } 102207753Smm 103207753Smm#elif defined(__OS2__) 104207753Smm unsigned long mem; 105207753Smm if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, 106207753Smm &mem, sizeof(mem)) == 0) 107207753Smm ret = mem; 108207753Smm 109207753Smm#elif defined(__DJGPP__) 110207753Smm __dpmi_free_mem_info meminfo; 111207753Smm if (__dpmi_get_free_memory_information(&meminfo) == 0 112207753Smm && meminfo.total_number_of_physical_pages 113207753Smm != (unsigned long)-1) 114207753Smm ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; 115207753Smm 116207753Smm#elif defined(__VMS) 117207753Smm int vms_mem; 118207753Smm int val = SYI$_MEMSIZE; 119207753Smm if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) 120207753Smm ret = (uint64_t)vms_mem * 8192; 121207753Smm 122213700Smm#elif defined(TUKLIB_PHYSMEM_AIX) 123213700Smm ret = _system_configuration.physmem; 124213700Smm 125207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF) 126207753Smm const long pagesize = sysconf(_SC_PAGESIZE); 127207753Smm const long pages = sysconf(_SC_PHYS_PAGES); 128213700Smm if (pagesize != -1 && pages != -1) 129207753Smm // According to docs, pagesize * pages can overflow. 130207753Smm // Simple case is 32-bit box with 4 GiB or more RAM, 131207753Smm // which may report exactly 4 GiB of RAM, and "long" 132207753Smm // being 32-bit will overflow. Casting to uint64_t 133207753Smm // hopefully avoids overflows in the near future. 134207753Smm ret = (uint64_t)pagesize * (uint64_t)pages; 135207753Smm 136207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL) 137207753Smm int name[2] = { 138207753Smm CTL_HW, 139207753Smm#ifdef HW_PHYSMEM64 140207753Smm HW_PHYSMEM64 141207753Smm#else 142207753Smm HW_PHYSMEM 143207753Smm#endif 144207753Smm }; 145207753Smm union { 146207753Smm uint32_t u32; 147207753Smm uint64_t u64; 148207753Smm } mem; 149207753Smm size_t mem_ptr_size = sizeof(mem.u64); 150207753Smm if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { 151207753Smm // IIRC, 64-bit "return value" is possible on some 64-bit 152207753Smm // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), 153207753Smm // so support both. 154207753Smm if (mem_ptr_size == sizeof(mem.u64)) 155207753Smm ret = mem.u64; 156207753Smm else if (mem_ptr_size == sizeof(mem.u32)) 157207753Smm ret = mem.u32; 158207753Smm } 159207753Smm 160213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 161213700Smm // Docs are unclear if "start" is needed, but it doesn't hurt 162213700Smm // much to have it. 163213700Smm int memkb; 164213700Smm int start = 0; 165213700Smm if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) 166213700Smm != -1) 167213700Smm ret = (uint64_t)memkb * 1024; 168213700Smm 169213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 170213700Smm struct pst_static pst; 171213700Smm if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) 172213700Smm ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; 173213700Smm 174207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 175207753Smm inv_state_t *st = NULL; 176207753Smm if (setinvent_r(&st) != -1) { 177207753Smm inventory_t *i; 178207753Smm while ((i = getinvent_r(st)) != NULL) { 179207753Smm if (i->inv_class == INV_MEMORY 180207753Smm && i->inv_type == INV_MAIN_MB) { 181207753Smm ret = (uint64_t)i->inv_state << 20; 182207753Smm break; 183207753Smm } 184207753Smm } 185207753Smm 186207753Smm endinvent_r(st); 187207753Smm } 188207753Smm 189207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO) 190207753Smm struct sysinfo si; 191207753Smm if (sysinfo(&si) == 0) 192207753Smm ret = (uint64_t)si.totalram * si.mem_unit; 193207753Smm#endif 194207753Smm 195207753Smm return ret; 196207753Smm} 197