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  }