summaryrefslogtreecommitdiffhomepage
path: root/pkg/ring0/pagetables/pagetables_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/ring0/pagetables/pagetables_test.go')
-rw-r--r--pkg/ring0/pagetables/pagetables_test.go157
1 files changed, 157 insertions, 0 deletions
diff --git a/pkg/ring0/pagetables/pagetables_test.go b/pkg/ring0/pagetables/pagetables_test.go
new file mode 100644
index 000000000..772f4fc5e
--- /dev/null
+++ b/pkg/ring0/pagetables/pagetables_test.go
@@ -0,0 +1,157 @@
+// 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 pagetables
+
+import (
+ "testing"
+
+ "gvisor.dev/gvisor/pkg/usermem"
+)
+
+type mapping struct {
+ start uintptr
+ length uintptr
+ addr uintptr
+ opts MapOpts
+}
+
+type checkVisitor struct {
+ expected []mapping // Input.
+ current int // Temporary.
+ found []mapping // Output.
+ failed string // Output.
+}
+
+func (v *checkVisitor) visit(start uintptr, pte *PTE, align uintptr) bool {
+ v.found = append(v.found, mapping{
+ start: start,
+ length: align + 1,
+ addr: pte.Address(),
+ opts: pte.Opts(),
+ })
+ if v.failed != "" {
+ // Don't keep looking for errors.
+ return false
+ }
+
+ if v.current >= len(v.expected) {
+ v.failed = "more mappings than expected"
+ } else if v.expected[v.current].start != start {
+ v.failed = "start didn't match expected"
+ } else if v.expected[v.current].length != (align + 1) {
+ v.failed = "end didn't match expected"
+ } else if v.expected[v.current].addr != pte.Address() {
+ v.failed = "address didn't match expected"
+ } else if v.expected[v.current].opts != pte.Opts() {
+ v.failed = "opts didn't match"
+ }
+ v.current++
+ return true
+}
+
+func (*checkVisitor) requiresAlloc() bool { return false }
+
+func (*checkVisitor) requiresSplit() bool { return false }
+
+func checkMappings(t *testing.T, pt *PageTables, m []mapping) {
+ // Iterate over all the mappings.
+ w := checkWalker{
+ pageTables: pt,
+ visitor: checkVisitor{
+ expected: m,
+ },
+ }
+ w.iterateRange(0, ^uintptr(0))
+
+ // Were we expected additional mappings?
+ if w.visitor.failed == "" && w.visitor.current != len(w.visitor.expected) {
+ w.visitor.failed = "insufficient mappings found"
+ }
+
+ // Emit a meaningful error message on failure.
+ if w.visitor.failed != "" {
+ t.Errorf("%s; got %#v, wanted %#v", w.visitor.failed, w.visitor.found, w.visitor.expected)
+ }
+}
+
+func TestUnmap(t *testing.T) {
+ pt := New(NewRuntimeAllocator())
+
+ // Map and unmap one entry.
+ pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.ReadWrite}, pteSize*42)
+ pt.Unmap(0x400000, pteSize)
+
+ checkMappings(t, pt, nil)
+}
+
+func TestReadOnly(t *testing.T) {
+ pt := New(NewRuntimeAllocator())
+
+ // Map one entry.
+ pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.Read}, pteSize*42)
+
+ checkMappings(t, pt, []mapping{
+ {0x400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.Read}},
+ })
+}
+
+func TestReadWrite(t *testing.T) {
+ pt := New(NewRuntimeAllocator())
+
+ // Map one entry.
+ pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.ReadWrite}, pteSize*42)
+
+ checkMappings(t, pt, []mapping{
+ {0x400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.ReadWrite}},
+ })
+}
+
+func TestSerialEntries(t *testing.T) {
+ pt := New(NewRuntimeAllocator())
+
+ // Map two sequential entries.
+ pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.ReadWrite}, pteSize*42)
+ pt.Map(0x401000, pteSize, MapOpts{AccessType: usermem.ReadWrite}, pteSize*47)
+
+ checkMappings(t, pt, []mapping{
+ {0x400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.ReadWrite}},
+ {0x401000, pteSize, pteSize * 47, MapOpts{AccessType: usermem.ReadWrite}},
+ })
+}
+
+func TestSpanningEntries(t *testing.T) {
+ pt := New(NewRuntimeAllocator())
+
+ // Span a pgd with two pages.
+ pt.Map(0x00007efffffff000, 2*pteSize, MapOpts{AccessType: usermem.Read}, pteSize*42)
+
+ checkMappings(t, pt, []mapping{
+ {0x00007efffffff000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.Read}},
+ {0x00007f0000000000, pteSize, pteSize * 43, MapOpts{AccessType: usermem.Read}},
+ })
+}
+
+func TestSparseEntries(t *testing.T) {
+ pt := New(NewRuntimeAllocator())
+
+ // Map two entries in different pgds.
+ pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.ReadWrite}, pteSize*42)
+ pt.Map(0x00007f0000000000, pteSize, MapOpts{AccessType: usermem.Read}, pteSize*47)
+
+ checkMappings(t, pt, []mapping{
+ {0x400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.ReadWrite}},
+ {0x00007f0000000000, pteSize, pteSize * 47, MapOpts{AccessType: usermem.Read}},
+ })
+}