kvm: honor memmap.File.MemoryType()

Updates #11436

PiperOrigin-RevId: 737689743
This commit is contained in:
Jamie Liu
2025-03-17 11:34:50 -07:00
committed by gVisor bot
parent 8482715727
commit fbca0560dd
9 changed files with 116 additions and 54 deletions
+30 -29
View File
@@ -52,29 +52,26 @@ func (p *PageTables) TTBR1_EL1(noFlush bool, asid uint16) uint64 {
// 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 (
mtDevicenGnRE = 0x1 << 2
mtNormal = 0x4 << 2
typeTable = 0x3 << 0
typeSect = 0x1 << 0
typePage = 0x3 << 0
pteValid = 0x1 << 0
pteTableBit = 0x1 << 1
pteTypeMask = 0x3 << 0
present = pteValid | pteTableBit
attrIndxShift = 2
attrIndxMask = 0x7
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 (
@@ -93,6 +90,9 @@ type MapOpts struct {
// User indicates the page is a user page.
User bool
// MemoryType is the memory type.
MemoryType hostarch.MemoryType
}
// PTE is a page table entry.
@@ -119,15 +119,15 @@ func (p *PTE) Valid() bool {
//go:nosplit
func (p *PTE) Opts() MapOpts {
v := atomic.LoadUintptr((*uintptr)(p))
return MapOpts{
AccessType: hostarch.AccessType{
Read: true,
Write: v&readOnly == 0,
Execute: v&xn == 0,
},
Global: v&nG == 0,
User: v&user != 0,
Global: v&nG == 0,
User: v&user != 0,
MemoryType: hostarch.MemoryType((v >> attrIndxShift) & attrIndxMask),
}
}
@@ -191,11 +191,12 @@ func (p *PTE) Set(addr uintptr, opts MapOpts) {
if opts.User {
v |= user
v |= mtNormal
} else {
v = v &^ user
v |= mtNormal
}
v |= uintptr(opts.MemoryType&attrIndxMask) << attrIndxShift
atomic.StoreUintptr((*uintptr)(p), v)
}
@@ -209,7 +210,7 @@ func (p *PTE) setPageTable(pt *PageTables, ptes *PTEs) {
// This should never happen.
panic("unaligned physical address!")
}
v := addr | typeTable | protDefault | mtNormal
v := addr | typeTable | protDefault | (uintptr(hostarch.MemoryTypeWriteBack) << attrIndxShift)
atomic.StoreUintptr((*uintptr)(p), v)
}
@@ -74,3 +74,13 @@ func TestSplit2MPage(t *testing.T) {
{0x00007f0000000000 + pmdSize - pteSize, pteSize, pmdSize*42 + pmdSize - pteSize, MapOpts{AccessType: hostarch.Read}},
})
}
func TestNumMemoryTypes(t *testing.T) {
// The PAT accommodates up to 8 entries. However, PTE.Set() currently
// assumes that NumMemoryTypes <= 4, since the location of the most
// significant bit of the PAT index in page table entries varies depending
// on page size (and is never bit 5 == writeThroughShift + 2).
if hostarch.NumMemoryTypes > 4 {
t.Errorf("PTE.Set() and PTE.Opts() must be altered to handle %d MemoryTypes", hostarch.NumMemoryTypes)
}
}
@@ -79,3 +79,10 @@ func TestSplit2MPage(t *testing.T) {
{0x0000ff0000000000 + pmdSize - pteSize, pteSize, pmdSize*42 + pmdSize - pteSize, MapOpts{AccessType: hostarch.Read, User: true}},
})
}
func TestNumMemoryTypes(t *testing.T) {
// MAIR accommodates up to 8 entries.
if hostarch.NumMemoryTypes > 8 {
t.Errorf("PTE.Set() and PTE.Opts() must be altered to map %d MemoryTypes to a smaller set of MAIR entries", hostarch.NumMemoryTypes)
}
}
+19 -13
View File
@@ -49,16 +49,17 @@ func (p *PageTables) CR3(noFlush bool, pcid uint16) uint64 {
// Bits in page table entries.
const (
present = 0x001
writable = 0x002
user = 0x004
writeThrough = 0x008
cacheDisable = 0x010
accessed = 0x020
dirty = 0x040
super = 0x080
global = 0x100
optionMask = executeDisable | 0xfff
present = 0x001
writable = 0x002
user = 0x004
accessed = 0x020
dirty = 0x040
super = 0x080
global = 0x100
optionMask = executeDisable | 0xfff
writeThroughShift = 3
patIndexMask = 0x3
)
// MapOpts are x86 options.
@@ -71,6 +72,9 @@ type MapOpts struct {
// User indicates the page is a user page.
User bool
// MemoryType is the memory type.
MemoryType hostarch.MemoryType
}
// PTE is a page table entry.
@@ -103,8 +107,9 @@ func (p *PTE) Opts() MapOpts {
Write: v&writable != 0,
Execute: v&executeDisable == 0,
},
Global: v&global != 0,
User: v&user != 0,
Global: v&global != 0,
User: v&user != 0,
MemoryType: hostarch.MemoryType((v >> writeThroughShift) & patIndexMask),
}
}
@@ -154,6 +159,7 @@ func (p *PTE) Set(addr uintptr, opts MapOpts) {
if opts.AccessType.Write {
v |= writable | dirty
}
v |= uintptr(opts.MemoryType&patIndexMask) << writeThroughShift
if p.IsSuper() {
// Note that this is inherited from the previous instance. Set
// does not change the value of Super. See above.
@@ -172,7 +178,7 @@ func (p *PTE) setPageTable(pt *PageTables, ptes *PTEs) {
// This should never happen.
panic("unaligned physical address!")
}
v := addr | present | user | writable | accessed | dirty
v := addr | present | user | writable | accessed | dirty | (uintptr(hostarch.MemoryTypeWriteBack) << writeThroughShift)
atomic.StoreUintptr((*uintptr)(p), v)
}
+8 -4
View File
@@ -98,8 +98,9 @@ func (as *addressSpace) Touch(c *vCPU) bool {
}
type hostMapEntry struct {
addr uintptr
length uintptr
addr uintptr
length uintptr
memType hostarch.MemoryType
}
// mapLocked maps the given host entry.
@@ -130,6 +131,7 @@ func (as *addressSpace) mapLocked(addr hostarch.Addr, m hostMapEntry, at hostarc
inv = as.pageTables.Map(addr, length, pagetables.MapOpts{
AccessType: at,
User: true,
MemoryType: m.memType,
}, physical) || inv
m.addr += length
m.length -= length
@@ -161,6 +163,7 @@ func (as *addressSpace) MapFile(addr hostarch.Addr, f memmap.File, fr memmap.Fil
if err != nil {
return err
}
mt := f.MemoryType()
// See block in mapLocked.
as.pageTables.Allocator.(*allocator).cpu = as.machine.Get()
@@ -186,8 +189,9 @@ func (as *addressSpace) MapFile(addr hostarch.Addr, f memmap.File, fr memmap.Fil
// Perform the mapping.
prev := as.mapLocked(addr, hostMapEntry{
addr: b.Addr(),
length: uintptr(b.Len()),
addr: b.Addr(),
length: uintptr(b.Len()),
memType: mt,
}, at)
inv = inv || prev
addr += hostarch.Addr(b.Len())
@@ -119,12 +119,6 @@ const (
// Arm64: Memory Attribute Indirection Register EL1.
const (
_MT_DEVICE_nGnRnE = 0
_MT_DEVICE_nGnRE = 1
_MT_DEVICE_GRE = 2
_MT_NORMAL_NC = 3
_MT_NORMAL = 4
_MT_NORMAL_WT = 5
_MT_ATTR_DEVICE_nGnRnE = 0x00
_MT_ATTR_DEVICE_nGnRE = 0x04
_MT_ATTR_DEVICE_GRE = 0x0c
@@ -132,7 +126,6 @@ const (
_MT_ATTR_NORMAL_WT = 0xbb
_MT_ATTR_NORMAL = 0xff
_MT_ATTR_MASK = 0xff
_MT_EL1_INIT = (_MT_ATTR_DEVICE_nGnRnE << (_MT_DEVICE_nGnRnE * 8)) | (_MT_ATTR_DEVICE_nGnRE << (_MT_DEVICE_nGnRE * 8)) | (_MT_ATTR_DEVICE_GRE << (_MT_DEVICE_GRE * 8)) | (_MT_ATTR_NORMAL_NC << (_MT_NORMAL_NC * 8)) | (_MT_ATTR_NORMAL << (_MT_NORMAL * 8)) | (_MT_ATTR_NORMAL_WT << (_MT_NORMAL_WT * 8))
)
const (
+5
View File
@@ -128,6 +128,11 @@ func (c *vCPU) initArchState() error {
return err
}
// Set up the PAT as required by ring0/pagetables.
if err := c.setPAT(); err != nil {
return err
}
// Set the entrypoint for the kernel.
kernelUserRegs.RIP = uint64(ring0.AddrOfStart())
kernelUserRegs.RAX = uint64(reflect.ValueOf(&c.CPU).Pointer())
@@ -23,6 +23,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/hostsyscall"
)
@@ -72,6 +73,36 @@ func (c *vCPU) setCPUID() error {
return nil
}
func (c *vCPU) setPAT() error {
// See Intel SDM Vol. 3, Sec. 13.12.2 "IA32_PAT MSR", or AMD64 APM Vol. 2,
// Sec. 7.8.1 "PAT Register".
const (
_MSR_IA32_PAT = 0x277
_PAT_UC = 0x00
_PAT_WC = 0x01
_PAT_WB = 0x06
)
registers := modelControlRegisters{
nmsrs: 1,
}
registers.entries[0].index = _MSR_IA32_PAT
if hostarch.NumMemoryTypes != 3 {
panic("additional memory types must be configured in PAT")
}
registers.entries[0].data = (_PAT_WB << (hostarch.MemoryTypeWriteBack * 8)) |
(_PAT_WC << (hostarch.MemoryTypeWriteCombine * 8)) |
(_PAT_UC << (hostarch.MemoryTypeUncached * 8))
if errno := hostsyscall.RawSyscallErrno(
unix.SYS_IOCTL,
uintptr(c.fd),
KVM_SET_MSRS,
uintptr(unsafe.Pointer(&registers))); errno != 0 {
return fmt.Errorf("error setting PAT: %v", errno)
}
return nil
}
// getTSCFreq gets the TSC frequency.
//
// If mustSucceed is true, then this function panics on error.
@@ -91,7 +91,12 @@ func (c *vCPU) initArchState() error {
}
// mair_el1
data = _MT_EL1_INIT
if hostarch.NumMemoryTypes != 3 {
panic("additional memory types must be configured in MAIR")
}
data = (_MT_ATTR_NORMAL << (hostarch.MemoryTypeWriteBack * 8)) |
(_MT_ATTR_NORMAL_NC << (hostarch.MemoryTypeWriteCombine * 8)) |
(_MT_ATTR_DEVICE_nGnRnE << (hostarch.MemoryTypeUncached * 8))
reg.id = _KVM_ARM64_REGS_MAIR_EL1
if err := c.setOneRegister(&reg); err != nil {
return err