git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/sysinfo/memory.go (about)

     1  // Copyright © 2016 Zlatko Čalušić
     2  //
     3  // Use of this source code is governed by an MIT-style license that can be found in the LICENSE file.
     4  
     5  package sysinfo
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"io/ioutil"
    11  	"strconv"
    12  )
    13  
    14  // Memory information.
    15  type Memory struct {
    16  	Type  string `json:"type,omitempty"`
    17  	Speed uint   `json:"speed,omitempty"` // RAM data rate in MT/s
    18  	Size  uint   `json:"size,omitempty"`  // RAM size in MB
    19  }
    20  
    21  func word(data []byte, index int) uint16 {
    22  	return binary.LittleEndian.Uint16(data[index : index+2])
    23  }
    24  
    25  func dword(data []byte, index int) uint32 {
    26  	return binary.LittleEndian.Uint32(data[index : index+4])
    27  }
    28  
    29  func qword(data []byte, index int) uint64 {
    30  	return binary.LittleEndian.Uint64(data[index : index+8])
    31  }
    32  
    33  func (si *SysInfo) getMemoryInfo() {
    34  	dmi, err := ioutil.ReadFile("/sys/firmware/dmi/tables/DMI")
    35  	if err != nil {
    36  		// Xen hypervisor
    37  		if targetKB := slurpFile("/sys/devices/system/xen_memory/xen_memory0/target_kb"); targetKB != "" {
    38  			si.Memory.Type = "DRAM"
    39  			size, _ := strconv.ParseUint(targetKB, 10, 64)
    40  			si.Memory.Size = uint(size) / 1024
    41  		}
    42  		return
    43  	}
    44  
    45  	si.Memory.Size = 0
    46  	var memSizeAlt uint
    47  loop:
    48  	for p := 0; p < len(dmi)-1; {
    49  		recType := dmi[p]
    50  		recLen := dmi[p+1]
    51  
    52  		switch recType {
    53  		case 4:
    54  			if si.CPU.Speed == 0 {
    55  				si.CPU.Speed = uint(word(dmi, p+0x16))
    56  			}
    57  		case 17:
    58  			size := uint(word(dmi, p+0x0c))
    59  			if size == 0 || size == 0xffff || size&0x8000 == 0x8000 {
    60  				break
    61  			}
    62  			if size == 0x7fff {
    63  				if recLen >= 0x20 {
    64  					size = uint(dword(dmi, p+0x1c))
    65  				} else {
    66  					break
    67  				}
    68  			}
    69  
    70  			si.Memory.Size += size
    71  
    72  			if si.Memory.Type == "" {
    73  				// SMBIOS Reference Specification Version 3.0.0, page 92
    74  				memTypes := [...]string{
    75  					"Other", "Unknown", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM", "ROM", "FLASH",
    76  					"EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM", "SDRAM", "SGRAM", "RDRAM",
    77  					"DDR", "DDR2", "DDR2 FB-DIMM", "Reserved", "Reserved", "Reserved", "DDR3",
    78  					"FBD2", "DDR4", "LPDDR", "LPDDR2", "LPDDR3", "LPDDR4",
    79  				}
    80  
    81  				if index := int(dmi[p+0x12]); index >= 1 && index <= len(memTypes) {
    82  					si.Memory.Type = memTypes[index-1]
    83  				}
    84  			}
    85  
    86  			if si.Memory.Speed == 0 && recLen >= 0x17 {
    87  				if speed := uint(word(dmi, p+0x15)); speed != 0 {
    88  					si.Memory.Speed = speed
    89  				}
    90  			}
    91  		case 19:
    92  			start := uint(dword(dmi, p+0x04))
    93  			end := uint(dword(dmi, p+0x08))
    94  			if start == 0xffffffff && end == 0xffffffff {
    95  				if recLen >= 0x1f {
    96  					start64 := qword(dmi, p+0x0f)
    97  					end64 := qword(dmi, p+0x17)
    98  					memSizeAlt += uint((end64 - start64 + 1) / 1048576)
    99  				}
   100  			} else {
   101  				memSizeAlt += (end - start + 1) / 1024
   102  			}
   103  		case 127:
   104  			break loop
   105  		}
   106  
   107  		for p += int(recLen); p < len(dmi)-1; {
   108  			if bytes.Equal(dmi[p:p+2], []byte{0, 0}) {
   109  				p += 2
   110  				break
   111  			}
   112  			p++
   113  		}
   114  	}
   115  
   116  	// Sometimes DMI type 17 has no information, so we fall back to DMI type 19, to at least get the RAM size.
   117  	if si.Memory.Size == 0 && memSizeAlt > 0 {
   118  		si.Memory.Type = "DRAM"
   119  		si.Memory.Size = memSizeAlt
   120  	}
   121  }