tuklib_physmem.c revision 291125
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) { 89207753Smm BOOL (WINAPI *gmse)(LPMEMORYSTATUSEX) = GetProcAddress( 90207753Smm kernel32, "GlobalMemoryStatusEx"); 91207753Smm if (gmse != NULL) { 92207753Smm MEMORYSTATUSEX meminfo; 93207753Smm meminfo.dwLength = sizeof(meminfo); 94207753Smm if (gmse(&meminfo)) 95207753Smm ret = meminfo.ullTotalPhys; 96207753Smm } 97207753Smm } 98207753Smm } 99207753Smm 100207753Smm if (ret == 0) { 101207753Smm // GlobalMemoryStatus() is supported by Windows 95 and later, 102207753Smm // so it is fine to link against it unconditionally. Note that 103207753Smm // GlobalMemoryStatus() has no return value. 104207753Smm MEMORYSTATUS meminfo; 105207753Smm meminfo.dwLength = sizeof(meminfo); 106207753Smm GlobalMemoryStatus(&meminfo); 107207753Smm ret = meminfo.dwTotalPhys; 108207753Smm } 109207753Smm 110207753Smm#elif defined(__OS2__) 111207753Smm unsigned long mem; 112207753Smm if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, 113207753Smm &mem, sizeof(mem)) == 0) 114207753Smm ret = mem; 115207753Smm 116207753Smm#elif defined(__DJGPP__) 117207753Smm __dpmi_free_mem_info meminfo; 118207753Smm if (__dpmi_get_free_memory_information(&meminfo) == 0 119207753Smm && meminfo.total_number_of_physical_pages 120207753Smm != (unsigned long)-1) 121207753Smm ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096; 122207753Smm 123207753Smm#elif defined(__VMS) 124207753Smm int vms_mem; 125207753Smm int val = SYI$_MEMSIZE; 126207753Smm if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL) 127207753Smm ret = (uint64_t)vms_mem * 8192; 128207753Smm 129278433Srpaulo#elif defined(AMIGA) || defined(__AROS__) 130278433Srpaulo ret = AvailMem(MEMF_TOTAL); 131278433Srpaulo 132291125Sdelphij#elif defined(__QNX__) 133291125Sdelphij const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo); 134291125Sdelphij size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry); 135291125Sdelphij const char *strings = SYSPAGE_ENTRY(strings)->data; 136291125Sdelphij 137291125Sdelphij for (size_t i = 0; i < count; ++i) 138291125Sdelphij if (strcmp(strings + entries[i].name, "ram") == 0) 139291125Sdelphij ret += entries[i].end - entries[i].start + 1; 140291125Sdelphij 141213700Smm#elif defined(TUKLIB_PHYSMEM_AIX) 142213700Smm ret = _system_configuration.physmem; 143213700Smm 144207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCONF) 145207753Smm const long pagesize = sysconf(_SC_PAGESIZE); 146207753Smm const long pages = sysconf(_SC_PHYS_PAGES); 147213700Smm if (pagesize != -1 && pages != -1) 148207753Smm // According to docs, pagesize * pages can overflow. 149207753Smm // Simple case is 32-bit box with 4 GiB or more RAM, 150207753Smm // which may report exactly 4 GiB of RAM, and "long" 151207753Smm // being 32-bit will overflow. Casting to uint64_t 152207753Smm // hopefully avoids overflows in the near future. 153207753Smm ret = (uint64_t)pagesize * (uint64_t)pages; 154207753Smm 155207753Smm#elif defined(TUKLIB_PHYSMEM_SYSCTL) 156207753Smm int name[2] = { 157207753Smm CTL_HW, 158207753Smm#ifdef HW_PHYSMEM64 159207753Smm HW_PHYSMEM64 160207753Smm#else 161207753Smm HW_PHYSMEM 162207753Smm#endif 163207753Smm }; 164207753Smm union { 165207753Smm uint32_t u32; 166207753Smm uint64_t u64; 167207753Smm } mem; 168207753Smm size_t mem_ptr_size = sizeof(mem.u64); 169207753Smm if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) { 170207753Smm // IIRC, 64-bit "return value" is possible on some 64-bit 171207753Smm // BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64), 172207753Smm // so support both. 173207753Smm if (mem_ptr_size == sizeof(mem.u64)) 174207753Smm ret = mem.u64; 175207753Smm else if (mem_ptr_size == sizeof(mem.u32)) 176207753Smm ret = mem.u32; 177207753Smm } 178207753Smm 179213700Smm#elif defined(TUKLIB_PHYSMEM_GETSYSINFO) 180213700Smm // Docs are unclear if "start" is needed, but it doesn't hurt 181213700Smm // much to have it. 182213700Smm int memkb; 183213700Smm int start = 0; 184213700Smm if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start) 185213700Smm != -1) 186213700Smm ret = (uint64_t)memkb * 1024; 187213700Smm 188213700Smm#elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC) 189213700Smm struct pst_static pst; 190213700Smm if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1) 191213700Smm ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size; 192213700Smm 193207753Smm#elif defined(TUKLIB_PHYSMEM_GETINVENT_R) 194207753Smm inv_state_t *st = NULL; 195207753Smm if (setinvent_r(&st) != -1) { 196207753Smm inventory_t *i; 197207753Smm while ((i = getinvent_r(st)) != NULL) { 198207753Smm if (i->inv_class == INV_MEMORY 199207753Smm && i->inv_type == INV_MAIN_MB) { 200207753Smm ret = (uint64_t)i->inv_state << 20; 201207753Smm break; 202207753Smm } 203207753Smm } 204207753Smm 205207753Smm endinvent_r(st); 206207753Smm } 207207753Smm 208207753Smm#elif defined(TUKLIB_PHYSMEM_SYSINFO) 209207753Smm struct sysinfo si; 210207753Smm if (sysinfo(&si) == 0) 211207753Smm ret = (uint64_t)si.totalram * si.mem_unit; 212207753Smm#endif 213207753Smm 214207753Smm return ret; 215207753Smm} 216