Skip to content

Commit

Permalink
Convert structs to SMBIOS tables
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Koch <[email protected]>
  • Loading branch information
hugelgupf committed Mar 25, 2024
1 parent 89d58af commit 3a88068
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
78 changes: 78 additions & 0 deletions dmidecode/struct_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ type fieldParser interface {
ParseField(t *smbios.Table, off int) (int, error)
}

type fieldWriter interface {
WriteField(t *smbios.Table) (int, error)
}

var (
fieldTagKey = "smbios" // Tag key for annotations.
fieldParserInterfaceType = reflect.TypeOf((*fieldParser)(nil)).Elem()
fieldWriterInterfaceType = reflect.TypeOf((*fieldWriter)(nil)).Elem()
)

func parseStruct(t *smbios.Table, off int, complete bool, sp interface{}) (int, error) {
Expand Down Expand Up @@ -148,3 +153,76 @@ func parseStruct(t *smbios.Table, off int, complete bool, sp interface{}) (int,

return off, nil
}

func toTable(t *smbios.Table, sp interface{}) (int, error) {
sv, ok := sp.(reflect.Value)
if !ok {
sv = reflect.Indirect(reflect.ValueOf(sp)) // must be a pointer to struct then, dereference it
}
svtn := sv.Type().Name()

//fmt.Printf("toTable t %s\n", svtn)

Check failure on line 164 in dmidecode/struct_parser.go

View workflow job for this annotation

GitHub Actions / lint

commentFormatting: put a space between `//` and comment text (gocritic)

n := 0
for i := 0; i < sv.NumField(); i++ {
f := sv.Type().Field(i)
fv := sv.Field(i)
ft := fv.Type()
tags := f.Tag.Get(fieldTagKey)
//fmt.Printf("toTable XX %02Xh f %s t %s k %s %s\n", n, f.Name, f.Type.Name(), fv.Kind(), tags)

Check failure on line 172 in dmidecode/struct_parser.go

View workflow job for this annotation

GitHub Actions / lint

commentFormatting: put a space between `//` and comment text (gocritic)
// Check tags first
ignore := false
for _, tag := range strings.Split(tags, ",") {
tp := strings.Split(tag, "=")
switch tp[0] {

Check failure on line 177 in dmidecode/struct_parser.go

View workflow job for this annotation

GitHub Actions / lint

singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
case "-":
ignore = true
}
}
if ignore {
continue
}
// fieldWriter takes precedence.
if reflect.PtrTo(ft).Implements(fieldWriterInterfaceType) {
m, err := fv.Addr().Interface().(fieldWriter).WriteField(t)
n += m
if err != nil {
return n, err

Check warning on line 190 in dmidecode/struct_parser.go

View check run for this annotation

Codecov / codecov/patch

dmidecode/struct_parser.go#L187-L190

Added lines #L187 - L190 were not covered by tests
}
continue

Check warning on line 192 in dmidecode/struct_parser.go

View check run for this annotation

Codecov / codecov/patch

dmidecode/struct_parser.go#L192

Added line #L192 was not covered by tests
}

var verr error
switch fv.Kind() {
case reflect.Uint8:
t.WriteByte(uint8(fv.Uint()))
n++
case reflect.Uint16:
t.WriteWord(uint16(fv.Uint()))
n += 2
case reflect.Uint32:
t.WriteDWord(uint32(fv.Uint()))
n += 4

Check warning on line 205 in dmidecode/struct_parser.go

View check run for this annotation

Codecov / codecov/patch

dmidecode/struct_parser.go#L203-L205

Added lines #L203 - L205 were not covered by tests
case reflect.Uint64:
t.WriteQWord(uint64(fv.Uint()))

Check failure on line 207 in dmidecode/struct_parser.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary conversion (unconvert)
n += 8
case reflect.String:
t.WriteString(fv.String())
n++
case reflect.Array:
t.WriteBytes(fv.Slice(0, fv.Len()).Bytes())
n += fv.Len()

Check warning on line 214 in dmidecode/struct_parser.go

View check run for this annotation

Codecov / codecov/patch

dmidecode/struct_parser.go#L212-L214

Added lines #L212 - L214 were not covered by tests
case reflect.Struct:
var m int
// If it's a struct, just invoke toTable recursively.
m, verr = toTable(t, fv)
n += m
default:
return n, fmt.Errorf("%s.%s: unsupported type %s", svtn, f.Name, fv.Kind())

Check warning on line 221 in dmidecode/struct_parser.go

View check run for this annotation

Codecov / codecov/patch

dmidecode/struct_parser.go#L220-L221

Added lines #L220 - L221 were not covered by tests
}
if verr != nil {
return n, fmt.Errorf("failed to parse %s.%s: %w", svtn, f.Name, verr)

Check warning on line 224 in dmidecode/struct_parser.go

View check run for this annotation

Codecov / codecov/patch

dmidecode/struct_parser.go#L224

Added line #L224 was not covered by tests
}
}
return n, nil
}
58 changes: 58 additions & 0 deletions dmidecode/struct_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,63 @@ func TestParseStructWithTPMDevice(t *testing.T) {
}
})
}
}

func TestToTable(t *testing.T) {
type foobar struct {
Foo uint8 `smbios:"default=0xe"`
}
type someStruct struct {
Off0 uint64
Off8 uint8
Off9 string
_ uint8 `smbios:"-"`
Off10 uint16
_ uint8 `smbios:"-"`
Off11 uint8 `smbios:"default=0xf"`
Off12 foobar
}

for _, tt := range []struct {
value any
err error
want *smbios.Table
}{
{
value: &someStruct{
Off0: 0x01,
Off8: 0xff,
Off9: "foobar",
Off10: 0x102,
Off11: 0x05,
Off12: foobar{
Foo: 0xe,
},
},
want: &smbios.Table{
Data: []byte{
0x1, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0xff, // Off8
0x1, // Off9
0x2, 0x1, // Off10
0x5, // Off11
0xe, // foobar
},
Strings: []string{
"foobar",
},
},
},
} {
t.Run("", func(t *testing.T) {
got := &smbios.Table{}
if _, err := toTable(got, tt.value); !errors.Is(err, tt.err) {
t.Errorf("toTable = %v, want %v", err, tt.err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("toTable = %v, want %v", got, tt.want)
}
})
}
}
39 changes: 39 additions & 0 deletions table.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,45 @@ func (t *Table) Len() int {
return len(t.Data) + headerLen
}

// WriteByte writes one byte to the table's data.
func (t *Table) WriteByte(b uint8) {
t.Data = append(t.Data, b)

Check warning on line 59 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L58-L59

Added lines #L58 - L59 were not covered by tests
}

// WriteWord writes two little endian bytes to the table's data.
func (t *Table) WriteWord(v uint16) {
t.Data = append(t.Data, 0, 0)
binary.LittleEndian.PutUint16(t.Data[len(t.Data)-2:], v)

Check warning on line 65 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L63-L65

Added lines #L63 - L65 were not covered by tests
}

// WriteDWord writes four little endian bytes to the table's data.
func (t *Table) WriteDWord(v uint32) {
t.Data = append(t.Data, 0, 0, 0, 0)
binary.LittleEndian.PutUint32(t.Data[len(t.Data)-4:], v)

Check warning on line 71 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L69-L71

Added lines #L69 - L71 were not covered by tests
}

// WriteQWord writes eight little endian bytes to the table's data.
func (t *Table) WriteQWord(v uint64) {
t.Data = append(t.Data, 0, 0, 0, 0, 0, 0, 0, 0)
binary.LittleEndian.PutUint64(t.Data[len(t.Data)-8:], v)

Check warning on line 77 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L75-L77

Added lines #L75 - L77 were not covered by tests
}

// WriteString writes a string index and appends the string to table.
func (t *Table) WriteString(s string) {
if s == "" {
t.WriteByte(0)
} else {
t.Strings = append(t.Strings, s)

Check warning on line 85 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L81-L85

Added lines #L81 - L85 were not covered by tests
// strings are 1-indexed bytes.
t.WriteByte(uint8(len(t.Strings)))

Check warning on line 87 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L87

Added line #L87 was not covered by tests
}
}

// WriteBytes writes arbitrary bytes to the Table.
func (t *Table) WriteBytes(v []byte) {
t.Data = append(t.Data, v...)

Check warning on line 93 in table.go

View check run for this annotation

Codecov / codecov/patch

table.go#L92-L93

Added lines #L92 - L93 were not covered by tests
}

// GetByteAt returns a byte from the structured part at the specified offset.
func (t *Table) GetByteAt(offset int) (uint8, error) {
if offset > len(t.Data)-1 {
Expand Down

0 comments on commit 3a88068

Please sign in to comment.