github.com/rminnich/u-root@v7.0.0+incompatible/pkg/smbios/table.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 smbios
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"strings"
    13  )
    14  
    15  // Table is a generic type of table that does not parsed fields,
    16  // it only allows access to its contents by offset.
    17  type Table struct {
    18  	Header
    19  	data    []byte   `smbios:"-"` // Structured part of the table.
    20  	strings []string `smbios:"-"` // Strings section.
    21  }
    22  
    23  var (
    24  	// ErrTableNotFound is retuned if table with specified type is not found.
    25  	ErrTableNotFound = errors.New("table not found")
    26  
    27  	// ErrUnsupportedTableType is returned by ParseTypedTable if this table type is not supported and cannot be parsed.
    28  	ErrUnsupportedTableType = errors.New("unsupported table type")
    29  
    30  	errEndOfTable = errors.New("end of table")
    31  
    32  	tableSep = []byte{0, 0}
    33  )
    34  
    35  const (
    36  	outOfSpec = "<OUT OF SPEC>"
    37  )
    38  
    39  // Len returns length of the structured part of the table.
    40  func (t *Table) Len() int {
    41  	return len(t.data)
    42  }
    43  
    44  // GetByteAt returns a byte from the structured part at the specified offset.
    45  func (t *Table) GetByteAt(offset int) (uint8, error) {
    46  	if offset > len(t.data)-1 {
    47  		return 0, fmt.Errorf("invalid offset %d", offset)
    48  	}
    49  	return t.data[offset], nil
    50  }
    51  
    52  // GetBytesAt returns a number of bytes from the structured part at the specified offset.
    53  func (t *Table) GetBytesAt(offset, length int) ([]byte, error) {
    54  	if offset > len(t.data)-length {
    55  		return nil, fmt.Errorf("invalid offset %d", offset)
    56  	}
    57  	return t.data[offset : offset+length], nil
    58  }
    59  
    60  // GetWordAt returns a 16-bit word from the structured part at the specified offset.
    61  func (t *Table) GetWordAt(offset int) (res uint16, err error) {
    62  	if offset > len(t.data)-2 {
    63  		return 0, fmt.Errorf("invalid offset %d", offset)
    64  	}
    65  	err = binary.Read(bytes.NewReader(t.data[offset:offset+2]), binary.LittleEndian, &res)
    66  	return res, err
    67  }
    68  
    69  // GetDWordAt returns a 32-bit word from the structured part at the specified offset.
    70  func (t *Table) GetDWordAt(offset int) (res uint32, err error) {
    71  	if offset > len(t.data)-4 {
    72  		return 0, fmt.Errorf("invalid offset %d", offset)
    73  	}
    74  	err = binary.Read(bytes.NewReader(t.data[offset:offset+4]), binary.LittleEndian, &res)
    75  	return res, err
    76  }
    77  
    78  // GetQWordAt returns a 64-bit word from the structured part at the specified offset.
    79  func (t *Table) GetQWordAt(offset int) (res uint64, err error) {
    80  	if offset > len(t.data)-8 {
    81  		return 0, fmt.Errorf("invalid offset %d", offset)
    82  	}
    83  	err = binary.Read(bytes.NewReader(t.data[offset:offset+8]), binary.LittleEndian, &res)
    84  	return res, err
    85  }
    86  
    87  // GetStringAt returns a string pointed to by the byte at the specified offset in the structured part.
    88  // NB: offset is not the string index.
    89  func (t *Table) GetStringAt(offset int) (string, error) {
    90  	if offset >= len(t.data) {
    91  		return "", fmt.Errorf("invalid offset %d", offset)
    92  	}
    93  	stringIndex := t.data[offset]
    94  	switch {
    95  	case stringIndex == 0:
    96  		return "Not Specified", nil
    97  	case int(stringIndex) <= len(t.strings):
    98  		return t.strings[stringIndex-1], nil
    99  	default:
   100  		return "<BAD INDEX>", fmt.Errorf("invalid string index %d", stringIndex)
   101  	}
   102  }
   103  
   104  func (t *Table) String() string {
   105  	lines := []string{
   106  		t.Header.String(),
   107  		"\tHeader and Data:",
   108  	}
   109  	data := t.data
   110  	for len(data) > 0 {
   111  		ld := data
   112  		if len(ld) > 16 {
   113  			ld = ld[:16]
   114  		}
   115  		ls := make([]string, len(ld))
   116  		for i, d := range ld {
   117  			ls[i] = fmt.Sprintf("%02X", d)
   118  		}
   119  		lines = append(lines, "\t\t"+strings.Join(ls, " "))
   120  		data = data[len(ld):]
   121  	}
   122  	if len(t.strings) > 0 {
   123  		lines = append(lines, "\tStrings:")
   124  		for _, s := range t.strings {
   125  			lines = append(lines, "\t\t"+s)
   126  		}
   127  	}
   128  	return strings.Join(lines, "\n")
   129  }
   130  
   131  // ParseTable parses a table from byte stream.
   132  // Returns the parsed table and remaining data.
   133  func ParseTable(data []byte) (*Table, []byte, error) {
   134  	var err error
   135  	var h Header
   136  	if err = h.Parse(data); err != nil {
   137  		return nil, data, err
   138  	}
   139  	if len(data) < int(h.Length)+2 /* string terminator length */ {
   140  		return nil, data, errors.New("data too short")
   141  	}
   142  	structData := data[:h.Length]
   143  	data = data[h.Length:]
   144  	stringData := data
   145  	var strings []string
   146  	for len(data) > 0 && err == nil {
   147  		end := bytes.IndexByte(stringData, 0)
   148  		if end < 0 {
   149  			return nil, data, errors.New("unterminated string")
   150  		}
   151  		s := string(stringData[:end])
   152  		stringData = stringData[end+1:]
   153  		if len(s) > 0 {
   154  			strings = append(strings, s)
   155  		}
   156  		if end == 0 { // End of strings
   157  			break
   158  		}
   159  	}
   160  	// One would think that next table always follows previous table's strings.
   161  	// One would be wrong.
   162  	endOfTableIndex := bytes.Index(data, tableSep)
   163  	if endOfTableIndex < 0 {
   164  		return nil, nil, errors.New("end of table not found")
   165  	}
   166  	data = data[endOfTableIndex+2:]
   167  	if h.Type == TableTypeEndOfTable {
   168  		err = errEndOfTable
   169  	}
   170  	return &Table{Header: h, data: structData, strings: strings}, data, err
   171  }
   172  
   173  func kmgt(v uint64) string {
   174  	switch {
   175  	case v >= 1024*1024*1024*1024 && v%(1024*1024*1024*1024) == 0:
   176  		return fmt.Sprintf("%d TB", v/(1024*1024*1024*1024))
   177  	case v >= 1024*1024*1024 && v%(1024*1024*1024) == 0:
   178  		return fmt.Sprintf("%d GB", v/(1024*1024*1024))
   179  	case v >= 1024*1024 && v%(1024*1024) == 0:
   180  		return fmt.Sprintf("%d MB", v/(1024*1024))
   181  	case v >= 1024 && v%1024 == 0:
   182  		return fmt.Sprintf("%d kB", v/1024)
   183  	default:
   184  		return fmt.Sprintf("%d bytes", v)
   185  	}
   186  }