github.com/linuxboot/fiano@v1.2.0/pkg/cbfs/image.go (about) 1 // Copyright 2018-2021 the LinuxBoot Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cbfs 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "fmt" 11 "io" 12 "os" 13 14 "github.com/linuxboot/fiano/pkg/fmap" 15 ) 16 17 type SegReader struct { 18 Type FileType 19 New func(f *File) (ReadWriter, error) 20 Name string 21 } 22 23 var SegReaders = make(map[FileType]*SegReader) 24 25 func RegisterFileReader(f *SegReader) error { 26 if r, ok := SegReaders[f.Type]; ok { 27 return fmt.Errorf("RegisterFileType: Slot of %v is owned by %s, can't add %s", r.Type, r.Name, f.Name) 28 } 29 SegReaders[f.Type] = f 30 Debug("Registered %v", f) 31 return nil 32 } 33 34 func NewImage(rs io.ReadSeeker) (*Image, error) { 35 // Suck the image in. Todo: write a thing that implements 36 // ReadSeeker on a []byte. 37 b, err := io.ReadAll(rs) 38 if err != nil { 39 return nil, fmt.Errorf("ReadAll: %v", err) 40 } 41 in := bytes.NewReader(b) 42 f, m, err := fmap.Read(in) 43 if err != nil { 44 return nil, err 45 } 46 Debug("Fmap %v", f) 47 var i = &Image{FMAP: f, FMAPMetadata: m, Data: b} 48 for _, a := range f.Areas { 49 Debug("Check %v", a.Name.String()) 50 if a.Name.String() == "COREBOOT" { 51 i.Area = &a 52 break 53 } 54 } 55 if i.Area == nil { 56 return nil, fmt.Errorf("No CBFS in fmap") 57 } 58 r := io.NewSectionReader(in, int64(i.Area.Offset), int64(i.Area.Size)) 59 60 for off := int64(0); off < int64(i.Area.Size); { 61 var f *File 62 63 if _, err := r.Seek(off, io.SeekStart); err != nil { 64 return nil, err 65 } 66 f, err := NewFile(r) 67 if err == CbfsHeaderMagicNotFound { 68 off = off + 16 69 continue 70 } 71 if err == io.EOF { 72 return i, nil 73 } 74 if err != nil { 75 return nil, err 76 } 77 78 Debug("It is %v type %v", f, f.Type) 79 Debug("Starting at %#02x + %#02x", i.Area.Offset, f.RecordStart) 80 81 sr, ok := SegReaders[f.Type] 82 if !ok { 83 Debug("No match found for type %v, %v", f.Type, ok) 84 sr = &SegReader{Type: f.Type, Name: "Unknown", New: NewUnknownRecord} 85 } 86 s, err := sr.New(f) 87 if err != nil { 88 return nil, err 89 } 90 Debug("Segment: %v", s) 91 if err := s.Read(bytes.NewReader(f.FData)); err != nil { 92 return nil, fmt.Errorf("Reading %#x byte subheader, type %v: %v", len(f.FData), f.Type, err) 93 } 94 Debug("Segment was readable") 95 i.Segs = append(i.Segs, s) 96 off, err = r.Seek(0, io.SeekCurrent) 97 if err != nil { 98 return nil, err 99 } 100 // Force alignment. 101 off = (off + 15) & (^15) 102 } 103 return i, nil 104 } 105 106 func (i *Image) WriteFile(name string, perm os.FileMode) error { 107 if err := os.WriteFile(name, i.Data, 0666); err != nil { 108 return err 109 } 110 return nil 111 } 112 113 // Update creates a new []byte for the cbfs. It is complicated a lot 114 // by the fact that endianness is not consistent in cbfs images. 115 func (i *Image) Update() error { 116 //FIXME: Support additional regions 117 for _, s := range i.Segs { 118 var b bytes.Buffer 119 if err := Write(&b, s.GetFile().FileHeader); err != nil { 120 return err 121 } 122 if _, err := b.Write(s.GetFile().Attr); err != nil { 123 return fmt.Errorf("Writing attr to cbfs record for %v: %v", s, err) 124 } 125 if err := s.Write(&b); err != nil { 126 return err 127 } 128 // This error should not happen but we need to check just in case. 129 end := uint32(len(b.Bytes())) + s.GetFile().RecordStart 130 if end > i.Area.Size { 131 return fmt.Errorf("Region [%#x, %#x] outside of CBFS [%#x, %#x]", s.GetFile().RecordStart, end, s.GetFile().RecordStart, i.Area.Size) 132 } 133 134 Debug("Copy %s %d bytes to i.Data[%d]", s.GetFile().Type.String(), len(b.Bytes()), i.Area.Offset+s.GetFile().RecordStart) 135 copy(i.Data[i.Area.Offset+s.GetFile().RecordStart:], b.Bytes()) 136 } 137 return nil 138 } 139 140 type mImage struct { 141 Offset uint32 142 Segments []ReadWriter 143 } 144 145 func (i *Image) MarshalJSON() ([]byte, error) { 146 return json.Marshal(mImage{Segments: i.Segs, Offset: i.Area.Offset}) 147 } 148 149 func (i *Image) String() string { 150 var s = "FMAP REGIOName: COREBOOT\n" 151 152 s += fmt.Sprintf("%-32s %-8s %-24s %-8s %-4s\n", "Name", "Offset", "Type", "Size", "Comp") 153 for _, seg := range i.Segs { 154 s = s + seg.String() + "\n" 155 } 156 return s 157 } 158 159 func (h *FileHeader) Deleted() bool { 160 t := h.Type 161 return t == TypeDeleted || t == TypeDeleted2 162 } 163 164 func (i *Image) Remove(n string) error { 165 found := -1 166 for x, s := range i.Segs { 167 if s.GetFile().Name == n { 168 found = x 169 } 170 } 171 if found == -1 { 172 return os.ErrExist 173 } 174 // You can not remove the master header 175 // Just remake the cbfs if you're doing that kind of surgery. 176 if found == 0 { 177 return os.ErrPermission 178 } 179 // Bootblock on x86 is at the end of CBFS and shall stay untouched. 180 if found == len(i.Segs)-1 && i.Segs[found].GetFile().Type == TypeBootBlock { 181 return os.ErrPermission 182 } 183 start, end := found, found+1 184 if i.Segs[start-1].GetFile().Deleted() { 185 start = start - 1 186 } 187 if i.Segs[end].GetFile().Deleted() { 188 end = end + 1 189 } 190 Debug("Remove: empty range [%d:%d]", start, end) 191 base := i.Segs[start].GetFile().RecordStart 192 top := i.Segs[end].GetFile().RecordStart 193 Debug("Remove: base %#x top %#x", base, top) 194 // 0x28: header size + 16-byte-aligned-size name 195 s := top - base - 0x28 196 i.Segs[found].GetFile().SubHeaderOffset = 0x28 197 i.Segs[found].GetFile().Size = s 198 del, _ := NewEmptyRecord(i.Segs[found].GetFile()) 199 Debug("Offset is 0x28, Size is %#x", s) 200 Debug("Remove: Replace %d..%d with %s", start, end, del.String()) 201 // At most, there will be an Empty record before us since 202 // things come pre-merged 203 i.Segs = append(append(i.Segs[:start], del), i.Segs[end:]...) 204 return nil 205 }