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 }