github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/exp/smbios_transfer/smbios_transfer.go (about) 1 // Copyright 2021 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 // smbios_transfer sends SMBIOS tables to BMC through IPMI blob interfaces. 6 // 7 // Synopsis: 8 // 9 // smbios_tranfer [-num_retries] 10 // 11 // Options: 12 // 13 // -num_retries: number of times to retry transferring SMBIOS tables 14 package main 15 16 import ( 17 "flag" 18 "fmt" 19 "log" 20 "os" 21 "path/filepath" 22 23 "github.com/mvdan/u-root-coreutils/pkg/ipmi" 24 "github.com/mvdan/u-root-coreutils/pkg/ipmi/blobs" 25 ) 26 27 const ( 28 maxWriteSize uint32 = 128 29 30 // IPMI blob ID on BMC 31 smbiosBlobID = "/smbios\x00" 32 33 sysfsPath = "/sys/firmware/dmi/tables" 34 ) 35 36 var retries = flag.Int("num_retries", 2, "Number of times to retry transferring SMBIOS tables") 37 38 func writeCommitSmbiosBlob(id string, data []uint8, h *blobs.BlobHandler) (rerr error) { 39 sessionID, err := h.BlobOpen(id, blobs.BMC_BLOB_OPEN_FLAG_WRITE) 40 if err != nil { 41 return fmt.Errorf("IPMI BlobOpen for %s failed: %v", id, err) 42 } 43 defer func() { 44 // If the function returned successfully but failed to close the blob, 45 // return an error. 46 if err := h.BlobClose(sessionID); err != nil { 47 err = fmt.Errorf("IPMI BlobClose %s failed: %v", id, err) 48 if rerr != nil { 49 rerr = fmt.Errorf("%v; %v", rerr, err) 50 return 51 } 52 rerr = err 53 } 54 }() 55 56 dataLen := uint32(len(data)) 57 58 // IPMI max message length defined in ipmi_msgdefs.h as IPMI_MAX_MSG_LENGTH. 59 // Read/write longer than the limit will be requested in multiple IPMI 60 // commands. 61 for offset := uint32(0); offset < dataLen; offset += maxWriteSize { 62 end := offset + maxWriteSize 63 if end > dataLen { 64 end = dataLen 65 } 66 if err = h.BlobWrite(sessionID, offset, data[offset:end]); err != nil { 67 return fmt.Errorf("IPMI BlobWrite %s failed: %v", id, err) 68 } 69 } 70 71 if err = h.BlobCommit(sessionID, []uint8{}); err != nil { 72 return fmt.Errorf("IPMI BlobCommit %s failed: %v", id, err) 73 } 74 75 return nil 76 } 77 78 func getSmbiosData() ([]uint8, error) { 79 tables, err := os.ReadFile(filepath.Join(sysfsPath, "DMI")) 80 if err != nil { 81 return nil, fmt.Errorf("error reading DMI data: %v", err) 82 } 83 84 entryPoint, err := os.ReadFile(filepath.Join(sysfsPath, "smbios_entry_point")) 85 if err != nil { 86 return nil, fmt.Errorf("error reading smbios_entry_point data: %v", err) 87 } 88 89 data := append(tables, entryPoint...) 90 return data, nil 91 } 92 93 func transferSmbiosData() error { 94 data, err := getSmbiosData() 95 if err != nil { 96 return fmt.Errorf("failed to get SMBIOS tables") 97 } 98 i, err := ipmi.Open(0) 99 if err != nil { 100 return err 101 } 102 h := blobs.NewBlobHandler(i) 103 104 blobCount, err := h.BlobGetCount() 105 if err != nil { 106 return fmt.Errorf("failed to get blob count: %v", err) 107 } 108 109 seen := false 110 for j := 0; j < blobCount; j++ { 111 id, err := h.BlobEnumerate(j) 112 if err != nil { 113 return fmt.Errorf("failed to enumerate blob %d: %v", j, err) 114 } 115 116 if id != smbiosBlobID { 117 continue 118 } 119 120 seen = true 121 if err = writeCommitSmbiosBlob(id, data, h); err != nil { 122 return fmt.Errorf("failed to write and commit blob %s: %v", id, err) 123 } 124 break 125 } 126 127 if !seen { 128 return fmt.Errorf("no smbios blob found") 129 } 130 131 return nil 132 } 133 134 func main() { 135 flag.Parse() 136 for r := 0; r < *retries; r++ { 137 log.Printf("Transferring SMBIOS tables, attempt %d/%d", r+1, *retries) 138 if err := transferSmbiosData(); err != nil { 139 log.Printf("Error tranferring SMBIOS tables over IPMI: %v", err) 140 } else { 141 log.Printf("SMBIOS tables are tranferred.") 142 break 143 } 144 } 145 }