github.com/linuxboot/fiano@v1.2.0/pkg/visitors/tightenme.go (about) 1 // Copyright 2019 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 visitors 6 7 import ( 8 "fmt" 9 10 "github.com/linuxboot/fiano/pkg/log" 11 "github.com/linuxboot/fiano/pkg/uefi" 12 ) 13 14 // TightenME tighten ME's belt to give more room for LinuxBoot 15 // This changes the ME Region Limit to fit the actual content as described in the ME partitions. Also update the BIOS Region Base to start just after the ME Region 16 type TightenME struct { 17 fd *uefi.FlashDescriptor 18 mer *uefi.MERegion 19 br *uefi.BIOSRegion 20 } 21 22 // Run wraps Visit and performs some setup and teardown tasks. 23 func (v *TightenME) Run(f uefi.Firmware) error { 24 err := f.Apply(v) 25 if err != nil { 26 return fmt.Errorf("error looking for IFD, ME and BIOS regions: %v", err) 27 } 28 29 return v.process() 30 } 31 32 // Visit applies the TightenME visitor to any Firmware type. 33 func (v *TightenME) Visit(f uefi.Firmware) error { 34 switch f := f.(type) { 35 case *uefi.FlashDescriptor: 36 v.fd = f 37 return nil 38 case *uefi.MERegion: 39 v.mer = f 40 return nil 41 case *uefi.BIOSRegion: 42 v.br = f 43 return nil 44 default: 45 return f.ApplyChildren(v) 46 } 47 } 48 49 func (v *TightenME) process() error { 50 // Ensuring IFD exists also ensure FRegion are populated in Regions 51 if v.fd == nil { 52 return fmt.Errorf("no IFD found") 53 } 54 if v.mer == nil { 55 return fmt.Errorf("no ME region found") 56 } 57 if v.br == nil { 58 return fmt.Errorf("no BIOS region found") 59 } 60 // TODO: We might be able to relax this restriction if there is a region 61 // in between that can be shifted, but this needs more tests... 62 if v.mer.FRegion.EndOffset() != v.br.FRegion.BaseOffset() { 63 return fmt.Errorf("ME and BIOS regions are not contiguous: ME end at %#x BIOS starts at %#x", v.mer.FRegion.EndOffset(), v.br.FRegion.BaseOffset()) 64 65 } 66 // Compute the new limit 67 updateOffset := uint64(v.mer.FRegion.BaseOffset()) + v.mer.FreeSpaceOffset 68 updateBase := (updateOffset + uefi.RegionBlockSize - 1) / uefi.RegionBlockSize 69 // align the new absolute offset to uefi.RegionBlockSize 70 updateOffset = updateBase * uefi.RegionBlockSize 71 bufOffset := updateOffset - uint64(v.mer.FRegion.BaseOffset()) 72 // check the zone if empty 73 buf := v.mer.Buf() 74 if !uefi.IsErased(buf[bufOffset:], uefi.Attributes.ErasePolarity) { 75 return fmt.Errorf("ME unused space in not erased as expected") 76 } 77 // Shrink ME Region 78 v.mer.FRegion.Limit = uint16(updateBase - 1) 79 v.mer.SetBuf(buf[:bufOffset]) 80 // Expand BIOS Region 81 offsetShift := uint64(v.br.FRegion.BaseOffset()) - updateOffset 82 v.br.FRegion.Base = uint16(updateBase) 83 v.br.Length += offsetShift 84 if v.br.Length > 16*1024*1024 { 85 log.Warnf("new BIOS Regions length %d (%#x) exceed 16MiB limit", v.br.Length, v.br.Length) 86 } 87 // update elements offsets 88 for i, e := range v.br.Elements { 89 switch f := e.Value.(type) { 90 case *uefi.FirmwareVolume: 91 f.FVOffset += offsetShift 92 case *uefi.BIOSPadding: 93 f.Offset += offsetShift 94 default: 95 return fmt.Errorf("unexpected Element at %d: %s", i, e.Type) 96 } 97 } 98 // insert BIOSPad 99 bp, err := uefi.NewBIOSPadding(buf[bufOffset:], 0) 100 if err != nil { 101 return fmt.Errorf("could not create BIOS Padding: %v", err) 102 } 103 v.br.Elements = append([]*uefi.TypedFirmware{uefi.MakeTyped(bp)}, v.br.Elements...) 104 // Assemble will regenerate IFD so regions will be updated in the image 105 106 return nil 107 } 108 109 func init() { 110 RegisterCLI("tighten_me", "tighten ME's belt to give more room for LinuxBoot", 0, func(args []string) (uefi.Visitor, error) { 111 return &TightenME{}, nil 112 }) 113 }