blob: 042d35fa72056f7906a7959e8d872b9e37da2bef [file] [log] [blame]
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)
}