github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/memio/mmap_linux.go (about)

     1  // Copyright 2012-2020 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 memio
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  var memPath = "/dev/mem"
    15  
    16  var pageSize = int64(syscall.Getpagesize())
    17  
    18  type syscalls interface {
    19  	Mmap(int, int64, int, int, int) ([]byte, error)
    20  	Munmap([]byte) error
    21  }
    22  
    23  type calls struct{}
    24  
    25  func (c *calls) Mmap(fd int, page int64, mapSize int, prot int, callid int) ([]byte, error) {
    26  	return syscall.Mmap(fd, page, mapSize, prot, callid)
    27  }
    28  
    29  func (c *calls) Munmap(mem []byte) error {
    30  	return syscall.Munmap(mem)
    31  }
    32  
    33  // MMap is a struct containing an os.File and an interface to system calls to manage mapped files.
    34  type MMap struct {
    35  	*os.File
    36  	syscalls
    37  }
    38  
    39  // mmap aligns the address and maps multiple pages when needed.
    40  func (m *MMap) mmap(f *os.File, addr int64, size int64, prot int) (mem []byte, offset int64, err error) {
    41  	page := addr &^ (pageSize - 1)
    42  	offset = addr - page
    43  	mapSize := offset + size
    44  	mem, err = m.Mmap(int(f.Fd()), int64(page), int(mapSize), prot, syscall.MAP_SHARED)
    45  	return
    46  }
    47  
    48  // ReadAt reads data from physical memory at address addr. On x86 platforms,
    49  // this uses the seek+read syscalls. On arm platforms, this uses mmap.
    50  func (m *MMap) ReadAt(addr int64, data UintN) error {
    51  	mem, offset, err := m.mmap(m.File, addr, data.Size(), syscall.PROT_READ)
    52  	if err != nil {
    53  		return fmt.Errorf("reading %#x/%d: %v", addr, data.Size(), err)
    54  	}
    55  	defer m.Munmap(mem)
    56  
    57  	// MMIO makes this a bit tricky. Reads must be conducted in one load
    58  	// operation. Review the generated assembly to make sure.
    59  	if err := data.read(unsafe.Pointer(&mem[offset])); err != nil {
    60  		return fmt.Errorf("reading %#x/%d: %v", addr, data.Size(), err)
    61  	}
    62  	return nil
    63  }
    64  
    65  // WriteAt writes data to physical memory at address addr. On x86 platforms, this
    66  // uses the seek+read syscalls. On arm platforms, this uses mmap.
    67  func (m *MMap) WriteAt(addr int64, data UintN) error {
    68  	mem, offset, err := m.mmap(m.File, addr, data.Size(), syscall.PROT_WRITE)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer m.Munmap(mem)
    73  
    74  	// MMIO makes this a bit tricky. Writes must be conducted in one store
    75  	// operation. Review the generated assembly to make sure.
    76  	return data.write(unsafe.Pointer(&mem[offset]))
    77  }
    78  
    79  // Close implements Close.
    80  func (m *MMap) Close() error {
    81  	return m.File.Close()
    82  }
    83  
    84  // NewMMap returns an Mmap for a file (usually a device) passed as a string.
    85  func NewMMap(path string) (*MMap, error) {
    86  	f, err := os.OpenFile(path, os.O_RDWR, 0)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	return &MMap{
    91  		File:     f,
    92  		syscalls: &calls{},
    93  	}, nil
    94  }
    95  
    96  // Read is deprecated. Still here for compatibility.
    97  // Use NewMMap() and the interface function instead.
    98  func Read(addr int64, data UintN) error {
    99  	mmap, err := NewMMap(memPath)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	defer mmap.Close()
   104  	return mmap.ReadAt(addr, data)
   105  }
   106  
   107  // Write is deprecated. Still here for compatibility.
   108  // Use NewMMap() and the interface function instead.
   109  func Write(addr int64, data UintN) error {
   110  	mmap, err := NewMMap(memPath)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	defer mmap.Close()
   115  	return mmap.WriteAt(addr, data)
   116  }