mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
enable ring0/pagetables to support arm64
Signed-off-by: Bin Lu <bin.lu@arm.com> COPYBARA_INTEGRATE_REVIEW=https://github.com/google/gvisor/pull/891 from lubinszARM:pr_pagetable 2385de75a8662af3ab1ae289dd74dd0e5dcfaf66 PiperOrigin-RevId: 282013224
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
load("//tools/go_stateify:defs.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
load("//tools/go_stateify:defs.bzl", "go_library")
|
||||
load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
config_setting(
|
||||
name = "aarch64",
|
||||
constraint_values = ["@bazel_tools//platforms:aarch64"],
|
||||
)
|
||||
|
||||
go_template(
|
||||
name = "generic_walker",
|
||||
srcs = [
|
||||
"walker_amd64.go",
|
||||
],
|
||||
srcs = ["walker_amd64.go"],
|
||||
opt_types = [
|
||||
"Visitor",
|
||||
],
|
||||
@@ -76,9 +79,13 @@ go_library(
|
||||
"allocator.go",
|
||||
"allocator_unsafe.go",
|
||||
"pagetables.go",
|
||||
"pagetables_aarch64.go",
|
||||
"pagetables_amd64.go",
|
||||
"pagetables_arm64.go",
|
||||
"pagetables_x86.go",
|
||||
"pcids_x86.go",
|
||||
"walker_amd64.go",
|
||||
"walker_arm64.go",
|
||||
"walker_empty.go",
|
||||
"walker_lookup.go",
|
||||
"walker_map.go",
|
||||
@@ -97,6 +104,7 @@ go_test(
|
||||
size = "small",
|
||||
srcs = [
|
||||
"pagetables_amd64_test.go",
|
||||
"pagetables_arm64_test.go",
|
||||
"pagetables_test.go",
|
||||
"walker_check.go",
|
||||
],
|
||||
|
||||
@@ -48,15 +48,6 @@ func New(a Allocator) *PageTables {
|
||||
return p
|
||||
}
|
||||
|
||||
// Init initializes a set of PageTables.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PageTables) Init(allocator Allocator) {
|
||||
p.Allocator = allocator
|
||||
p.root = p.Allocator.NewPTEs()
|
||||
p.rootPhysical = p.Allocator.PhysicalFor(p.root)
|
||||
}
|
||||
|
||||
// mapVisitor is used for map.
|
||||
type mapVisitor struct {
|
||||
target uintptr // Input.
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// 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 pagetables
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
)
|
||||
|
||||
// archPageTables is architecture-specific data.
|
||||
type archPageTables struct {
|
||||
// root is the pagetable root for kernel space.
|
||||
root *PTEs
|
||||
|
||||
// rootPhysical is the cached physical address of the root.
|
||||
//
|
||||
// This is saved only to prevent constant translation.
|
||||
rootPhysical uintptr
|
||||
|
||||
asid uint16
|
||||
}
|
||||
|
||||
// TTBR0_EL1 returns the translation table base register 0.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PageTables) TTBR0_EL1(noFlush bool, asid uint16) uint64 {
|
||||
return uint64(p.rootPhysical) | (uint64(asid)&ttbrASIDMask)<<ttbrASIDOffset
|
||||
}
|
||||
|
||||
// TTBR1_EL1 returns the translation table base register 1.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PageTables) TTBR1_EL1(noFlush bool, asid uint16) uint64 {
|
||||
return uint64(p.archPageTables.rootPhysical) | (uint64(asid)&ttbrASIDMask)<<ttbrASIDOffset
|
||||
}
|
||||
|
||||
// Bits in page table entries.
|
||||
const (
|
||||
typeTable = 0x3 << 0
|
||||
typeSect = 0x1 << 0
|
||||
typePage = 0x3 << 0
|
||||
pteValid = 0x1 << 0
|
||||
pteTableBit = 0x1 << 1
|
||||
pteTypeMask = 0x3 << 0
|
||||
present = pteValid | pteTableBit
|
||||
user = 0x1 << 6 /* AP[1] */
|
||||
readOnly = 0x1 << 7 /* AP[2] */
|
||||
accessed = 0x1 << 10
|
||||
dbm = 0x1 << 51
|
||||
writable = dbm
|
||||
cont = 0x1 << 52
|
||||
pxn = 0x1 << 53
|
||||
xn = 0x1 << 54
|
||||
dirty = 0x1 << 55
|
||||
nG = 0x1 << 11
|
||||
shared = 0x3 << 8
|
||||
)
|
||||
|
||||
const (
|
||||
mtNormal = 0x4 << 2
|
||||
)
|
||||
|
||||
const (
|
||||
executeDisable = xn
|
||||
optionMask = 0xfff | 0xfff<<48
|
||||
protDefault = accessed | shared | mtNormal
|
||||
)
|
||||
|
||||
// MapOpts are x86 options.
|
||||
type MapOpts struct {
|
||||
// AccessType defines permissions.
|
||||
AccessType usermem.AccessType
|
||||
|
||||
// Global indicates the page is globally accessible.
|
||||
Global bool
|
||||
|
||||
// User indicates the page is a user page.
|
||||
User bool
|
||||
}
|
||||
|
||||
// PTE is a page table entry.
|
||||
type PTE uintptr
|
||||
|
||||
// Clear clears this PTE, including sect page information.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) Clear() {
|
||||
atomic.StoreUintptr((*uintptr)(p), 0)
|
||||
}
|
||||
|
||||
// Valid returns true iff this entry is valid.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) Valid() bool {
|
||||
return atomic.LoadUintptr((*uintptr)(p))&present != 0
|
||||
}
|
||||
|
||||
// Opts returns the PTE options.
|
||||
//
|
||||
// These are all options except Valid and Sect.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) Opts() MapOpts {
|
||||
v := atomic.LoadUintptr((*uintptr)(p))
|
||||
|
||||
return MapOpts{
|
||||
AccessType: usermem.AccessType{
|
||||
Read: true,
|
||||
Write: v&readOnly == 0,
|
||||
Execute: v&xn == 0,
|
||||
},
|
||||
Global: v&nG == 0,
|
||||
User: v&user != 0,
|
||||
}
|
||||
}
|
||||
|
||||
// SetSect sets this page as a sect page.
|
||||
//
|
||||
// The page must not be valid or a panic will result.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) SetSect() {
|
||||
if p.Valid() {
|
||||
// This is not allowed.
|
||||
panic("SetSect called on valid page!")
|
||||
}
|
||||
atomic.StoreUintptr((*uintptr)(p), typeSect)
|
||||
}
|
||||
|
||||
// IsSect returns true iff this page is a sect page.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) IsSect() bool {
|
||||
return atomic.LoadUintptr((*uintptr)(p))&pteTypeMask == typeSect
|
||||
}
|
||||
|
||||
// Set sets this PTE value.
|
||||
//
|
||||
// This does not change the sect page property.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) Set(addr uintptr, opts MapOpts) {
|
||||
if !opts.AccessType.Any() {
|
||||
p.Clear()
|
||||
return
|
||||
}
|
||||
v := (addr &^ optionMask) | protDefault | nG | readOnly
|
||||
|
||||
if p.IsSect() {
|
||||
// Note that this is inherited from the previous instance. Set
|
||||
// does not change the value of Sect. See above.
|
||||
v |= typeSect
|
||||
} else {
|
||||
v |= typePage
|
||||
}
|
||||
|
||||
if opts.Global {
|
||||
v = v &^ nG
|
||||
}
|
||||
|
||||
if opts.AccessType.Execute {
|
||||
v = v &^ executeDisable
|
||||
} else {
|
||||
v |= executeDisable
|
||||
}
|
||||
if opts.AccessType.Write {
|
||||
v = v &^ readOnly
|
||||
}
|
||||
|
||||
if opts.User {
|
||||
v |= user
|
||||
} else {
|
||||
v = v &^ user
|
||||
}
|
||||
atomic.StoreUintptr((*uintptr)(p), v)
|
||||
}
|
||||
|
||||
// setPageTable sets this PTE value and forces the write bit and sect bit to
|
||||
// be cleared. This is used explicitly for breaking sect pages.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) setPageTable(pt *PageTables, ptes *PTEs) {
|
||||
addr := pt.Allocator.PhysicalFor(ptes)
|
||||
if addr&^optionMask != addr {
|
||||
// This should never happen.
|
||||
panic("unaligned physical address!")
|
||||
}
|
||||
v := addr | typeTable | protDefault
|
||||
atomic.StoreUintptr((*uintptr)(p), v)
|
||||
}
|
||||
|
||||
// Address extracts the address. This should only be used if Valid returns true.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PTE) Address() uintptr {
|
||||
return atomic.LoadUintptr((*uintptr)(p)) &^ optionMask
|
||||
}
|
||||
@@ -41,5 +41,14 @@ const (
|
||||
entriesPerPage = 512
|
||||
)
|
||||
|
||||
// Init initializes a set of PageTables.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PageTables) Init(allocator Allocator) {
|
||||
p.Allocator = allocator
|
||||
p.root = p.Allocator.NewPTEs()
|
||||
p.rootPhysical = p.Allocator.PhysicalFor(p.root)
|
||||
}
|
||||
|
||||
// PTEs is a collection of entries.
|
||||
type PTEs [entriesPerPage]PTE
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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.
|
||||
|
||||
package pagetables
|
||||
|
||||
// Address constraints.
|
||||
//
|
||||
// The lowerTop and upperBottom currently apply to four-level pagetables;
|
||||
// additional refactoring would be necessary to support five-level pagetables.
|
||||
const (
|
||||
lowerTop = 0x0000ffffffffffff
|
||||
upperBottom = 0xffff000000000000
|
||||
pteShift = 12
|
||||
pmdShift = 21
|
||||
pudShift = 30
|
||||
pgdShift = 39
|
||||
|
||||
pteMask = 0x1ff << pteShift
|
||||
pmdMask = 0x1ff << pmdShift
|
||||
pudMask = 0x1ff << pudShift
|
||||
pgdMask = 0x1ff << pgdShift
|
||||
|
||||
pteSize = 1 << pteShift
|
||||
pmdSize = 1 << pmdShift
|
||||
pudSize = 1 << pudShift
|
||||
pgdSize = 1 << pgdShift
|
||||
|
||||
ttbrASIDOffset = 55
|
||||
ttbrASIDMask = 0xff
|
||||
|
||||
entriesPerPage = 512
|
||||
)
|
||||
|
||||
// Init initializes a set of PageTables.
|
||||
//
|
||||
//go:nosplit
|
||||
func (p *PageTables) Init(allocator Allocator) {
|
||||
p.Allocator = allocator
|
||||
p.root = p.Allocator.NewPTEs()
|
||||
p.rootPhysical = p.Allocator.PhysicalFor(p.root)
|
||||
p.archPageTables.root = p.Allocator.NewPTEs()
|
||||
p.archPageTables.rootPhysical = p.Allocator.PhysicalFor(p.archPageTables.root)
|
||||
}
|
||||
|
||||
// PTEs is a collection of entries.
|
||||
type PTEs [entriesPerPage]PTE
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 pagetables
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
)
|
||||
|
||||
func Test2MAnd4K(t *testing.T) {
|
||||
pt := New(NewRuntimeAllocator())
|
||||
|
||||
// Map a small page and a huge page.
|
||||
pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.ReadWrite, User: true}, pteSize*42)
|
||||
pt.Map(0x0000ff0000000000, pmdSize, MapOpts{AccessType: usermem.Read, User: true}, pmdSize*47)
|
||||
|
||||
pt.Map(0xffff000000400000, pteSize, MapOpts{AccessType: usermem.ReadWrite, User: false}, pteSize*42)
|
||||
pt.Map(0xffffff0000000000, pmdSize, MapOpts{AccessType: usermem.Read, User: false}, pmdSize*47)
|
||||
|
||||
checkMappings(t, pt, []mapping{
|
||||
{0x400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.ReadWrite, User: true}},
|
||||
{0x0000ff0000000000, pmdSize, pmdSize * 47, MapOpts{AccessType: usermem.Read, User: true}},
|
||||
{0xffff000000400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.ReadWrite, User: false}},
|
||||
{0xffffff0000000000, pmdSize, pmdSize * 47, MapOpts{AccessType: usermem.Read, User: false}},
|
||||
})
|
||||
}
|
||||
|
||||
func Test1GAnd4K(t *testing.T) {
|
||||
pt := New(NewRuntimeAllocator())
|
||||
|
||||
// Map a small page and a super page.
|
||||
pt.Map(0x400000, pteSize, MapOpts{AccessType: usermem.ReadWrite, User: true}, pteSize*42)
|
||||
pt.Map(0x0000ff0000000000, pudSize, MapOpts{AccessType: usermem.Read, User: true}, pudSize*47)
|
||||
|
||||
checkMappings(t, pt, []mapping{
|
||||
{0x400000, pteSize, pteSize * 42, MapOpts{AccessType: usermem.ReadWrite, User: true}},
|
||||
{0x0000ff0000000000, pudSize, pudSize * 47, MapOpts{AccessType: usermem.Read, User: true}},
|
||||
})
|
||||
}
|
||||
|
||||
func TestSplit1GPage(t *testing.T) {
|
||||
pt := New(NewRuntimeAllocator())
|
||||
|
||||
// Map a super page and knock out the middle.
|
||||
pt.Map(0x0000ff0000000000, pudSize, MapOpts{AccessType: usermem.Read, User: true}, pudSize*42)
|
||||
pt.Unmap(usermem.Addr(0x0000ff0000000000+pteSize), pudSize-(2*pteSize))
|
||||
|
||||
checkMappings(t, pt, []mapping{
|
||||
{0x0000ff0000000000, pteSize, pudSize * 42, MapOpts{AccessType: usermem.Read, User: true}},
|
||||
{0x0000ff0000000000 + pudSize - pteSize, pteSize, pudSize*42 + pudSize - pteSize, MapOpts{AccessType: usermem.Read, User: true}},
|
||||
})
|
||||
}
|
||||
|
||||
func TestSplit2MPage(t *testing.T) {
|
||||
pt := New(NewRuntimeAllocator())
|
||||
|
||||
// Map a huge page and knock out the middle.
|
||||
pt.Map(0x0000ff0000000000, pmdSize, MapOpts{AccessType: usermem.Read, User: true}, pmdSize*42)
|
||||
pt.Unmap(usermem.Addr(0x0000ff0000000000+pteSize), pmdSize-(2*pteSize))
|
||||
|
||||
checkMappings(t, pt, []mapping{
|
||||
{0x0000ff0000000000, pteSize, pmdSize * 42, MapOpts{AccessType: usermem.Read, User: true}},
|
||||
{0x0000ff0000000000 + pmdSize - pteSize, pteSize, pmdSize*42 + pmdSize - pteSize, MapOpts{AccessType: usermem.Read, User: true}},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
// 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 pagetables
|
||||
|
||||
// Visitor is a generic type.
|
||||
type Visitor interface {
|
||||
// visit is called on each PTE.
|
||||
visit(start uintptr, pte *PTE, align uintptr)
|
||||
|
||||
// requiresAlloc indicates that new entries should be allocated within
|
||||
// the walked range.
|
||||
requiresAlloc() bool
|
||||
|
||||
// requiresSplit indicates that entries in the given range should be
|
||||
// split if they are huge or jumbo pages.
|
||||
requiresSplit() bool
|
||||
}
|
||||
|
||||
// Walker walks page tables.
|
||||
type Walker struct {
|
||||
// pageTables are the tables to walk.
|
||||
pageTables *PageTables
|
||||
|
||||
// Visitor is the set of arguments.
|
||||
visitor Visitor
|
||||
}
|
||||
|
||||
// iterateRange iterates over all appropriate levels of page tables for the given range.
|
||||
//
|
||||
// If requiresAlloc is true, then Set _must_ be called on all given PTEs. The
|
||||
// exception is sect pages. If a valid sect page (huge or jumbo) cannot be
|
||||
// installed, then the walk will continue to individual entries.
|
||||
//
|
||||
// This algorithm will attempt to maximize the use of sect pages whenever
|
||||
// possible. Whether a sect page is provided will be clear through the range
|
||||
// provided in the callback.
|
||||
//
|
||||
// Note that if requiresAlloc is true, then no gaps will be present. However,
|
||||
// if alloc is not set, then the iteration will likely be full of gaps.
|
||||
//
|
||||
// Note that this function should generally be avoided in favor of Map, Unmap,
|
||||
// etc. when not necessary.
|
||||
//
|
||||
// Precondition: start must be page-aligned.
|
||||
//
|
||||
// Precondition: start must be less than end.
|
||||
//
|
||||
// Precondition: If requiresAlloc is true, then start and end should not span
|
||||
// non-canonical ranges. If they do, a panic will result.
|
||||
//
|
||||
//go:nosplit
|
||||
func (w *Walker) iterateRange(start, end uintptr) {
|
||||
if start%pteSize != 0 {
|
||||
panic("unaligned start")
|
||||
}
|
||||
if end < start {
|
||||
panic("start > end")
|
||||
}
|
||||
if start < lowerTop {
|
||||
if end <= lowerTop {
|
||||
w.iterateRangeCanonical(start, end)
|
||||
} else if end > lowerTop && end <= upperBottom {
|
||||
if w.visitor.requiresAlloc() {
|
||||
panic("alloc spans non-canonical range")
|
||||
}
|
||||
w.iterateRangeCanonical(start, lowerTop)
|
||||
} else {
|
||||
if w.visitor.requiresAlloc() {
|
||||
panic("alloc spans non-canonical range")
|
||||
}
|
||||
w.iterateRangeCanonical(start, lowerTop)
|
||||
w.iterateRangeCanonical(upperBottom, end)
|
||||
}
|
||||
} else if start < upperBottom {
|
||||
if end <= upperBottom {
|
||||
if w.visitor.requiresAlloc() {
|
||||
panic("alloc spans non-canonical range")
|
||||
}
|
||||
} else {
|
||||
if w.visitor.requiresAlloc() {
|
||||
panic("alloc spans non-canonical range")
|
||||
}
|
||||
w.iterateRangeCanonical(upperBottom, end)
|
||||
}
|
||||
} else {
|
||||
w.iterateRangeCanonical(start, end)
|
||||
}
|
||||
}
|
||||
|
||||
// next returns the next address quantized by the given size.
|
||||
//
|
||||
//go:nosplit
|
||||
func next(start uintptr, size uintptr) uintptr {
|
||||
start &= ^(size - 1)
|
||||
start += size
|
||||
return start
|
||||
}
|
||||
|
||||
// iterateRangeCanonical walks a canonical range.
|
||||
//
|
||||
//go:nosplit
|
||||
func (w *Walker) iterateRangeCanonical(start, end uintptr) {
|
||||
pgdEntryIndex := w.pageTables.root
|
||||
if start >= upperBottom {
|
||||
pgdEntryIndex = w.pageTables.archPageTables.root
|
||||
}
|
||||
|
||||
for pgdIndex := (uint16((start & pgdMask) >> pgdShift)); start < end && pgdIndex < entriesPerPage; pgdIndex++ {
|
||||
var (
|
||||
pgdEntry = &pgdEntryIndex[pgdIndex]
|
||||
pudEntries *PTEs
|
||||
)
|
||||
if !pgdEntry.Valid() {
|
||||
if !w.visitor.requiresAlloc() {
|
||||
// Skip over this entry.
|
||||
start = next(start, pgdSize)
|
||||
continue
|
||||
}
|
||||
|
||||
// Allocate a new pgd.
|
||||
pudEntries = w.pageTables.Allocator.NewPTEs()
|
||||
pgdEntry.setPageTable(w.pageTables, pudEntries)
|
||||
} else {
|
||||
pudEntries = w.pageTables.Allocator.LookupPTEs(pgdEntry.Address())
|
||||
}
|
||||
|
||||
// Map the next level.
|
||||
clearPUDEntries := uint16(0)
|
||||
|
||||
for pudIndex := uint16((start & pudMask) >> pudShift); start < end && pudIndex < entriesPerPage; pudIndex++ {
|
||||
var (
|
||||
pudEntry = &pudEntries[pudIndex]
|
||||
pmdEntries *PTEs
|
||||
)
|
||||
if !pudEntry.Valid() {
|
||||
if !w.visitor.requiresAlloc() {
|
||||
// Skip over this entry.
|
||||
clearPUDEntries++
|
||||
start = next(start, pudSize)
|
||||
continue
|
||||
}
|
||||
|
||||
// This level has 1-GB sect pages. Is this
|
||||
// entire region at least as large as a single
|
||||
// PUD entry? If so, we can skip allocating a
|
||||
// new page for the pmd.
|
||||
if start&(pudSize-1) == 0 && end-start >= pudSize {
|
||||
pudEntry.SetSect()
|
||||
w.visitor.visit(uintptr(start), pudEntry, pudSize-1)
|
||||
if pudEntry.Valid() {
|
||||
start = next(start, pudSize)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new pud.
|
||||
pmdEntries = w.pageTables.Allocator.NewPTEs()
|
||||
pudEntry.setPageTable(w.pageTables, pmdEntries)
|
||||
|
||||
} else if pudEntry.IsSect() {
|
||||
// Does this page need to be split?
|
||||
if w.visitor.requiresSplit() && (start&(pudSize-1) != 0 || end < next(start, pudSize)) {
|
||||
// Install the relevant entries.
|
||||
pmdEntries = w.pageTables.Allocator.NewPTEs()
|
||||
for index := uint16(0); index < entriesPerPage; index++ {
|
||||
pmdEntries[index].SetSect()
|
||||
pmdEntries[index].Set(
|
||||
pudEntry.Address()+(pmdSize*uintptr(index)),
|
||||
pudEntry.Opts())
|
||||
}
|
||||
pudEntry.setPageTable(w.pageTables, pmdEntries)
|
||||
} else {
|
||||
// A sect page to be checked directly.
|
||||
w.visitor.visit(uintptr(start), pudEntry, pudSize-1)
|
||||
|
||||
// Might have been cleared.
|
||||
if !pudEntry.Valid() {
|
||||
clearPUDEntries++
|
||||
}
|
||||
|
||||
// Note that the sect page was changed.
|
||||
start = next(start, pudSize)
|
||||
continue
|
||||
}
|
||||
|
||||
} else {
|
||||
pmdEntries = w.pageTables.Allocator.LookupPTEs(pudEntry.Address())
|
||||
}
|
||||
|
||||
// Map the next level, since this is valid.
|
||||
clearPMDEntries := uint16(0)
|
||||
|
||||
for pmdIndex := uint16((start & pmdMask) >> pmdShift); start < end && pmdIndex < entriesPerPage; pmdIndex++ {
|
||||
var (
|
||||
pmdEntry = &pmdEntries[pmdIndex]
|
||||
pteEntries *PTEs
|
||||
)
|
||||
if !pmdEntry.Valid() {
|
||||
if !w.visitor.requiresAlloc() {
|
||||
// Skip over this entry.
|
||||
clearPMDEntries++
|
||||
start = next(start, pmdSize)
|
||||
continue
|
||||
}
|
||||
|
||||
// This level has 2-MB huge pages. If this
|
||||
// region is contined in a single PMD entry?
|
||||
// As above, we can skip allocating a new page.
|
||||
if start&(pmdSize-1) == 0 && end-start >= pmdSize {
|
||||
pmdEntry.SetSect()
|
||||
w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1)
|
||||
if pmdEntry.Valid() {
|
||||
start = next(start, pmdSize)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new pmd.
|
||||
pteEntries = w.pageTables.Allocator.NewPTEs()
|
||||
pmdEntry.setPageTable(w.pageTables, pteEntries)
|
||||
|
||||
} else if pmdEntry.IsSect() {
|
||||
// Does this page need to be split?
|
||||
if w.visitor.requiresSplit() && (start&(pmdSize-1) != 0 || end < next(start, pmdSize)) {
|
||||
// Install the relevant entries.
|
||||
pteEntries = w.pageTables.Allocator.NewPTEs()
|
||||
for index := uint16(0); index < entriesPerPage; index++ {
|
||||
pteEntries[index].Set(
|
||||
pmdEntry.Address()+(pteSize*uintptr(index)),
|
||||
pmdEntry.Opts())
|
||||
}
|
||||
pmdEntry.setPageTable(w.pageTables, pteEntries)
|
||||
} else {
|
||||
// A huge page to be checked directly.
|
||||
w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1)
|
||||
|
||||
// Might have been cleared.
|
||||
if !pmdEntry.Valid() {
|
||||
clearPMDEntries++
|
||||
}
|
||||
|
||||
// Note that the huge page was changed.
|
||||
start = next(start, pmdSize)
|
||||
continue
|
||||
}
|
||||
|
||||
} else {
|
||||
pteEntries = w.pageTables.Allocator.LookupPTEs(pmdEntry.Address())
|
||||
}
|
||||
|
||||
// Map the next level, since this is valid.
|
||||
clearPTEEntries := uint16(0)
|
||||
|
||||
for pteIndex := uint16((start & pteMask) >> pteShift); start < end && pteIndex < entriesPerPage; pteIndex++ {
|
||||
var (
|
||||
pteEntry = &pteEntries[pteIndex]
|
||||
)
|
||||
if !pteEntry.Valid() && !w.visitor.requiresAlloc() {
|
||||
clearPTEEntries++
|
||||
start += pteSize
|
||||
continue
|
||||
}
|
||||
|
||||
// At this point, we are guaranteed that start%pteSize == 0.
|
||||
w.visitor.visit(uintptr(start), pteEntry, pteSize-1)
|
||||
if !pteEntry.Valid() {
|
||||
if w.visitor.requiresAlloc() {
|
||||
panic("PTE not set after iteration with requiresAlloc!")
|
||||
}
|
||||
clearPTEEntries++
|
||||
}
|
||||
|
||||
// Note that the pte was changed.
|
||||
start += pteSize
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we no longer need this page.
|
||||
if clearPTEEntries == entriesPerPage {
|
||||
pmdEntry.Clear()
|
||||
w.pageTables.Allocator.FreePTEs(pteEntries)
|
||||
clearPMDEntries++
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we no longer need this page.
|
||||
if clearPMDEntries == entriesPerPage {
|
||||
pudEntry.Clear()
|
||||
w.pageTables.Allocator.FreePTEs(pmdEntries)
|
||||
clearPUDEntries++
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we no longer need this page.
|
||||
if clearPUDEntries == entriesPerPage {
|
||||
pgdEntry.Clear()
|
||||
w.pageTables.Allocator.FreePTEs(pudEntries)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user