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  }