summaryrefslogtreecommitdiffhomepage
path: root/pkg/hostarch
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/hostarch')
-rw-r--r--pkg/hostarch/BUILD42
-rw-r--r--pkg/hostarch/access_type.go126
-rw-r--r--pkg/hostarch/addr.go125
-rw-r--r--pkg/hostarch/addr_range_seq_test.go197
-rw-r--r--pkg/hostarch/addr_range_seq_unsafe.go280
-rw-r--r--pkg/hostarch/hostarch.go7
-rw-r--r--pkg/hostarch/hostarch_arm64.go54
-rw-r--r--pkg/hostarch/hostarch_x86.go38
8 files changed, 869 insertions, 0 deletions
diff --git a/pkg/hostarch/BUILD b/pkg/hostarch/BUILD
new file mode 100644
index 000000000..1e8def4d9
--- /dev/null
+++ b/pkg/hostarch/BUILD
@@ -0,0 +1,42 @@
+load("//tools:defs.bzl", "go_library", "go_test")
+load("//tools/go_generics:defs.bzl", "go_template_instance")
+
+package(licenses = ["notice"])
+
+go_template_instance(
+ name = "addr_range",
+ out = "addr_range.go",
+ package = "hostarch",
+ prefix = "Addr",
+ template = "//pkg/segment:generic_range",
+ types = {
+ "T": "Addr",
+ },
+)
+
+go_test(
+ name = "hostarch_test",
+ size = "small",
+ srcs = [
+ "addr_range_seq_test.go",
+ ],
+ library = ":hostarch",
+)
+
+go_library(
+ name = "hostarch",
+ srcs = [
+ "access_type.go",
+ "addr.go",
+ "addr_range.go",
+ "addr_range_seq_unsafe.go",
+ "hostarch.go",
+ "hostarch_arm64.go",
+ "hostarch_x86.go",
+ ],
+ visibility = ["//:sandbox"],
+ deps = [
+ "//pkg/gohacks",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
+)
diff --git a/pkg/hostarch/access_type.go b/pkg/hostarch/access_type.go
new file mode 100644
index 000000000..e30476840
--- /dev/null
+++ b/pkg/hostarch/access_type.go
@@ -0,0 +1,126 @@
+// Copyright 2018 The gVisor Authors.
+//
+// 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 hostarch
+
+import "golang.org/x/sys/unix"
+
+// AccessType specifies memory access types. This is used for
+// setting mapping permissions, as well as communicating faults.
+//
+// +stateify savable
+type AccessType struct {
+ // Read is read access.
+ Read bool
+
+ // Write is write access.
+ Write bool
+
+ // Execute is executable access.
+ Execute bool
+}
+
+// String returns a pretty representation of access. This looks like the
+// familiar r-x, rw-, etc. and can be relied on as such.
+func (a AccessType) String() string {
+ bits := [3]byte{'-', '-', '-'}
+ if a.Read {
+ bits[0] = 'r'
+ }
+ if a.Write {
+ bits[1] = 'w'
+ }
+ if a.Execute {
+ bits[2] = 'x'
+ }
+ return string(bits[:])
+}
+
+// Any returns true iff at least one of Read, Write or Execute is true.
+func (a AccessType) Any() bool {
+ return a.Read || a.Write || a.Execute
+}
+
+// Prot returns the system prot (unix.PROT_READ, etc.) for this access.
+func (a AccessType) Prot() int {
+ var prot int
+ if a.Read {
+ prot |= unix.PROT_READ
+ }
+ if a.Write {
+ prot |= unix.PROT_WRITE
+ }
+ if a.Execute {
+ prot |= unix.PROT_EXEC
+ }
+ return prot
+}
+
+// SupersetOf returns true iff the access types in a are a superset of the
+// access types in other.
+func (a AccessType) SupersetOf(other AccessType) bool {
+ if !a.Read && other.Read {
+ return false
+ }
+ if !a.Write && other.Write {
+ return false
+ }
+ if !a.Execute && other.Execute {
+ return false
+ }
+ return true
+}
+
+// Intersect returns the access types set in both a and other.
+func (a AccessType) Intersect(other AccessType) AccessType {
+ return AccessType{
+ Read: a.Read && other.Read,
+ Write: a.Write && other.Write,
+ Execute: a.Execute && other.Execute,
+ }
+}
+
+// Union returns the access types set in either a or other.
+func (a AccessType) Union(other AccessType) AccessType {
+ return AccessType{
+ Read: a.Read || other.Read,
+ Write: a.Write || other.Write,
+ Execute: a.Execute || other.Execute,
+ }
+}
+
+// Effective returns the set of effective access types allowed by a, even if
+// some types are not explicitly allowed.
+func (a AccessType) Effective() AccessType {
+ // In Linux, Write and Execute access generally imply Read access. See
+ // mm/mmap.c:protection_map.
+ //
+ // The notable exception is get_user_pages, which only checks against
+ // the original vma flags. That said, most user memory accesses do not
+ // use GUP.
+ if a.Write || a.Execute {
+ a.Read = true
+ }
+ return a
+}
+
+// Convenient access types.
+var (
+ NoAccess = AccessType{}
+ Read = AccessType{Read: true}
+ Write = AccessType{Write: true}
+ Execute = AccessType{Execute: true}
+ ReadWrite = AccessType{Read: true, Write: true}
+ AnyAccess = AccessType{Read: true, Write: true, Execute: true}
+)
diff --git a/pkg/hostarch/addr.go b/pkg/hostarch/addr.go
new file mode 100644
index 000000000..0cf0f3c81
--- /dev/null
+++ b/pkg/hostarch/addr.go
@@ -0,0 +1,125 @@
+// Copyright 2018 The gVisor Authors.
+//
+// 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 hostarch
+
+import (
+ "fmt"
+)
+
+// Addr represents a generic virtual address.
+//
+// +stateify savable
+type Addr uintptr
+
+// AddLength adds the given length to start and returns the result. ok is true
+// iff adding the length did not overflow the range of Addr.
+//
+// Note: This function is usually used to get the end of an address range
+// defined by its start address and length. Since the resulting end is
+// exclusive, end == 0 is technically valid, and corresponds to a range that
+// extends to the end of the address space, but ok will be false. This isn't
+// expected to ever come up in practice.
+func (v Addr) AddLength(length uint64) (end Addr, ok bool) {
+ end = v + Addr(length)
+ // The second half of the following check is needed in case uintptr is
+ // smaller than 64 bits.
+ ok = end >= v && length <= uint64(^Addr(0))
+ return
+}
+
+// RoundDown returns the address rounded down to the nearest page boundary.
+func (v Addr) RoundDown() Addr {
+ return v & ^Addr(PageSize-1)
+}
+
+// RoundUp returns the address rounded up to the nearest page boundary. ok is
+// true iff rounding up did not wrap around.
+func (v Addr) RoundUp() (addr Addr, ok bool) {
+ addr = Addr(v + PageSize - 1).RoundDown()
+ ok = addr >= v
+ return
+}
+
+// MustRoundUp is equivalent to RoundUp, but panics if rounding up wraps
+// around.
+func (v Addr) MustRoundUp() Addr {
+ addr, ok := v.RoundUp()
+ if !ok {
+ panic(fmt.Sprintf("hostarch.Addr(%d).RoundUp() wraps", v))
+ }
+ return addr
+}
+
+// HugeRoundDown returns the address rounded down to the nearest huge page
+// boundary.
+func (v Addr) HugeRoundDown() Addr {
+ return v & ^Addr(HugePageSize-1)
+}
+
+// HugeRoundUp returns the address rounded up to the nearest huge page boundary.
+// ok is true iff rounding up did not wrap around.
+func (v Addr) HugeRoundUp() (addr Addr, ok bool) {
+ addr = Addr(v + HugePageSize - 1).HugeRoundDown()
+ ok = addr >= v
+ return
+}
+
+// PageOffset returns the offset of v into the current page.
+func (v Addr) PageOffset() uint64 {
+ return uint64(v & Addr(PageSize-1))
+}
+
+// IsPageAligned returns true if v.PageOffset() == 0.
+func (v Addr) IsPageAligned() bool {
+ return v.PageOffset() == 0
+}
+
+// AddrRange is a range of Addrs.
+//
+// type AddrRange <generated by go_generics>
+
+// ToRange returns [v, v+length).
+func (v Addr) ToRange(length uint64) (AddrRange, bool) {
+ end, ok := v.AddLength(length)
+ return AddrRange{v, end}, ok
+}
+
+// IsPageAligned returns true if ar.Start.IsPageAligned() and
+// ar.End.IsPageAligned().
+func (ar AddrRange) IsPageAligned() bool {
+ return ar.Start.IsPageAligned() && ar.End.IsPageAligned()
+}
+
+// String implements fmt.Stringer.String.
+func (ar AddrRange) String() string {
+ return fmt.Sprintf("[%#x, %#x)", ar.Start, ar.End)
+}
+
+// PageRoundDown/Up are equivalent to Addr.RoundDown/Up, but without the
+// potentially truncating conversion from uint64 to Addr. This is necessary
+// because there is no way to define generic "PageRoundDown/Up" functions in Go.
+
+// PageRoundDown returns x rounded down to the nearest page boundary.
+func PageRoundDown(x uint64) uint64 {
+ return x &^ (PageSize - 1)
+}
+
+// PageRoundUp returns x rounded up to the nearest page boundary.
+// ok is true iff rounding up did not wrap around.
+func PageRoundUp(x uint64) (addr uint64, ok bool) {
+ addr = PageRoundDown(x + PageSize - 1)
+ ok = addr >= x
+ return
+}
diff --git a/pkg/hostarch/addr_range_seq_test.go b/pkg/hostarch/addr_range_seq_test.go
new file mode 100644
index 000000000..5726dfd19
--- /dev/null
+++ b/pkg/hostarch/addr_range_seq_test.go
@@ -0,0 +1,197 @@
+// Copyright 2018 The gVisor Authors.
+//
+// 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 hostarch
+
+import (
+ "testing"
+)
+
+var addrRangeSeqTests = []struct {
+ desc string
+ ranges []AddrRange
+}{
+ {
+ desc: "Empty sequence",
+ },
+ {
+ desc: "Single empty AddrRange",
+ ranges: []AddrRange{
+ {0x10, 0x10},
+ },
+ },
+ {
+ desc: "Single non-empty AddrRange of length 1",
+ ranges: []AddrRange{
+ {0x10, 0x11},
+ },
+ },
+ {
+ desc: "Single non-empty AddrRange of length 2",
+ ranges: []AddrRange{
+ {0x10, 0x12},
+ },
+ },
+ {
+ desc: "Multiple non-empty AddrRanges",
+ ranges: []AddrRange{
+ {0x10, 0x11},
+ {0x20, 0x22},
+ },
+ },
+ {
+ desc: "Multiple AddrRanges including empty AddrRanges",
+ ranges: []AddrRange{
+ {0x10, 0x10},
+ {0x20, 0x20},
+ {0x30, 0x33},
+ {0x40, 0x44},
+ {0x50, 0x50},
+ {0x60, 0x60},
+ {0x70, 0x77},
+ {0x80, 0x88},
+ {0x90, 0x90},
+ {0xa0, 0xa0},
+ },
+ },
+}
+
+func testAddrRangeSeqEqualityWithTailIteration(t *testing.T, ars AddrRangeSeq, wantRanges []AddrRange) {
+ var wantLen int64
+ for _, ar := range wantRanges {
+ wantLen += int64(ar.Length())
+ }
+
+ var i int
+ for !ars.IsEmpty() {
+ if gotLen := ars.NumBytes(); gotLen != wantLen {
+ t.Errorf("Iteration %d: %v.NumBytes(): got %d, wanted %d", i, ars, gotLen, wantLen)
+ }
+ if gotN, wantN := ars.NumRanges(), len(wantRanges)-i; gotN != wantN {
+ t.Errorf("Iteration %d: %v.NumRanges(): got %d, wanted %d", i, ars, gotN, wantN)
+ }
+ got := ars.Head()
+ if i >= len(wantRanges) {
+ t.Errorf("Iteration %d: %v.Head(): got %s, wanted <end of sequence>", i, ars, got)
+ } else if want := wantRanges[i]; got != want {
+ t.Errorf("Iteration %d: %v.Head(): got %s, wanted %s", i, ars, got, want)
+ }
+ ars = ars.Tail()
+ wantLen -= int64(got.Length())
+ i++
+ }
+ if gotLen := ars.NumBytes(); gotLen != 0 || wantLen != 0 {
+ t.Errorf("Iteration %d: %v.NumBytes(): got %d, wanted %d (which should be 0)", i, ars, gotLen, wantLen)
+ }
+ if gotN := ars.NumRanges(); gotN != 0 {
+ t.Errorf("Iteration %d: %v.NumRanges(): got %d, wanted 0", i, ars, gotN)
+ }
+}
+
+func TestAddrRangeSeqTailIteration(t *testing.T) {
+ for _, test := range addrRangeSeqTests {
+ t.Run(test.desc, func(t *testing.T) {
+ testAddrRangeSeqEqualityWithTailIteration(t, AddrRangeSeqFromSlice(test.ranges), test.ranges)
+ })
+ }
+}
+
+func TestAddrRangeSeqDropFirstEmpty(t *testing.T) {
+ var ars AddrRangeSeq
+ if got, want := ars.DropFirst(1), ars; got != want {
+ t.Errorf("%v.DropFirst(1): got %v, wanted %v", ars, got, want)
+ }
+}
+
+func TestAddrRangeSeqDropSingleByteIteration(t *testing.T) {
+ // Tests AddrRangeSeq iteration using Head/DropFirst, simulating
+ // I/O-per-AddrRange.
+ for _, test := range addrRangeSeqTests {
+ t.Run(test.desc, func(t *testing.T) {
+ // Figure out what AddrRanges we expect to see.
+ var wantLen int64
+ var wantRanges []AddrRange
+ for _, ar := range test.ranges {
+ wantLen += int64(ar.Length())
+ wantRanges = append(wantRanges, ar)
+ if ar.Length() == 0 {
+ // We "do" 0 bytes of I/O and then call DropFirst(0),
+ // advancing to the next AddrRange.
+ continue
+ }
+ // Otherwise we "do" 1 byte of I/O and then call DropFirst(1),
+ // advancing the AddrRange by 1 byte, or to the next AddrRange
+ // if this one is exhausted.
+ for ar.Start++; ar.Length() != 0; ar.Start++ {
+ wantRanges = append(wantRanges, ar)
+ }
+ }
+ t.Logf("Expected AddrRanges: %s (%d bytes)", wantRanges, wantLen)
+
+ ars := AddrRangeSeqFromSlice(test.ranges)
+ var i int
+ for !ars.IsEmpty() {
+ if gotLen := ars.NumBytes(); gotLen != wantLen {
+ t.Errorf("Iteration %d: %v.NumBytes(): got %d, wanted %d", i, ars, gotLen, wantLen)
+ }
+ got := ars.Head()
+ if i >= len(wantRanges) {
+ t.Errorf("Iteration %d: %v.Head(): got %s, wanted <end of sequence>", i, ars, got)
+ } else if want := wantRanges[i]; got != want {
+ t.Errorf("Iteration %d: %v.Head(): got %s, wanted %s", i, ars, got, want)
+ }
+ if got.Length() == 0 {
+ ars = ars.DropFirst(0)
+ } else {
+ ars = ars.DropFirst(1)
+ wantLen--
+ }
+ i++
+ }
+ if gotLen := ars.NumBytes(); gotLen != 0 || wantLen != 0 {
+ t.Errorf("Iteration %d: %v.NumBytes(): got %d, wanted %d (which should be 0)", i, ars, gotLen, wantLen)
+ }
+ })
+ }
+}
+
+func TestAddrRangeSeqTakeFirstEmpty(t *testing.T) {
+ var ars AddrRangeSeq
+ if got, want := ars.TakeFirst(1), ars; got != want {
+ t.Errorf("%v.TakeFirst(1): got %v, wanted %v", ars, got, want)
+ }
+}
+
+func TestAddrRangeSeqTakeFirst(t *testing.T) {
+ ranges := []AddrRange{
+ {0x10, 0x11},
+ {0x20, 0x22},
+ {0x30, 0x30},
+ {0x40, 0x44},
+ {0x50, 0x55},
+ {0x60, 0x60},
+ {0x70, 0x77},
+ }
+ ars := AddrRangeSeqFromSlice(ranges).TakeFirst(5)
+ want := []AddrRange{
+ {0x10, 0x11}, // +1 byte (total 1 byte), not truncated
+ {0x20, 0x22}, // +2 bytes (total 3 bytes), not truncated
+ {0x30, 0x30}, // +0 bytes (total 3 bytes), no change
+ {0x40, 0x42}, // +2 bytes (total 5 bytes), partially truncated
+ {0x50, 0x50}, // +0 bytes (total 5 bytes), fully truncated
+ {0x60, 0x60}, // +0 bytes (total 5 bytes), "fully truncated" (no change)
+ {0x70, 0x70}, // +0 bytes (total 5 bytes), fully truncated
+ }
+ testAddrRangeSeqEqualityWithTailIteration(t, ars, want)
+}
diff --git a/pkg/hostarch/addr_range_seq_unsafe.go b/pkg/hostarch/addr_range_seq_unsafe.go
new file mode 100644
index 000000000..ecc17d595
--- /dev/null
+++ b/pkg/hostarch/addr_range_seq_unsafe.go
@@ -0,0 +1,280 @@
+// Copyright 2018 The gVisor Authors.
+//
+// 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 hostarch
+
+import (
+ "bytes"
+ "fmt"
+ "unsafe"
+
+ "gvisor.dev/gvisor/pkg/gohacks"
+)
+
+// An AddrRangeSeq represents a sequence of AddrRanges.
+//
+// AddrRangeSeqs are immutable and may be copied by value. The zero value of
+// AddrRangeSeq represents an empty sequence.
+//
+// An AddrRangeSeq may contain AddrRanges with a length of 0. This is necessary
+// since zero-length AddrRanges are significant to MM bounds checks.
+type AddrRangeSeq struct {
+ // If length is 0, then the AddrRangeSeq represents no AddrRanges.
+ // Invariants: data == 0; offset == 0; limit == 0.
+ //
+ // If length is 1, then the AddrRangeSeq represents the single
+ // AddrRange{offset, offset+limit}. Invariants: data == 0.
+ //
+ // Otherwise, length >= 2, and the AddrRangeSeq represents the `length`
+ // AddrRanges in the array of AddrRanges starting at address `data`,
+ // starting at `offset` bytes into the first AddrRange and limited to the
+ // following `limit` bytes. (AddrRanges after `limit` are still iterated,
+ // but are truncated to a length of 0.) Invariants: data != 0; offset <=
+ // data[0].Length(); limit > 0; offset+limit <= the combined length of all
+ // AddrRanges in the array.
+ data unsafe.Pointer
+ length int
+ offset Addr
+ limit Addr
+}
+
+// AddrRangeSeqOf returns an AddrRangeSeq representing the single AddrRange ar.
+func AddrRangeSeqOf(ar AddrRange) AddrRangeSeq {
+ return AddrRangeSeq{
+ length: 1,
+ offset: ar.Start,
+ limit: ar.Length(),
+ }
+}
+
+// AddrRangeSeqFromSlice returns an AddrRangeSeq representing all AddrRanges in
+// slice.
+//
+// Whether the returned AddrRangeSeq shares memory with slice is unspecified;
+// clients should avoid mutating slices passed to AddrRangeSeqFromSlice.
+//
+// Preconditions: The combined length of all AddrRanges in slice <=
+// math.MaxInt64.
+func AddrRangeSeqFromSlice(slice []AddrRange) AddrRangeSeq {
+ var limit int64
+ for _, ar := range slice {
+ len64 := int64(ar.Length())
+ if len64 < 0 {
+ panic(fmt.Sprintf("Length of AddrRange %v overflows int64", ar))
+ }
+ sum := limit + len64
+ if sum < limit {
+ panic(fmt.Sprintf("Total length of AddrRanges %v overflows int64", slice))
+ }
+ limit = sum
+ }
+ return addrRangeSeqFromSliceLimited(slice, limit)
+}
+
+// Preconditions:
+// * The combined length of all AddrRanges in slice <= limit.
+// * limit >= 0.
+// * If len(slice) != 0, then limit > 0.
+func addrRangeSeqFromSliceLimited(slice []AddrRange, limit int64) AddrRangeSeq {
+ switch len(slice) {
+ case 0:
+ return AddrRangeSeq{}
+ case 1:
+ return AddrRangeSeq{
+ length: 1,
+ offset: slice[0].Start,
+ limit: Addr(limit),
+ }
+ default:
+ return AddrRangeSeq{
+ data: unsafe.Pointer(&slice[0]),
+ length: len(slice),
+ limit: Addr(limit),
+ }
+ }
+}
+
+// IsEmpty returns true if ars.NumRanges() == 0.
+//
+// Note that since AddrRangeSeq may contain AddrRanges with a length of zero,
+// an AddrRange representing 0 bytes (AddrRangeSeq.NumBytes() == 0) is not
+// necessarily empty.
+func (ars AddrRangeSeq) IsEmpty() bool {
+ return ars.length == 0
+}
+
+// NumRanges returns the number of AddrRanges in ars.
+func (ars AddrRangeSeq) NumRanges() int {
+ return ars.length
+}
+
+// NumBytes returns the number of bytes represented by ars.
+func (ars AddrRangeSeq) NumBytes() int64 {
+ return int64(ars.limit)
+}
+
+// Head returns the first AddrRange in ars.
+//
+// Preconditions: !ars.IsEmpty().
+func (ars AddrRangeSeq) Head() AddrRange {
+ if ars.length == 0 {
+ panic("empty AddrRangeSeq")
+ }
+ if ars.length == 1 {
+ return AddrRange{ars.offset, ars.offset + ars.limit}
+ }
+ ar := *(*AddrRange)(ars.data)
+ ar.Start += ars.offset
+ if ar.Length() > ars.limit {
+ ar.End = ar.Start + ars.limit
+ }
+ return ar
+}
+
+// Tail returns an AddrRangeSeq consisting of all AddrRanges in ars after the
+// first.
+//
+// Preconditions: !ars.IsEmpty().
+func (ars AddrRangeSeq) Tail() AddrRangeSeq {
+ if ars.length == 0 {
+ panic("empty AddrRangeSeq")
+ }
+ if ars.length == 1 {
+ return AddrRangeSeq{}
+ }
+ return ars.externalTail()
+}
+
+// Preconditions: ars.length >= 2.
+func (ars AddrRangeSeq) externalTail() AddrRangeSeq {
+ headLen := (*AddrRange)(ars.data).Length() - ars.offset
+ var tailLimit int64
+ if ars.limit > headLen {
+ tailLimit = int64(ars.limit - headLen)
+ }
+ var extSlice []AddrRange
+ extSliceHdr := (*gohacks.SliceHeader)(unsafe.Pointer(&extSlice))
+ extSliceHdr.Data = ars.data
+ extSliceHdr.Len = ars.length
+ extSliceHdr.Cap = ars.length
+ return addrRangeSeqFromSliceLimited(extSlice[1:], tailLimit)
+}
+
+// DropFirst returns an AddrRangeSeq equivalent to ars, but with the first n
+// bytes omitted. If n > ars.NumBytes(), DropFirst returns an empty
+// AddrRangeSeq.
+//
+// If !ars.IsEmpty() and ars.Head().Length() == 0, DropFirst will always omit
+// at least ars.Head(), even if n == 0. This guarantees that the basic pattern
+// of:
+//
+// for !ars.IsEmpty() {
+// n, err = doIOWith(ars.Head())
+// if err != nil {
+// return err
+// }
+// ars = ars.DropFirst(n)
+// }
+//
+// works even in the presence of zero-length AddrRanges.
+//
+// Preconditions: n >= 0.
+func (ars AddrRangeSeq) DropFirst(n int) AddrRangeSeq {
+ if n < 0 {
+ panic(fmt.Sprintf("invalid n: %d", n))
+ }
+ return ars.DropFirst64(int64(n))
+}
+
+// DropFirst64 is equivalent to DropFirst but takes an int64.
+func (ars AddrRangeSeq) DropFirst64(n int64) AddrRangeSeq {
+ if n < 0 {
+ panic(fmt.Sprintf("invalid n: %d", n))
+ }
+ if Addr(n) > ars.limit {
+ return AddrRangeSeq{}
+ }
+ // Handle initial empty AddrRange.
+ switch ars.length {
+ case 0:
+ return AddrRangeSeq{}
+ case 1:
+ if ars.limit == 0 {
+ return AddrRangeSeq{}
+ }
+ default:
+ if rawHeadLen := (*AddrRange)(ars.data).Length(); ars.offset == rawHeadLen {
+ ars = ars.externalTail()
+ }
+ }
+ for n != 0 {
+ // Calling ars.Head() here is surprisingly expensive, so inline getting
+ // the head's length.
+ var headLen Addr
+ if ars.length == 1 {
+ headLen = ars.limit
+ } else {
+ headLen = (*AddrRange)(ars.data).Length() - ars.offset
+ }
+ if Addr(n) < headLen {
+ // Dropping ends partway through the head AddrRange.
+ ars.offset += Addr(n)
+ ars.limit -= Addr(n)
+ return ars
+ }
+ n -= int64(headLen)
+ ars = ars.Tail()
+ }
+ return ars
+}
+
+// TakeFirst returns an AddrRangeSeq equivalent to ars, but iterating at most n
+// bytes. TakeFirst never removes AddrRanges from ars; AddrRanges beyond the
+// first n bytes are reduced to a length of zero, but will still be iterated.
+//
+// Preconditions: n >= 0.
+func (ars AddrRangeSeq) TakeFirst(n int) AddrRangeSeq {
+ if n < 0 {
+ panic(fmt.Sprintf("invalid n: %d", n))
+ }
+ return ars.TakeFirst64(int64(n))
+}
+
+// TakeFirst64 is equivalent to TakeFirst but takes an int64.
+func (ars AddrRangeSeq) TakeFirst64(n int64) AddrRangeSeq {
+ if n < 0 {
+ panic(fmt.Sprintf("invalid n: %d", n))
+ }
+ if ars.limit > Addr(n) {
+ ars.limit = Addr(n)
+ }
+ return ars
+}
+
+// String implements fmt.Stringer.String.
+func (ars AddrRangeSeq) String() string {
+ // This is deliberately chosen to be the same as fmt's automatic stringer
+ // for []AddrRange.
+ var buf bytes.Buffer
+ buf.WriteByte('[')
+ var sep string
+ for !ars.IsEmpty() {
+ buf.WriteString(sep)
+ sep = " "
+ buf.WriteString(ars.Head().String())
+ ars = ars.Tail()
+ }
+ buf.WriteByte(']')
+ return buf.String()
+}
diff --git a/pkg/hostarch/hostarch.go b/pkg/hostarch/hostarch.go
new file mode 100644
index 000000000..fdd29c567
--- /dev/null
+++ b/pkg/hostarch/hostarch.go
@@ -0,0 +1,7 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package hostarch contains host arch address operations for user memory.
+package hostarch
diff --git a/pkg/hostarch/hostarch_arm64.go b/pkg/hostarch/hostarch_arm64.go
new file mode 100644
index 000000000..a31a8aeeb
--- /dev/null
+++ b/pkg/hostarch/hostarch_arm64.go
@@ -0,0 +1,54 @@
+// Copyright 2019 The gVisor Authors.
+//
+// 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.
+
+// +build arm64
+
+package hostarch
+
+import (
+ "encoding/binary"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ // PageSize is the system page size.
+ // arm64 support 4K/16K/64K page size,
+ // which can be get by unix.Getpagesize().
+ // Currently, only 4K page size is supported.
+ PageSize = 1 << PageShift
+
+ // HugePageSize is the system huge page size.
+ HugePageSize = 1 << HugePageShift
+
+ // PageShift is the binary log of the system page size.
+ PageShift = 12
+
+ // HugePageShift is the binary log of the system huge page size.
+ // Should be calculated by "PageShift + (PageShift - 3)"
+ // when multiple page size support is ready.
+ HugePageShift = 21
+)
+
+var (
+ // ByteOrder is the native byte order (little endian).
+ ByteOrder = binary.LittleEndian
+)
+
+func init() {
+ // Make sure the page size is 4K on arm64 platform.
+ if size := unix.Getpagesize(); size != PageSize {
+ panic("Only 4K page size is supported on arm64!")
+ }
+}
diff --git a/pkg/hostarch/hostarch_x86.go b/pkg/hostarch/hostarch_x86.go
new file mode 100644
index 000000000..af6ef2b7f
--- /dev/null
+++ b/pkg/hostarch/hostarch_x86.go
@@ -0,0 +1,38 @@
+// Copyright 2018 The gVisor Authors.
+//
+// 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.
+
+// +build amd64 386
+
+package hostarch
+
+import "encoding/binary"
+
+const (
+ // PageSize is the system page size.
+ PageSize = 1 << PageShift
+
+ // HugePageSize is the system huge page size.
+ HugePageSize = 1 << HugePageShift
+
+ // PageShift is the binary log of the system page size.
+ PageShift = 12
+
+ // HugePageShift is the binary log of the system huge page size.
+ HugePageShift = 21
+)
+
+var (
+ // ByteOrder is the native byte order (little endian).
+ ByteOrder = binary.LittleEndian
+)