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:
parent
6ead253fbd
commit
8251e9070c
|
@ -0,0 +1,239 @@
|
|||
package cbfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
type CBFSReader interface {
|
||||
GetFile(name string) ([]byte, error)
|
||||
ListFiles() ([]string, error)
|
||||
}
|
||||
|
||||
type ArchType uint32
|
||||
type FileType uint32
|
||||
|
||||
type CBFSHeader struct {
|
||||
Magic uint32
|
||||
Version uint32
|
||||
ROMSize uint32
|
||||
BootBlockSize uint32
|
||||
Align uint32
|
||||
Offset uint32
|
||||
Architecture ArchType
|
||||
Pad [1]uint32
|
||||
}
|
||||
|
||||
func (a ArchType) String() string {
|
||||
switch a {
|
||||
case 0xFFFFFFFF:
|
||||
return "unknown"
|
||||
case 0x00000001:
|
||||
return "x86"
|
||||
case 0x00000010:
|
||||
return "arm"
|
||||
default:
|
||||
return fmt.Sprintf("0x%x", a)
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileType) String() string {
|
||||
switch f {
|
||||
case 0xffffffff:
|
||||
return "null"
|
||||
case 0x10:
|
||||
return "stage"
|
||||
case 0x20:
|
||||
return "payload"
|
||||
case 0x30:
|
||||
return "optionrom"
|
||||
case 0x40:
|
||||
return "bootsplash"
|
||||
case 0x50:
|
||||
return "raw"
|
||||
case 0x51:
|
||||
return "vsa"
|
||||
case 0x52:
|
||||
return "mbi"
|
||||
case 0x53:
|
||||
return "microcode"
|
||||
case 0xaa:
|
||||
return "cmos_default"
|
||||
case 0x1aa:
|
||||
return "cmos_layout"
|
||||
default:
|
||||
return fmt.Sprintf("0x%x", uint32(f))
|
||||
}
|
||||
}
|
||||
|
||||
func (c CBFSHeader) String() (ret string) {
|
||||
ret = fmt.Sprintf("bootblocksize: %d\n", c.BootBlockSize)
|
||||
ret += fmt.Sprintf("romsize: %d\n", c.ROMSize)
|
||||
ret += fmt.Sprintf("offset: 0x%x\n", c.Offset)
|
||||
ret += fmt.Sprintf("alignment: %d bytes\n", c.Align)
|
||||
ret += fmt.Sprintf("architecture: %v\n", c.Architecture)
|
||||
ret += fmt.Sprintf("version: 0x%x\n", c.Version)
|
||||
return ret
|
||||
}
|
||||
|
||||
const sizeofFileHeader = 24
|
||||
const CBFSHeaderMagic = 0x4F524243
|
||||
|
||||
type CBFSFileHeader struct {
|
||||
Magic [8]byte
|
||||
Len uint32
|
||||
Type FileType
|
||||
CheckSum uint32
|
||||
Offset uint32
|
||||
}
|
||||
|
||||
type cBFSFile struct {
|
||||
headerOffset uint64
|
||||
header CBFSFileHeader
|
||||
name string
|
||||
}
|
||||
|
||||
type cBFSDesc struct {
|
||||
file *os.File
|
||||
end uint64
|
||||
headerPos uint64
|
||||
rOMStart uint64
|
||||
fileNames map[string]cBFSFile
|
||||
files []cBFSFile
|
||||
header CBFSHeader
|
||||
}
|
||||
|
||||
func (c cBFSDesc) align(offset uint32) uint32 {
|
||||
a := uint32(c.header.Align)
|
||||
return (a + offset - 1) & ^(a - 1)
|
||||
}
|
||||
|
||||
func (c cBFSDesc) ListFiles() (files []string, err error) {
|
||||
for name, _ := range c.fileNames {
|
||||
files = append(files, name)
|
||||
}
|
||||
sort.Strings(files)
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (c cBFSDesc) GetFile(name string) ([]byte, error) {
|
||||
file, ok := c.fileNames[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file not found: %s", name)
|
||||
}
|
||||
_, err := c.file.Seek(int64(file.headerOffset)+int64(file.header.Offset), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := make([]byte, file.header.Len, file.header.Len)
|
||||
r, err := c.file.Read(ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r != len(ret) {
|
||||
return nil, fmt.Errorf("incomplete read")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (c cBFSDesc) String() (ret string) {
|
||||
ret = c.header.String()
|
||||
ret += "\n"
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(buf, 15, 0, 1, ' ', 0)
|
||||
fmt.Fprintln(w, "Name\tOffset\tType\tSize\t")
|
||||
for _, file := range c.files {
|
||||
name := file.name
|
||||
if file.header.Type == 0xffffffff {
|
||||
name = "(empty)"
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t0x%x\t%v\t%d\t\n",
|
||||
name, file.headerOffset-c.rOMStart,
|
||||
file.header.Type, file.header.Len)
|
||||
}
|
||||
w.Flush()
|
||||
ret += buf.String()
|
||||
return ret
|
||||
}
|
||||
|
||||
func openGeneric(cbfs *cBFSDesc) (CBFSReader, error) {
|
||||
_, err := cbfs.file.Seek(int64(cbfs.end-4), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headerPos := int32(0)
|
||||
binary.Read(cbfs.file, binary.LittleEndian, &headerPos)
|
||||
if headerPos < 0 {
|
||||
cbfs.headerPos = cbfs.end - uint64(-headerPos)
|
||||
} else {
|
||||
cbfs.headerPos = uint64(headerPos)
|
||||
}
|
||||
_, err = cbfs.file.Seek(int64(cbfs.headerPos), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(cbfs.file, binary.BigEndian, &cbfs.header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cbfs.header.Magic != CBFSHeaderMagic {
|
||||
return nil, fmt.Errorf("invalid header magic")
|
||||
}
|
||||
|
||||
cbfs.fileNames = map[string]cBFSFile{}
|
||||
|
||||
curptr := cbfs.end - uint64(cbfs.header.ROMSize) + uint64(cbfs.header.Offset)
|
||||
cbfs.rOMStart = cbfs.end - uint64(cbfs.header.ROMSize)
|
||||
for {
|
||||
file := cBFSFile{headerOffset: curptr}
|
||||
_, err = cbfs.file.Seek(int64(curptr), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(cbfs.file, binary.BigEndian, &file.header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if string(file.header.Magic[:]) != "LARCHIVE" {
|
||||
return *cbfs, nil
|
||||
}
|
||||
name := make([]byte, file.header.Offset-sizeofFileHeader, file.header.Offset-sizeofFileHeader)
|
||||
_, err = cbfs.file.Read(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameStr := string(name)
|
||||
idx := strings.Index(nameStr, "\000")
|
||||
if idx >= 0 {
|
||||
nameStr = nameStr[0:idx]
|
||||
}
|
||||
file.name = nameStr
|
||||
cbfs.fileNames[nameStr] = file
|
||||
cbfs.files = append(cbfs.files, file)
|
||||
curptr += uint64(cbfs.align(file.header.Offset + file.header.Len))
|
||||
}
|
||||
}
|
||||
|
||||
func OpenFile(file *os.File) (CBFSReader, error) {
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cbfs := cBFSDesc{file: file, end: uint64(stat.Size())}
|
||||
return openGeneric(&cbfs)
|
||||
}
|
||||
|
||||
func OpenROM() (CBFSReader, error) {
|
||||
file, err := os.Open("/dev/mem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cbfs := cBFSDesc{file: file, end: 0x100000000}
|
||||
return openGeneric(&cbfs)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package kconfig
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseKConfig(raw []byte) map[string]string {
|
||||
buffer := bytes.NewBuffer(raw)
|
||||
|
||||
scanner := bufio.NewScanner(buffer)
|
||||
ret := map[string]string{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
idx := strings.Index(line, "=")
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
ret[line[0:idx]] = line[idx+1:]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func UnQuote(in string) string {
|
||||
return in[1 : len(in)-1]
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"cbfs"
|
||||
"cbtables"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"kconfig"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var ClobberDir = flag.Bool("clobber", false, "Clobber temporary output when finished. Useful for debugging.")
|
||||
|
||||
func RunAndSave(output string, name string, arg ...string) {
|
||||
cmd := exec.Command(name, arg...)
|
||||
|
||||
f, err := os.Create(output)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cmd.Stdout = f
|
||||
cmd.Stderr = f
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cmd.Wait()
|
||||
}
|
||||
|
||||
/* Missing features: serial, upload, ssh */
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
cb, err := cbfs.OpenROM()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
config, err := cb.GetFile("config")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
revision, err := cb.GetFile("revision")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
parsedConfig := kconfig.ParseKConfig(config)
|
||||
mainboardDir := kconfig.UnQuote(parsedConfig["CONFIG_MAINBOARD_DIR"])
|
||||
|
||||
tempDir, err := ioutil.TempDir("", "coreboot_board_status")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tbl, err := cbtables.Open()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
taggedVersion, err := tbl.GetVersion()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
versionTimestamp, err := tbl.GetVersionTimestamp()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
timestampFormated := versionTimestamp.UTC().Format("2006-01-02T15:04:05Z")
|
||||
outputDir := tempDir + "/" + mainboardDir + "/" + taggedVersion + "/" + timestampFormated
|
||||
os.MkdirAll(outputDir, 0755)
|
||||
fmt.Printf("Temporarily placing output in %s\n", outputDir)
|
||||
cf, err := os.Create(outputDir + "/cbfs.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cf.Close()
|
||||
fmt.Fprintf(cf, "%v", cb)
|
||||
|
||||
cf, err = os.Create(outputDir + "/config.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cf.Close()
|
||||
cf.Write(config)
|
||||
|
||||
cf, err = os.Create(outputDir + "/revision.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cf.Close()
|
||||
cf.Write(revision)
|
||||
|
||||
RunAndSave(outputDir+"/kernel_log.txt", "dmesg")
|
||||
|
||||
cons, lost, err := tbl.GetConsole()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cf, err = os.Create(outputDir + "/coreboot_console.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cf.Close()
|
||||
cf.Write(cons)
|
||||
switch lost {
|
||||
case 0:
|
||||
case 1:
|
||||
fmt.Fprintf(cf, "\none byte lost\n")
|
||||
default:
|
||||
fmt.Fprintf(cf, "\n%d bytes lost\n", lost)
|
||||
}
|
||||
timest, err := tbl.GetTimestamps()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ts, err := os.Create(outputDir + "/coreboot_timestamps.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer ts.Close()
|
||||
fmt.Fprintf(ts, "%v", timest)
|
||||
|
||||
if *ClobberDir {
|
||||
os.RemoveAll(tempDir)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue