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