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 }