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 }