(root)/
xz-5.4.5/
src/
common/
tuklib_physmem.c
       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  }