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 }