github.com/TrenchBoot/u-root@v6.0.1-0.20200623220532-550e36da3258+incompatible/pkg/ipmi/oem.go (about)

     1  // Copyright 2020 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ipmi
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"strconv"
    11  	"strings"
    12  	"unsafe"
    13  
    14  	"github.com/u-root/u-root/pkg/smbios"
    15  )
    16  
    17  const (
    18  	_IPMI_FB_OEM_NET_FUNCTION1 = 0x30
    19  	_IPMI_FB_OEM_NET_FUNCTION2 = 0x36
    20  
    21  	_FB_OEM_SET_PROC_INFO       = 0x10
    22  	_FB_OEM_SET_DIMM_INFO       = 0x12
    23  	_FB_OEM_SET_BIOS_BOOT_ORDER = 0x52
    24  	_FB_OEM_GET_BIOS_BOOT_ORDER = 0x53
    25  )
    26  
    27  type ProcessorInfo struct {
    28  	ManufacturerID        [3]uint8
    29  	Index                 uint8
    30  	ParameterSelector     uint8
    31  	ProductName           [48]byte
    32  	CoreNumber            uint8
    33  	ThreadNumberLSB       uint8
    34  	ThreadNumberMSB       uint8
    35  	ProcessorFrequencyLSB uint8
    36  	ProcessorFrequencyMSB uint8
    37  	Revision1             uint8
    38  	Revision2             uint8
    39  }
    40  
    41  type DimmInfo struct {
    42  	ManufacturerID          [3]uint8
    43  	Index                   uint8
    44  	ParameterSelector       uint8
    45  	DIMMPresent             uint8
    46  	NodeNumber              uint8
    47  	ChannelNumber           uint8
    48  	DIMMNumber              uint8
    49  	DIMMType                uint8
    50  	DIMMSpeed               uint16
    51  	DIMMSize                uint32
    52  	ModulePartNumber        [20]byte
    53  	ModuleSerialNumber      uint32
    54  	ModuleManufacturerIDLSB uint8
    55  	ModuleManufacturerIDMSB uint8
    56  }
    57  
    58  var BoardManufacturer = map[string][3]uint8{
    59  	"Wiwynn": {0x0, 0x9c, 0x9c},
    60  }
    61  
    62  func (i *IPMI) SendOemIpmiProcessorInfo(info []ProcessorInfo) error {
    63  	req := &req{}
    64  	req.msg.cmd = _FB_OEM_SET_PROC_INFO
    65  	req.msg.netfn = _IPMI_FB_OEM_NET_FUNCTION2
    66  
    67  	for index := 0; index < len(info); index++ {
    68  		for param := 1; param <= 2; param++ {
    69  			data, err := info[index].marshall(param)
    70  			if err != nil {
    71  				return err
    72  			}
    73  
    74  			req.msg.data = unsafe.Pointer(&data[0])
    75  			req.msg.dataLen = uint16(len(data))
    76  
    77  			_, err = i.sendrecv(req)
    78  			if err != nil {
    79  				return err
    80  			}
    81  		}
    82  	}
    83  	return nil
    84  }
    85  
    86  func (i *IPMI) SendOemIpmiDimmInfo(info []DimmInfo) error {
    87  	req := &req{}
    88  	req.msg.cmd = _FB_OEM_SET_DIMM_INFO
    89  	req.msg.netfn = _IPMI_FB_OEM_NET_FUNCTION2
    90  
    91  	for index := 0; index < len(info); index++ {
    92  		for param := 1; param <= 6; param++ {
    93  			//If DIMM is not present, only send the information of DIMM location
    94  			if info[index].DIMMPresent != 0x01 && param >= 2 {
    95  				continue
    96  			}
    97  
    98  			data, err := info[index].marshall(param)
    99  			if err != nil {
   100  				return err
   101  			}
   102  
   103  			req.msg.data = unsafe.Pointer(&data[0])
   104  			req.msg.dataLen = uint16(len(data))
   105  
   106  			_, err = i.sendrecv(req)
   107  			if err != nil {
   108  				return err
   109  			}
   110  		}
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (p *ProcessorInfo) marshall(param int) ([]byte, error) {
   117  	var data []byte
   118  	buf := &bytes.Buffer{}
   119  
   120  	if err := binary.Write(buf, binary.LittleEndian, *p); err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	buf.Bytes()[4] = byte(param)
   125  
   126  	switch param {
   127  	case 1:
   128  		data = make([]byte, 53)
   129  		copy(data[:], buf.Bytes()[:53])
   130  	case 2:
   131  		data = make([]byte, 12)
   132  		copy(data[0:5], buf.Bytes()[0:5])
   133  		copy(data[5:12], buf.Bytes()[53:60])
   134  	}
   135  
   136  	return data, nil
   137  }
   138  
   139  func (d *DimmInfo) marshall(param int) ([]byte, error) {
   140  	var data []byte
   141  	buf := &bytes.Buffer{}
   142  
   143  	if err := binary.Write(buf, binary.LittleEndian, *d); err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	buf.Bytes()[4] = byte(param)
   148  
   149  	switch param {
   150  	case 1:
   151  		data = make([]byte, 9)
   152  		copy(data[:], buf.Bytes()[:9])
   153  	case 2:
   154  		data = make([]byte, 6)
   155  		copy(data[0:5], buf.Bytes()[0:5])
   156  		copy(data[5:6], buf.Bytes()[9:10])
   157  	case 3:
   158  		data = make([]byte, 11)
   159  		copy(data[0:5], buf.Bytes()[0:5])
   160  		copy(data[5:11], buf.Bytes()[10:16])
   161  	case 4:
   162  		data = make([]byte, 25)
   163  		copy(data[0:5], buf.Bytes()[0:5])
   164  		copy(data[5:25], buf.Bytes()[16:36])
   165  	case 5:
   166  		data = make([]byte, 9)
   167  		copy(data[0:5], buf.Bytes()[0:5])
   168  		copy(data[5:9], buf.Bytes()[36:40])
   169  	case 6:
   170  		data = make([]byte, 7)
   171  		copy(data[0:5], buf.Bytes()[0:5])
   172  		copy(data[5:7], buf.Bytes()[40:42])
   173  	}
   174  
   175  	return data, nil
   176  }
   177  
   178  func GetOemIpmiProcessorInfo(si *smbios.Info) ([]ProcessorInfo, error) {
   179  	t1, err := si.GetSystemInfo()
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	t4, err := si.GetProcessorInfo()
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	info := make([]ProcessorInfo, len(t4))
   190  
   191  	boardManufacturerID, ok := BoardManufacturer[t1.Manufacturer]
   192  
   193  	for index := 0; index < len(t4); index++ {
   194  		if ok {
   195  			info[index].ManufacturerID = boardManufacturerID
   196  		}
   197  
   198  		info[index].Index = uint8(index)
   199  		copy(info[index].ProductName[:], t4[index].Version)
   200  		info[index].CoreNumber = uint8(t4[index].GetCoreCount())
   201  		info[index].ThreadNumberLSB = uint8(t4[index].GetThreadCount() & 0x00ff)
   202  		info[index].ThreadNumberMSB = uint8(t4[index].GetThreadCount() >> 8)
   203  		info[index].ProcessorFrequencyLSB = uint8(t4[index].MaxSpeed & 0x00ff)
   204  		info[index].ProcessorFrequencyMSB = uint8(t4[index].MaxSpeed >> 8)
   205  		info[index].Revision1 = 0
   206  		info[index].Revision2 = 0
   207  	}
   208  
   209  	return info, nil
   210  }
   211  
   212  // DIMM type: bit[7:6] for DDR3 00-Normal Voltage(1.5V), 01-Ultra Low Voltage(1.25V), 10-Low Voltage(1.35V), 11-Reserved
   213  //                     for DDR4 00~10-Reserved, 11-Normal Voltage(1.2V)
   214  //            bit[5:0] 0x00=SDRAM, 0x01=DDR1 RAM, 0x02-Rambus, 0x03-DDR2 RAM, 0x04-FBDIMM, 0x05-DDR3 RAM, 0x06-DDR4 RAM
   215  func detectDimmType(meminfo *DimmInfo, t17 *smbios.MemoryDevice) {
   216  	if t17.Type == smbios.MemoryDeviceTypeDDR3 {
   217  		switch t17.ConfiguredVoltage {
   218  		case 1500:
   219  			meminfo.DIMMType = 0x05
   220  		case 1250:
   221  			meminfo.DIMMType = 0x45
   222  		case 1350:
   223  			meminfo.DIMMType = 0x85
   224  		default:
   225  			meminfo.DIMMType = 0x05
   226  		}
   227  	} else {
   228  		switch t17.Type {
   229  		case smbios.MemoryDeviceTypeSDRAM:
   230  			meminfo.DIMMType = 0x00
   231  		case smbios.MemoryDeviceTypeDDR:
   232  			meminfo.DIMMType = 0x01
   233  		case smbios.MemoryDeviceTypeRDRAM:
   234  			meminfo.DIMMType = 0x02
   235  		case smbios.MemoryDeviceTypeDDR2:
   236  			meminfo.DIMMType = 0x03
   237  		case smbios.MemoryDeviceTypeDDR2FBDIMM:
   238  			meminfo.DIMMType = 0x04
   239  		case smbios.MemoryDeviceTypeDDR4:
   240  			meminfo.DIMMType = 0xC6
   241  		default:
   242  			meminfo.DIMMType = 0xC6
   243  		}
   244  	}
   245  }
   246  
   247  func GetOemIpmiDimmInfo(si *smbios.Info) ([]DimmInfo, error) {
   248  	t1, err := si.GetSystemInfo()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	t17, err := si.GetMemoryDevices()
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	info := make([]DimmInfo, len(t17))
   259  
   260  	boardManufacturerID, ok := BoardManufacturer[t1.Manufacturer]
   261  
   262  	for index := 0; index < len(t17); index++ {
   263  		if ok {
   264  			info[index].ManufacturerID = boardManufacturerID
   265  		}
   266  
   267  		info[index].Index = uint8(index)
   268  
   269  		if t17[index].AssetTag == "NO DIMM" {
   270  			info[index].DIMMPresent = 0xFF // 0xFF - Not Present
   271  		} else {
   272  			info[index].DIMMPresent = 0x01 // 0x01 - Present
   273  		}
   274  
   275  		data := strings.Split(strings.TrimPrefix(t17[index].BankLocator, "_"), "_")
   276  		dimm, _ := strconv.ParseUint(strings.TrimPrefix(data[2], "Dimm"), 16, 8)
   277  		channel, _ := strconv.ParseUint(strings.TrimPrefix(data[1], "Channel"), 16, 8)
   278  		node, _ := strconv.ParseUint(strings.TrimPrefix(data[0], "Node"), 16, 8)
   279  		info[index].DIMMNumber = uint8(dimm)
   280  		info[index].ChannelNumber = uint8(channel)
   281  		info[index].NodeNumber = uint8(node)
   282  		detectDimmType(&info[index], t17[index])
   283  		info[index].DIMMSpeed = t17[index].Speed
   284  		info[index].DIMMSize = uint32(t17[index].Size)
   285  		copy(info[index].ModulePartNumber[:], t17[index].PartNumber)
   286  		sn, _ := strconv.ParseUint(t17[index].SerialNumber, 16, 32)
   287  		info[index].ModuleSerialNumber = uint32(sn)
   288  		memoryDeviceManufacturerID, ok := smbios.MemoryDeviceManufacturer[t17[index].Manufacturer]
   289  		if ok {
   290  			info[index].ModuleManufacturerIDLSB = uint8(memoryDeviceManufacturerID & 0x00ff)
   291  			info[index].ModuleManufacturerIDMSB = uint8(memoryDeviceManufacturerID >> 8)
   292  		}
   293  	}
   294  
   295  	return info, nil
   296  }
   297  
   298  // Get BIOS boot order data and check if CMOS clear bit and valid bit are both set
   299  func (i *IPMI) IsCMOSClearSet() (bool, []byte, error) {
   300  	req := &req{}
   301  	req.msg.cmd = _FB_OEM_GET_BIOS_BOOT_ORDER
   302  	req.msg.netfn = _IPMI_FB_OEM_NET_FUNCTION1
   303  
   304  	recv, err := i.sendrecv(req)
   305  	if err != nil {
   306  		return false, nil, err
   307  	}
   308  	// recv[1] bit 1: CMOS clear, bit 7: valid bit, check if both are set
   309  	if len(recv) > 6 && (recv[1]&0x82) == 0x82 {
   310  		return true, recv[1:], nil
   311  	}
   312  	return false, nil, nil
   313  }
   314  
   315  // Set BIOS boot order with both CMOS clear and valid bits cleared
   316  func (i *IPMI) ClearCMOSClearValidBits(data []byte) error {
   317  	req := &req{}
   318  	req.msg.cmd = _FB_OEM_SET_BIOS_BOOT_ORDER
   319  	req.msg.netfn = _IPMI_FB_OEM_NET_FUNCTION1
   320  	// Clear bit 1 and bit 7
   321  	data[0] &= 0x7d
   322  	req.msg.data = unsafe.Pointer(&data[0])
   323  	req.msg.dataLen = 6
   324  
   325  	if _, err := i.sendrecv(req); err != nil {
   326  		return err
   327  	}
   328  	return nil
   329  }