github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/ebda/ebda.go (about)

     1  // Copyright 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 ebda looks for the Extended Bios Data Area (EBDA) pointer in /dev/mem,
     6  // and provides access to the EBDA. This is useful for us to read or write to the EBDA,
     7  // for example to copy the RSDP into the EBDA.
     8  // * The address 0x40E contains the pointer to the start of the EBDA, shifted right by 4 bits
     9  // * We take that and find the EBDA, where the first byte usually encodes the length of the
    10  //   the area in KiB.
    11  // * If the pointer is not set, there may be no EBDA.
    12  package ebda
    13  
    14  import (
    15  	"encoding/binary"
    16  	"errors"
    17  	"fmt"
    18  	"io"
    19  )
    20  
    21  // EBDAAddressOffset is the traditional offset where the location of the EBDA is stored.
    22  const EBDAAddressOffset = 0x40E
    23  
    24  // EBDA represents an EBDA region in memory
    25  type EBDA struct {
    26  	BaseOffset int64
    27  	Length     int64
    28  	Data       []byte
    29  }
    30  
    31  func findEBDAOffset(f io.ReadSeeker) (int64, error) {
    32  	var ep uint16
    33  	if _, err := f.Seek(EBDAAddressOffset, io.SeekStart); err != nil {
    34  		return 0, fmt.Errorf("error seeking to memory offset %#X for the EBDA pointer, got: %v", EBDAAddressOffset, err)
    35  	}
    36  
    37  	// read the ebda pointer
    38  	if err := binary.Read(f, binary.LittleEndian, &ep); err != nil {
    39  		return 0, fmt.Errorf("unable to read EBDA Pointer: %v", err)
    40  	}
    41  
    42  	if ep == 0 {
    43  		return 0, errors.New("ebda offset is 0! unable to proceed")
    44  	}
    45  	// The EBDA pointer is stored shifted 4 bits right.
    46  	// We've never seen one that's not that way, though osdev implies there are some.
    47  	// For reference: https://wiki.osdev.org/Memory_Map_(x86)
    48  	return int64(ep) << 4, nil
    49  }
    50  
    51  // ReadEBDA assumes it's been passed in /dev/mem and searches for and reads the EBDA.
    52  func ReadEBDA(f io.ReadSeeker) (*EBDA, error) {
    53  	var err error
    54  	var eSize uint8
    55  	e := &EBDA{}
    56  
    57  	if e.BaseOffset, err = findEBDAOffset(f); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	if _, err = f.Seek(e.BaseOffset, io.SeekStart); err != nil {
    62  		return nil, fmt.Errorf("error seeking to memory offset %#X for the start of the EBDA, got: %v", e.BaseOffset, err)
    63  	}
    64  
    65  	// Read length
    66  	if err := binary.Read(f, binary.LittleEndian, &eSize); err != nil {
    67  		return nil, fmt.Errorf("error reading EBDA length, got: %v", err)
    68  	}
    69  	e.Length = int64(eSize) << 10
    70  
    71  	// Find the size of this segment if the first byte is not set.
    72  	// We assume that the EBDA never crosses a segment for now.
    73  	// This is a reasonable assumption in most cases, since most EBDAs I've seen are in the 0x9XXXX range,
    74  	// and the 0xA0000 segment is off limits.
    75  	if e.Length == 0 {
    76  		e.Length = 0x10000 - (e.BaseOffset & 0xffff)
    77  	}
    78  	e.Data = make([]byte, e.Length)
    79  
    80  	if _, err = f.Seek(e.BaseOffset, io.SeekStart); err != nil {
    81  		return nil, fmt.Errorf("error seeking to memory offset %#X for the start of the EBDA, got: %v", e.BaseOffset, err)
    82  	}
    83  	if err = binary.Read(f, binary.LittleEndian, e.Data); err != nil {
    84  		return nil, fmt.Errorf("error reading EBDA region, tried to read from %#X of size %#X, got %v", e.BaseOffset, e.Length, err)
    85  	}
    86  
    87  	return e, nil
    88  }
    89  
    90  // WriteEBDA assumes it's been passed in /dev/mem and searches for and writes to the EBDA.
    91  func WriteEBDA(e *EBDA, f io.ReadWriteSeeker) error {
    92  	var err error
    93  	if e.BaseOffset == 0 {
    94  		// Don't know why this is 0, but try to find one from /dev/mem
    95  		if e.BaseOffset, err = findEBDAOffset(f); err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	if e.Length&0x3ff != 0 {
   101  		// Length can't be encoded as KiB.
   102  		return fmt.Errorf("length is not an integer multiple of 1 KiB, got %#X", e.Length)
   103  	}
   104  	if e.Length != int64(len(e.Data)) {
   105  		return fmt.Errorf("length field is not equal to buffer length. length field: %#X, buffer length: %#X", e.Length, len(e.Data))
   106  	}
   107  
   108  	// Update length field in buffer
   109  	eLen := e.Length >> 10
   110  	if eLen > 0xff {
   111  		// Length is too big to be written into one byte. fail.
   112  		return fmt.Errorf("length is greater than 255 KiB, cannot be marshalled into one byte. Length: %dKiB", eLen)
   113  	}
   114  
   115  	// Cheat since it's only one byte
   116  	e.Data[0] = byte(eLen)
   117  
   118  	if _, err = f.Seek(e.BaseOffset, io.SeekStart); err != nil {
   119  		return fmt.Errorf("error seeking to memory offset %#X for the start of the EBDA, got: %v", e.BaseOffset, err)
   120  	}
   121  
   122  	return binary.Write(f, binary.LittleEndian, e.Data)
   123  }