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 }