github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/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  const MaxAddr = ^uintptr(0)
   169  
   170  // FindSpaceAbove finds a continguous piece of sz points within Ranges and
   171  // returns a space.Start >= minAddr.
   172  func (rs Ranges) FindSpaceAbove(sz uint, minAddr uintptr) (space Range, err error) {
   173  	return rs.FindSpaceIn(sz, RangeFromInterval(minAddr, MaxAddr))
   174  }
   175  
   176  // FindSpaceIn finds a continguous piece of sz points within Ranges and returns
   177  // a Range where space.Start >= limit.Start, with space.End() < limit.End().
   178  func (rs Ranges) FindSpaceIn(sz uint, limit Range) (space Range, err error) {
   179  	for _, r := range rs {
   180  		if overlap := r.Intersect(limit); overlap != nil && overlap.Size >= sz {
   181  			return Range{Start: overlap.Start, Size: sz}, nil
   182  		}
   183  	}
   184  	return Range{}, ErrNotEnoughSpace{Size: sz}
   185  }
   186  
   187  // Sort sorts ranges by their start point.
   188  func (rs Ranges) Sort() {
   189  	sort.Slice(rs, func(i, j int) bool {
   190  		return rs[i].Start < rs[j].Start
   191  	})
   192  }
   193  
   194  // pool stores byte slices pointed by the pointers Segments.Buf to
   195  // prevent underlying arrays to be collected by garbage collector.
   196  var pool [][]byte
   197  
   198  // Segment defines kernel memory layout.
   199  type Segment struct {
   200  	// Buf is a buffer in user space.
   201  	Buf Range
   202  
   203  	// Phys is a physical address of kernel.
   204  	Phys Range
   205  }
   206  
   207  // NewSegment creates new Segment.
   208  // Segments should be created using NewSegment method to prevent
   209  // data pointed by Segment.Buf to be collected by garbage collector.
   210  func NewSegment(buf []byte, phys Range) Segment {
   211  	pool = append(pool, buf)
   212  	return Segment{
   213  		Buf: Range{
   214  			Start: uintptr((unsafe.Pointer(&buf[0]))),
   215  			Size:  uint(len(buf)),
   216  		},
   217  		Phys: phys,
   218  	}
   219  }
   220  
   221  func (s Segment) String() string {
   222  	return fmt.Sprintf("(virt: %s, phys: %s)", s.Buf, s.Phys)
   223  }
   224  
   225  func ptrToSlice(ptr uintptr, size int) []byte {
   226  	var data []byte
   227  
   228  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&data))
   229  	sh.Data = ptr
   230  	sh.Len = size
   231  	sh.Cap = size
   232  
   233  	return data
   234  }
   235  
   236  func (s *Segment) tryMerge(s2 Segment) (ok bool) {
   237  	if s.Phys.Disjunct(s2.Phys) {
   238  		return false
   239  	}
   240  
   241  	// Virtual memory ranges should never overlap,
   242  	// concatenate ranges.
   243  	a := s.Buf.toSlice()
   244  	b := s2.Buf.toSlice()
   245  	c := append(a, b...)
   246  
   247  	phys := s.Phys
   248  	// s1 and s2 overlap somewhat.
   249  	if !s.Phys.IsSupersetOf(s2.Phys) {
   250  		phys.Size = uint(s2.Phys.Start-s.Phys.Start) + s2.Phys.Size
   251  	}
   252  
   253  	*s = NewSegment(c, phys)
   254  	return true
   255  }
   256  
   257  func alignUp(p uint) uint {
   258  	return (p + pageMask) &^ pageMask
   259  }
   260  
   261  func alignUpPtr(p uintptr) uintptr {
   262  	return uintptr(alignUp(uint(p)))
   263  }
   264  
   265  // AlignPhys fixes s to the kexec_load preconditions.
   266  //
   267  // s's physical addresses must be multiples of the page size.
   268  //
   269  // E.g. if page size is 0x1000:
   270  // Segment {
   271  //   Buf:  {Start: 0x1011, Size: 0x1022}
   272  //   Phys: {Start: 0x2011, Size: 0x1022}
   273  // }
   274  // has to become
   275  // Segment {
   276  //   Buf:  {Start: 0x1000, Size: 0x1033}
   277  //   Phys: {Start: 0x2000, Size: 0x2000}
   278  // }
   279  func AlignPhys(s Segment) Segment {
   280  	orig := s.Phys.Start
   281  	// Find the page address of the starting point.
   282  	s.Phys.Start = s.Phys.Start &^ uintptr(pageMask)
   283  
   284  	diff := orig - s.Phys.Start
   285  
   286  	// Round up to page size.
   287  	s.Phys.Size = alignUp(s.Phys.Size + uint(diff))
   288  
   289  	if s.Buf.Start < diff && diff > 0 {
   290  		panic("cannot have virtual memory address within first page")
   291  	}
   292  	s.Buf.Start -= diff
   293  
   294  	if s.Buf.Size > 0 {
   295  		s.Buf.Size += uint(diff)
   296  	}
   297  	return s
   298  }
   299  
   300  // Segments is a collection of segments.
   301  type Segments []Segment
   302  
   303  // PhysContains returns whether p exists in any of segs' physical memory
   304  // ranges.
   305  func (segs Segments) PhysContains(p uintptr) bool {
   306  	for _, s := range segs {
   307  		if s.Phys.Contains(p) {
   308  			return true
   309  		}
   310  	}
   311  	return false
   312  }
   313  
   314  // Insert inserts s assuming it does not overlap with an existing segment.
   315  func (segs *Segments) Insert(s Segment) {
   316  	*segs = append(*segs, s)
   317  	segs.sort()
   318  }
   319  
   320  func (segs Segments) sort() {
   321  	sort.Slice(segs, func(i, j int) bool {
   322  		return segs[i].Phys.Start < segs[j].Phys.Start
   323  	})
   324  }
   325  
   326  // Dedup deduplicates overlapping and merges adjacent segments in segs.
   327  func Dedup(segs Segments) Segments {
   328  	var s Segments
   329  	sort.Slice(segs, func(i, j int) bool {
   330  		if segs[i].Phys.Start == segs[j].Phys.Start {
   331  			// let segs[i] be the superset of segs[j]
   332  			return segs[i].Phys.Size > segs[j].Phys.Size
   333  		}
   334  		return segs[i].Phys.Start < segs[j].Phys.Start
   335  	})
   336  
   337  	for _, seg := range segs {
   338  		doIt := true
   339  		for i := range s {
   340  			if merged := s[i].tryMerge(seg); merged {
   341  				doIt = false
   342  				break
   343  			}
   344  		}
   345  		if doIt {
   346  			s = append(s, seg)
   347  		}
   348  	}
   349  	return s
   350  }
   351  
   352  // Memory provides routines to work with physical memory ranges.
   353  type Memory struct {
   354  	// Phys defines the layout of physical memory.
   355  	//
   356  	// Phys is used to tell loaded operating systems what memory is usable
   357  	// as RAM, and what memory is reserved (for ACPI or other reasons).
   358  	Phys MemoryMap
   359  
   360  	// Segments are the segments used to load a new operating system.
   361  	//
   362  	// Each segment also contains a physical memory region it maps to.
   363  	Segments Segments
   364  }
   365  
   366  // LoadElfSegments loads loadable ELF segments.
   367  func (m *Memory) LoadElfSegments(r io.ReaderAt) error {
   368  	f, err := elf.NewFile(r)
   369  	if err != nil {
   370  		return err
   371  	}
   372  
   373  	for _, p := range f.Progs {
   374  		if p.Type != elf.PT_LOAD {
   375  			continue
   376  		}
   377  
   378  		d := make([]byte, p.Filesz)
   379  		n, err := r.ReadAt(d, int64(p.Off))
   380  		if err != nil {
   381  			return err
   382  		}
   383  		if n < len(d) {
   384  			return fmt.Errorf("not all data of the segment was read")
   385  		}
   386  		// TODO(hugelgupf): check if this is within availableRAM??
   387  		s := NewSegment(d, Range{
   388  			Start: uintptr(p.Paddr),
   389  			Size:  uint(p.Memsz),
   390  		})
   391  		m.Segments.Insert(s)
   392  	}
   393  	return nil
   394  }
   395  
   396  // ParseMemoryMap reads firmware provided memory map from /sys/firmware/memmap.
   397  func (m *Memory) ParseMemoryMap() error {
   398  	p, err := ParseMemoryMap()
   399  	if err != nil {
   400  		return err
   401  	}
   402  	m.Phys = p
   403  	return nil
   404  }
   405  
   406  var memoryMapRoot = "/sys/firmware/memmap/"
   407  
   408  // ParseMemoryMap reads firmware provided memory map from /sys/firmware/memmap.
   409  func ParseMemoryMap() (MemoryMap, error) {
   410  	return internalParseMemoryMap(memoryMapRoot)
   411  }
   412  
   413  func internalParseMemoryMap(memoryMapDir string) (MemoryMap, error) {
   414  	type memRange struct {
   415  		// start and end addresses are inclusive
   416  		start, end uintptr
   417  		typ        RangeType
   418  	}
   419  
   420  	ranges := make(map[string]memRange)
   421  	walker := func(name string, info os.FileInfo, err error) error {
   422  		if err != nil {
   423  			return err
   424  		}
   425  		if info.IsDir() {
   426  			return nil
   427  		}
   428  
   429  		const (
   430  			// file names
   431  			start = "start"
   432  			end   = "end"
   433  			typ   = "type"
   434  		)
   435  
   436  		base := path.Base(name)
   437  		if base != start && base != end && base != typ {
   438  			return fmt.Errorf("unexpected file %q", name)
   439  		}
   440  		dir := path.Dir(name)
   441  
   442  		b, err := ioutil.ReadFile(name)
   443  		if err != nil {
   444  			return fmt.Errorf("error reading file %q: %v", name, err)
   445  		}
   446  
   447  		data := strings.TrimSpace(string(b))
   448  		r := ranges[dir]
   449  		if base == typ {
   450  			typ, ok := sysfsToRangeType[data]
   451  			if !ok {
   452  				log.Printf("Sysfs file %q contains unrecognized memory map type %q, defaulting to Reserved", name, data)
   453  				r.typ = RangeReserved
   454  			} else {
   455  				r.typ = typ
   456  			}
   457  			ranges[dir] = r
   458  			return nil
   459  		}
   460  
   461  		v, err := strconv.ParseUint(data, 0, 64)
   462  		if err != nil {
   463  			return err
   464  		}
   465  		switch base {
   466  		case start:
   467  			r.start = uintptr(v)
   468  		case end:
   469  			r.end = uintptr(v)
   470  		}
   471  		ranges[dir] = r
   472  		return nil
   473  	}
   474  
   475  	if err := filepath.Walk(memoryMapDir, walker); err != nil {
   476  		return nil, err
   477  	}
   478  
   479  	var phys []TypedRange
   480  	for _, r := range ranges {
   481  		// Range's end address is exclusive, while Linux's sysfs prints
   482  		// the end address inclusive.
   483  		//
   484  		// E.g. sysfs will contain
   485  		//
   486  		// start: 0x100, end: 0x1ff
   487  		//
   488  		// while we represent
   489  		//
   490  		// start: 0x100, size: 0x100.
   491  		phys = append(phys, TypedRange{
   492  			Range: RangeFromInterval(r.start, r.end+1),
   493  			Type:  r.typ,
   494  		})
   495  	}
   496  	sort.Slice(phys, func(i, j int) bool {
   497  		return phys[i].Start < phys[j].Start
   498  	})
   499  	return phys, nil
   500  }
   501  
   502  // M1 is 1 Megabyte in bits.
   503  const M1 = 1 << 20
   504  
   505  // FindSpace returns pointer to the physical memory, where array of size sz can
   506  // be stored during next AddKexecSegment call.
   507  func (m Memory) FindSpace(sz uint) (Range, error) {
   508  	// Allocate full pages.
   509  	sz = alignUp(sz)
   510  
   511  	// Don't use memory below 1M, just in case.
   512  	return m.AvailableRAM().FindSpaceAbove(sz, M1)
   513  }
   514  
   515  // ReservePhys reserves page-aligned sz bytes in the physical memmap within
   516  // the given limit address range.
   517  func (m *Memory) ReservePhys(sz uint, limit Range) (Range, error) {
   518  	sz = alignUp(sz)
   519  
   520  	r, err := m.AvailableRAM().FindSpaceIn(sz, limit)
   521  	if err != nil {
   522  		return Range{}, err
   523  	}
   524  
   525  	m.Phys.Insert(TypedRange{
   526  		Range: r,
   527  		Type:  RangeReserved,
   528  	})
   529  	return r, nil
   530  }
   531  
   532  // AddPhysSegment reserves len(d) bytes in the physical memmap within limit and
   533  // adds a kexec segment with d in that range.
   534  func (m *Memory) AddPhysSegment(d []byte, limit Range) (Range, error) {
   535  	r, err := m.ReservePhys(uint(len(d)), limit)
   536  	if err != nil {
   537  		return Range{}, err
   538  	}
   539  	m.Segments.Insert(NewSegment(d, r))
   540  	return r, nil
   541  }
   542  
   543  // AddKexecSegment adds d to a new kexec segment
   544  func (m *Memory) AddKexecSegment(d []byte) (Range, error) {
   545  	r, err := m.FindSpace(uint(len(d)))
   546  	if err != nil {
   547  		return Range{}, err
   548  	}
   549  	m.Segments.Insert(NewSegment(d, r))
   550  	return r, nil
   551  }
   552  
   553  // AvailableRAM returns page-aligned unused regions of RAM.
   554  //
   555  // AvailableRAM takes all RAM-marked pages in the memory map and subtracts the
   556  // kexec segments already allocated. RAM segments begin at a page boundary.
   557  //
   558  // E.g if page size is 4K and RAM segments are
   559  //            [{start:0 size:8192} {start:8192 size:8000}]
   560  // and kexec segments are
   561  //            [{start:40 size:50} {start:8000 size:2000}]
   562  // result should be
   563  //            [{start:0 size:40} {start:4096 end:8000 - 4096}]
   564  func (m Memory) AvailableRAM() Ranges {
   565  	ram := m.Phys.FilterByType(RangeRAM)
   566  
   567  	// Remove all points in Segments from available RAM.
   568  	for _, s := range m.Segments {
   569  		ram = ram.Minus(s.Phys)
   570  	}
   571  
   572  	// Only return Ranges starting at an aligned size.
   573  	var alignedRanges Ranges
   574  	for _, r := range ram {
   575  		alignedStart := alignUpPtr(r.Start)
   576  		if alignedStart < r.End() {
   577  			alignedRanges = append(alignedRanges, Range{
   578  				Start: alignedStart,
   579  				Size:  r.Size - uint(alignedStart-r.Start),
   580  			})
   581  		}
   582  	}
   583  	return alignedRanges
   584  }
   585  
   586  // RangeType defines type of a TypedRange based on the Linux
   587  // kernel string provided by firmware memory map.
   588  type RangeType string
   589  
   590  const (
   591  	RangeRAM      RangeType = "System RAM"
   592  	RangeDefault            = "Default"
   593  	RangeACPI               = "ACPI Tables"
   594  	RangeNVS                = "ACPI Non-volatile Storage"
   595  	RangeReserved           = "Reserved"
   596  )
   597  
   598  // String implements fmt.Stringer.
   599  func (r RangeType) String() string {
   600  	return string(r)
   601  }
   602  
   603  var sysfsToRangeType = map[string]RangeType{
   604  	"System RAM":                RangeRAM,
   605  	"Default":                   RangeDefault,
   606  	"ACPI Tables":               RangeACPI,
   607  	"ACPI Non-volatile Storage": RangeNVS,
   608  	"Reserved":                  RangeReserved,
   609  	"reserved":                  RangeReserved,
   610  }
   611  
   612  // TypedRange represents range of physical memory.
   613  type TypedRange struct {
   614  	Range
   615  	Type RangeType
   616  }
   617  
   618  func (tr TypedRange) String() string {
   619  	return fmt.Sprintf("{addr: %s, type: %s}", tr.Range, tr.Type)
   620  }
   621  
   622  // MemoryMap defines the layout of physical memory.
   623  //
   624  // MemoryMap defines which ranges in memory are usable RAM and which are
   625  // reserved for various reasons.
   626  type MemoryMap []TypedRange
   627  
   628  // Filter only returns ranges of the given typ.
   629  func (m MemoryMap) FilterByType(typ RangeType) Ranges {
   630  	var rs Ranges
   631  	for _, tr := range m {
   632  		if tr.Type == typ {
   633  			rs = append(rs, tr.Range)
   634  		}
   635  	}
   636  	return rs
   637  }
   638  
   639  func (m MemoryMap) sort() {
   640  	sort.Slice(m, func(i, j int) bool {
   641  		return m[i].Start < m[j].Start
   642  	})
   643  }
   644  
   645  // Insert a new TypedRange into the memory map, removing chunks of other ranges
   646  // as necessary.
   647  //
   648  // Assumes that TypedRange is a valid range -- no checking.
   649  func (m *MemoryMap) Insert(r TypedRange) {
   650  	var newMap MemoryMap
   651  
   652  	// Remove points in r from all existing physical ranges.
   653  	for _, q := range *m {
   654  		split := q.Range.Minus(r.Range)
   655  		for _, r2 := range split {
   656  			newMap = append(newMap, TypedRange{Range: r2, Type: q.Type})
   657  		}
   658  	}
   659  
   660  	newMap = append(newMap, r)
   661  	newMap.sort()
   662  	*m = newMap
   663  }