Home | History | Annotate | Line # | Download | only in common
      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 = GetModuleHandleA("kernel32.dll");
     95 		if (kernel32 != NULL) {
     96 			typedef BOOL (WINAPI *gmse_type)(LPMEMORYSTATUSEX);
     97 			gmse_type gmse = (gmse_type)GetProcAddress(
     98 					kernel32, "GlobalMemoryStatusEx");
     99 			if (gmse != NULL) {
    100 				MEMORYSTATUSEX meminfo;
    101 				meminfo.dwLength = sizeof(meminfo);
    102 				if (gmse(&meminfo))
    103 					ret = meminfo.ullTotalPhys;
    104 			}
    105 		}
    106 	}
    107 
    108 	if (ret == 0) {
    109 		// GlobalMemoryStatus() is supported by Windows 95 and later,
    110 		// so it is fine to link against it unconditionally. Note that
    111 		// GlobalMemoryStatus() has no return value.
    112 		MEMORYSTATUS meminfo;
    113 		meminfo.dwLength = sizeof(meminfo);
    114 		GlobalMemoryStatus(&meminfo);
    115 		ret = meminfo.dwTotalPhys;
    116 	}
    117 */
    118 
    119 #elif defined(__OS2__)
    120 	unsigned long mem;
    121 	if (DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
    122 			&mem, sizeof(mem)) == 0)
    123 		ret = mem;
    124 
    125 #elif defined(__DJGPP__)
    126 	__dpmi_free_mem_info meminfo;
    127 	if (__dpmi_get_free_memory_information(&meminfo) == 0
    128 			&& meminfo.total_number_of_physical_pages
    129 				!= (unsigned long)-1)
    130 		ret = (uint64_t)meminfo.total_number_of_physical_pages * 4096;
    131 
    132 #elif defined(__VMS)
    133 	int vms_mem;
    134 	int val = SYI$_MEMSIZE;
    135 	if (LIB$GETSYI(&val, &vms_mem, 0, 0, 0, 0) == SS$_NORMAL)
    136 		ret = (uint64_t)vms_mem * 8192;
    137 
    138 #elif defined(AMIGA) || defined(__AROS__)
    139 	ret = AvailMem(MEMF_TOTAL);
    140 
    141 #elif defined(__QNX__)
    142 	const struct asinfo_entry *entries = SYSPAGE_ENTRY(asinfo);
    143 	size_t count = SYSPAGE_ENTRY_SIZE(asinfo) / sizeof(struct asinfo_entry);
    144 	const char *strings = SYSPAGE_ENTRY(strings)->data;
    145 
    146 	for (size_t i = 0; i < count; ++i)
    147 		if (strcmp(strings + entries[i].name, "ram") == 0)
    148 			ret += entries[i].end - entries[i].start + 1;
    149 
    150 #elif defined(TUKLIB_PHYSMEM_AIX)
    151 	ret = (uint64_t)_system_configuration.physmem;
    152 
    153 #elif defined(TUKLIB_PHYSMEM_SYSCONF)
    154 	const long pagesize = sysconf(_SC_PAGESIZE);
    155 	const long pages = sysconf(_SC_PHYS_PAGES);
    156 	if (pagesize != -1 && pages != -1)
    157 		// According to docs, pagesize * pages can overflow.
    158 		// Simple case is 32-bit box with 4 GiB or more RAM,
    159 		// which may report exactly 4 GiB of RAM, and "long"
    160 		// being 32-bit will overflow. Casting to uint64_t
    161 		// hopefully avoids overflows in the near future.
    162 		ret = (uint64_t)pagesize * (uint64_t)pages;
    163 
    164 #elif defined(TUKLIB_PHYSMEM_SYSCTL)
    165 	int name[2] = {
    166 		CTL_HW,
    167 #ifdef HW_PHYSMEM64
    168 		HW_PHYSMEM64
    169 #else
    170 		HW_PHYSMEM
    171 #endif
    172 	};
    173 	union {
    174 		uint32_t u32;
    175 		uint64_t u64;
    176 	} mem;
    177 	size_t mem_ptr_size = sizeof(mem.u64);
    178 	if (sysctl(name, 2, &mem.u64, &mem_ptr_size, NULL, 0) != -1) {
    179 		// IIRC, 64-bit "return value" is possible on some 64-bit
    180 		// BSD systems even with HW_PHYSMEM (instead of HW_PHYSMEM64),
    181 		// so support both.
    182 		if (mem_ptr_size == sizeof(mem.u64))
    183 			ret = mem.u64;
    184 		else if (mem_ptr_size == sizeof(mem.u32))
    185 			ret = mem.u32;
    186 	}
    187 
    188 #elif defined(TUKLIB_PHYSMEM_GETSYSINFO)
    189 	// Docs are unclear if "start" is needed, but it doesn't hurt
    190 	// much to have it.
    191 	int memkb;
    192 	int start = 0;
    193 	if (getsysinfo(GSI_PHYSMEM, (caddr_t)&memkb, sizeof(memkb), &start)
    194 			!= -1)
    195 		ret = (uint64_t)memkb * 1024;
    196 
    197 #elif defined(TUKLIB_PHYSMEM_PSTAT_GETSTATIC)
    198 	struct pst_static pst;
    199 	if (pstat_getstatic(&pst, sizeof(pst), 1, 0) != -1)
    200 		ret = (uint64_t)pst.physical_memory * (uint64_t)pst.page_size;
    201 
    202 #elif defined(TUKLIB_PHYSMEM_GETINVENT_R)
    203 	inv_state_t *st = NULL;
    204 	if (setinvent_r(&st) != -1) {
    205 		inventory_t *i;
    206 		while ((i = getinvent_r(st)) != NULL) {
    207 			if (i->inv_class == INV_MEMORY
    208 					&& i->inv_type == INV_MAIN_MB) {
    209 				ret = (uint64_t)i->inv_state << 20;
    210 				break;
    211 			}
    212 		}
    213 
    214 		endinvent_r(st);
    215 	}
    216 
    217 #elif defined(TUKLIB_PHYSMEM_SYSINFO)
    218 	struct sysinfo si;
    219 	if (sysinfo(&si) == 0)
    220 		ret = (uint64_t)si.totalram * si.mem_unit;
    221 #endif
    222 
    223 	return ret;
    224 }
    225