github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+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  
    33  // Len returns length of the structured part of the table.
    34  func (t *Table) Len() int {
    35  	return len(t.data)
    36  }
    37  
    38  // GetByteAt returns a byte from the structured part at the specified offset.
    39  func (t *Table) GetByteAt(offset int) (uint8, error) {
    40  	if offset > len(t.data)-1 {
    41  		return 0, fmt.Errorf("invalid offset %d", offset)
    42  	}
    43  	return t.data[offset], nil
    44  }
    45  
    46  // GetBytesAt returns a number of bytes from the structured part at the specified offset.
    47  func (t *Table) GetBytesAt(offset, length int) ([]byte, error) {
    48  	if offset > len(t.data)-length {
    49  		return nil, fmt.Errorf("invalid offset %d", offset)
    50  	}
    51  	return t.data[offset : offset+length], nil
    52  }
    53  
    54  // GetWordAt returns a 16-bit word from the structured part at the specified offset.
    55  func (t *Table) GetWordAt(offset int) (res uint16, err error) {
    56  	if offset > len(t.data)-2 {
    57  		return 0, fmt.Errorf("invalid offset %d", offset)
    58  	}
    59  	err = binary.Read(bytes.NewReader(t.data[offset:offset+2]), binary.LittleEndian, &res)
    60  	return res, err
    61  }
    62  
    63  // GetDWordAt returns a 32-bit word from the structured part at the specified offset.
    64  func (t *Table) GetDWordAt(offset int) (res uint32, err error) {
    65  	if offset > len(t.data)-4 {
    66  		return 0, fmt.Errorf("invalid offset %d", offset)
    67  	}
    68  	err = binary.Read(bytes.NewReader(t.data[offset:offset+4]), binary.LittleEndian, &res)
    69  	return res, err
    70  }
    71  
    72  // GetQWordAt returns a 64-bit word from the structured part at the specified offset.
    73  func (t *Table) GetQWordAt(offset int) (res uint64, err error) {
    74  	if offset > len(t.data)-8 {
    75  		return 0, fmt.Errorf("invalid offset %d", offset)
    76  	}
    77  	err = binary.Read(bytes.NewReader(t.data[offset:offset+8]), binary.LittleEndian, &res)
    78  	return res, err
    79  }
    80  
    81  // GetStringAt returns a string pointed to by the byte at the specified offset in the structured part.
    82  // NB: offset is not the string index.
    83  func (t *Table) GetStringAt(offset int) (string, error) {
    84  	if offset >= len(t.data) {
    85  		return "", fmt.Errorf("invalid offset %d", offset)
    86  	}
    87  	stringIndex := t.data[offset]
    88  	switch {
    89  	case stringIndex == 0:
    90  		return "", nil
    91  	case int(stringIndex) <= len(t.strings):
    92  		return t.strings[stringIndex-1], nil
    93  	default:
    94  		return "", fmt.Errorf("invalid string index %d", stringIndex)
    95  	}
    96  }
    97  
    98  func (t *Table) String() string {
    99  	lines := []string{
   100  		t.Header.String(),
   101  		"\tHeader and Data:",
   102  	}
   103  	data := t.data
   104  	for len(data) > 0 {
   105  		ld := data
   106  		if len(ld) > 16 {
   107  			ld = ld[:16]
   108  		}
   109  		ls := make([]string, len(ld))
   110  		for i, d := range ld {
   111  			ls[i] = fmt.Sprintf("%02X", d)
   112  		}
   113  		lines = append(lines, "\t\t"+strings.Join(ls, " "))
   114  		data = data[len(ld):]
   115  	}
   116  	if len(t.strings) > 0 {
   117  		lines = append(lines, "\tStrings:")
   118  		for _, s := range t.strings {
   119  			lines = append(lines, "\t\t"+s)
   120  		}
   121  	}
   122  	return strings.Join(lines, "\n")
   123  }
   124  
   125  // ParseTable parses a table from byte stream.
   126  // Returns the parsed table and remaining data.
   127  func ParseTable(data []byte) (*Table, []byte, error) {
   128  	var err error
   129  	var h Header
   130  	if err = h.Parse(data); err != nil {
   131  		return nil, data, err
   132  	}
   133  	if len(data) < int(h.Length)+2 /* string terminator length */ {
   134  		return nil, data, errors.New("data too short")
   135  	}
   136  	structData := data[:h.Length]
   137  	data = data[h.Length:]
   138  	var strings []string
   139  	for len(data) > 0 && err == nil {
   140  		end := bytes.IndexByte(data, 0)
   141  		if end < 0 {
   142  			return nil, data, errors.New("unterminated string")
   143  		}
   144  		s := string(data[:end])
   145  		data = data[end+1:]
   146  		if len(s) > 0 {
   147  			strings = append(strings, s)
   148  		}
   149  		if end == 0 { // End of strings
   150  			if len(strings) == 0 {
   151  				// There's an extra 0 at the end.
   152  				data = data[1:]
   153  			}
   154  			break
   155  		}
   156  	}
   157  	if h.Type == TableTypeEndOfTable {
   158  		err = errEndOfTable
   159  	}
   160  	return &Table{Header: h, data: structData, strings: strings}, data, err
   161  }