github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+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 }