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