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 }