| 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) |
| } |