Skip to content

Commit 3f73db1

Browse files
authored
Merge pull request #470 from europaul/add-tpm
Introduce pkg/tpm to detect and describe TPMs
2 parents ed1c31c + 5c7fcf4 commit 3f73db1

11 files changed

Lines changed: 530 additions & 1 deletion

File tree

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,53 @@ Example output from my personal workstation:
12361236
watchdog present: true
12371237
```
12381238

1239+
### TPM (Linux only)
1240+
1241+
The `ghw.TPM()` function returns a `ghw.TPMInfo` struct that contains
1242+
information about the Trusted Platform Module(s) (TPM) on the host system.
1243+
1244+
The `ghw.TPMInfo` struct contains a `Devices` field:
1245+
1246+
* `ghw.TPMInfo.Devices` is a slice of pointers to `ghw.TPMDevice` structs, one
1247+
for each TPM detected under `/sys/class/tpm`
1248+
1249+
Each `ghw.TPMDevice` struct contains multiple fields:
1250+
1251+
* `ghw.TPMDevice.ManufacturerName` is a string with the human-readable TPM
1252+
manufacturer short name (e.g. `AMD`, `IFX` for Infineon, `STM` for
1253+
STMicroelectronics)
1254+
* `ghw.TPMDevice.ManufacturerVendorID` is a string with the 16-bit TCG Vendor
1255+
ID assigned to the manufacturer, when the host exposes it (e.g. `0x1414`)
1256+
* `ghw.TPMDevice.FirmwareVersion` is a string with the TPM firmware version, if
1257+
exposed by the driver
1258+
* `ghw.TPMDevice.SpecVersion` is a string with the TCG specification version the
1259+
TPM implements (e.g. `1.2` or `2.0`)
1260+
1261+
```go
1262+
package main
1263+
1264+
import (
1265+
"fmt"
1266+
1267+
"github.com/jaypipes/ghw"
1268+
)
1269+
1270+
func main() {
1271+
tpm, err := ghw.TPM()
1272+
if err != nil {
1273+
fmt.Printf("Error getting TPM info: %v", err)
1274+
}
1275+
1276+
fmt.Printf("%v\n", tpm)
1277+
}
1278+
```
1279+
1280+
Example output from my personal workstation:
1281+
1282+
```
1283+
tpm manufacturer_name=STM manufacturer_vendor_id= firmware_version= spec_version=2.0
1284+
```
1285+
12391286
## Advanced Usage
12401287

12411288
### Disabling warning messages

alias.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
pciaddress "github.com/jaypipes/ghw/pkg/pci/address"
2323
"github.com/jaypipes/ghw/pkg/product"
2424
"github.com/jaypipes/ghw/pkg/topology"
25+
"github.com/jaypipes/ghw/pkg/tpm"
2526
"github.com/jaypipes/ghw/pkg/usb"
2627
"github.com/jaypipes/ghw/pkg/watchdog"
2728
)
@@ -206,3 +207,10 @@ type WatchdogInfo = watchdog.Info
206207
var (
207208
Watchdog = watchdog.New
208209
)
210+
211+
type TPMInfo = tpm.Info
212+
type TPMDevice = tpm.Device
213+
214+
var (
215+
TPM = tpm.New
216+
)

cmd/ghwc/commands/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func showAll(cmd *cobra.Command, args []string) error {
106106
showAccelerator,
107107
showUSB,
108108
showWatchdog,
109+
showTPM,
109110
} {
110111
err := f(cmd, args)
111112
if err != nil {

cmd/ghwc/commands/tpm.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Use and distribution licensed under the Apache license version 2.
3+
//
4+
// See the COPYING file in the root project directory for full text.
5+
//
6+
7+
package commands
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/jaypipes/ghw"
13+
"github.com/spf13/cobra"
14+
)
15+
16+
// tpmCmd represents the `tpm` command
17+
var tpmCmd = &cobra.Command{
18+
Use: "tpm",
19+
Short: "Show TPM information for the host system",
20+
RunE: showTPM,
21+
}
22+
23+
// showTPM shows TPM information for the host system.
24+
func showTPM(cmd *cobra.Command, args []string) error {
25+
tpm, err := ghw.TPM(cmd.Context())
26+
if err != nil {
27+
return fmt.Errorf("error getting TPM info: %w", err)
28+
}
29+
30+
switch outputFormat {
31+
case outputFormatHuman:
32+
fmt.Printf("%v\n", tpm)
33+
case outputFormatJSON:
34+
fmt.Printf("%s\n", tpm.JSONString(pretty))
35+
case outputFormatYAML:
36+
fmt.Printf("%s", tpm.YAMLString())
37+
}
38+
return nil
39+
}
40+
41+
func init() {
42+
rootCmd.AddCommand(tpmCmd)
43+
}

host.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/jaypipes/ghw/pkg/pci"
2424
"github.com/jaypipes/ghw/pkg/product"
2525
"github.com/jaypipes/ghw/pkg/topology"
26+
"github.com/jaypipes/ghw/pkg/tpm"
2627
"github.com/jaypipes/ghw/pkg/usb"
2728
"github.com/jaypipes/ghw/pkg/watchdog"
2829
)
@@ -44,6 +45,7 @@ type HostInfo struct {
4445
PCI *pci.Info `json:"pci"`
4546
USB *usb.Info `json:"usb"`
4647
Watchdog *watchdog.Info `json:"watchdog"`
48+
TPM *tpm.Info `json:"tpm"`
4749
}
4850

4951
// Host returns a pointer to a HostInfo struct that contains fields with
@@ -106,6 +108,10 @@ func Host(args ...any) (*HostInfo, error) {
106108
if err != nil {
107109
return nil, err
108110
}
111+
tpmInfo, err := tpm.New(ctx)
112+
if err != nil {
113+
return nil, err
114+
}
109115

110116
return &HostInfo{
111117
CPU: cpuInfo,
@@ -122,14 +128,15 @@ func Host(args ...any) (*HostInfo, error) {
122128
PCI: pciInfo,
123129
USB: usbInfo,
124130
Watchdog: watchdogInfo,
131+
TPM: tpmInfo,
125132
}, nil
126133
}
127134

128135
// String returns a newline-separated output of the HostInfo's component
129136
// structs' String-ified output
130137
func (info *HostInfo) String() string {
131138
return fmt.Sprintf(
132-
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
139+
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
133140
info.Block.String(),
134141
info.CPU.String(),
135142
info.GPU.String(),
@@ -144,6 +151,7 @@ func (info *HostInfo) String() string {
144151
info.PCI.String(),
145152
info.USB.String(),
146153
info.Watchdog.String(),
154+
info.TPM.String(),
147155
)
148156
}
149157

pkg/linuxpath/path_linux.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type Paths struct {
7878
SysClassDRM string
7979
SysClassDMI string
8080
SysClassNet string
81+
SysClassTPM string
8182
SysClassWatchdog string
8283
SysFirmwareDeviceTree string
8384
RunUdevData string
@@ -105,6 +106,7 @@ func New(ctx context.Context) *Paths {
105106
SysClassDRM: filepath.Join(chroot, roots.Sys, "class", "drm"),
106107
SysClassDMI: filepath.Join(chroot, roots.Sys, "class", "dmi"),
107108
SysClassNet: filepath.Join(chroot, roots.Sys, "class", "net"),
109+
SysClassTPM: filepath.Join(chroot, roots.Sys, "class", "tpm"),
108110
SysClassWatchdog: filepath.Join(chroot, roots.Sys, "class", "watchdog"),
109111
SysFirmwareDeviceTree: filepath.Join(chroot, roots.Sys, "firmware", "devicetree", "base"),
110112
RunUdevData: filepath.Join(chroot, roots.Run, "udev", "data"),

pkg/snapshot/clonetree_linux.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ func ExpectedCloneStaticContent() []string {
5252
"/sys/devices/system/node/node*/memory*",
5353
"/sys/devices/system/node/node*/hugepages/hugepages-*/*",
5454
"/sys/class/watchdog/*",
55+
"/sys/class/tpm/tpm*/caps",
56+
"/sys/class/tpm/tpm*/tpm_version_major",
57+
"/sys/class/tpm/tpm*/device/vendor",
5558
}
5659
}
5760

pkg/tpm/tpm.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//
2+
// Use and distribution licensed under the Apache license version 2.
3+
//
4+
// See the COPYING file in the root project directory for full text.
5+
//
6+
7+
package tpm
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/jaypipes/ghw/internal/config"
13+
"github.com/jaypipes/ghw/pkg/marshal"
14+
)
15+
16+
// Device describes a single Trusted Platform Module (TPM) present on the host
17+
// system.
18+
type Device struct {
19+
// ManufacturerName is the human-readable name of the TPM manufacturer,
20+
// decoded from the four-character short name the TPM reports (e.g. "AMD",
21+
// "IFX" for Infineon, "STM" for STMicroelectronics).
22+
ManufacturerName string `json:"manufacturer_name"`
23+
// ManufacturerVendorID is the 16-bit TCG Vendor ID assigned to the TPM
24+
// manufacturer by the Trusted Computing Group. Note that this is a
25+
// separate namespace from the PCI vendor ID. It may be empty when the
26+
// host only exposes the manufacturer's short name.
27+
// See: https://trustedcomputinggroup.org/resource/vendor-id-registry/
28+
ManufacturerVendorID string `json:"manufacturer_vendor_id"`
29+
// SpecVersion is the TCG TPM library (specification) version the device
30+
// implements, i.e. the ISO/IEC 11889 version, e.g. "1.2" or "2.0".
31+
SpecVersion string `json:"spec_version"`
32+
// FirmwareVersion is the TPM firmware version, when the host exposes it.
33+
FirmwareVersion string `json:"firmware_version"`
34+
}
35+
36+
// String returns a short string describing the TPM device.
37+
func (d *Device) String() string {
38+
return fmt.Sprintf(
39+
"manufacturer_name=%s manufacturer_vendor_id=%s firmware_version=%s spec_version=%s",
40+
d.ManufacturerName, d.ManufacturerVendorID, d.FirmwareVersion, d.SpecVersion,
41+
)
42+
}
43+
44+
// Info describes the Trusted Platform Module(s) on the host system.
45+
type Info struct {
46+
// Devices are the TPM devices detected under /sys/class/tpm. A host can,
47+
// in principle, expose more than one TPM device.
48+
Devices []*Device `json:"devices"`
49+
}
50+
51+
// String returns a short string with summary information about the TPM(s) on
52+
// the host system.
53+
func (i *Info) String() string {
54+
switch len(i.Devices) {
55+
case 0:
56+
return "tpm (no devices)"
57+
case 1:
58+
return "tpm " + i.Devices[0].String()
59+
default:
60+
return fmt.Sprintf("tpm (%d devices)", len(i.Devices))
61+
}
62+
}
63+
64+
// New returns a pointer to an Info struct that contains information about
65+
// the TPM(s) on the host system.
66+
func New(args ...any) (*Info, error) {
67+
ctx := config.ContextFromArgs(args...)
68+
info := &Info{}
69+
if err := info.load(ctx); err != nil {
70+
return nil, err
71+
}
72+
return info, nil
73+
}
74+
75+
// simple private struct used to encapsulate TPM information in a top-level
76+
// "tpm" YAML/JSON map/object key
77+
type tpmPrinter struct {
78+
Info *Info `json:"tpm"`
79+
}
80+
81+
// YAMLString returns a string with the TPM information formatted as YAML
82+
// under a top-level "tpm:" key
83+
func (i *Info) YAMLString() string {
84+
return marshal.SafeYAML(tpmPrinter{i})
85+
}
86+
87+
// JSONString returns a string with the TPM information formatted as JSON
88+
// under a top-level "tpm:" key
89+
func (i *Info) JSONString(indent bool) string {
90+
return marshal.SafeJSON(tpmPrinter{i}, indent)
91+
}

0 commit comments

Comments
 (0)