From 8251e9070c6f4168654b75a4d0fd1f756d128ebd Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 23 Nov 2014 16:19:48 +0100 Subject: Rewrite board_status in go. This allows easy creation of redistribuable binary. Change-Id: I12a82d509cd4bd46baeb4f4066e509c69301ab95 Signed-off-by: Vladimir Serbinenko Reviewed-on: http://review.coreboot.org/7565 Tested-by: build bot (Jenkins) Reviewed-by: Edward O'Callaghan Reviewed-by: Ronald G. Minnich --- util/board_status/go/src/cbtables/cbtables.go | 391 ++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 util/board_status/go/src/cbtables/cbtables.go (limited to 'util/board_status/go/src/cbtables') 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 = "" + } + 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) +} -- cgit v1.2.3