From 1b5062263b4a3ca3dc0271d9e06ad0113197344c Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Wed, 6 Jun 2018 21:47:39 -0700 Subject: Add allocator abstraction for page tables. In order to prevent possible garbage collection and reuse of page table pages prior to invalidation, introduce a former allocator abstraction that can ensure entries are held during a single traversal. This also cleans up the abstraction and splits it out of the machine itself. PiperOrigin-RevId: 199581636 Change-Id: I2257d5d7ffd9c36f9b7ecd42f769261baeaf115c --- pkg/sentry/platform/kvm/BUILD | 1 + pkg/sentry/platform/kvm/address_space.go | 5 ++- pkg/sentry/platform/kvm/allocator.go | 69 +++++++++++++++++++++++++++++++ pkg/sentry/platform/kvm/bluepill_fault.go | 4 +- pkg/sentry/platform/kvm/kvm.go | 2 +- pkg/sentry/platform/kvm/machine.go | 6 +-- pkg/sentry/platform/kvm/machine_unsafe.go | 12 ------ pkg/sentry/platform/kvm/physical_map.go | 14 ++++--- 8 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 pkg/sentry/platform/kvm/allocator.go (limited to 'pkg/sentry/platform/kvm') diff --git a/pkg/sentry/platform/kvm/BUILD b/pkg/sentry/platform/kvm/BUILD index 004938080..89d98c5c7 100644 --- a/pkg/sentry/platform/kvm/BUILD +++ b/pkg/sentry/platform/kvm/BUILD @@ -28,6 +28,7 @@ go_library( srcs = [ "address_space.go", "address_space_unsafe.go", + "allocator.go", "bluepill.go", "bluepill_amd64.go", "bluepill_amd64.s", diff --git a/pkg/sentry/platform/kvm/address_space.go b/pkg/sentry/platform/kvm/address_space.go index 4c76883ad..15d45f5bc 100644 --- a/pkg/sentry/platform/kvm/address_space.go +++ b/pkg/sentry/platform/kvm/address_space.go @@ -84,7 +84,7 @@ func (as *addressSpace) Touch(c *vCPU) bool { func (as *addressSpace) mapHost(addr usermem.Addr, m hostMapEntry, at usermem.AccessType) (inv bool) { for m.length > 0 { - physical, length, ok := TranslateToPhysical(m.addr) + physical, length, ok := translateToPhysical(m.addr) if !ok { panic("unable to translate segment") } @@ -227,4 +227,7 @@ func (as *addressSpace) Unmap(addr usermem.Addr, length uint64) { func (as *addressSpace) Release() { as.Unmap(0, ^uint64(0)) as.pageTables.Release() + + // Free all pages from the allocator. + as.pageTables.Allocator.(allocator).base.Drain() } diff --git a/pkg/sentry/platform/kvm/allocator.go b/pkg/sentry/platform/kvm/allocator.go new file mode 100644 index 000000000..80066bfc5 --- /dev/null +++ b/pkg/sentry/platform/kvm/allocator.go @@ -0,0 +1,69 @@ +// Copyright 2018 Google Inc. +// +// 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 kvm + +import ( + "fmt" + + "gvisor.googlesource.com/gvisor/pkg/sentry/platform/ring0/pagetables" +) + +type allocator struct { + base *pagetables.RuntimeAllocator +} + +// newAllocator is used to define the allocator. +func newAllocator() allocator { + return allocator{ + base: pagetables.NewRuntimeAllocator(), + } +} + +// NewPTEs implements pagetables.Allocator.NewPTEs. +// +//go:nosplit +func (a allocator) NewPTEs() *pagetables.PTEs { + return a.base.NewPTEs() +} + +// PhysicalFor returns the physical address for a set of PTEs. +// +//go:nosplit +func (a allocator) PhysicalFor(ptes *pagetables.PTEs) uintptr { + virtual := a.base.PhysicalFor(ptes) + physical, _, ok := translateToPhysical(virtual) + if !ok { + panic(fmt.Sprintf("PhysicalFor failed for %p", ptes)) + } + return physical +} + +// LookupPTEs implements pagetables.Allocator.LookupPTEs. +// +//go:nosplit +func (a allocator) LookupPTEs(physical uintptr) *pagetables.PTEs { + virtualStart, physicalStart, _, ok := calculateBluepillFault(physical) + if !ok { + panic(fmt.Sprintf("LookupPTEs failed for 0x%x", physical)) + } + return a.base.LookupPTEs(virtualStart + (physical - physicalStart)) +} + +// FreePTEs implements pagetables.Allocator.FreePTEs. +// +//go:nosplit +func (a allocator) FreePTEs(ptes *pagetables.PTEs) { + a.base.FreePTEs(ptes) +} diff --git a/pkg/sentry/platform/kvm/bluepill_fault.go b/pkg/sentry/platform/kvm/bluepill_fault.go index 7c8c7bc37..8650cd78f 100644 --- a/pkg/sentry/platform/kvm/bluepill_fault.go +++ b/pkg/sentry/platform/kvm/bluepill_fault.go @@ -46,7 +46,7 @@ func yield() { // calculateBluepillFault calculates the fault address range. // //go:nosplit -func calculateBluepillFault(m *machine, physical uintptr) (virtualStart, physicalStart, length uintptr, ok bool) { +func calculateBluepillFault(physical uintptr) (virtualStart, physicalStart, length uintptr, ok bool) { alignedPhysical := physical &^ uintptr(usermem.PageSize-1) for _, pr := range physicalRegions { end := pr.physical + pr.length @@ -82,7 +82,7 @@ func handleBluepillFault(m *machine, physical uintptr) (uintptr, bool) { // fault. This all has to be done in this function because we're in a // signal handler context. (We can't call any functions that might // split the stack.) - virtualStart, physicalStart, length, ok := calculateBluepillFault(m, physical) + virtualStart, physicalStart, length, ok := calculateBluepillFault(physical) if !ok { return 0, false } diff --git a/pkg/sentry/platform/kvm/kvm.go b/pkg/sentry/platform/kvm/kvm.go index 6defb1c46..13c363993 100644 --- a/pkg/sentry/platform/kvm/kvm.go +++ b/pkg/sentry/platform/kvm/kvm.go @@ -121,7 +121,7 @@ func (*KVM) MaxUserAddress() usermem.Addr { // NewAddressSpace returns a new pagetable root. func (k *KVM) NewAddressSpace(_ interface{}) (platform.AddressSpace, <-chan struct{}, error) { // Allocate page tables and install system mappings. - pageTables := k.machine.kernel.PageTables.New() + pageTables := k.machine.kernel.PageTables.New(newAllocator()) applyPhysicalRegions(func(pr physicalRegion) bool { // Map the kernel in the upper half. pageTables.Map( diff --git a/pkg/sentry/platform/kvm/machine.go b/pkg/sentry/platform/kvm/machine.go index 5a6109ced..949abd838 100644 --- a/pkg/sentry/platform/kvm/machine.go +++ b/pkg/sentry/platform/kvm/machine.go @@ -133,7 +133,7 @@ func newMachine(vm int, vCPUs int) (*machine, error) { vCPUs = n } m.kernel = ring0.New(ring0.KernelOpts{ - PageTables: pagetables.New(m, pagetablesOpts), + PageTables: pagetables.New(newAllocator(), pagetablesOpts), }) // Initialize architecture state. @@ -211,7 +211,7 @@ func newMachine(vm int, vCPUs int) (*machine, error) { return // skip region. } for virtual := vr.virtual; virtual < vr.virtual+vr.length; { - physical, length, ok := TranslateToPhysical(virtual) + physical, length, ok := translateToPhysical(virtual) if !ok { // This must be an invalid region that was // knocked out by creation of the physical map. @@ -239,7 +239,7 @@ func newMachine(vm int, vCPUs int) (*machine, error) { // This panics on error. func (m *machine) mapPhysical(physical, length uintptr) { for end := physical + length; physical < end; { - _, physicalStart, length, ok := calculateBluepillFault(m, physical) + _, physicalStart, length, ok := calculateBluepillFault(physical) if !ok { // Should never happen. panic("mapPhysical on unknown physical address") diff --git a/pkg/sentry/platform/kvm/machine_unsafe.go b/pkg/sentry/platform/kvm/machine_unsafe.go index 516098a2b..86323c891 100644 --- a/pkg/sentry/platform/kvm/machine_unsafe.go +++ b/pkg/sentry/platform/kvm/machine_unsafe.go @@ -21,7 +21,6 @@ import ( "unsafe" "gvisor.googlesource.com/gvisor/pkg/abi/linux" - "gvisor.googlesource.com/gvisor/pkg/sentry/platform/ring0/pagetables" ) //go:linkname entersyscall runtime.entersyscall @@ -30,17 +29,6 @@ func entersyscall() //go:linkname exitsyscall runtime.exitsyscall func exitsyscall() -// TranslateToVirtual implements pagetables.Translater.TranslateToPhysical. -func (m *machine) TranslateToPhysical(ptes *pagetables.PTEs) uintptr { - // The length doesn't matter because all these translations require - // only a single page, which is guaranteed to be satisfied. - physical, _, ok := TranslateToPhysical(uintptr(unsafe.Pointer(ptes))) - if !ok { - panic("unable to translate pagetables.Node to physical address") - } - return physical -} - // mapRunData maps the vCPU run data. func mapRunData(fd int) (*runData, error) { r, _, errno := syscall.RawSyscall6( diff --git a/pkg/sentry/platform/kvm/physical_map.go b/pkg/sentry/platform/kvm/physical_map.go index 5d55c9486..81a98656d 100644 --- a/pkg/sentry/platform/kvm/physical_map.go +++ b/pkg/sentry/platform/kvm/physical_map.go @@ -205,17 +205,19 @@ func applyPhysicalRegions(fn func(pr physicalRegion) bool) bool { return true } -// TranslateToPhysical translates the given virtual address. +// translateToPhysical translates the given virtual address. // // Precondition: physicalInit must have been called. -func TranslateToPhysical(virtual uintptr) (physical uintptr, length uintptr, ok bool) { - ok = !applyPhysicalRegions(func(pr physicalRegion) bool { +// +//go:nosplit +func translateToPhysical(virtual uintptr) (physical uintptr, length uintptr, ok bool) { + for _, pr := range physicalRegions { if pr.virtual <= virtual && virtual < pr.virtual+pr.length { physical = pr.physical + (virtual - pr.virtual) length = pr.length - (virtual - pr.virtual) - return false + ok = true + return } - return true - }) + } return } -- cgit v1.2.3