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 }