github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/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  	"debug/elf"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"log"
    13  	"os"
    14  	"path"
    15  	"path/filepath"
    16  	"reflect"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  	"unsafe"
    21  )
    22  
    23  var pageMask = uint(os.Getpagesize() - 1)
    24  
    25  // ErrNotEnoughSpace is returned by the FindSpace family of functions if no
    26  // range is large enough to accommodate the request.
    27  type ErrNotEnoughSpace struct {
    28  	Size uint
    29  }
    30  
    31  func (e ErrNotEnoughSpace) Error() string {
    32  	return fmt.Sprintf("not enough space to allocate %#x bytes", e.Size)
    33  }
    34  
    35  // Range represents a contiguous uintptr interval [Start, Start+Size).
    36  type Range struct {
    37  	// Start is the inclusive start of the range.
    38  	Start uintptr
    39  
    40  	// Size is the number of elements in the range.
    41  	//
    42  	// Start+Size is the exclusive end of the range.
    43  	Size uint
    44  }
    45  
    46  // RangeFromInterval returns a Range representing [start, end).
    47  func RangeFromInterval(start, end uintptr) Range {
    48  	return Range{
    49  		Start: start,
    50  		Size:  uint(end - start),
    51  	}
    52  }
    53  
    54  // String returns [Start, Start+Size) as a string.
    55  func (r Range) String() string {
    56  	return fmt.Sprintf("[%#x, %#x)", r.Start, r.End())
    57  }
    58  
    59  // End returns the uintptr *after* the end of the interval.
    60  func (r Range) End() uintptr {
    61  	return r.Start + uintptr(r.Size)
    62  }
    63  
    64  // Adjacent returns true if r and r2 do not overlap, but are immediately next
    65  // to each other.
    66  func (r Range) Adjacent(r2 Range) bool {
    67  	return r2.End() == r.Start || r.End() == r2.Start
    68  }
    69  
    70  // Contains returns true iff p is in the interval described by r.
    71  func (r Range) Contains(p uintptr) bool {
    72  	return r.Start <= p && p < r.End()
    73  }
    74  
    75  func min(a, b uintptr) uintptr {
    76  	if a < b {
    77  		return a
    78  	}
    79  	return b
    80  }
    81  
    82  func max(a, b uintptr) uintptr {
    83  	if a > b {
    84  		return a
    85  	}
    86  	return b
    87  }
    88  
    89  // Intersect returns the continuous range of points common to r and r2 if there
    90  // is one.
    91  func (r Range) Intersect(r2 Range) *Range {
    92  	if !r.Overlaps(r2) {
    93  		return nil
    94  	}
    95  	i := RangeFromInterval(max(r.Start, r2.Start), min(r.End(), r2.End()))
    96  	return &i
    97  }
    98  
    99  // Minus removes all points in r2 from r.
   100  func (r Range) Minus(r2 Range) []Range {
   101  	var result []Range
   102  	if r.Contains(r2.Start) && r.Start != r2.Start {
   103  		result = append(result, Range{
   104  			Start: r.Start,
   105  			Size:  uint(r2.Start - r.Start),
   106  		})
   107  	}
   108  	if r.Contains(r2.End()) && r.End() != r2.End() {
   109  		result = append(result, Range{
   110  			Start: r2.End(),
   111  			Size:  uint(r.End() - r2.End()),
   112  		})
   113  	}
   114  	// Neither end was in r?
   115  	//
   116  	// Either r is a subset of r2 and r disappears completely, or they are
   117  	// completely disjunct.
   118  	if len(result) == 0 && r.Disjunct(r2) {
   119  		result = append(result, r)
   120  	}
   121  	return result
   122  }
   123  
   124  // Overlaps returns true if r and r2 overlap.
   125  func (r Range) Overlaps(r2 Range) bool {
   126  	return r.Start < r2.End() && r2.Start < r.End()
   127  }
   128  
   129  // IsSupersetOf returns true if r2 in r.
   130  func (r Range) IsSupersetOf(r2 Range) bool {
   131  	return r.Start <= r2.Start && r.End() >= r2.End()
   132  }
   133  
   134  // Disjunct returns true if r and r2 do not overlap.
   135  func (r Range) Disjunct(r2 Range) bool {
   136  	return !r.Overlaps(r2)
   137  }
   138  
   139  func (r Range) toSlice() []byte {
   140  	var data []byte
   141  
   142  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
   143  	sh.Data = r.Start
   144  	sh.Len = int(r.Size)
   145  	sh.Cap = int(r.Size)
   146  
   147  	return data
   148  }
   149  
   150  // Ranges is a list of non-overlapping ranges.
   151  type Ranges []Range
   152  
   153  // Minus removes all points in r from all ranges in rs.
   154  func (rs Ranges) Minus(r Range) Ranges {
   155  	var ram Ranges
   156  	for _, oldRange := range rs {
   157  		ram = append(ram, oldRange.Minus(r)...)
   158  	}
   159  	return ram
   160  }
   161  
   162  // FindSpace finds a continguous piece of sz points within Ranges and returns
   163  // the Range pointing to it.
   164  func (rs Ranges) FindSpace(sz uint) (space Range, err error) {
   165  	return rs.FindSpaceAbove(sz, 0)
   166  }
   167  
   168  // MaxAddr is the highest address in a 64bit address space.
   169  const MaxAddr = ^uintptr(0)
   170  
   171  // FindSpaceAbove finds a continguous piece of sz points within Ranges and
   172  // returns a space.Start >= minAddr.
   173  func (rs Ranges) FindSpaceAbove(sz uint, minAddr uintptr) (space Range, err error) {
   174  	return rs.FindSpaceIn(sz, RangeFromInterval(minAddr, MaxAddr))
   175  }
   176  
   177  // FindSpaceIn finds a continguous piece of sz points within Ranges and returns
   178  // a Range where space.Start >= limit.Start, with space.End() < limit.End().
   179  func (rs Ranges) FindSpaceIn(sz uint, limit Range) (space Range, err error) {
   180  	for _, r := range rs {
   181  		if overlap := r.Intersect(limit); overlap != nil && overlap.Size >= sz {
   182  			return Range{Start: overlap.Start, Size: sz}, nil
   183  		}
   184  	}
   185  	return Range{}, ErrNotEnoughSpace{Size: sz}
   186  }
   187  
   188  // Sort sorts ranges by their start point.
   189  func (rs Ranges) Sort() {
   190  	sort.Slice(rs, func(i, j int) bool {
   191  		return rs[i].Start < rs[j].Start
   192  	})
   193  }
   194  
   195  // pool stores byte slices pointed by the pointers Segments.Buf to
   196  // prevent underlying arrays to be collected by garbage collector.
   197  var pool [][]byte
   198  
   199  // Segment defines kernel memory layout.
   200  type Segment struct {
   201  	// Buf is a buffer in user space.
   202  	Buf Range
   203  
   204  	// Phys is a physical address of kernel.
   205  	Phys Range
   206  }
   207  
   208  // NewSegment creates new Segment.
   209  // Segments should be created using NewSegment method to prevent
   210  // data pointed by Segment.Buf to be collected by garbage collector.
   211  func NewSegment(buf []byte, phys Range) Segment {
   212  	pool = append(pool, buf)
   213  	return Segment{
   214  		Buf: Range{
   215  			Start: uintptr((unsafe.Pointer(&buf[0]))),
   216  			Size:  uint(len(buf)),
   217  		},
   218  		Phys: phys,
   219  	}
   220  }
   221  
   222  func (s Segment) String() string {
   223  	return fmt.Sprintf("(virt: %s, phys: %s)", s.Buf, s.Phys)
   224  }
   225  
   226  func (s *Segment) tryMerge(s2 Segment) (ok bool) {
   227  	if s.Phys.Disjunct(s2.Phys) {
   228  		return false
   229  	}
   230  
   231  	// Virtual memory ranges should never overlap,
   232  	// concatenate ranges.
   233  	a := s.Buf.toSlice()
   234  	b := s2.Buf.toSlice()
   235  	c := append(a, b...)
   236  
   237  	phys := s.Phys
   238  	// s1 and s2 overlap somewhat.
   239  	if !s.Phys.IsSupersetOf(s2.Phys) {
   240  		phys.Size = uint(s2.Phys.Start-s.Phys.Start) + s2.Phys.Size
   241  	}
   242  
   243  	*s = NewSegment(c, phys)
   244  	return true
   245  }
   246  
   247  func alignUp(p uint) uint {
   248  	return (p + pageMask) &^ pageMask
   249  }
   250  
   251  func alignUpPtr(p uintptr) uintptr {
   252  	return uintptr(alignUp(uint(p)))
   253  }
   254  
   255  // AlignPhys fixes s to the kexec_load preconditions.
   256  //
   257  // s's physical addresses must be multiples of the page size.
   258  //
   259  // E.g. if page size is 0x1000:
   260  // Segment {
   261  //   Buf:  {Start: 0x1011, Size: 0x1022}
   262  //   Phys: {Start: 0x2011, Size: 0x1022}
   263  // }
   264  // has to become
   265  // Segment {
   266  //   Buf:  {Start: 0x1000, Size: 0x1033}
   267  //   Phys: {Start: 0x2000, Size: 0x2000}
   268  // }
   269  func AlignPhys(s Segment) Segment {
   270  	orig := s.Phys.Start
   271  	// Find the page address of the starting point.
   272  	s.Phys.Start = s.Phys.Start &^ uintptr(pageMask)
   273  
   274  	diff := orig - s.Phys.Start
   275  
   276  	// Round up to page size.
   277  	s.Phys.Size = alignUp(s.Phys.Size + uint(diff))
   278  
   279  	if s.Buf.Start < diff && diff > 0 {
   280  		panic("cannot have virtual memory address within first page")
   281  	}
   282  	s.Buf.Start -= diff
   283  
   284  	if s.Buf.Size > 0 {
   285  		s.Buf.Size += uint(diff)
   286  	}
   287  	return s
   288  }
   289  
   290  // Segments is a collection of segments.
   291  type Segments []Segment
   292  
   293  // PhysContains returns whether p exists in any of segs' physical memory
   294  // ranges.
   295  func (segs Segments) PhysContains(p uintptr) bool {
   296  	for _, s := range segs {
   297  		if s.Phys.Contains(p) {
   298  			return true
   299  		}
   300  	}
   301  	return false
   302  }
   303  
   304  // Insert inserts s assuming it does not overlap with an existing segment.
   305  func (segs *Segments) Insert(s Segment) {
   306  	*segs = append(*segs, s)
   307  	segs.sort()
   308  }
   309  
   310  func (segs Segments) sort() {
   311  	sort.Slice(segs, func(i, j int) bool {
   312  		return segs[i].Phys.Start < segs[j].Phys.Start
   313  	})
   314  }
   315  
   316  // Dedup deduplicates overlapping and merges adjacent segments in segs.
   317  func Dedup(segs Segments) Segments {
   318  	var s Segments
   319  	sort.Slice(segs, func(i, j int) bool {
   320  		if segs[i].Phys.Start == segs[j].Phys.Start {
   321  			// let segs[i] be the superset of segs[j]
   322  			return segs[i].Phys.Size > segs[j].Phys.Size
   323  		}
   324  		return segs[i].Phys.Start < segs[j].Phys.Start
   325  	})
   326  
   327  	for _, seg := range segs {
   328  		doIt := true
   329  		for i := range s {
   330  			if merged := s[i].tryMerge(seg); merged {
   331  				doIt = false
   332  				break
   333  			}
   334  		}
   335  		if doIt {
   336  			s = append(s, seg)
   337  		}
   338  	}
   339  	return s
   340  }
   341  
   342  // Memory provides routines to work with physical memory ranges.
   343  type Memory struct {
   344  	// Phys defines the layout of physical memory.
   345  	//
   346  	// Phys is used to tell loaded operating systems what memory is usable
   347  	// as RAM, and what memory is reserved (for ACPI or other reasons).
   348  	Phys MemoryMap
   349  
   350  	// Segments are the segments used to load a new operating system.
   351  	//
   352  	// Each segment also contains a physical memory region it maps to.
   353  	Segments Segments
   354  }
   355  
   356  // LoadElfSegments loads loadable ELF segments.
   357  func (m *Memory) LoadElfSegments(r io.ReaderAt) error {
   358  	f, err := elf.NewFile(r)
   359  	if err != nil {
   360  		return err
   361  	}
   362  
   363  	for _, p := range f.Progs {
   364  		if p.Type != elf.PT_LOAD {
   365  			continue
   366  		}
   367  
   368  		d := make([]byte, p.Filesz)
   369  		n, err := r.ReadAt(d, int64(p.Off))
   370  		if err != nil {
   371  			return err
   372  		}
   373  		if n < len(d) {
   374  			return fmt.Errorf("not all data of the segment was read")
   375  		}
   376  		// TODO(hugelgupf): check if this is within availableRAM??
   377  		s := NewSegment(d, Range{
   378  			Start: uintptr(p.Paddr),
   379  			Size:  uint(p.Memsz),
   380  		})
   381  		m.Segments.Insert(s)
   382  	}
   383  	return nil
   384  }
   385  
   386  // ParseMemoryMap reads firmware provided memory map from /sys/firmware/memmap.
   387  func (m *Memory) ParseMemoryMap() error {
   388  	p, err := ParseMemoryMap()
   389  	if err != nil {
   390  		return err
   391  	}
   392  	m.Phys = p
   393  	return nil
   394  }
   395  
   396  var memoryMapRoot = "/sys/firmware/memmap/"
   397  
   398  // ParseMemoryMap reads firmware provided memory map from /sys/firmware/memmap.
   399  func ParseMemoryMap() (MemoryMap, error) {
   400  	return internalParseMemoryMap(memoryMapRoot)
   401  }
   402  
   403  func internalParseMemoryMap(memoryMapDir string) (MemoryMap, error) {
   404  	type memRange struct {
   405  		// start and end addresses are inclusive
   406  		start, end uintptr
   407  		typ        RangeType
   408  	}
   409  
   410  	ranges := make(map[string]memRange)
   411  	walker := func(name string, info os.FileInfo, err error) error {
   412  		if err != nil {
   413  			return err
   414  		}
   415  		if info.IsDir() {
   416  			return nil
   417  		}
   418  
   419  		const (
   420  			// file names
   421  			start = "start"
   422  			end   = "end"
   423  			typ   = "type"
   424  		)
   425  
   426  		base := path.Base(name)
   427  		if base != start && base != end && base != typ {
   428  			return fmt.Errorf("unexpected file %q", name)
   429  		}
   430  		dir := path.Dir(name)
   431  
   432  		b, err := ioutil.ReadFile(name)
   433  		if err != nil {
   434  			return fmt.Errorf("error reading file %q: %v", name, err)
   435  		}
   436  
   437  		data := strings.TrimSpace(string(b))
   438  		r := ranges[dir]
   439  		if base == typ {
   440  			typ, ok := sysfsToRangeType[data]
   441  			if !ok {
   442  				log.Printf("Sysfs file %q contains unrecognized memory map type %q, defaulting to Reserved", name, data)
   443  				r.typ = RangeReserved
   444  			} else {
   445  				r.typ = typ
   446  			}
   447  			ranges[dir] = r
   448  			return nil
   449  		}
   450  
   451  		v, err := strconv.ParseUint(data, 0, 64)
   452  		if err != nil {
   453  			return err
   454  		}
   455  		switch base {
   456  		case start:
   457  			r.start = uintptr(v)
   458  		case end:
   459  			r.end = uintptr(v)
   460  		}
   461  		ranges[dir] = r
   462  		return nil
   463  	}
   464  
   465  	if err := filepath.Walk(memoryMapDir, walker); err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	var phys []TypedRange
   470  	for _, r := range ranges {
   471  		// Range's end address is exclusive, while Linux's sysfs prints
   472  		// the end address inclusive.
   473  		//
   474  		// E.g. sysfs will contain
   475  		//
   476  		// start: 0x100, end: 0x1ff
   477  		//
   478  		// while we represent
   479  		//
   480  		// start: 0x100, size: 0x100.
   481  		phys = append(phys, TypedRange{
   482  			Range: RangeFromInterval(r.start, r.end+1),
   483  			Type:  r.typ,
   484  		})
   485  	}
   486  	sort.Slice(phys, func(i, j int) bool {
   487  		return phys[i].Start < phys[j].Start
   488  	})
   489  	return phys, nil
   490  }
   491  
   492  // M1 is 1 Megabyte in bits.
   493  const M1 = 1 << 20
   494  
   495  // FindSpace returns pointer to the physical memory, where array of size sz can
   496  // be stored during next AddKexecSegment call.
   497  func (m Memory) FindSpace(sz uint) (Range, error) {
   498  	// Allocate full pages.
   499  	sz = alignUp(sz)
   500  
   501  	// Don't use memory below 1M, just in case.
   502  	return m.AvailableRAM().FindSpaceAbove(sz, M1)
   503  }
   504  
   505  // ReservePhys reserves page-aligned sz bytes in the physical memmap within
   506  // the given limit address range.
   507  func (m *Memory) ReservePhys(sz uint, limit Range) (Range, error) {
   508  	sz = alignUp(sz)
   509  
   510  	r, err := m.AvailableRAM().FindSpaceIn(sz, limit)
   511  	if err != nil {
   512  		return Range{}, err
   513  	}
   514  
   515  	m.Phys.Insert(TypedRange{
   516  		Range: r,
   517  		Type:  RangeReserved,
   518  	})
   519  	return r, nil
   520  }
   521  
   522  // AddPhysSegment reserves len(d) bytes in the physical memmap within limit and
   523  // adds a kexec segment with d in that range.
   524  func (m *Memory) AddPhysSegment(d []byte, limit Range) (Range, error) {
   525  	r, err := m.ReservePhys(uint(len(d)), limit)
   526  	if err != nil {
   527  		return Range{}, err
   528  	}
   529  	m.Segments.Insert(NewSegment(d, r))
   530  	return r, nil
   531  }
   532  
   533  // AddKexecSegment adds d to a new kexec segment
   534  func (m *Memory) AddKexecSegment(d []byte) (Range, error) {
   535  	r, err := m.FindSpace(uint(len(d)))
   536  	if err != nil {
   537  		return Range{}, err
   538  	}
   539  	m.Segments.Insert(NewSegment(d, r))
   540  	return r, nil
   541  }
   542  
   543  // AvailableRAM returns page-aligned unused regions of RAM.
   544  //
   545  // AvailableRAM takes all RAM-marked pages in the memory map and subtracts the
   546  // kexec segments already allocated. RAM segments begin at a page boundary.
   547  //
   548  // E.g if page size is 4K and RAM segments are
   549  //            [{start:0 size:8192} {start:8192 size:8000}]
   550  // and kexec segments are
   551  //            [{start:40 size:50} {start:8000 size:2000}]
   552  // result should be
   553  //            [{start:0 size:40} {start:4096 end:8000 - 4096}]
   554  func (m Memory) AvailableRAM() Ranges {
   555  	ram := m.Phys.FilterByType(RangeRAM)
   556  
   557  	// Remove all points in Segments from available RAM.
   558  	for _, s := range m.Segments {
   559  		ram = ram.Minus(s.Phys)
   560  	}
   561  
   562  	// Only return Ranges starting at an aligned size.
   563  	var alignedRanges Ranges
   564  	for _, r := range ram {
   565  		alignedStart := alignUpPtr(r.Start)
   566  		if alignedStart < r.End() {
   567  			alignedRanges = append(alignedRanges, Range{
   568  				Start: alignedStart,
   569  				Size:  r.Size - uint(alignedStart-r.Start),
   570  			})
   571  		}
   572  	}
   573  	return alignedRanges
   574  }
   575  
   576  // RangeType defines type of a TypedRange based on the Linux
   577  // kernel string provided by firmware memory map.
   578  type RangeType string
   579  
   580  // These are the range types we know Linux uses.
   581  const (
   582  	RangeRAM      RangeType = "System RAM"
   583  	RangeDefault  RangeType = "Default"
   584  	RangeACPI     RangeType = "ACPI Tables"
   585  	RangeNVS      RangeType = "ACPI Non-volatile Storage"
   586  	RangeReserved RangeType = "Reserved"
   587  )
   588  
   589  // String implements fmt.Stringer.
   590  func (r RangeType) String() string {
   591  	return string(r)
   592  }
   593  
   594  var sysfsToRangeType = map[string]RangeType{
   595  	"System RAM":                RangeRAM,
   596  	"Default":                   RangeDefault,
   597  	"ACPI Tables":               RangeACPI,
   598  	"ACPI Non-volatile Storage": RangeNVS,
   599  	"Reserved":                  RangeReserved,
   600  	"reserved":                  RangeReserved,
   601  }
   602  
   603  // TypedRange represents range of physical memory.
   604  type TypedRange struct {
   605  	Range
   606  	Type RangeType
   607  }
   608  
   609  func (tr TypedRange) String() string {
   610  	return fmt.Sprintf("{addr: %s, type: %s}", tr.Range, tr.Type)
   611  }
   612  
   613  // MemoryMap defines the layout of physical memory.
   614  //
   615  // MemoryMap defines which ranges in memory are usable RAM and which are
   616  // reserved for various reasons.
   617  type MemoryMap []TypedRange
   618  
   619  // FilterByType only returns ranges of the given typ.
   620  func (m MemoryMap) FilterByType(typ RangeType) Ranges {
   621  	var rs Ranges
   622  	for _, tr := range m {
   623  		if tr.Type == typ {
   624  			rs = append(rs, tr.Range)
   625  		}
   626  	}
   627  	return rs
   628  }
   629  
   630  func (m MemoryMap) sort() {
   631  	sort.Slice(m, func(i, j int) bool {
   632  		return m[i].Start < m[j].Start
   633  	})
   634  }
   635  
   636  // Insert a new TypedRange into the memory map, removing chunks of other ranges
   637  // as necessary.
   638  //
   639  // Assumes that TypedRange is a valid range -- no checking.
   640  func (m *MemoryMap) Insert(r TypedRange) {
   641  	var newMap MemoryMap
   642  
   643  	// Remove points in r from all existing physical ranges.
   644  	for _, q := range *m {
   645  		split := q.Range.Minus(r.Range)
   646  		for _, r2 := range split {
   647  			newMap = append(newMap, TypedRange{Range: r2, Type: q.Type})
   648  		}
   649  	}
   650  
   651  	newMap = append(newMap, r)
   652  	newMap.sort()
   653  	*m = newMap
   654  }