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

     1  // Copyright 2016-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  //go:build linux
     6  // +build linux
     7  
     8  package main
     9  
    10  import (
    11  	"bytes"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"syscall"
    17  )
    18  
    19  type offsetReader struct {
    20  	name string
    21  	base int64
    22  	r    io.ReaderAt
    23  }
    24  
    25  var _ io.ReaderAt = &offsetReader{}
    26  
    27  // ReadAt implements io.ReaderAt.
    28  // The offset is adjusted by the base. After that
    29  // it is bytes.Buffer's job to deal with all the
    30  // corner cases.
    31  // Note that this is not a section reader, since the offset
    32  // is maintained. This makes it easy to use ROM addresses
    33  // without adjusting them.
    34  // For one example, the coreboot MEM_CONSOLE tag has an absolute
    35  // address in it. Once a proper offset reader is created,
    36  // that absolute address is used unchanged.
    37  func (o *offsetReader) ReadAt(b []byte, i int64) (int, error) {
    38  	// This is the line that makes it "not a section reader".
    39  	debug("readat %#x base %#x %d bytes....", i, o.base, len(b))
    40  	i -= o.base
    41  	debug("\treadat %d bytes at %#x", len(b), i)
    42  	n, err := o.r.ReadAt(b, i)
    43  	debug("\t... i %#x n %d err %v", i, n, err)
    44  	if err != nil && err != io.EOF {
    45  		return n, fmt.Errorf("Reading at #%x for %d bytes: %v", i, len(b), err)
    46  	}
    47  	return n, err
    48  }
    49  
    50  func mapit(f *os.File, addr int64, sz int) (io.ReaderAt, error) {
    51  	if addr+int64(size) > int64(0xffffffff) {
    52  		return nil, fmt.Errorf("cbmem tables can only be in 32-bit space and (%#x-%#x is outside it", addr, addr+int64(sz))
    53  	}
    54  	ba := (addr >> 12) << 12
    55  	basz := sz + int(addr-ba)
    56  	debug("Map %#x %#x", ba, basz)
    57  	// we are limited to 32 bits.
    58  	b, err := syscall.Mmap(int(f.Fd()), ba, basz, syscall.PROT_READ, syscall.MAP_SHARED)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("mmap %d bytes at %#x: %v", sz, addr, err)
    61  	}
    62  	off := int(addr - ba)
    63  	debug("new reader b len %d off %d off + size %d", len(b), off, off+sz)
    64  	return bytes.NewReader(b[off : off+sz]), nil
    65  }
    66  
    67  func newOffsetReader(f *os.File, off int64, sz int) (*offsetReader, error) {
    68  	debug("newOffsetReader(%v, %#x, %#x)", f, off, sz)
    69  	r, err := mapit(f, off, sz)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return &offsetReader{base: off, r: r}, nil
    74  }
    75  
    76  // readOneSize reads an entry of any type. This Size variant is for
    77  // the console log only, though we know of no case in which it is
    78  // larger than 1M. We really want the SectionReader as a way to ReadAt
    79  // for the binary.Read. Any meaningful limit will be enforced by the kernel.
    80  func readOneSize(r io.ReaderAt, i interface{}, o int64, n int64) error {
    81  	err := binary.Read(io.NewSectionReader(r, o, n), binary.LittleEndian, i)
    82  	if err != nil {
    83  		return fmt.Errorf("Trying to read section for %T: %v", r, err)
    84  	}
    85  	return nil
    86  }
    87  
    88  // readOneSize reads an entry of any type, limited to 64K.
    89  func readOne(r io.ReaderAt, i interface{}, o int64) error {
    90  	return readOneSize(r, i, o, 65536)
    91  }