mirror of
https://github.com/clearml/go-nvlib
synced 2025-04-15 21:12:11 +00:00
Add pkg/mmio as a direct port from mig-parted/internal/mmio
Signed-off-by: Kevin Klues <kklues@nvidia.com>
This commit is contained in:
parent
98d311e418
commit
613fd315f3
124
pkg/mmio/mmio.go
Normal file
124
pkg/mmio/mmio.go
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package mmio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gitlab.com/nvidia/cloud-native/go-nvlib/pkg/bytes"
|
||||
)
|
||||
|
||||
type Mmio interface {
|
||||
bytes.Raw
|
||||
bytes.Reader
|
||||
bytes.Writer
|
||||
Sync() error
|
||||
Close() error
|
||||
Slice(offset int, size int) Mmio
|
||||
LittleEndian() Mmio
|
||||
BigEndian() Mmio
|
||||
}
|
||||
|
||||
type mmio struct {
|
||||
bytes.Bytes
|
||||
}
|
||||
|
||||
func open(path string, offset int, size int, flags int) (Mmio, error) {
|
||||
var mmap_flags int
|
||||
switch flags {
|
||||
case os.O_RDONLY:
|
||||
mmap_flags = syscall.PROT_READ
|
||||
case os.O_RDWR:
|
||||
mmap_flags = syscall.PROT_READ | syscall.PROT_WRITE
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid flags: %v\n", flags)
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(path, flags, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %v\n", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get file info: %v\n", err)
|
||||
}
|
||||
|
||||
if size > int(fi.Size()) {
|
||||
return nil, fmt.Errorf("requested size larger than file size")
|
||||
}
|
||||
|
||||
if size < 0 {
|
||||
size = int(fi.Size())
|
||||
}
|
||||
|
||||
mmap, err := syscall.Mmap(
|
||||
int(file.Fd()),
|
||||
int64(offset),
|
||||
size,
|
||||
mmap_flags,
|
||||
syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to mmap file: %v\n", err)
|
||||
}
|
||||
|
||||
return &mmio{bytes.New(&mmap)}, nil
|
||||
}
|
||||
|
||||
func OpenRO(path string, offset int, size int) (Mmio, error) {
|
||||
return open(path, offset, size, os.O_RDONLY)
|
||||
}
|
||||
|
||||
func OpenRW(path string, offset int, size int) (Mmio, error) {
|
||||
return open(path, offset, size, os.O_RDWR)
|
||||
}
|
||||
|
||||
func (m *mmio) Slice(offset int, size int) Mmio {
|
||||
return &mmio{m.Bytes.Slice(offset, size)}
|
||||
}
|
||||
|
||||
func (m *mmio) LittleEndian() Mmio {
|
||||
return &mmio{m.Bytes.LittleEndian()}
|
||||
}
|
||||
|
||||
func (m *mmio) BigEndian() Mmio {
|
||||
return &mmio{m.Bytes.BigEndian()}
|
||||
}
|
||||
|
||||
func (m *mmio) Close() error {
|
||||
err := syscall.Munmap(*m.Bytes.Raw())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to munmap file: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mmio) Sync() error {
|
||||
_, _, errno := syscall.Syscall(
|
||||
syscall.SYS_MSYNC,
|
||||
uintptr(unsafe.Pointer(&(*m.Bytes.Raw())[0])),
|
||||
uintptr(m.Len()),
|
||||
uintptr(syscall.MS_SYNC|syscall.MS_INVALIDATE))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to msync file: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
152
pkg/mmio/mmio_test.go
Normal file
152
pkg/mmio/mmio_test.go
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package mmio
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMmioRead(t *testing.T) {
|
||||
source := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
offset int
|
||||
}
|
||||
testCases := func() []testCase {
|
||||
var testCases []testCase
|
||||
for i := 0; i < len(source)/2; i++ {
|
||||
tc := testCase{
|
||||
fmt.Sprintf("offset: %v", i),
|
||||
i,
|
||||
}
|
||||
testCases = append(testCases, tc)
|
||||
}
|
||||
return testCases
|
||||
}()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
mmio, err := MockOpenRO(&source, 0, len(source))
|
||||
require.Nil(t, err, "Unexpected error from OpenRO")
|
||||
|
||||
defer func() {
|
||||
err = mmio.Close()
|
||||
require.Nil(t, err, "Unexpected error from Close")
|
||||
}()
|
||||
|
||||
r8 := mmio.Read8(tc.offset)
|
||||
require.Equal(t, r8, source[tc.offset])
|
||||
|
||||
r16 := mmio.Read16(tc.offset)
|
||||
require.Equal(t, r16, binary.LittleEndian.Uint16(source[tc.offset:tc.offset+2]))
|
||||
|
||||
r32 := mmio.Read32(tc.offset)
|
||||
require.Equal(t, r32, binary.LittleEndian.Uint32(source[tc.offset:tc.offset+4]))
|
||||
|
||||
r64 := mmio.Read64(tc.offset)
|
||||
require.Equal(t, r64, binary.LittleEndian.Uint64(source[tc.offset:tc.offset+8]))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMmioWrite(t *testing.T) {
|
||||
source := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
offset int
|
||||
}
|
||||
testCases := func() []testCase {
|
||||
var testCases []testCase
|
||||
for i := 0; i < len(source)/2; i++ {
|
||||
tc := testCase{
|
||||
fmt.Sprintf("offset: %v", i),
|
||||
i,
|
||||
}
|
||||
testCases = append(testCases, tc)
|
||||
}
|
||||
return testCases
|
||||
}()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
mmio, err := MockOpenRW(&source, 0, len(source))
|
||||
require.Nil(t, err, "Unexpected error from OpenRW")
|
||||
|
||||
defer func() {
|
||||
err = mmio.Close()
|
||||
require.Nil(t, err, "Unexpected error from Close")
|
||||
}()
|
||||
|
||||
r8 := mmio.Read8(tc.offset)
|
||||
mmio.Write8(tc.offset, (1<<8)-1)
|
||||
require.Equal(t, r8, source[tc.offset])
|
||||
r8 = mmio.Read8(tc.offset)
|
||||
require.Equal(t, r8, uint8((1<<8)-1))
|
||||
mmio.Sync()
|
||||
require.Equal(t, r8, source[tc.offset])
|
||||
mmio.Write8(tc.offset, uint8(tc.offset))
|
||||
mmio.Sync()
|
||||
|
||||
r16 := mmio.Read16(tc.offset)
|
||||
mmio.Write16(tc.offset, (1<<16)-1)
|
||||
require.Equal(t, r16, binary.LittleEndian.Uint16(source[tc.offset:tc.offset+2]))
|
||||
r16 = mmio.Read16(tc.offset)
|
||||
require.Equal(t, r16, uint16((1<<16)-1))
|
||||
mmio.Sync()
|
||||
require.Equal(t, r16, binary.LittleEndian.Uint16(source[tc.offset:tc.offset+2]))
|
||||
mmio.Write8(tc.offset+0, uint8(tc.offset+0))
|
||||
mmio.Write8(tc.offset+1, uint8(tc.offset+1))
|
||||
mmio.Sync()
|
||||
|
||||
r32 := mmio.Read32(tc.offset)
|
||||
mmio.Write32(tc.offset, (1<<32)-1)
|
||||
require.Equal(t, r32, binary.LittleEndian.Uint32(source[tc.offset:tc.offset+4]))
|
||||
r32 = mmio.Read32(tc.offset)
|
||||
require.Equal(t, r32, uint32((1<<32)-1))
|
||||
mmio.Sync()
|
||||
require.Equal(t, r32, binary.LittleEndian.Uint32(source[tc.offset:tc.offset+4]))
|
||||
mmio.Write8(tc.offset+0, uint8(tc.offset+0))
|
||||
mmio.Write8(tc.offset+1, uint8(tc.offset+1))
|
||||
mmio.Write8(tc.offset+2, uint8(tc.offset+2))
|
||||
mmio.Write8(tc.offset+3, uint8(tc.offset+3))
|
||||
mmio.Sync()
|
||||
|
||||
r64 := mmio.Read64(tc.offset)
|
||||
mmio.Write64(tc.offset, (1<<64)-1)
|
||||
require.Equal(t, r64, binary.LittleEndian.Uint64(source[tc.offset:tc.offset+8]))
|
||||
r64 = mmio.Read64(tc.offset)
|
||||
require.Equal(t, r64, uint64((1<<64)-1))
|
||||
mmio.Sync()
|
||||
require.Equal(t, r64, binary.LittleEndian.Uint64(source[tc.offset:tc.offset+8]))
|
||||
mmio.Write8(tc.offset+0, uint8(tc.offset+0))
|
||||
mmio.Write8(tc.offset+1, uint8(tc.offset+1))
|
||||
mmio.Write8(tc.offset+2, uint8(tc.offset+2))
|
||||
mmio.Write8(tc.offset+3, uint8(tc.offset+3))
|
||||
mmio.Write8(tc.offset+4, uint8(tc.offset+4))
|
||||
mmio.Write8(tc.offset+5, uint8(tc.offset+5))
|
||||
mmio.Write8(tc.offset+6, uint8(tc.offset+6))
|
||||
mmio.Write8(tc.offset+7, uint8(tc.offset+7))
|
||||
mmio.Sync()
|
||||
})
|
||||
}
|
||||
}
|
72
pkg/mmio/mock.go
Normal file
72
pkg/mmio/mock.go
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package mmio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlab.com/nvidia/cloud-native/go-nvlib/pkg/bytes"
|
||||
)
|
||||
|
||||
type mockMmio struct {
|
||||
mmio
|
||||
source *[]byte
|
||||
offset int
|
||||
rw bool
|
||||
}
|
||||
|
||||
func mockOpen(source *[]byte, offset int, size int, rw bool) (Mmio, error) {
|
||||
if size < 0 {
|
||||
size = len(*source) - offset
|
||||
}
|
||||
if (offset + size) > len(*source) {
|
||||
return nil, fmt.Errorf("offset+size out of range")
|
||||
}
|
||||
|
||||
data := append([]byte{}, (*source)[offset:offset+size]...)
|
||||
|
||||
m := &mockMmio{}
|
||||
m.Bytes = bytes.New(&data).LittleEndian()
|
||||
m.source = source
|
||||
m.offset = offset
|
||||
m.rw = rw
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func MockOpenRO(source *[]byte, offset int, size int) (Mmio, error) {
|
||||
return mockOpen(source, offset, size, false)
|
||||
}
|
||||
|
||||
func MockOpenRW(source *[]byte, offset int, size int) (Mmio, error) {
|
||||
return mockOpen(source, offset, size, true)
|
||||
}
|
||||
|
||||
func (m *mockMmio) Close() error {
|
||||
m = &mockMmio{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockMmio) Sync() error {
|
||||
if !m.rw {
|
||||
return fmt.Errorf("opened read-only")
|
||||
}
|
||||
for i := range *m.Bytes.Raw() {
|
||||
(*m.source)[m.offset+i] = (*m.Bytes.Raw())[i]
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user