xref: /aosp_15_r20/external/coreboot/util/board_status/go/src/cbfs/cbfs.go (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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