forked from google/periph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
view.go
283 lines (258 loc) · 7.58 KB
/
view.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// Copyright 2016 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package pmem
import (
"fmt"
"io"
"os"
"reflect"
"sync"
"unsafe"
"periph.io/x/periph/host/fs"
)
// Slice can be transparently viewed as []byte, []uint32 or a struct.
type Slice []byte
// Uint32 returns a view of the byte slice as a []uint32.
func (s *Slice) Uint32() []uint32 {
header := *(*reflect.SliceHeader)(unsafe.Pointer(s))
header.Len /= 4
header.Cap /= 4
return *(*[]uint32)(unsafe.Pointer(&header))
}
// Bytes implements Mem.
func (s *Slice) Bytes() []byte {
return *s
}
// AsPOD implements Mem.
func (s *Slice) AsPOD(pp interface{}) error {
if pp == nil {
return wrapf("require Ptr, got nil")
}
vpp := reflect.ValueOf(pp)
if elemSize, err := isPS(len(*s), vpp); err == nil {
p := vpp.Elem()
t := p.Type().Elem()
if elemSize > len(*s) {
return wrapf("can't map slice of struct %s (size %d) on [%d]byte", t, elemSize, len(*s))
}
nbElems := len(*s) / elemSize
// Use casting black magic to set the internal slice headers.
hdr := (*reflect.SliceHeader)(unsafe.Pointer(p.UnsafeAddr()))
hdr.Data = ((*reflect.SliceHeader)(unsafe.Pointer(s))).Data
hdr.Len = nbElems
hdr.Cap = nbElems
return nil
}
size, err := isPP(vpp)
if err != nil {
return err
}
p := vpp.Elem()
t := p.Type().Elem()
if size > len(*s) {
return wrapf("can't map struct %s (size %d) on [%d]byte", t, size, len(*s))
}
// Use casting black magic to read the internal slice headers.
dest := unsafe.Pointer(((*reflect.SliceHeader)(unsafe.Pointer(s))).Data)
// Use reflection black magic to write to the original pointer.
p.Set(reflect.NewAt(t, dest))
return nil
}
// View represents a view of physical memory memory mapped into user space.
//
// It is usually used to map CPU registers into user space, usually I/O
// registers and the likes.
//
// It is not required to call Close(), the kernel will clean up on process
// shutdown.
type View struct {
Slice
orig []uint8 // Reference rounded to the lowest 4Kb page containing Slice.
phys uint64 // physical address of the base of Slice.
}
// Close unmaps the memory from the user address space.
//
// This is done naturally by the OS on process teardown (when the process
// exits) so this is not a hard requirement to call this function.
func (v *View) Close() error {
return munmap(v.orig)
}
// PhysAddr implements Mem.
func (v *View) PhysAddr() uint64 {
return v.phys
}
// MapGPIO returns a CPU specific memory mapping of the CPU I/O registers using
// /dev/gpiomem.
//
// At the moment, /dev/gpiomem is only supported on Raspbian Jessie via a
// specific kernel driver.
func MapGPIO() (*View, error) {
if isLinux {
return mapGPIOLinux()
}
return nil, wrapf("/dev/gpiomem is not supported on this platform")
}
// Map returns a memory mapped view of arbitrary physical memory range using OS
// provided functionality.
//
// Maps size of memory, rounded on a 4kb window.
//
// This function is dangerous and should be used wisely. It normally requires
// super privileges (root). On Linux, it leverages /dev/mem.
func Map(base uint64, size int) (*View, error) {
if isLinux {
return mapLinux(base, size)
}
return nil, wrapf("physical memory mapping is not supported on this platform")
}
// MapAsPOD is a leaky shorthand of calling Map(base, sizeof(v)) then AsPOD(v).
//
// There is no way to reclaim the memory map.
//
// A slice cannot be used, as it does not have inherent size. Use an aray
// instead.
func MapAsPOD(base uint64, i interface{}) error {
// Automatically determine the necessary size. Because of this, slice of
// unspecified length cannot be used here.
if i == nil {
return wrapf("require Ptr, got nil")
}
v := reflect.ValueOf(i)
size, err := isPP(v)
if err != nil {
return err
}
m, err := Map(base, size)
if err != nil {
return err
}
return m.AsPOD(i)
}
//
// Keep a cache of open file handles instead of opening and closing repeatedly.
var (
mu sync.Mutex
gpioMemErr error
gpioMemView *View
devMem fileIO
devMemErr error
openFile = openFileOrig
)
type fileIO interface {
io.Closer
io.Seeker
io.Reader
Fd() uintptr
}
func openFileOrig(path string, flag int) (fileIO, error) {
f, err := fs.Open(path, flag)
if err != nil {
return nil, err
}
return f, nil
}
// mapGPIOLinux is purely Raspbian specific.
func mapGPIOLinux() (*View, error) {
mu.Lock()
defer mu.Unlock()
if gpioMemView == nil && gpioMemErr == nil {
if f, err := openFile("/dev/gpiomem", os.O_RDWR|os.O_SYNC); err == nil {
defer f.Close()
if i, err := mmap(f.Fd(), 0, pageSize); err == nil {
gpioMemView = &View{Slice: i, orig: i, phys: 0}
} else {
gpioMemErr = wrapf("failed to memory map in user space GPIO memory: %v", err)
}
} else {
gpioMemErr = wrapf("failed to open GPIO memory: %v", err)
}
}
return gpioMemView, gpioMemErr
}
// mapLinux leverages /dev/mem to map a view of physical memory.
func mapLinux(base uint64, size int) (*View, error) {
f, err := openDevMemLinux()
if err != nil {
return nil, err
}
// Align base and size at 4Kb.
offset := int(base & 0xFFF)
i, err := mmap(f.Fd(), int64(base&^0xFFF), (size+offset+0xFFF)&^0xFFF)
if err != nil {
return nil, wrapf("mapping at 0x%x failed: %v", base, err)
}
return &View{Slice: i[offset : offset+size], orig: i, phys: base + uint64(offset)}, nil
}
func openDevMemLinux() (fileIO, error) {
mu.Lock()
defer mu.Unlock()
if devMem == nil && devMemErr == nil {
if devMem, devMemErr = openFile("/dev/mem", os.O_RDWR|os.O_SYNC); devMemErr != nil {
devMemErr = wrapf("failed to open physical memory: %v", devMemErr)
}
}
return devMem, devMemErr
}
func isAcceptableInner(t reflect.Type) error {
switch k := t.Kind(); k {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
return nil
case reflect.Array:
return isAcceptableInner(t.Elem())
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
if err := isAcceptableInner(t.Field(i).Type); err != nil {
return err
}
}
return nil
default:
return wrapf("require Ptr to Ptr to a POD type, got Ptr to Ptr to %s", k)
}
}
// isPP makes sure it is a pointer to a nil-pointer to something. It does
// sanity checks to reduce likelihood of a panic().
func isPP(pp reflect.Value) (int, error) {
if k := pp.Kind(); k != reflect.Ptr {
return 0, wrapf("require Ptr, got %s of %s", k, pp.Type().Name())
}
p := pp.Elem()
if k := p.Kind(); k != reflect.Ptr {
return 0, wrapf("require Ptr to Ptr, got %s", k)
}
if !p.IsNil() {
return 0, wrapf("require Ptr to Ptr to be nil")
}
// p.Elem() can't be used since it's a nil pointer. Use the type instead.
t := p.Type().Elem()
if err := isAcceptableInner(t); err != nil {
return 0, err
}
return int(t.Size()), nil
}
// isPS makes sure it is a pointer to a nil-slice of something. It does
// sanity checks to reduce likelihood of a panic().
func isPS(bufSize int, ps reflect.Value) (int, error) {
if k := ps.Kind(); k != reflect.Ptr {
return 0, wrapf("require Ptr, got %s of %s", k, ps.Type().Name())
}
s := ps.Elem()
if k := s.Kind(); k != reflect.Slice {
return 0, wrapf("require Ptr to Slice, got %s", k)
}
if !s.IsNil() {
return 0, wrapf("require Ptr to Slice to be nil")
}
// s.Elem() can't be used since it's a nil slice. Use the type instead.
t := s.Type().Elem()
if err := isAcceptableInner(t); err != nil {
return 0, err
}
return int(t.Size()), nil
}
func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("pmem: "+format, a...)
}