github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/dmidecode/dmidecode.go (about)

     1  // Copyright 2016-2019 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 main
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"strconv"
    12  	"strings"
    13  
    14  	flag "github.com/spf13/pflag"
    15  
    16  	"github.com/u-root/u-root/pkg/smbios"
    17  )
    18  
    19  var (
    20  	flagDumpBin  = flag.String("dump-bin", "", `Do not decode the entries, instead dump the DMI data to a file in binary form. The generated file is suitable to pass to --from-dump later.`)
    21  	flagFromDump = flag.String("from-dump", "", `Read the DMI data from a binary file previously generated using --dump-bin.`)
    22  	flagType     = flag.StringSliceP("type", "t", nil, `Only  display  the  entries of type TYPE. TYPE can be either a DMI type number, or a comma-separated list of type numbers, or a keyword from the following list: bios, system, baseboard, chassis, processor, memory, cache, connector, slot. If this option is used more than once, the set of displayed entries will be the union of all the given types. If TYPE is not provided or not valid, a list of all valid keywords is printed and dmidecode exits with an error.`)
    23  	// NB: When adding flags, update resetFlags in dmidecode_test.
    24  )
    25  
    26  var (
    27  	typeGroups = map[string][]uint8{
    28  		"bios":      {0, 13},
    29  		"system":    {1, 12, 15, 23, 32},
    30  		"baseboard": {2, 10, 41},
    31  		"chassis":   {3},
    32  		"processor": {4},
    33  		"memory":    {5, 6, 16, 17},
    34  		"cache":     {7},
    35  		"connector": {8},
    36  		"slot":      {9},
    37  	}
    38  )
    39  
    40  type dmiDecodeError struct {
    41  	error
    42  	code int
    43  }
    44  
    45  // parseTypeFilter parses the --type argument(s) and returns a set of types taht should be included.
    46  func parseTypeFilter(typeStrings []string) (map[smbios.TableType]bool, error) {
    47  	types := map[smbios.TableType]bool{}
    48  	for _, ts := range typeStrings {
    49  		if tg, ok := typeGroups[strings.ToLower(ts)]; ok {
    50  			for _, t := range tg {
    51  				types[smbios.TableType(t)] = true
    52  			}
    53  		} else {
    54  			u, err := strconv.ParseUint(ts, 0, 8)
    55  			if err != nil {
    56  				return nil, fmt.Errorf("Invalid type: %s", ts)
    57  			}
    58  			types[smbios.TableType(uint8(u))] = true
    59  		}
    60  	}
    61  	return types, nil
    62  }
    63  
    64  func dumpBin(textOut io.Writer, entryData, tableData []byte, fileName string) *dmiDecodeError {
    65  	// Need to rewrite address to be compatible with dmidecode(8).
    66  	e32, e64, err := smbios.ParseEntry(entryData)
    67  	if err != nil {
    68  		return &dmiDecodeError{code: 1, error: fmt.Errorf("error parsing entry point structure: %v", err)}
    69  	}
    70  	var edata []byte
    71  	switch {
    72  	case e32 != nil:
    73  		e32.StructTableAddr = 0x20
    74  		edata, _ = e32.MarshalBinary()
    75  	case e64 != nil:
    76  		e64.StructTableAddr = 0x20
    77  		edata, _ = e64.MarshalBinary()
    78  	}
    79  	f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0644)
    80  	if err != nil {
    81  		return &dmiDecodeError{code: 1, error: fmt.Errorf("error opening file for writing: %v", err)}
    82  	}
    83  	defer f.Close()
    84  	fmt.Fprintf(textOut, "# Writing %d bytes to %s.\n", len(edata), fileName)
    85  	if _, err := f.Write(edata); err != nil {
    86  		return &dmiDecodeError{code: 1, error: fmt.Errorf("error writing entry: %v", err)}
    87  	}
    88  	for i := len(edata); i < 0x20; i++ {
    89  		if _, err := f.Write([]byte{0}); err != nil {
    90  			return &dmiDecodeError{code: 1, error: fmt.Errorf("error writing entry: %v", err)}
    91  		}
    92  	}
    93  	fmt.Fprintf(textOut, "# Writing %d bytes to %s.\n", len(tableData), fileName)
    94  	if _, err := f.Write(tableData); err != nil {
    95  		return &dmiDecodeError{code: 1, error: fmt.Errorf("error writing table data: %v", err)}
    96  	}
    97  	return nil
    98  }
    99  
   100  func dmiDecode(textOut io.Writer) *dmiDecodeError {
   101  	typeFilter, err := parseTypeFilter(*flagType)
   102  	if err != nil {
   103  		return &dmiDecodeError{code: 2, error: fmt.Errorf("invalid --type: %v", err)}
   104  	}
   105  	fmt.Fprintf(textOut, "# dmidecode-go\n") // TODO: version.
   106  	entryData, tableData, err := getData(textOut, *flagFromDump, "/sys/firmware/dmi/tables")
   107  	if err != nil {
   108  		return &dmiDecodeError{code: 1, error: fmt.Errorf("error parsing loading data: %v", err)}
   109  	}
   110  	if *flagDumpBin != "" {
   111  		return dumpBin(textOut, entryData, tableData, *flagDumpBin)
   112  	}
   113  	si, err := smbios.ParseInfo(entryData, tableData)
   114  	if err != nil {
   115  		return &dmiDecodeError{code: 1, error: fmt.Errorf("error parsing data: %v", err)}
   116  	}
   117  	if si.Entry64 != nil {
   118  		fmt.Fprintf(textOut, "SMBIOS %d.%d.%d present.\n", si.MajorVersion(), si.MinorVersion(), si.DocRev())
   119  	} else {
   120  		fmt.Fprintf(textOut, "SMBIOS %d.%d present.\n", si.MajorVersion(), si.MinorVersion())
   121  	}
   122  	if si.Entry32 != nil {
   123  		fmt.Fprintf(textOut, "%d structures occupying %d bytes.\n", si.Entry32.NumberOfStructs, si.Entry32.StructTableLength)
   124  	}
   125  	fmt.Fprintf(textOut, "\n")
   126  	for _, t := range si.Tables {
   127  		if len(typeFilter) != 0 && !typeFilter[t.Type] {
   128  			continue
   129  		}
   130  		pt, err := smbios.ParseTypedTable(t)
   131  		if err != nil {
   132  			if err != smbios.ErrUnsupportedTableType {
   133  				fmt.Fprintf(os.Stderr, "%s\n", err)
   134  			}
   135  			// Print as raw table
   136  			pt = t
   137  		}
   138  		fmt.Fprintf(textOut, "%s\n\n", pt)
   139  	}
   140  	return nil
   141  }
   142  
   143  func main() {
   144  	flag.Parse()
   145  	err := dmiDecode(os.Stdout)
   146  	if err != nil {
   147  		fmt.Fprintf(os.Stderr, "%s\n", err)
   148  		os.Exit(err.code)
   149  	}
   150  }