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 }