aboutsummaryrefslogtreecommitdiff
path: root/util/board_status/go/src/cbtables/cbtables.go
diff options
context:
space:
mode:
Diffstat (limited to 'util/board_status/go/src/cbtables/cbtables.go')
-rw-r--r--util/board_status/go/src/cbtables/cbtables.go391
1 files changed, 391 insertions, 0 deletions
diff --git a/util/board_status/go/src/cbtables/cbtables.go b/util/board_status/go/src/cbtables/cbtables.go
new file mode 100644
index 0000000000..291400617d
--- /dev/null
+++ b/util/board_status/go/src/cbtables/cbtables.go
@@ -0,0 +1,391 @@
+package cbtables
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "strings"
+ "time"
+)
+
+type Header struct {
+ Signature [4]uint8 /* LBIO */
+ HeaderBytes uint32
+ HeaderChecksum uint32
+ TableBytes uint32
+ TableChecksum uint32
+ TableEntries uint32
+}
+
+type Record struct {
+ Tag uint32
+ Size uint32
+}
+
+type rawTable struct {
+ record Record
+ payload []byte
+}
+
+type parsedTables struct {
+ mem *os.File
+ raw []rawTable
+ typeMap map[uint32][]byte
+}
+
+var headerSignature [4]byte = [4]byte{'L', 'B', 'I', 'O'}
+
+const HeaderSize = 24
+const (
+ TagVersion = 0x0004
+ TagForward = 0x0011
+ TagTimestamps = 0x0016
+ TagConsole = 0x0017
+ TagVersionTimestamp = 0x0026
+)
+
+type CBTablesReader interface {
+ GetConsole() (cons []byte, lost uint32, err error)
+ GetTimestamps() (*TimeStamps, error)
+ GetVersion() (string, error)
+ GetVersionTimestamp() (time.Time, error)
+}
+
+type CBMemConsole struct {
+ Size uint32
+ Cursor uint32
+}
+
+type TimeStampEntry struct {
+ EntryID uint32
+ EntryStamp uint64
+}
+
+type TimeStampHeader struct {
+ BaseTime uint64
+ MaxEntries uint32
+ NumEntries uint32
+}
+
+type TimeStamps struct {
+ Head TimeStampHeader
+ Entries []TimeStampEntry
+ FrequencyMHZ uint32
+}
+
+var timeStampNames map[uint32]string = map[uint32]string{
+ 1: "start of rom stage",
+ 2: "before ram initialization",
+ 3: "after ram initialization",
+ 4: "end of romstage",
+ 5: "start of verified boot",
+ 6: "end of verified boot",
+ 8: "start of copying ram stage",
+ 9: "end of copying ram stage",
+ 10: "start of ramstage",
+ 30: "device enumeration",
+ 40: "device configuration",
+ 50: "device enable",
+ 60: "device initialization",
+ 70: "device setup done",
+ 75: "cbmem post",
+ 80: "write tables",
+ 90: "load payload",
+ 98: "ACPI wake jump",
+ 99: "selfboot jump",
+ 1000: "depthcharge start",
+ 1001: "RO parameter init",
+ 1002: "RO vboot init",
+ 1003: "RO vboot select firmware",
+ 1004: "RO vboot select&load kernel",
+ 1010: "RW vboot select&load kernel",
+ 1020: "vboot select&load kernel",
+ 1100: "crossystem data",
+ 1101: "start kernel",
+}
+
+func formatSep(val uint64) string {
+ ret := ""
+ for val > 1000 {
+ ret = fmt.Sprintf(",%03d", val%1000) + ret
+ val /= 1000
+ }
+ ret = fmt.Sprintf("%d", val) + ret
+ return ret
+}
+
+func formatElapsedTime(ticks uint64, frequency uint32) string {
+ if frequency == 0 {
+ return formatSep(ticks) + " cycles"
+ }
+ us := ticks / uint64(frequency)
+ return formatSep(us) + " us"
+}
+
+func (t TimeStamps) String() string {
+ ret := fmt.Sprintf("%d entries total\n\n", len(t.Entries))
+ for i, e := range t.Entries {
+ name, ok := timeStampNames[e.EntryID]
+ if !ok {
+ name = "<unknown>"
+ }
+ ret += fmt.Sprintf("%4d:%-30s %s", e.EntryID, name, formatElapsedTime(e.EntryStamp, t.FrequencyMHZ))
+ if i != 0 {
+ ret += fmt.Sprintf(" (%s)", formatElapsedTime(e.EntryStamp-t.Entries[i-1].EntryStamp, t.FrequencyMHZ))
+ }
+ ret += "\n"
+ }
+ return ret
+}
+
+func getFrequency() uint32 {
+ /* On non-x86 platforms the timestamp entries are in usecs */
+ if runtime.GOARCH != "386" && runtime.GOARCH != "amd64" {
+ return 1
+ }
+
+ cpuf, err := os.Open("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq")
+ if err != nil {
+ return 0
+ }
+
+ freq := uint64(0)
+ fmt.Fscanf(cpuf, "%d", &freq)
+ return uint32(freq / 1000)
+}
+
+func (p parsedTables) GetVersion() (string, error) {
+ str, ok := p.typeMap[TagVersion]
+ if !ok {
+ return "", fmt.Errorf("no coreboot version")
+ }
+ s := string(str)
+ idx := strings.Index(s, "\000")
+ if idx >= 0 {
+ s = s[0:idx]
+ }
+ return s, nil
+}
+
+func (p parsedTables) GetVersionTimestamp() (time.Time, error) {
+ raw, ok := p.typeMap[TagVersionTimestamp]
+ if !ok {
+ return time.Time{}, fmt.Errorf("no coreboot version timestamp")
+ }
+ ts := uint32(0)
+ err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &ts)
+ if err != nil {
+ return time.Time{}, err
+ }
+ return time.Unix(int64(ts), 0), nil
+}
+
+func (p parsedTables) GetTimestamps() (*TimeStamps, error) {
+ addr := uint64(0)
+ addrRaw, ok := p.typeMap[TagTimestamps]
+ if !ok {
+ return nil, fmt.Errorf("no coreboot console")
+ }
+ err := binary.Read(bytes.NewReader(addrRaw), binary.LittleEndian, &addr)
+ if err != nil {
+ return nil, err
+ }
+ mem := p.mem
+ _, err = mem.Seek(int64(addr), 0)
+ if err != nil {
+ return nil, err
+ }
+ var head TimeStampHeader
+ err = binary.Read(mem, binary.LittleEndian, &head)
+ if err != nil {
+ return nil, err
+ }
+
+ entries := make([]TimeStampEntry, head.NumEntries, head.NumEntries)
+ err = binary.Read(mem, binary.LittleEndian, &entries)
+ if err != nil {
+ return nil, err
+ }
+
+ return &TimeStamps{Head: head, Entries: entries, FrequencyMHZ: getFrequency()}, nil
+}
+
+func (p parsedTables) GetConsole() (console []byte, lost uint32, err error) {
+ addr := uint64(0)
+ addrRaw, ok := p.typeMap[TagConsole]
+ if !ok {
+ return nil, 0, fmt.Errorf("no coreboot console")
+ }
+ err = binary.Read(bytes.NewReader(addrRaw), binary.LittleEndian, &addr)
+ if err != nil {
+ return nil, 0, err
+ }
+ mem := p.mem
+ _, err = mem.Seek(int64(addr), 0)
+ if err != nil {
+ return nil, 0, err
+ }
+ var consDesc CBMemConsole
+ err = binary.Read(mem, binary.LittleEndian, &consDesc)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ readSize := consDesc.Cursor
+ lost = 0
+ if readSize > consDesc.Size {
+ lost = readSize - consDesc.Size
+ readSize = consDesc.Size
+ }
+
+ cons := make([]byte, readSize, readSize)
+ mem.Read(cons)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return cons, lost, nil
+}
+
+func IPChecksum(b []byte) uint16 {
+ sum := uint32(0)
+ /* Oh boy: coreboot really does is little-endian way. */
+ for i := 0; i < len(b); i += 2 {
+ sum += uint32(b[i])
+ }
+ for i := 1; i < len(b); i += 2 {
+ sum += uint32(b[i]) << 8
+ }
+
+ sum = (sum >> 16) + (sum & 0xffff)
+ sum += (sum >> 16)
+ return uint16(^sum & 0xffff)
+}
+
+func readFromBase(mem *os.File, base uint64) ([]byte, error) {
+ _, err := mem.Seek(int64(base), 0)
+ if err != nil {
+ return nil, err
+ }
+ var headRaw [HeaderSize]byte
+ var head Header
+ _, err = mem.Read(headRaw[:])
+ if err != nil {
+ return nil, err
+ }
+
+ err = binary.Read(bytes.NewReader(headRaw[:]), binary.LittleEndian, &head)
+ if err != nil {
+ return nil, err
+ }
+ if bytes.Compare(head.Signature[:], headerSignature[:]) != 0 || head.HeaderBytes == 0 {
+ return nil, nil
+ }
+ if IPChecksum(headRaw[:]) != 0 {
+ return nil, nil
+ }
+ table := make([]byte, head.TableBytes, head.TableBytes)
+ _, err = mem.Seek(int64(base)+int64(head.HeaderBytes), 0)
+ if err != nil {
+ return nil, err
+ }
+ _, err = mem.Read(table)
+ if err != nil {
+ return nil, err
+ }
+
+ if uint32(IPChecksum(table)) != head.TableChecksum {
+ return nil, nil
+ }
+ return table, nil
+}
+
+func scanFromBase(mem *os.File, base uint64) ([]byte, error) {
+ for i := uint64(0); i < 0x1000; i += 0x10 {
+ b, err := readFromBase(mem, base+i)
+ if err != nil {
+ return nil, err
+ }
+ if b != nil {
+ return b, nil
+ }
+ }
+ return nil, fmt.Errorf("no coreboot table found")
+}
+
+func readTables(mem *os.File) ([]byte, error) {
+ switch runtime.GOARCH {
+ case "arm":
+ dt, err := os.Open("/proc/device-tree/firmware/coreboot/coreboot-table")
+ defer dt.Close()
+ if err != nil {
+ return nil, err
+ }
+ var base uint32
+ err = binary.Read(dt, binary.BigEndian, &base)
+ if err != nil {
+ return nil, err
+ }
+ return scanFromBase(mem, uint64(base))
+ case "386", "amd64":
+ tbl, err := scanFromBase(mem, 0)
+ if err == nil {
+ return tbl, nil
+ }
+ return scanFromBase(mem, 0xf0000)
+ default:
+ return nil, fmt.Errorf("unsuppurted arch: %s", runtime.GOARCH)
+ }
+}
+
+func parseTables(mem *os.File, raw []byte) (p parsedTables, err error) {
+ reader := bytes.NewBuffer(raw)
+ p.typeMap = map[uint32][]byte{}
+ for {
+ record := Record{}
+ err = binary.Read(reader, binary.LittleEndian, &record)
+ if err == io.EOF {
+ p.mem = mem
+ return p, nil
+ }
+ if err != nil {
+ return p, err
+ }
+ payload := make([]byte, record.Size-8, record.Size-8)
+ reader.Read(payload)
+ p.raw = append(p.raw, rawTable{record: record, payload: payload})
+ p.typeMap[record.Tag] = payload
+ if record.Tag == TagForward {
+ base := uint64(0)
+ err = binary.Read(bytes.NewBuffer(payload), binary.LittleEndian, &base)
+ if err != nil {
+ return p, err
+ }
+ raw, err := readFromBase(mem, base)
+ if err != nil {
+ return p, err
+ }
+ if raw == nil {
+ return p, fmt.Errorf("no coreboot table found")
+ }
+ reader = bytes.NewBuffer(raw)
+ }
+ }
+}
+
+func Open() (reader CBTablesReader, err error) {
+ mem, err := os.Open("/dev/mem")
+ if err != nil {
+ return nil, err
+ }
+
+ tables, err := readTables(mem)
+ if err != nil {
+ return nil, err
+ }
+
+ return parseTables(mem, tables)
+}