1package cbfs 2 3import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "os" 8 "sort" 9 "strings" 10 "text/tabwriter" 11) 12 13type CBFSReader interface { 14 GetFile(name string) ([]byte, error) 15 ListFiles() ([]string, error) 16} 17 18type ArchType uint32 19type FileType uint32 20 21type CBFSHeader struct { 22 Magic uint32 23 Version uint32 24 ROMSize uint32 25 BootBlockSize uint32 26 Align uint32 27 Offset uint32 28 Architecture ArchType 29 Pad [1]uint32 30} 31 32func (a ArchType) String() string { 33 switch a { 34 case 0xFFFFFFFF: 35 return "unknown" 36 case 0x00000001: 37 return "x86" 38 case 0x00000010: 39 return "arm" 40 default: 41 return fmt.Sprintf("0x%x", a) 42 } 43} 44 45func (f FileType) String() string { 46 switch f { 47 case 0xffffffff: 48 return "null" 49 case 0x10: 50 return "stage" 51 case 0x20: 52 return "payload" 53 case 0x30: 54 return "optionrom" 55 case 0x40: 56 return "bootsplash" 57 case 0x50: 58 return "raw" 59 case 0x51: 60 return "vsa" 61 case 0x52: 62 return "mbi" 63 case 0x53: 64 return "microcode" 65 case 0xaa: 66 return "cmos_default" 67 case 0x1aa: 68 return "cmos_layout" 69 default: 70 return fmt.Sprintf("0x%x", uint32(f)) 71 } 72} 73 74func (c CBFSHeader) String() (ret string) { 75 ret = fmt.Sprintf("bootblocksize: %d\n", c.BootBlockSize) 76 ret += fmt.Sprintf("romsize: %d\n", c.ROMSize) 77 ret += fmt.Sprintf("offset: 0x%x\n", c.Offset) 78 ret += fmt.Sprintf("alignment: %d bytes\n", c.Align) 79 ret += fmt.Sprintf("architecture: %v\n", c.Architecture) 80 ret += fmt.Sprintf("version: 0x%x\n", c.Version) 81 return ret 82} 83 84const sizeofFileHeader = 24 85const CBFSHeaderMagic = 0x4F524243 86 87type CBFSFileHeader struct { 88 Magic [8]byte 89 Len uint32 90 Type FileType 91 CheckSum uint32 92 Offset uint32 93} 94 95type cBFSFile struct { 96 headerOffset uint64 97 header CBFSFileHeader 98 name string 99} 100 101type cBFSDesc struct { 102 file *os.File 103 end uint64 104 headerPos uint64 105 rOMStart uint64 106 fileNames map[string]cBFSFile 107 files []cBFSFile 108 header CBFSHeader 109} 110 111func (c cBFSDesc) align(offset uint32) uint32 { 112 a := uint32(c.header.Align) 113 return (a + offset - 1) & ^(a - 1) 114} 115 116func (c cBFSDesc) ListFiles() (files []string, err error) { 117 for name, _ := range c.fileNames { 118 files = append(files, name) 119 } 120 sort.Strings(files) 121 return files, nil 122} 123 124func (c cBFSDesc) GetFile(name string) ([]byte, error) { 125 file, ok := c.fileNames[name] 126 if !ok { 127 return nil, fmt.Errorf("file not found: %s", name) 128 } 129 _, err := c.file.Seek(int64(file.headerOffset)+int64(file.header.Offset), 0) 130 if err != nil { 131 return nil, err 132 } 133 ret := make([]byte, file.header.Len, file.header.Len) 134 r, err := c.file.Read(ret) 135 if err != nil { 136 return nil, err 137 } 138 if r != len(ret) { 139 return nil, fmt.Errorf("incomplete read") 140 } 141 return ret, nil 142} 143 144func (c cBFSDesc) String() (ret string) { 145 ret = c.header.String() 146 ret += "\n" 147 buf := bytes.NewBuffer([]byte{}) 148 w := new(tabwriter.Writer) 149 w.Init(buf, 15, 0, 1, ' ', 0) 150 fmt.Fprintln(w, "Name\tOffset\tType\tSize\t") 151 for _, file := range c.files { 152 name := file.name 153 if file.header.Type == 0xffffffff { 154 name = "(empty)" 155 } 156 fmt.Fprintf(w, "%s\t0x%x\t%v\t%d\t\n", 157 name, file.headerOffset-c.rOMStart, 158 file.header.Type, file.header.Len) 159 } 160 w.Flush() 161 ret += buf.String() 162 return ret 163} 164 165func openGeneric(cbfs *cBFSDesc) (CBFSReader, error) { 166 _, err := cbfs.file.Seek(int64(cbfs.end-4), 0) 167 if err != nil { 168 return nil, err 169 } 170 headerPos := int32(0) 171 binary.Read(cbfs.file, binary.LittleEndian, &headerPos) 172 if headerPos < 0 { 173 cbfs.headerPos = cbfs.end - uint64(-headerPos) 174 } else { 175 cbfs.headerPos = uint64(headerPos) 176 } 177 _, err = cbfs.file.Seek(int64(cbfs.headerPos), 0) 178 if err != nil { 179 return nil, err 180 } 181 err = binary.Read(cbfs.file, binary.BigEndian, &cbfs.header) 182 if err != nil { 183 return nil, err 184 } 185 if cbfs.header.Magic != CBFSHeaderMagic { 186 return nil, fmt.Errorf("invalid header magic") 187 } 188 189 cbfs.fileNames = map[string]cBFSFile{} 190 191 curptr := cbfs.end - uint64(cbfs.header.ROMSize) + uint64(cbfs.header.Offset) 192 cbfs.rOMStart = cbfs.end - uint64(cbfs.header.ROMSize) 193 for { 194 file := cBFSFile{headerOffset: curptr} 195 _, err = cbfs.file.Seek(int64(curptr), 0) 196 if err != nil { 197 return nil, err 198 } 199 err = binary.Read(cbfs.file, binary.BigEndian, &file.header) 200 if err != nil { 201 return nil, err 202 } 203 if string(file.header.Magic[:]) != "LARCHIVE" { 204 return *cbfs, nil 205 } 206 name := make([]byte, file.header.Offset-sizeofFileHeader, file.header.Offset-sizeofFileHeader) 207 _, err = cbfs.file.Read(name) 208 if err != nil { 209 return nil, err 210 } 211 nameStr := string(name) 212 idx := strings.Index(nameStr, "\000") 213 if idx >= 0 { 214 nameStr = nameStr[0:idx] 215 } 216 file.name = nameStr 217 cbfs.fileNames[nameStr] = file 218 cbfs.files = append(cbfs.files, file) 219 curptr += uint64(cbfs.align(file.header.Offset + file.header.Len)) 220 } 221} 222 223func OpenFile(file *os.File) (CBFSReader, error) { 224 stat, err := file.Stat() 225 if err != nil { 226 return nil, err 227 } 228 cbfs := cBFSDesc{file: file, end: uint64(stat.Size())} 229 return openGeneric(&cbfs) 230} 231 232func OpenROM() (CBFSReader, error) { 233 file, err := os.Open("/dev/mem") 234 if err != nil { 235 return nil, err 236 } 237 cbfs := cBFSDesc{file: file, end: 0x100000000} 238 return openGeneric(&cbfs) 239} 240