Rewrite board_status in go.
This allows easy creation of redistribuable binary. Change-Id: I12a82d509cd4bd46baeb4f4066e509c69301ab95 Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com> Reviewed-on: http://review.coreboot.org/7565 Tested-by: build bot (Jenkins) Reviewed-by: Edward O'Callaghan <eocallaghan@alterapraxis.com> Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
391
util/board_status/go/src/cbtables/cbtables.go
Normal file
391
util/board_status/go/src/cbtables/cbtables.go
Normal file
@ -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)
|
||||
}
|
Reference in New Issue
Block a user