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 }