github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/exp/fixrsdp/fixrsdp.go (about)

     1  // Copyright 2019-2021 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  // fixrsdp copies the existing RSDP into the EBDA region in low mem.
     6  //
     7  // This is because LinuxBoot tends to be EFI booted, which places the RSDP
     8  // outside of the low 1M or the EBDA. If Linuxboot legacy boots the following
     9  // operating systems, such as with kexec, they may not have a way to find the
    10  // RSDP afterwards.  All u-root commands that open /dev/mem should also flock
    11  // it to ensure safe, sequential access.
    12  package main
    13  
    14  import (
    15  	"bytes"
    16  	"log"
    17  	"os"
    18  
    19  	"github.com/mvdan/u-root-coreutils/pkg/acpi"
    20  	"github.com/mvdan/u-root-coreutils/pkg/boot/ebda"
    21  )
    22  
    23  func main() {
    24  	// Find the RSDP.
    25  	r, err := acpi.GetRSDP()
    26  	if err != nil {
    27  		log.Fatalf("Unable to find system RSDP, got: %v", err)
    28  	}
    29  
    30  	rData := r.AllData()
    31  	rLen := len(rData)
    32  
    33  	base := r.RSDPAddr()
    34  	// Check if ACPI rsdp is already in low memory
    35  	if base >= 0xe0000 && base+int64(rLen) < 0xffff0 {
    36  		log.Printf("RSDP is already in low memory at %#X, no need to fix.", base)
    37  		return
    38  	}
    39  
    40  	// Find the EBDA
    41  	f, err := os.OpenFile("/dev/mem", os.O_RDWR, 0)
    42  	if err != nil {
    43  		log.Fatal(err)
    44  	}
    45  	defer f.Close()
    46  
    47  	e, err := ebda.ReadEBDA(f)
    48  	if err != nil {
    49  		log.Fatal(err)
    50  	}
    51  	log.Printf("EBDA starts at %#X, length %#X bytes", e.BaseOffset, e.Length)
    52  
    53  	// Scan low 1K of EBDA for an empty spot that is 16 byte aligned
    54  	emptyStart := 0
    55  	for i := 16; i < (1024-rLen) && i < (int(e.Length)-rLen); i += 16 {
    56  		// Check if there's an empty spot to put the RSDP table.
    57  		if bytes.Equal(e.Data[i:i+rLen], make([]byte, rLen)) {
    58  			emptyStart = i
    59  			log.Printf("Found empty space at %#X offset into EBDA, will copy RSDP there", emptyStart)
    60  			break
    61  		}
    62  	}
    63  
    64  	if emptyStart == 0 {
    65  		log.Fatal("Unable to find empty space to put RSDP")
    66  	}
    67  
    68  	copy(e.Data[emptyStart:emptyStart+rLen], rData)
    69  
    70  	if err = ebda.WriteEBDA(e, f); err != nil {
    71  		log.Fatal(err)
    72  	}
    73  	// Verify write, depending on the kernel settings like CONFIG_STRICT_DEVMEM, writes can silently fail.
    74  	v, err := ebda.ReadEBDA(f)
    75  	if err != nil {
    76  		log.Fatalf("Error reading EBDA: %v", err)
    77  	}
    78  	res := bytes.Compare(e.Data, v.Data)
    79  	if res != 0 {
    80  		log.Fatal("Write verification failed !")
    81  	}
    82  }