github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/kexec/memory_linux.go (about)

     1  // Copyright 2015-2019 the u-root 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 kexec
     6  
     7  import (
     8  	"bytes"
     9  	"debug/elf"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"reflect"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  	"unsafe"
    21  
    22  	"github.com/mvdan/u-root-coreutils/pkg/align"
    23  	"github.com/mvdan/u-root-coreutils/pkg/dt"
    24  )
    25  
    26  var pageMask = uint(os.Getpagesize() - 1)
    27  
    28  // ErrNotEnoughSpace is returned by the FindSpace family of functions if no
    29  // range is large enough to accommodate the request.
    30  type ErrNotEnoughSpace struct {
    31  	Size uint
    32  }
    33  
    34  func (e ErrNotEnoughSpace) Error() string {
    35  	return fmt.Sprintf("not enough space to allocate %#x bytes", e.Size)
    36  }
    37  
    38  // Range represents a contiguous uintptr interval [Start, Start+Size).
    39  type Range struct {
    40  	// Start is the inclusive start of the range.
    41  	Start uintptr
    42  
    43  	// Size is the number of elements in the range.
    44  	//
    45  	// Start+Size is the exclusive end of the range.
    46  	Size uint
    47  }
    48  
    49  // RangeFromInterval returns a Range representing [start, end).
    50  func RangeFromInterval(start, end uintptr) Range {
    51  	return Range{
    52  		Start: start,
    53  		Size:  uint(end - start),
    54  	}
    55  }
    56  
    57  // String returns [Start, Start+Size) as a string.
    58  func (r Range) String() string {
    59  	return fmt.Sprintf("[%#x, %#x)", r.Start, r.End())
    60  }
    61  
    62  // End returns the uintptr *after* the end of the interval.
    63  func (r Range) End() uintptr {
    64  	return r.Start + uintptr(r.Size)
    65  }
    66  
    67  // Adjacent returns true if r and r2 do not overlap, but are immediately next
    68  // to each other.
    69  func (r Range) Adjacent(r2 Range) bool {
    70  	return r2.End() == r.Start || r.End() == r2.Start
    71  }
    72  
    73  // Contains returns true iff p is in the interval described by r.
    74  func (r Range) Contains(p uintptr) bool {
    75  	return r.Start <= p && p < r.End()
    76  }
    77  
    78  func min(a, b uintptr) uintptr {
    79  	if a < b {
    80  		return a
    81  	}
    82  	return b
    83  }
    84  
    85  func minuint(a, b uint) uint {
    86  	if a < b {
    87  		return a
    88  	}
    89  	return b
    90  }
    91  
    92  func max(a, b uintptr) uintptr {
    93  	if a > b {
    94  		return a
    95  	}
    96  	return b
    97  }
    98  
    99  // Intersect returns the continuous range of points common to r and r2 if there
   100  // is one.
   101  func (r Range) Intersect(r2 Range) *Range {
   102  	if !r.Overlaps(r2) {
   103  		return nil
   104  	}
   105  	i := RangeFromInterval(max(r.Start, r2.Start), min(r.End(), r2.End()))
   106  	return &i
   107  }
   108  
   109  // Minus removes all points in r2 from r.
   110  func (r Range) Minus(r2 Range) []Range {
   111  	var result []Range
   112  	if r.Contains(r2.Start) && r.Start != r2.Start {
   113  		result = append(result, Range{
   114  			Start: r.Start,
   115  			Size:  uint(r2.Start - r.Start),
   116  		})
   117  	}
   118  	if r.Contains(r2.End()) && r.End() != r2.End() {
   119  		result = append(result, Range{
   120  			Start: r2.End(),
   121  			Size:  uint(r.End() - r2.End()),
   122  		})
   123  	}
   124  	// Neither end was in r?
   125  	//
   126  	// Either r is a subset of r2 and r disappears completely, or they are
   127  	// completely disjunct.
   128  	if len(result) == 0 && r.Disjunct(r2) {
   129  		result = append(result, r)
   130  	}
   131  	return result
   132  }
   133  
   134  // Overlaps returns true if r and r2 overlap.
   135  func (r Range) Overlaps(r2 Range) bool {
   136  	return r.Start < r2.End() && r2.Start < r.End()
   137  }
   138  
   139  // IsSupersetOf returns true if r2 in r.
   140  func (r Range) IsSupersetOf(r2 Range) bool {
   141  	return r.Start <= r2.Start && r.End() >= r2.End()
   142  }
   143  
   144  // Disjunct returns true if r and r2 do not overlap.
   145  func (r Range) Disjunct(r2 Range) bool {
   146  	return !r.Overlaps(r2)
   147  }
   148  
   149  func (r Range) toSlice() []byte {
   150  	var data []byte
   151  
   152  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
   153  	sh.Data = r.Start
   154  	sh.Len = int(r.Size)
   155  	sh.Cap = int(r.Size)
   156  
   157  	return data
   158  }
   159  
   160  // Ranges is a list of non-overlapping ranges.
   161  type Ranges []Range
   162  
   163  // Minus removes all points in r from all ranges in rs.
   164  func (rs Ranges) Minus(r Range) Ranges {
   165  	var ram Ranges
   166  	for _, oldRange := range rs {
   167  		ram = append(ram, oldRange.Minus(r)...)
   168  	}
   169  	return ram
   170  }
   171  
   172  // FindSpace finds a continguous piece of sz points within Ranges and returns
   173  // the Range pointing to it.
   174  //
   175  // If alignSizeBytes is zero, align up by page size.
   176  func (rs Ranges) FindSpace(sz uint) (space Range, err error) {
   177  	return rs.FindSpaceAbove(sz, 0)
   178  }
   179  
   180  // MaxAddr is the highest address in a 64bit address space.
   181  const MaxAddr = ^uintptr(0)
   182  
   183  // FindSpaceAbove finds a continguous piece of sz points within Ranges and
   184  // returns a space.Start >= minAddr.
   185  func (rs Ranges) FindSpaceAbove(sz uint, minAddr uintptr) (space Range, err error) {
   186  	return rs.FindSpaceIn(sz, RangeFromInterval(minAddr, MaxAddr))
   187  }
   188  
   189  // FindSpaceIn finds a continguous piece of sz points within Ranges and returns
   190  // a Range where space.Start >= limit.Start, with space.End() < limit.End().
   191  func (rs Ranges) FindSpaceIn(sz uint, limit Range) (space Range, err error) {
   192  	for _, r := range rs {
   193  		if overlap := r.Intersect(limit); overlap != nil && overlap.Size >= sz {
   194  			return Range{Start: overlap.Start, Size: sz}, nil
   195  		}
   196  	}
   197  	return Range{}, ErrNotEnoughSpace{Size: sz}
   198  }
   199  
   200  // Sort sorts ranges by their start point.
   201  func (rs Ranges) Sort() {
   202  	sort.Slice(rs, func(i, j int) bool {
   203  		if rs[i].Start == rs[j].Start {
   204  			// let rs[i] be the superset of rs[j]
   205  			return rs[i].Size > rs[j].Size
   206  		}
   207  
   208  		return rs[i].Start < rs[j].Start
   209  	})
   210  }
   211  
   212  // pool stores byte slices pointed by the pointers Segments.Buf to
   213  // prevent underlying arrays to be collected by garbage collector.
   214  var pool [][]byte
   215  
   216  // Segment defines kernel memory layout.
   217  type Segment struct {
   218  	// Buf is a buffer in user space.
   219  	Buf Range
   220  
   221  	// Phys is a physical address of kernel.
   222  	Phys Range
   223  }
   224  
   225  // NewSegment creates new Segment.
   226  // Segments should be created using NewSegment method to prevent
   227  // data pointed by Segment.Buf to be collected by garbage collector.
   228  func NewSegment(buf []byte, phys Range) Segment {
   229  	if buf == nil {
   230  		return Segment{
   231  			Buf: Range{
   232  				Start: 0,
   233  				Size:  0,
   234  			},
   235  			Phys: phys,
   236  		}
   237  	}
   238  	pool = append(pool, buf)
   239  	return Segment{
   240  		Buf: Range{
   241  			Start: uintptr((unsafe.Pointer(&buf[0]))),
   242  			Size:  uint(len(buf)),
   243  		},
   244  		Phys: phys,
   245  	}
   246  }
   247  
   248  func (s Segment) String() string {
   249  	return fmt.Sprintf("(userspace: %s, phys: %s)", s.Buf, s.Phys)
   250  }
   251  
   252  // AlignAndMerge adjusts segs to the preconditions of kexec_load.
   253  //
   254  // Pre-conditions: segs physical ranges are disjoint.
   255  // Post-conditions: segs physical start addresses & size aligned to page size.
   256  func AlignAndMerge(segs Segments) (Segments, error) {
   257  	sort.Slice(segs, func(i, j int) bool {
   258  		if segs[i].Phys.Start == segs[j].Phys.Start {
   259  			// let segs[i] be the superset of segs[j]
   260  			return segs[i].Phys.Size > segs[j].Phys.Size
   261  		}
   262  		return segs[i].Phys.Start < segs[j].Phys.Start
   263  	})
   264  
   265  	// We index 0 below.
   266  	if len(segs) == 0 {
   267  		return segs, nil
   268  	}
   269  
   270  	// Physical ranges may not overlap.
   271  	//
   272  	// Overlapping ranges could be allowed if the corresponding
   273  	// intersecting buffer ranges contain the same bytes. TBD whether
   274  	// that's needed.
   275  	for i := 0; i < len(segs)-1; i++ {
   276  		if segs[i].Phys.Overlaps(segs[i+1].Phys) {
   277  			return nil, fmt.Errorf("segment %s and %s overlap in the physical space", segs[i], segs[i+1])
   278  		}
   279  	}
   280  
   281  	// Since segments' physical ranges are guaranteed to be disjoint, the
   282  	// only condition under which they overlap is if an aligned Phys.Start
   283  	// address overlaps. In that case, merge the segments.
   284  	//
   285  	// The sorting guarantees we can step through linearly.
   286  	var newSegs Segments
   287  	newSegs = append(newSegs, AlignPhysStart(segs[0]))
   288  
   289  	for i := 1; i < len(segs); i++ {
   290  		cand := AlignPhysStart(segs[i])
   291  
   292  		// Does the aligned segment overlap with the previous
   293  		// segment? We'll have to merge the (unaligned) segment with
   294  		// the last segment.
   295  		if cand.Phys.Overlaps(newSegs[len(newSegs)-1].Phys) {
   296  			if ok := newSegs[len(newSegs)-1].mergeDisjoint(segs[i]); !ok {
   297  				// This should be impossible as long as
   298  				// mergeDisjoint and Overlaps have a matching
   299  				// definition of overlapping.
   300  				return nil, fmt.Errorf("could not merge disjoint segments")
   301  			}
   302  		} else {
   303  			newSegs = append(newSegs, cand)
   304  		}
   305  	}
   306  
   307  	// Align the sizes. This is guaranteed to still produce disjoint
   308  	// physical ranges.
   309  	for i := range newSegs {
   310  		// If we adjust the size up, we must curtail the buffer.
   311  		//
   312  		// If Phys.Size = 1 and Buf.Size = 8, the caller only expected
   313  		// 1 byte to go into the physical range.
   314  		//
   315  		// We don't need to deal with the inverse, because kexec_load
   316  		// will fill the remainder of the segment with zeros anyway
   317  		// when buf.Size < phys.Size.
   318  		if newSegs[i].Buf.Size > newSegs[i].Phys.Size {
   319  			newSegs[i].Buf.Size = newSegs[i].Phys.Size
   320  		}
   321  		newSegs[i].Phys.Size = align.UpPage(newSegs[i].Phys.Size)
   322  	}
   323  	return newSegs, nil
   324  }
   325  
   326  // realBufPad adjusts s.Buf.Size = s.Phys.Size. Buf will either gain some zeros
   327  // or be truncated.
   328  func (s Segment) realBufPad() []byte {
   329  	switch {
   330  	case s.Buf.Size == s.Phys.Size:
   331  		return s.Buf.toSlice()
   332  
   333  	case s.Buf.Size < s.Phys.Size:
   334  		return append(s.Buf.toSlice(), make([]byte, int(s.Phys.Size-s.Buf.Size))...)
   335  
   336  	case s.Buf.Size > s.Phys.Size:
   337  		return s.Buf.toSlice()[:s.Phys.Size]
   338  	}
   339  	return nil
   340  }
   341  
   342  // realBufTruncate adjusts s.Buf.Size = s.Phys.Size, except when Buf is smaller
   343  // than Phys. Buf will either remain the same or be truncated.
   344  func (s Segment) realBufTruncate() []byte {
   345  	switch {
   346  	case s.Buf.Size == s.Phys.Size:
   347  		return s.Buf.toSlice()
   348  
   349  	case s.Buf.Size < s.Phys.Size:
   350  		return s.Buf.toSlice()
   351  
   352  	case s.Buf.Size > s.Phys.Size:
   353  		return s.Buf.toSlice()[:s.Phys.Size]
   354  	}
   355  	return nil
   356  }
   357  
   358  func (s *Segment) mergeDisjoint(s2 Segment) bool {
   359  	if s.Phys.Overlaps(s2.Phys) {
   360  		return false
   361  	}
   362  	// Must be s < s2
   363  	if s.Phys.Start > s2.Phys.Start {
   364  		return false
   365  	}
   366  
   367  	a := s.realBufPad()
   368  	// Second half can drop the extra padded zeroes.
   369  	b := s2.realBufTruncate()
   370  	diffSize := s2.Phys.Start - s.Phys.End()
   371  	// Zeros for the middle.
   372  	buf := append(a, make([]byte, int(diffSize))...)
   373  	buf = append(buf, b...)
   374  
   375  	phys := s.Phys
   376  	phys.Size += uint(diffSize) + s2.Phys.Size
   377  	*s = NewSegment(buf, phys)
   378  	return true
   379  }
   380  
   381  // AlignPhysStart aligns s.Phys.Start to the page size. AlignPhysStart does not
   382  // align the size of the segment.
   383  func AlignPhysStart(s Segment) Segment {
   384  	orig := s.Phys.Start
   385  	// Find the page address of the starting point.
   386  	s.Phys.Start = s.Phys.Start &^ uintptr(pageMask)
   387  	diff := orig - s.Phys.Start
   388  	s.Phys.Size = s.Phys.Size + uint(diff)
   389  
   390  	if s.Buf.Start < diff && diff > 0 {
   391  		panic("cannot have virtual memory address within first page")
   392  	}
   393  	s.Buf.Start -= diff
   394  
   395  	if s.Buf.Size > 0 {
   396  		s.Buf.Size += uint(diff)
   397  	}
   398  	return s
   399  }
   400  
   401  // Segments is a collection of segments.
   402  type Segments []Segment
   403  
   404  // PhysContains returns whether p exists in any of segs' physical memory
   405  // ranges.
   406  func (segs Segments) PhysContains(p uintptr) bool {
   407  	for _, s := range segs {
   408  		if s.Phys.Contains(p) {
   409  			return true
   410  		}
   411  	}
   412  	return false
   413  }
   414  
   415  // Phys returns all physical address ranges.
   416  func (segs Segments) Phys() Ranges {
   417  	var r Ranges
   418  	for _, s := range segs {
   419  		r = append(r, s.Phys)
   420  	}
   421  	return r
   422  }
   423  
   424  // IsSupersetOf checks whether all segments in o are present in s and contain
   425  // the same buffer content.
   426  func (segs Segments) IsSupersetOf(o Segments) error {
   427  	for _, seg := range o {
   428  		size := minuint(seg.Phys.Size, seg.Buf.Size)
   429  		if size == 0 {
   430  			continue
   431  		}
   432  		r := Range{Start: seg.Phys.Start, Size: size}
   433  		buf := segs.GetPhys(r)
   434  		if buf == nil {
   435  			return fmt.Errorf("phys %s not found", r)
   436  		}
   437  		if !bytes.Equal(buf, seg.Buf.toSlice()[:size]) {
   438  			return fmt.Errorf("phys %s contains different bytes", r)
   439  		}
   440  	}
   441  	return nil
   442  }
   443  
   444  // GetPhys gets the buffer corresponding to the physical address range r.
   445  func (segs Segments) GetPhys(r Range) []byte {
   446  	for _, seg := range segs {
   447  		if seg.Phys.IsSupersetOf(r) {
   448  			offset := r.Start - seg.Phys.Start
   449  			// TODO: This could be out of range.
   450  			buf := seg.Buf.toSlice()[int(offset) : int(offset)+int(r.Size)]
   451  			return buf
   452  		}
   453  	}
   454  	return nil
   455  }
   456  
   457  // Insert inserts s assuming it does not overlap with an existing segment.
   458  func (segs *Segments) Insert(s Segment) {
   459  	*segs = append(*segs, s)
   460  	segs.sort()
   461  }
   462  
   463  func (segs Segments) sort() {
   464  	sort.Slice(segs, func(i, j int) bool {
   465  		return segs[i].Phys.Start < segs[j].Phys.Start
   466  	})
   467  }
   468  
   469  // Memory provides routines to work with physical memory ranges.
   470  type Memory struct {
   471  	// Phys defines the layout of physical memory.
   472  	//
   473  	// Phys is used to tell loaded operating systems what memory is usable
   474  	// as RAM, and what memory is reserved (for ACPI or other reasons).
   475  	Phys MemoryMap
   476  
   477  	// Segments are the segments used to load a new operating system.
   478  	//
   479  	// Each segment also contains a physical memory region it maps to.
   480  	Segments Segments
   481  }
   482  
   483  // LoadElfSegments loads loadable ELF segments.
   484  func (m *Memory) LoadElfSegments(r io.ReaderAt) (Object, error) {
   485  	f, err := ObjectNewFile(r)
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  
   490  	for _, p := range f.Progs() {
   491  		if p.Type != elf.PT_LOAD {
   492  			continue
   493  		}
   494  
   495  		var d []byte
   496  		// Only load segment if there are some data. The kexec call will zero out the rest of the buffer (all of it if Filesz=0):
   497  		// | bufsz bytes are copied from the source buffer to the target kernel buffer. If bufsz is less than memsz, then the excess bytes in the kernel buffer are zeroed out.
   498  		// http://man7.org/linux/man-pages/man2/kexec_load.2.html
   499  		if p.Filesz != 0 {
   500  			d = make([]byte, p.Filesz)
   501  			n, err := r.ReadAt(d, int64(p.Off))
   502  			if err != nil {
   503  				return nil, err
   504  			}
   505  			if n < len(d) {
   506  				return nil, fmt.Errorf("not all data of the segment was read")
   507  			}
   508  		}
   509  		// TODO(hugelgupf): check if this is within availableRAM??
   510  		s := NewSegment(d, Range{
   511  			Start: uintptr(p.Paddr),
   512  			Size:  uint(p.Memsz),
   513  		})
   514  		m.Segments.Insert(s)
   515  	}
   516  	return f, nil
   517  }
   518  
   519  // ParseMemoryMap reads firmware provided memory map from /sys/firmware/memmap.
   520  func (m *Memory) ParseMemoryMap() error {
   521  	p, err := ParseMemoryMap()
   522  	if err != nil {
   523  		return err
   524  	}
   525  	m.Phys = p
   526  	return nil
   527  }
   528  
   529  // ParseMemoryMapFromFDT reads firmware provided memory map from an FDT.
   530  func (m *Memory) ParseMemoryMapFromFDT(fdt *dt.FDT) error {
   531  	var phys MemoryMap
   532  	addMemory := func(n *dt.Node) error {
   533  		p, found := n.LookProperty("device_type")
   534  		if !found {
   535  			return nil
   536  		}
   537  		t, err := p.AsString()
   538  		if err != nil || t != "memory" {
   539  			return nil
   540  		}
   541  		p, found = n.LookProperty("reg")
   542  		if found {
   543  			r, err := p.AsRegion()
   544  			if err != nil {
   545  				return err
   546  			}
   547  			phys = append(phys, TypedRange{
   548  				Range: Range{Start: uintptr(r.Start), Size: uint(r.Size)},
   549  				Type:  RangeRAM,
   550  			})
   551  		}
   552  		return nil
   553  	}
   554  	err := fdt.RootNode.Walk(addMemory)
   555  	if err != nil {
   556  		return err
   557  	}
   558  
   559  	reserveMemory := func(n *dt.Node) error {
   560  		p, found := n.LookProperty("reg")
   561  		if found {
   562  			r, err := p.AsRegion()
   563  			if err != nil {
   564  				return err
   565  			}
   566  
   567  			phys.Insert(TypedRange{
   568  				Range: Range{Start: uintptr(r.Start), Size: uint(r.Size)},
   569  				Type:  RangeReserved,
   570  			})
   571  		}
   572  		return nil
   573  	}
   574  	resv, found := fdt.NodeByName("reserved-memory")
   575  	if found {
   576  		err := resv.Walk(reserveMemory)
   577  		if err != nil {
   578  			return err
   579  		}
   580  	}
   581  
   582  	for _, r := range fdt.ReserveEntries {
   583  		phys.Insert(TypedRange{
   584  			Range: Range{Start: uintptr(r.Address), Size: uint(r.Size)},
   585  			Type:  RangeReserved,
   586  		})
   587  	}
   588  
   589  	for _, r := range phys {
   590  		log.Printf("memmap: 0x%016x 0x%016x %s", r.Start, r.Size, r.Type)
   591  	}
   592  	m.Phys = phys
   593  	return nil
   594  }
   595  
   596  var memoryMapRoot = "/sys/firmware/memmap/"
   597  
   598  // ParseMemoryMap reads firmware provided memory map from /sys/firmware/memmap.
   599  func ParseMemoryMap() (MemoryMap, error) {
   600  	return internalParseMemoryMap(memoryMapRoot)
   601  }
   602  
   603  func internalParseMemoryMap(memoryMapDir string) (MemoryMap, error) {
   604  	type memRange struct {
   605  		// start and end addresses are inclusive
   606  		start, end uintptr
   607  		typ        RangeType
   608  	}
   609  
   610  	ranges := make(map[string]memRange)
   611  	walker := func(name string, info os.FileInfo, err error) error {
   612  		if err != nil {
   613  			return err
   614  		}
   615  		if info.IsDir() {
   616  			return nil
   617  		}
   618  
   619  		const (
   620  			// file names
   621  			start = "start"
   622  			end   = "end"
   623  			typ   = "type"
   624  		)
   625  
   626  		base := path.Base(name)
   627  		if base != start && base != end && base != typ {
   628  			return fmt.Errorf("unexpected file %q", name)
   629  		}
   630  		dir := path.Dir(name)
   631  
   632  		b, err := os.ReadFile(name)
   633  		if err != nil {
   634  			return fmt.Errorf("error reading file %q: %v", name, err)
   635  		}
   636  
   637  		data := strings.TrimSpace(string(b))
   638  		r := ranges[dir]
   639  		if base == typ {
   640  			typ, ok := sysfsToRangeType[data]
   641  			if !ok {
   642  				log.Printf("Sysfs file %q contains unrecognized memory map type %q, defaulting to Reserved", name, data)
   643  				r.typ = RangeReserved
   644  			} else {
   645  				r.typ = typ
   646  			}
   647  			ranges[dir] = r
   648  			return nil
   649  		}
   650  
   651  		v, err := strconv.ParseUint(data, 0, strconv.IntSize)
   652  		if err != nil {
   653  			return err
   654  		}
   655  		switch base {
   656  		case start:
   657  			r.start = uintptr(v)
   658  		case end:
   659  			r.end = uintptr(v)
   660  		}
   661  		ranges[dir] = r
   662  		return nil
   663  	}
   664  
   665  	if err := filepath.Walk(memoryMapDir, walker); err != nil {
   666  		return nil, err
   667  	}
   668  
   669  	var phys []TypedRange
   670  	for _, r := range ranges {
   671  		// Range's end address is exclusive, while Linux's sysfs prints
   672  		// the end address inclusive.
   673  		//
   674  		// E.g. sysfs will contain
   675  		//
   676  		// start: 0x100, end: 0x1ff
   677  		//
   678  		// while we represent
   679  		//
   680  		// start: 0x100, size: 0x100.
   681  		phys = append(phys, TypedRange{
   682  			Range: RangeFromInterval(r.start, r.end+1),
   683  			Type:  r.typ,
   684  		})
   685  	}
   686  	sort.Slice(phys, func(i, j int) bool {
   687  		return phys[i].Start < phys[j].Start
   688  	})
   689  	return phys, nil
   690  }
   691  
   692  // M1 is 1 Megabyte in bits.
   693  const M1 = 1 << 20
   694  
   695  // FindSpace returns pointer to the physical memory, where array of size sz can
   696  // be stored during next AddKexecSegment call.
   697  //
   698  // Align up to at least a page size if alignSizeBytes is smaller.
   699  func (m Memory) FindSpace(sz, alignSizeBytes uint) (Range, error) {
   700  	if alignSizeBytes == 0 {
   701  		alignSizeBytes = uint(os.Getpagesize())
   702  	}
   703  	sz = align.Up(sz, alignSizeBytes)
   704  
   705  	// Don't use memory below 1M, just in case.
   706  	return m.AvailableRAM().FindSpaceAbove(sz, M1)
   707  }
   708  
   709  // ReservePhys reserves page-aligned sz bytes in the physical memmap within
   710  // the given limit address range.
   711  func (m *Memory) ReservePhys(sz uint, limit Range) (Range, error) {
   712  	sz = align.UpPage(sz)
   713  
   714  	r, err := m.AvailableRAM().FindSpaceIn(sz, limit)
   715  	if err != nil {
   716  		return Range{}, err
   717  	}
   718  
   719  	m.Phys.Insert(TypedRange{
   720  		Range: r,
   721  		Type:  RangeReserved,
   722  	})
   723  	return r, nil
   724  }
   725  
   726  // AddPhysSegment reserves len(d) bytes in the physical memmap within limit and
   727  // adds a kexec segment with d in that range.
   728  func (m *Memory) AddPhysSegment(d []byte, limit Range) (Range, error) {
   729  	r, err := m.ReservePhys(uint(len(d)), limit)
   730  	if err != nil {
   731  		return Range{}, err
   732  	}
   733  	m.Segments.Insert(NewSegment(d, r))
   734  	return r, nil
   735  }
   736  
   737  // AddKexecSegment adds d to a new kexec segment
   738  func (m *Memory) AddKexecSegment(d []byte) (Range, error) {
   739  	r, err := m.FindSpace(uint(len(d)), uint(os.Getpagesize()))
   740  	if err != nil {
   741  		return Range{}, err
   742  	}
   743  	m.Segments.Insert(NewSegment(d, r))
   744  	return r, nil
   745  }
   746  
   747  // AddKexecSegmentExplicit adds d to a new kexec segment, but allows asking
   748  // for extra space, secifying alignment size, and setting text_offset.
   749  func (m *Memory) AddKexecSegmentExplicit(d []byte, sz, offset, alignSizeBytes uint) (Range, error) {
   750  	if sz < uint(len(d)) {
   751  		return Range{}, fmt.Errorf("length of d is more than size requested")
   752  	}
   753  	if offset > sz {
   754  		return Range{}, fmt.Errorf("offset is larger than size requested")
   755  	}
   756  	r, err := m.FindSpace(sz, alignSizeBytes)
   757  	if err != nil {
   758  		return Range{}, err
   759  	}
   760  	r.Start = uintptr(uint(r.Start) + offset)
   761  	m.Segments.Insert(NewSegment(d, r))
   762  	return r, nil
   763  }
   764  
   765  // AvailableRAM returns page-aligned unused regions of RAM.
   766  //
   767  // AvailableRAM takes all RAM-marked pages in the memory map and subtracts the
   768  // kexec segments already allocated. RAM segments begin at a page boundary.
   769  //
   770  // E.g if page size is 4K and RAM segments are
   771  //
   772  //	[{start:0 size:8192} {start:8192 size:8000}]
   773  //
   774  // and kexec segments are
   775  //
   776  //	[{start:40 size:50} {start:8000 size:2000}]
   777  //
   778  // result should be
   779  //
   780  //	[{start:0 size:40} {start:4096 end:8000 - 4096}]
   781  func (m Memory) AvailableRAM() Ranges {
   782  	ram := m.Phys.FilterByType(RangeRAM)
   783  
   784  	// Remove all points in Segments from available RAM.
   785  	for _, s := range m.Segments {
   786  		ram = ram.Minus(s.Phys)
   787  	}
   788  
   789  	// Only return Ranges starting at an aligned size.
   790  	var alignedRanges Ranges
   791  	for _, r := range ram {
   792  		alignedStart := uintptr(align.UpPage(uint(r.Start)))
   793  		if alignedStart < r.End() {
   794  			alignedRanges = append(alignedRanges, Range{
   795  				Start: alignedStart,
   796  				Size:  r.Size - uint(alignedStart-r.Start),
   797  			})
   798  		}
   799  	}
   800  	return alignedRanges
   801  }
   802  
   803  // RangeType defines type of a TypedRange based on the Linux
   804  // kernel string provided by firmware memory map.
   805  type RangeType string
   806  
   807  // These are the range types we know Linux uses.
   808  const (
   809  	RangeRAM      RangeType = "System RAM"
   810  	RangeDefault  RangeType = "Default"
   811  	RangeACPI     RangeType = "ACPI Tables"
   812  	RangeNVS      RangeType = "ACPI Non-volatile Storage"
   813  	RangeReserved RangeType = "Reserved"
   814  )
   815  
   816  // String implements fmt.Stringer.
   817  func (r RangeType) String() string {
   818  	return string(r)
   819  }
   820  
   821  var sysfsToRangeType = map[string]RangeType{
   822  	"System RAM":                RangeRAM,
   823  	"Default":                   RangeDefault,
   824  	"ACPI Tables":               RangeACPI,
   825  	"ACPI Non-volatile Storage": RangeNVS,
   826  	"Reserved":                  RangeReserved,
   827  	"reserved":                  RangeReserved,
   828  }
   829  
   830  // TypedRange represents range of physical memory.
   831  type TypedRange struct {
   832  	Range
   833  	Type RangeType
   834  }
   835  
   836  func (tr TypedRange) String() string {
   837  	return fmt.Sprintf("{addr: %s, type: %s}", tr.Range, tr.Type)
   838  }
   839  
   840  // MemoryMap defines the layout of physical memory.
   841  //
   842  // MemoryMap defines which ranges in memory are usable RAM and which are
   843  // reserved for various reasons.
   844  type MemoryMap []TypedRange
   845  
   846  // FilterByType only returns ranges of the given typ.
   847  func (m MemoryMap) FilterByType(typ RangeType) Ranges {
   848  	var rs Ranges
   849  	for _, tr := range m {
   850  		if tr.Type == typ {
   851  			rs = append(rs, tr.Range)
   852  		}
   853  	}
   854  	return rs
   855  }
   856  
   857  func (m MemoryMap) sort() {
   858  	sort.Slice(m, func(i, j int) bool {
   859  		return m[i].Start < m[j].Start
   860  	})
   861  }
   862  
   863  // Insert a new TypedRange into the memory map, removing chunks of other ranges
   864  // as necessary.
   865  //
   866  // Assumes that TypedRange is a valid range -- no checking.
   867  func (m *MemoryMap) Insert(r TypedRange) {
   868  	var newMap MemoryMap
   869  
   870  	// Remove points in r from all existing physical ranges.
   871  	for _, q := range *m {
   872  		split := q.Range.Minus(r.Range)
   873  		for _, r2 := range split {
   874  			newMap = append(newMap, TypedRange{Range: r2, Type: q.Type})
   875  		}
   876  	}
   877  
   878  	newMap = append(newMap, r)
   879  	newMap.sort()
   880  	*m = newMap
   881  }
   882  
   883  // PayloadMemType defines type of a memory map entry
   884  type PayloadMemType uint32
   885  
   886  // Payload memory type (PayloadMemType) in UefiPayload
   887  const (
   888  	PayloadTypeRAM      = 1
   889  	PayloadTypeDefault  = 2
   890  	PayloadTypeACPI     = 3
   891  	PayloadTypeNVS      = 4
   892  	PayloadTypeReserved = 5
   893  )
   894  
   895  // payloadMemoryMapEntry represent a memory map entry in payload param
   896  type payloadMemoryMapEntry struct {
   897  	Start uint64
   898  	End   uint64
   899  	Type  PayloadMemType
   900  }
   901  
   902  // PayloadMemoryMapParam is payload's MemoryMap parameter
   903  type PayloadMemoryMapParam []payloadMemoryMapEntry
   904  
   905  var rangeTypeToPayloadMemType = map[RangeType]PayloadMemType{
   906  	RangeRAM:      PayloadTypeRAM,
   907  	RangeDefault:  PayloadTypeDefault,
   908  	RangeACPI:     PayloadTypeACPI,
   909  	RangeNVS:      PayloadTypeNVS,
   910  	RangeReserved: PayloadTypeReserved,
   911  }
   912  
   913  func convertToPayloadMemType(rt RangeType) PayloadMemType {
   914  	mt, ok := rangeTypeToPayloadMemType[rt]
   915  	if !ok {
   916  		// return reserved if range type is not recognized
   917  		return PayloadTypeReserved
   918  	}
   919  	return mt
   920  }
   921  
   922  // AsPayloadParam converts MemoryMap to a PayloadMemoryMapParam
   923  func (m *MemoryMap) AsPayloadParam() PayloadMemoryMapParam {
   924  	var p PayloadMemoryMapParam
   925  	for _, entry := range *m {
   926  		p = append(p, payloadMemoryMapEntry{
   927  			Start: uint64(entry.Start),
   928  			End:   uint64(entry.Start) + uint64(entry.Size) - 1,
   929  			Type:  convertToPayloadMemType(entry.Type),
   930  		})
   931  	}
   932  	return p
   933  }