github.com/linuxboot/fiano@v1.2.0/pkg/uefi/biosregion.go (about)

     1  // Copyright 2018 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 uefi
     6  
     7  import (
     8  	"errors"
     9  )
    10  
    11  // BIOSPadding holds the padding in between firmware volumes
    12  // This may sometimes hold data, even though it shouldn't. We need
    13  // to preserve it though.
    14  type BIOSPadding struct {
    15  	buf    []byte
    16  	Offset uint64
    17  
    18  	// Metadata
    19  	ExtractPath string
    20  }
    21  
    22  // NewBIOSPadding parses a sequence of bytes and returns a BIOSPadding
    23  // object.
    24  func NewBIOSPadding(buf []byte, offset uint64) (*BIOSPadding, error) {
    25  	bp := &BIOSPadding{buf: buf, Offset: offset}
    26  	return bp, nil
    27  }
    28  
    29  // Buf returns the buffer
    30  func (bp *BIOSPadding) Buf() []byte {
    31  	return bp.buf
    32  }
    33  
    34  // SetBuf sets the buffer
    35  func (bp *BIOSPadding) SetBuf(buf []byte) {
    36  	bp.buf = buf
    37  }
    38  
    39  // Apply a visitor to the BIOSPadding.
    40  func (bp *BIOSPadding) Apply(v Visitor) error {
    41  	return v.Visit(bp)
    42  }
    43  
    44  // ApplyChildren applies a visitor to all the direct children of the BIOSPadding
    45  func (bp *BIOSPadding) ApplyChildren(v Visitor) error {
    46  	return nil
    47  }
    48  
    49  // BIOSRegion represents the Bios Region in the firmware.
    50  // It holds all the FVs as well as padding
    51  type BIOSRegion struct {
    52  	// holds the raw data
    53  	buf      []byte
    54  	Elements []*TypedFirmware `json:",omitempty"`
    55  
    56  	// Metadata for extraction and recovery
    57  	ExtractPath string
    58  	Length      uint64
    59  	// This is a pointer to the FlashRegion struct laid out in the ifd.
    60  	FRegion    *FlashRegion
    61  	RegionType FlashRegionType
    62  }
    63  
    64  // Type returns the flash region type.
    65  func (br *BIOSRegion) Type() FlashRegionType {
    66  	return RegionTypeBIOS
    67  }
    68  
    69  // SetFlashRegion sets the Flash Region.
    70  func (br *BIOSRegion) SetFlashRegion(fr *FlashRegion) {
    71  	br.FRegion = fr
    72  }
    73  
    74  // FlashRegion gets the Flash Region.
    75  func (br *BIOSRegion) FlashRegion() (fr *FlashRegion) {
    76  	return br.FRegion
    77  }
    78  
    79  // NewBIOSRegion parses a sequence of bytes and returns a Region
    80  // object, if a valid one is passed, or an error. It also points to the
    81  // Region struct uncovered in the ifd.
    82  func NewBIOSRegion(buf []byte, r *FlashRegion, _ FlashRegionType) (Region, error) {
    83  	br := BIOSRegion{FRegion: r, Length: uint64(len(buf)),
    84  		RegionType: RegionTypeBIOS}
    85  	var absOffset uint64
    86  
    87  	// Copy the buffer
    88  	if ReadOnly {
    89  		br.buf = buf
    90  	} else {
    91  		br.buf = make([]byte, len(buf))
    92  		copy(br.buf, buf)
    93  	}
    94  
    95  	for {
    96  		offset := FindFirmwareVolumeOffset(buf)
    97  		if offset < 0 {
    98  			// no firmware volume found, stop searching
    99  			// There shouldn't be padding near the end, but store it in case anyway
   100  			if len(buf) != 0 {
   101  				bp, err := NewBIOSPadding(buf, absOffset)
   102  				if err != nil {
   103  					return nil, err
   104  				}
   105  				br.Elements = append(br.Elements, MakeTyped(bp))
   106  			}
   107  			break
   108  		}
   109  		if offset > 0 {
   110  			// There is some padding here, store it in case there is data.
   111  			// We could check and conditionally store, but that makes things more complicated
   112  			bp, err := NewBIOSPadding(buf[:offset], absOffset)
   113  			if err != nil {
   114  				return nil, err
   115  			}
   116  			br.Elements = append(br.Elements, MakeTyped(bp))
   117  		}
   118  		absOffset += uint64(offset)                                  // Find start of volume relative to bios region.
   119  		fv, err := NewFirmwareVolume(buf[offset:], absOffset, false) // False as top level FVs are not resizable
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  		absOffset += fv.Length
   124  		buf = buf[uint64(offset)+fv.Length:]
   125  		br.Elements = append(br.Elements, MakeTyped(fv))
   126  	}
   127  	return &br, nil
   128  }
   129  
   130  // Buf returns the buffer.
   131  // Used mostly for things interacting with the Firmware interface.
   132  func (br *BIOSRegion) Buf() []byte {
   133  	return br.buf
   134  }
   135  
   136  // SetBuf sets the buffer.
   137  // Used mostly for things interacting with the Firmware interface.
   138  func (br *BIOSRegion) SetBuf(buf []byte) {
   139  	br.buf = buf
   140  }
   141  
   142  // Apply calls the visitor on the BIOSRegion.
   143  func (br *BIOSRegion) Apply(v Visitor) error {
   144  	return v.Visit(br)
   145  }
   146  
   147  // ApplyChildren calls the visitor on each child node of BIOSRegion.
   148  func (br *BIOSRegion) ApplyChildren(v Visitor) error {
   149  	for _, f := range br.Elements {
   150  		if err := f.Value.Apply(v); err != nil {
   151  			return err
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  // FirstFV finds the first firmware volume in the BIOSRegion.
   158  func (br *BIOSRegion) FirstFV() (*FirmwareVolume, error) {
   159  	for _, e := range br.Elements {
   160  		if f, ok := e.Value.(*FirmwareVolume); ok {
   161  			return f, nil
   162  		}
   163  	}
   164  	return nil, errors.New("no firmware volumes in BIOS Region")
   165  }