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