2001-10-03 17:10:38 +04:00
/////////////////////////////////////////////////////////////////////////
2011-02-25 00:54:04 +03:00
// $Id$
2001-10-03 17:10:38 +04:00
/////////////////////////////////////////////////////////////////////////
//
2021-07-27 22:18:34 +03:00
// Copyright (C) 2001-2021 The Bochs Project
2001-04-10 05:04:59 +04:00
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
2009-01-16 21:18:59 +03:00
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA
2007-11-18 02:28:33 +03:00
/////////////////////////////////////////////////////////////////////////
2001-04-10 05:04:59 +04:00
2001-05-24 22:46:34 +04:00
# define NEED_CPU_REG_SHORTCUTS 1
2001-04-10 05:04:59 +04:00
# include "bochs.h"
2006-03-07 01:03:16 +03:00
# include "cpu.h"
2019-10-26 23:09:30 +03:00
# include "msr.h"
merge in BRANCH-io-cleanup.
To see the commit logs for this use either cvsweb or
cvs update -r BRANCH-io-cleanup and then 'cvs log' the various files.
In general this provides a generic interface for logging.
logfunctions:: is a class that is inherited by some classes, and also
. allocated as a standalone global called 'genlog'. All logging uses
. one of the ::info(), ::error(), ::ldebug(), ::panic() methods of this
. class through 'BX_INFO(), BX_ERROR(), BX_DEBUG(), BX_PANIC()' macros
. respectively.
.
. An example usage:
. BX_INFO(("Hello, World!\n"));
iofunctions:: is a class that is allocated once by default, and assigned
as the iofunction of each logfunctions instance. It is this class that
maintains the file descriptor and other output related code, at this
point using vfprintf(). At some future point, someone may choose to
write a gui 'console' for bochs to which messages would be redirected
simply by assigning a different iofunction class to the various logfunctions
objects.
More cleanup is coming, but this works for now. If you want to see alot
of debugging output, in main.cc, change onoff[LOGLEV_DEBUG]=0 to =1.
Comments, bugs, flames, to me: todd@fries.net
2001-05-15 18:49:57 +04:00
# define LOG_THIS BX_CPU_THIS_PTR
2001-04-10 05:04:59 +04:00
2021-01-30 23:13:34 +03:00
# include "memory/memory-bochs.h"
2021-01-30 21:29:28 +03:00
# include "pc_system.h"
2001-04-10 05:04:59 +04:00
// X86 Registers Which Affect Paging:
// ==================================
//
// CR0:
// bit 31: PG, Paging (386+)
// bit 16: WP, Write Protect (486+)
// 0: allow supervisor level writes into user level RO pages
// 1: inhibit supervisor level writes into user level RO pages
//
// CR3:
// bit 31..12: PDBR, Page Directory Base Register (386+)
// bit 4: PCD, Page level Cache Disable (486+)
// Controls caching of current page directory. Affects only the processor's
// internal caches (L1 and L2).
// This flag ignored if paging disabled (PG=0) or cache disabled (CD=1).
// Values:
// 0: Page Directory can be cached
// 1: Page Directory not cached
// bit 3: PWT, Page level Writes Transparent (486+)
// Controls write-through or write-back caching policy of current page
// directory. Affects only the processor's internal caches (L1 and L2).
// This flag ignored if paging disabled (PG=0) or cache disabled (CD=1).
// Values:
// 0: write-back caching enabled
// 1: write-through caching enabled
//
// CR4:
// bit 4: PSE, Page Size Extension (Pentium+)
// 0: 4KByte pages (typical)
// 1: 4MByte or 2MByte pages
// bit 5: PAE, Physical Address Extension (Pentium Pro+)
// 0: 32bit physical addresses
// 1: 36bit physical addresses
// bit 7: PGE, Page Global Enable (Pentium Pro+)
// The global page feature allows frequently used or shared pages
// to be marked as global (PDE or PTE bit 8). Global pages are
// not flushed from TLB on a task switch or write to CR3.
// Values:
// 0: disables global page feature
// 1: enables global page feature
//
2007-09-20 21:33:35 +04:00
// page size extention and physical address size extention matrix (legacy mode)
// ==============================================================================
// CR0.PG CR4.PAE CR4.PSE PDPE.PS PDE.PS | page size physical address size
// ==============================================================================
// 0 X X R X | -- paging disabled
// 1 0 0 R X | 4K 32bits
// 1 0 1 R 0 | 4K 32bits
// 1 0 1 R 1 | 4M 32bits
// 1 1 X R 0 | 4K 36bits
// 1 1 X R 1 | 2M 36bits
// page size extention and physical address size extention matrix (long mode)
// ==============================================================================
// CR0.PG CR4.PAE CR4.PSE PDPE.PS PDE.PS | page size physical address size
// ==============================================================================
// 1 1 X 0 0 | 4K 52bits
// 1 1 X 0 1 | 2M 52bits
// 1 1 X 1 - | 1G 52bits
2001-04-10 05:04:59 +04:00
// Page Directory/Table Entry Fields Defined:
// ==========================================
2004-10-21 22:20:40 +04:00
// NX: No Execute
// This bit controls the ability to execute code from all physical
// pages mapped by the table entry.
// 0: Code can be executed from the mapped physical pages
// 1: Code cannot be executed
// The NX bit can only be set when the no-execute page-protection
2008-02-03 00:46:54 +03:00
// feature is enabled by setting EFER.NXE=1, If EFER.NXE=0, the
// NX bit is treated as reserved. In this case, #PF occurs if the
2004-10-21 22:20:40 +04:00
// NX bit is not cleared to zero.
//
2001-04-10 05:04:59 +04:00
// G: Global flag
// Indiciates a global page when set. When a page is marked
// global and the PGE flag in CR4 is set, the page table or
// directory entry for the page is not invalidated in the TLB
// when CR3 is loaded or a task switch occurs. Only software
// clears and sets this flag. For page directory entries that
// point to page tables, this flag is ignored and the global
// characteristics of a page are set in the page table entries.
//
// PS: Page Size flag
// Only used in page directory entries. When PS=0, the page
// size is 4KBytes and the page directory entry points to a
// page table. When PS=1, the page size is 4MBytes for
// normal 32-bit addressing and 2MBytes if extended physical
2004-10-21 22:20:40 +04:00
// addressing.
//
// PAT: Page-Attribute Table
// This bit is only present in the lowest level of the page
2008-02-03 00:46:54 +03:00
// translation hierarchy. The PAT bit is the high-order bit
// of a 3-bit index into the PAT register. The other two
// bits involved in forming the index are the PCD and PWT
2004-10-21 22:20:40 +04:00
// bits.
2001-04-10 05:04:59 +04:00
//
// D: Dirty bit:
// Processor sets the Dirty bit in the 2nd-level page table before a
// write operation to an address mapped by that page table entry.
// Dirty bit in directory entries is undefined.
//
// A: Accessed bit:
// Processor sets the Accessed bits in both levels of page tables before
// a read/write operation to a page.
//
// PCD: Page level Cache Disable
// Controls caching of individual pages or page tables.
// This allows a per-page based mechanism to disable caching, for
// those pages which contained memory mapped IO, or otherwise
// should not be cached. Processor ignores this flag if paging
// is not used (CR0.PG=0) or the cache disable bit is set (CR0.CD=1).
// Values:
// 0: page or page table can be cached
// 1: page or page table is not cached (prevented)
//
// PWT: Page level Write Through
// Controls the write-through or write-back caching policy of individual
// pages or page tables. Processor ignores this flag if paging
// is not used (CR0.PG=0) or the cache disable bit is set (CR0.CD=1).
// Values:
// 0: write-back caching
// 1: write-through caching
//
// U/S: User/Supervisor level
// 0: Supervisor level - for the OS, drivers, etc.
// 1: User level - application code and data
//
// R/W: Read/Write access
// 0: read-only access
// 1: read/write access
//
// P: Present
// 0: Not present
// 1: Present
// ==========================================
// Combined page directory/page table protection:
// ==============================================
// There is one column for the combined effect on a 386
// and one column for the combined effect on a 486+ CPU.
2012-02-14 00:06:04 +04:00
// The 386 CPU behavior is not supported by Bochs.
2001-04-10 05:04:59 +04:00
//
// +----------------+-----------------+----------------+----------------+
// | Page Directory| Page Table | Combined 386 | Combined 486+ |
// |Privilege Type | Privilege Type | Privilege Type| Privilege Type|
// |----------------+-----------------+----------------+----------------|
// |User R | User R | User R | User R |
// |User R | User RW | User R | User R |
// |User RW | User R | User R | User R |
// |User RW | User RW | User RW | User RW |
// |User R | Supervisor R | User R | Supervisor RW |
// |User R | Supervisor RW | User R | Supervisor RW |
// |User RW | Supervisor R | User R | Supervisor RW |
// |User RW | Supervisor RW | User RW | Supervisor RW |
// |Supervisor R | User R | User R | Supervisor RW |
// |Supervisor R | User RW | User R | Supervisor RW |
// |Supervisor RW | User R | User R | Supervisor RW |
// |Supervisor RW | User RW | User RW | Supervisor RW |
// |Supervisor R | Supervisor R | Supervisor RW | Supervisor RW |
// |Supervisor R | Supervisor RW | Supervisor RW | Supervisor RW |
// |Supervisor RW | Supervisor R | Supervisor RW | Supervisor RW |
// |Supervisor RW | Supervisor RW | Supervisor RW | Supervisor RW |
// +----------------+-----------------+----------------+----------------+
// Page Fault Error Code Format:
// =============================
//
// bits 31..4: Reserved
// bit 3: RSVD (Pentium Pro+)
// 0: fault caused by reserved bits set to 1 in a page directory
// when the PSE or PAE flags in CR4 are set to 1
// 1: fault was not caused by reserved bit violation
// bit 2: U/S (386+)
// 0: fault originated when in supervior mode
// 1: fault originated when in user mode
// bit 1: R/W (386+)
// 0: access causing the fault was a read
// 1: access causing the fault was a write
// bit 0: P (386+)
// 0: fault caused by a nonpresent page
// 1: fault caused by a page level protection violation
// Some paging related notes:
// ==========================
//
// - When the processor is running in supervisor level, all pages are both
// readable and writable (write-protect ignored). When running at user
// level, only pages which belong to the user level are accessible;
// read/write & read-only are readable, read/write are writable.
//
// - If the Present bit is 0 in either level of page table, an
// access which uses these entries will generate a page fault.
//
// - (A)ccess bit is used to report read or write access to a page
// or 2nd level page table.
//
// - (D)irty bit is used to report write access to a page.
//
// - Processor running at CPL=0,1,2 maps to U/S=0
// Processor running at CPL=3 maps to U/S=1
2007-08-30 20:48:10 +04:00
2012-01-15 21:54:13 +04:00
// bit [11] of the TLB lpf used for TLB_NoHostPtr valid indication
2008-08-15 18:30:50 +04:00
# define TLB_LPFOf(laddr) AlignedAccessLPFOf(laddr, 0x7ff)
2001-04-10 05:04:59 +04:00
# if BX_CPU_LEVEL >= 4
# define BX_PRIV_CHECK_SIZE 32
# else
# define BX_PRIV_CHECK_SIZE 16
# endif
// The 'priv_check' array is used to decide if the current access
// has the proper paging permissions. An index is formed, based
// on parameters such as the access type and level, the write protect
// flag and values cached in the TLB. The format of the index into this
// array is:
//
// |4 |3 |2 |1 |0 |
// |wp|us|us|rw|rw|
// | | | | |
// | | | | +---> r/w of current access
// | | +--+------> u/s,r/w combined of page dir & table (cached)
// | +------------> u/s of current access
2007-07-09 19:16:14 +04:00
// +---------------> Current CR0.WP value
2016-03-02 23:44:42 +03:00
//
// CR0.WP = 0 CR0.WP = 1
// -----------------------------------------------------------------------------------------
// 0 0 0 0 | sys read from supervisor page | Allowed | Allowed
// 0 0 0 1 | sys write to read only supervisor page | Allowed | Not Allowed
// 0 0 1 0 | sys read from supervisor page | Allowed | Allowed
// 0 0 1 1 | sys write to supervisor page | Allowed | Allowed
// 0 1 0 0 | sys read from read only user page | Allowed | Allowed
// 0 1 0 1 | sys write to read only user page | Allowed | Not Allowed
// 0 1 1 0 | sys read from user page | Allowed | Allowed
// 0 1 1 1 | sys write to user page | Allowed | Allowed
// 1 0 0 0 | user read from read only supervisor page | Not Allowed | Not Allowed
// 1 0 0 1 | user write to read only supervisor page | Not Allowed | Not Allowed
// 1 0 1 0 | user read from supervisor page | Not Allowed | Not Allowed
// 1 0 1 1 | user write to supervisor page | Not Allowed | Not Allowed
// 1 1 0 0 | user read from read only user page | Allowed | Allowed
// 1 1 0 1 | user write to read only user page | Not Allowed | Not Allowed
// 1 1 1 0 | user read from user page | Allowed | Allowed
// 1 1 1 1 | user write to user page | Allowed | Allowed
//
2002-09-05 07:09:59 +04:00
2010-04-08 19:50:39 +04:00
/* 0xff0bbb0b */
static const Bit8u priv_check [ BX_PRIV_CHECK_SIZE ] =
{
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 1 , 1 ,
# if BX_CPU_LEVEL >= 4
1 , 0 , 1 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 1 , 1
# endif
} ;
2009-09-26 10:05:23 +04:00
2019-12-20 10:42:07 +03:00
// The 'priv_check' array for shadow stack accesses
//
// |3 |2 |1 |0 |
// |us|us|rw|rw|
// | | | |
// | | | +---> r/w of current access
// | +--+------> u/s,r/w combined of page dir & table (cached)
// +------------> u/s of current access
//
// -------------------------------------------------------------------
// 0 0 0 0 | sys read from supervisor page | Allowed
// 0 0 0 1 | sys write to read only supervisor page | Allowed : shadow stack page looks like read only page
// 0 0 1 0 | sys read from supervisor page | Allowed
// 0 0 1 1 | sys write to supervisor page | Allowed
// 0 1 0 0 | sys read from read only user page | Not Allowed : supervisor-mode shadow-stack access is not allowed to a user-mode page
// 0 1 0 1 | sys write to read only user page | Not Allowed : supervisor-mode shadow-stack access is not allowed to a user-mode page
// 0 1 1 0 | sys read from user page | Not Allowed : supervisor-mode shadow-stack access is not allowed to a user-mode page
// 0 1 1 1 | sys write to user page | Not Allowed : supervisor-mode shadow-stack access is not allowed to a user-mode page
// 1 0 0 0 | user read from read only supervisor page | Not Allowed : user-mode shadow-stack access is not allowed to a supervisor-mode page
// 1 0 0 1 | user write to read only supervisor page | Not Allowed : user-mode shadow-stack access is not allowed to a supervisor-mode page
// 1 0 1 0 | user read from supervisor page | Not Allowed : user-mode shadow-stack access is not allowed to a supervisor-mode page
// 1 0 1 1 | user write to supervisor page | Not Allowed : user-mode shadow-stack access is not allowed to a supervisor-mode page
// 1 1 0 0 | user read from read only user page | Allowed
// 1 1 0 1 | user write to read only user page | Allowed : shadow stack page looks like read only page
// 1 1 1 0 | user read from user page | Allowed
// 1 1 1 1 | user write to user page | Allowed
//
2018-01-27 22:31:39 +03:00
const Bit64u BX_PAGING_PHY_ADDRESS_RESERVED_BITS = BX_PHY_ADDRESS_RESERVED_BITS & BX_CONST64 ( 0xfffffffffffff ) ;
2009-10-31 22:16:09 +03:00
2018-01-27 22:31:39 +03:00
const Bit64u PAGE_DIRECTORY_NX_BIT = BX_CONST64 ( 0x8000000000000000 ) ;
2002-09-05 07:09:59 +04:00
2018-01-27 22:31:39 +03:00
const Bit64u BX_CR3_PAGING_MASK = BX_CONST64 ( 0x000ffffffffff000 ) ;
2010-04-04 13:04:12 +04:00
2002-09-05 07:09:59 +04:00
// Each entry in the TLB cache has 3 entries:
2007-12-17 00:03:46 +03:00
//
2002-09-05 07:09:59 +04:00
// lpf: Linear Page Frame (page aligned linear address of page)
2008-08-15 02:26:15 +04:00
// bits 32..12 Linear page frame
// bit 11 0: TLB HostPtr access allowed, 1: not allowed
// bit 10...0 Invalidate index
2007-12-17 00:03:46 +03:00
//
2002-09-05 07:09:59 +04:00
// ppf: Physical Page Frame (page aligned phy address of page)
2005-06-15 00:55:57 +04:00
//
2007-12-17 00:03:46 +03:00
// hostPageAddr:
// Host Page Frame address used for direct access to
// the mem.vector[] space allocated for the guest physical
// memory. If this is zero, it means that a pointer
// to the host space could not be generated, likely because
// that page of memory is not standard memory (it might
// be memory mapped IO, ROM, etc).
2005-06-15 00:55:57 +04:00
//
2007-12-17 00:03:46 +03:00
// accessBits:
//
// bit 31: Page is a global page.
2001-04-10 05:04:59 +04:00
//
2007-12-17 00:03:46 +03:00
// The following bits are used for a very efficient permissions
2002-09-05 07:09:59 +04:00
// check. The goal is to be able, using only the current privilege
// level and access type, to determine if the page tables allow the
// access to occur or at least should rewalk the page tables. On
// the first read access, permissions are set to only read, so a
// rewalk is necessary when a subsequent write fails the tests.
// This allows for the dirty bit to be set properly, but for the
// test to be efficient. Note that the CR0.WP flag is not present.
// The values in the following flags is based on the current CR0.WP
// value, necessitating a TLB flush when CR0.WP changes.
2001-04-10 05:04:59 +04:00
//
2013-01-16 21:28:20 +04:00
// The test bit:
2019-12-20 10:42:07 +03:00
// OK = 1 << ((S<<2) | (W<<1) | U)
2010-03-23 22:58:20 +03:00
//
2019-12-20 10:42:07 +03:00
// where S:1=Shadow Stack (CET)
2010-03-23 22:58:20 +03:00
// W:1=Write, 0=Read;
// U:1=CPL3, 0=CPL0-2
2022-08-23 21:38:00 +03:00
//
2008-08-15 02:26:15 +04:00
// Thus for reads, it is:
2013-01-16 21:28:20 +04:00
// OK = 0x01 << ( U )
2010-03-23 22:58:20 +03:00
// for writes:
2013-01-16 21:28:20 +04:00
// OK = 0x04 << ( U )
2019-12-20 10:42:07 +03:00
// for shadow stack reads:
2013-01-16 21:28:20 +04:00
// OK = 0x10 << ( U )
2019-12-20 10:42:07 +03:00
// for shadow stack writes:
// OK = 0x40 << ( U )
2013-01-16 21:28:20 +04:00
//
// bit 3: Write from User privilege is OK
// bit 2: Write from System privilege is OK
// bit 1: Read from User privilege is OK
// bit 0: Read from System privilege is OK
2001-04-10 05:04:59 +04:00
//
2012-02-29 02:39:33 +04:00
// Note, that the TLB should have TLB_NoHostPtr bit set in the lpf when
// direct access through host pointer is NOT allowed for the page.
// A memory operation asking for a direct access through host pointer
2022-08-23 21:38:00 +03:00
// will not set TLB_NoHostPtr bit in its lpf and thus get TLB miss
2012-02-29 02:39:33 +04:00
// result when the direct access is not allowed.
2005-06-15 00:55:57 +04:00
//
2019-12-09 21:37:02 +03:00
const Bit32u TLB_NoHostPtr = 0x800 ; /* set this bit when direct access is NOT allowed */
2004-12-17 01:21:35 +03:00
2014-10-14 19:59:10 +04:00
# include "cpustats.h"
2002-09-17 00:23:38 +04:00
2008-05-11 23:36:06 +04:00
// ==============================================================
2002-09-17 00:23:38 +04:00
2008-08-14 01:51:54 +04:00
void BX_CPU_C : : TLB_flush ( void )
2001-04-10 05:04:59 +04:00
{
2014-10-14 19:59:10 +04:00
INC_TLBFLUSH_STAT ( tlbGlobalFlushes ) ;
2002-09-06 18:58:56 +04:00
2008-08-23 17:55:37 +04:00
invalidate_prefetch_q ( ) ;
2012-03-25 15:54:32 +04:00
invalidate_stack_cache ( ) ;
2019-12-09 21:37:02 +03:00
BX_CPU_THIS_PTR DTLB . flush ( ) ;
BX_CPU_THIS_PTR ITLB . flush ( ) ;
2008-12-19 19:03:25 +03:00
2008-08-14 01:51:54 +04:00
# if BX_SUPPORT_MONITOR_MWAIT
// invalidating of the TLB might change translation for monitored page
// and cause subsequent MWAIT instruction to wait forever
2022-07-14 05:51:15 +03:00
BX_CPU_THIS_PTR wakeup_monitor ( ) ;
2008-08-14 01:51:54 +04:00
# endif
2015-10-09 08:33:44 +03:00
// break all links bewteen traces
BX_CPU_THIS_PTR iCache . breakLinks ( ) ;
2008-08-14 01:51:54 +04:00
}
2009-06-15 13:30:56 +04:00
# if BX_CPU_LEVEL >= 6
2008-08-14 01:51:54 +04:00
void BX_CPU_C : : TLB_flushNonGlobal ( void )
{
2014-10-14 19:59:10 +04:00
INC_TLBFLUSH_STAT ( tlbNonGlobalFlushes ) ;
2008-08-14 01:51:54 +04:00
2008-08-23 17:55:37 +04:00
invalidate_prefetch_q ( ) ;
2012-03-25 15:54:32 +04:00
invalidate_stack_cache ( ) ;
2019-12-09 21:37:02 +03:00
BX_CPU_THIS_PTR DTLB . flushNonGlobal ( ) ;
BX_CPU_THIS_PTR ITLB . flushNonGlobal ( ) ;
2012-01-15 23:38:00 +04:00
2008-04-26 00:08:23 +04:00
# if BX_SUPPORT_MONITOR_MWAIT
// invalidating of the TLB might change translation for monitored page
// and cause subsequent MWAIT instruction to wait forever
2022-07-14 05:51:15 +03:00
BX_CPU_THIS_PTR wakeup_monitor ( ) ;
2008-04-26 00:08:23 +04:00
# endif
2015-10-09 08:33:44 +03:00
// break all links bewteen traces
BX_CPU_THIS_PTR iCache . breakLinks ( ) ;
2001-04-10 05:04:59 +04:00
}
2008-08-14 01:51:54 +04:00
# endif
2006-03-02 01:32:24 +03:00
void BX_CPU_C : : TLB_invlpg ( bx_address laddr )
{
2008-08-23 17:55:37 +04:00
invalidate_prefetch_q ( ) ;
2012-03-25 15:54:32 +04:00
invalidate_stack_cache ( ) ;
2014-12-18 20:52:40 +03:00
BX_DEBUG ( ( " TLB_invlpg(0x " FMT_ADDRX " ): invalidate TLB entry " , laddr ) ) ;
2019-12-09 21:37:02 +03:00
BX_CPU_THIS_PTR DTLB . invlpg ( laddr ) ;
BX_CPU_THIS_PTR ITLB . invlpg ( laddr ) ;
2008-04-26 00:08:23 +04:00
# if BX_SUPPORT_MONITOR_MWAIT
// invalidating of the TLB entry might change translation for monitored
// page and cause subsequent MWAIT instruction to wait forever
2022-07-14 05:51:15 +03:00
BX_CPU_THIS_PTR wakeup_monitor ( ) ;
2008-04-26 00:08:23 +04:00
# endif
2015-10-09 08:33:44 +03:00
// break all links bewteen traces
BX_CPU_THIS_PTR iCache . breakLinks ( ) ;
2006-03-02 01:32:24 +03:00
}
2018-02-16 10:57:32 +03:00
void BX_CPP_AttrRegparmN ( 1 ) BX_CPU_C : : INVLPG ( bxInstruction_c * i )
2001-04-10 05:04:59 +04:00
{
2011-08-24 01:25:34 +04:00
// CPL is always 0 in real mode
if ( /* !real_mode() && */ CPL ! = 0 ) {
2013-12-03 00:06:59 +04:00
BX_ERROR ( ( " %s: priveledge check failed, generate #GP(0) " , i - > getIaOpcodeNameShort ( ) ) ) ;
2010-03-14 18:51:27 +03:00
exception ( BX_GP_EXCEPTION , 0 ) ;
2006-06-10 02:29:07 +04:00
}
2001-04-10 05:04:59 +04:00
2015-05-17 00:06:59 +03:00
bx_address eaddr = BX_CPU_RESOLVE_ADDR ( i ) ;
2008-08-08 13:22:49 +04:00
bx_address laddr = get_laddr ( i - > seg ( ) , eaddr ) ;
2008-11-29 22:28:10 +03:00
2009-02-04 19:05:47 +03:00
# if BX_SUPPORT_VMX
2012-01-18 01:50:15 +04:00
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
2012-11-02 11:46:50 +04:00
if ( VMEXIT ( VMX_VM_EXEC_CTRL2_INVLPG_VMEXIT ) ) VMexit ( VMX_VMEXIT_INVLPG , laddr ) ;
2012-01-18 01:50:15 +04:00
}
2009-02-04 19:05:47 +03:00
# endif
2011-12-25 23:35:29 +04:00
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR in_svm_guest ) {
2012-11-02 11:46:50 +04:00
if ( SVM_INTERCEPT ( SVM_INTERCEPT0_INVLPG ) )
Svm_Vmexit ( SVM_VMEXIT_INVLPG , BX_SUPPORT_SVM_EXTENSION ( BX_CPUID_SVM_DECODE_ASSIST ) ? laddr : 0 ) ;
2011-12-25 23:35:29 +04:00
}
# endif
2008-11-29 22:28:10 +03:00
# if BX_SUPPORT_X86_64
2011-06-28 20:29:11 +04:00
if ( IsCanonical ( laddr ) )
2008-11-29 22:28:10 +03:00
# endif
2011-06-28 20:04:40 +04:00
{
BX_INSTR_TLB_CNTRL ( BX_CPU_ID , BX_INSTR_INVLPG , laddr ) ;
TLB_invlpg ( laddr ) ;
}
2011-07-07 00:01:18 +04:00
BX_NEXT_TRACE ( i ) ;
2001-04-10 05:04:59 +04:00
}
2007-08-30 20:48:10 +04:00
// error checking order - page not present, reserved bits, protection
2018-01-27 22:31:39 +03:00
enum {
2019-12-20 10:42:07 +03:00
ERROR_NOT_PRESENT = 0x00 ,
ERROR_PROTECTION = 0x01 ,
ERROR_WRITE_ACCESS = 0x02 ,
ERROR_USER_ACCESS = 0x04 ,
ERROR_RESERVED = 0x08 ,
ERROR_CODE_ACCESS = 0x10 ,
ERROR_PKEY = 0x20 ,
ERROR_SHADOW_STACK = 0x40 ,
2018-01-27 22:31:39 +03:00
} ;
2007-08-30 20:48:10 +04:00
2008-12-06 01:34:42 +03:00
void BX_CPU_C : : page_fault ( unsigned fault , bx_address laddr , unsigned user , unsigned rw )
2007-08-30 20:48:10 +04:00
{
2008-12-06 01:34:42 +03:00
unsigned isWrite = rw & 1 ;
2007-08-30 20:48:10 +04:00
2012-02-20 00:15:23 +04:00
Bit32u error_code = fault | ( user < < 2 ) | ( isWrite < < 1 ) ;
2011-05-29 20:28:26 +04:00
# if BX_CPU_LEVEL >= 6
if ( rw = = BX_EXECUTE ) {
if ( BX_CPU_THIS_PTR cr4 . get_SMEP ( ) )
error_code | = ERROR_CODE_ACCESS ; // I/D = 1
if ( BX_CPU_THIS_PTR cr4 . get_PAE ( ) & & BX_CPU_THIS_PTR efer . get_NXE ( ) )
error_code | = ERROR_CODE_ACCESS ;
}
2007-08-30 20:48:10 +04:00
# endif
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
2021-01-30 11:35:35 +03:00
bool is_shadow_stack = ( rw & 4 ) ! = 0 ;
2019-12-20 10:42:07 +03:00
if ( is_shadow_stack )
error_code | = ERROR_SHADOW_STACK ;
# endif
2009-01-31 13:43:24 +03:00
2011-12-28 20:12:28 +04:00
# if BX_SUPPORT_SVM
SvmInterceptException ( BX_HARDWARE_EXCEPTION , BX_PF_EXCEPTION , error_code , 1 , laddr ) ; // before the CR2 was modified
# endif
2009-01-31 13:43:24 +03:00
# if BX_SUPPORT_VMX
2012-07-26 20:03:26 +04:00
VMexit_Event ( BX_HARDWARE_EXCEPTION , BX_PF_EXCEPTION , error_code , 1 , laddr ) ; // before the CR2 was modified
2009-01-31 13:43:24 +03:00
# endif
2007-08-30 20:48:10 +04:00
BX_CPU_THIS_PTR cr2 = laddr ;
# if BX_SUPPORT_X86_64
BX_DEBUG ( ( " page fault for address %08x%08x @ %08x%08x " ,
GET32H ( laddr ) , GET32L ( laddr ) , GET32H ( RIP ) , GET32L ( RIP ) ) ) ;
# else
BX_DEBUG ( ( " page fault for address %08x @ %08x " , laddr , EIP ) ) ;
# endif
2010-03-14 18:51:27 +03:00
exception ( BX_PF_EXCEPTION , error_code ) ;
2007-08-30 20:48:10 +04:00
}
2018-01-27 22:31:39 +03:00
enum {
BX_LEVEL_PML4 = 3 ,
BX_LEVEL_PDPTE = 2 ,
BX_LEVEL_PDE = 1 ,
BX_LEVEL_PTE = 0
} ;
2010-04-03 23:21:07 +04:00
2012-01-15 21:54:13 +04:00
static const char * bx_paging_level [ 4 ] = { " PTE " , " PDE " , " PDPE " , " PML4 " } ; // keep it 4 letters
2010-05-16 09:23:18 +04:00
2019-12-15 21:45:04 +03:00
// combined_access legend:
// -----------------------
// 00 |
// 01 | R/W
// 02 | U/S
// 03 |
2019-12-20 10:42:07 +03:00
// 07 | Shadow Stack
2019-12-15 21:45:04 +03:00
// 08 | Global
// 11-09 | memtype (3 bits)
enum {
BX_COMBINED_ACCESS_WRITE = 0x2 ,
BX_COMBINED_ACCESS_USER = 0x4 ,
2019-12-20 10:42:07 +03:00
BX_COMBINED_SHADOW_STACK = 0x80 ,
2019-12-15 21:45:04 +03:00
BX_COMBINED_GLOBAL_PAGE = 0x100 ,
} ;
2022-07-30 15:26:47 +03:00
# define IS_USER_PAGE(combined_access) !!((combined_access) & BX_COMBINED_ACCESS_USER)
2009-06-15 13:30:56 +04:00
# if BX_CPU_LEVEL >= 6
2008-04-26 00:08:23 +04:00
2010-04-01 16:23:52 +04:00
// Format of a Long Mode Non-Leaf Entry
// -----------------------------------------------------------
// 00 | Present (P)
// 01 | R/W
// 02 | U/S
// 03 | Page-Level Write-Through (PWT)
// 04 | Page-Level Cache-Disable (PCD)
// 05 | Accessed (A)
// 06 | (ignored)
// 07 | Page Size (PS), must be 0 if no Large Page on the level
// 11-08 | (ignored)
// PA-12 | Physical address of 4-KByte aligned page-directory-pointer table
// 51-PA | Reserved (must be zero)
// 62-52 | (ignored)
// 63 | Execute-Disable (XD) (if EFER.NXE=1, reserved otherwise)
// -----------------------------------------------------------
2018-01-27 22:31:39 +03:00
const Bit64u PAGING_PAE_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS ;
2010-04-01 16:23:52 +04:00
2012-02-19 16:16:58 +04:00
// in legacy PAE mode bits [62:52] are reserved. bit 63 is NXE
2018-01-27 22:31:39 +03:00
const Bit64u PAGING_LEGACY_PAE_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS | BX_CONST64 ( 0x7ff0000000000000 ) ;
2012-02-19 16:16:58 +04:00
2010-04-01 16:23:52 +04:00
// Format of a PDPTE that References a 1-GByte Page
// -----------------------------------------------------------
// 00 | Present (P)
// 01 | R/W
// 02 | U/S
// 03 | Page-Level Write-Through (PWT)
// 04 | Page-Level Cache-Disable (PCD)
// 05 | Accessed (A)
// 06 | (ignored)
// 07 | Page Size, must be 1 to indicate a 1-GByte Page
// 08 | Global (G) (if CR4.PGE=1, ignored otherwise)
// 11-09 | (ignored)
// 12 | PAT (if PAT is supported, reserved otherwise)
// 29-13 | Reserved (must be zero)
// PA-30 | Physical address of the 1-Gbyte Page
// 51-PA | Reserved (must be zero)
// 62-52 | (ignored)
// 63 | Execute-Disable (XD) (if EFER.NXE=1, reserved otherwise)
// -----------------------------------------------------------
2018-01-27 22:31:39 +03:00
const Bit64u PAGING_PAE_PDPTE1G_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS | BX_CONST64 ( 0x3FFFE000 ) ;
2010-04-01 16:23:52 +04:00
// Format of a PAE PDE that Maps a 2-MByte Page
// -----------------------------------------------------------
// 00 | Present (P)
// 01 | R/W
// 02 | U/S
// 03 | Page-Level Write-Through (PWT)
// 04 | Page-Level Cache-Disable (PCD)
// 05 | Accessed (A)
// 06 | Dirty (D)
// 07 | Page Size (PS), must be 1 to indicate a 2-MByte Page
// 08 | Global (G) (if CR4.PGE=1, ignored otherwise)
// 11-09 | (ignored)
// 12 | PAT (if PAT is supported, reserved otherwise)
// 20-13 | Reserved (must be zero)
// PA-21 | Physical address of the 2-MByte page
// 51-PA | Reserved (must be zero)
// 62-52 | ignored in long mode, reserved (must be 0) in legacy PAE mode
// 63 | Execute-Disable (XD) (if EFER.NXE=1, reserved otherwise)
// -----------------------------------------------------------
2018-01-27 22:31:39 +03:00
const Bit64u PAGING_PAE_PDE2M_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS | BX_CONST64 ( 0x001FE000 ) ;
2010-04-01 16:23:52 +04:00
// Format of a PAE PTE that Maps a 4-KByte Page
// -----------------------------------------------------------
// 00 | Present (P)
// 01 | R/W
// 02 | U/S
// 03 | Page-Level Write-Through (PWT)
// 04 | Page-Level Cache-Disable (PCD)
// 05 | Accessed (A)
// 06 | Dirty (D)
// 07 | PAT (if PAT is supported, reserved otherwise)
// 08 | Global (G) (if CR4.PGE=1, ignored otherwise)
// 11-09 | (ignored)
// PA-12 | Physical address of the 4-KByte page
// 51-PA | Reserved (must be zero)
// 62-52 | ignored in long mode, reserved (must be 0) in legacy PAE mode
// 63 | Execute-Disable (XD) (if EFER.NXE=1, reserved otherwise)
// -----------------------------------------------------------
2021-01-30 11:35:35 +03:00
int BX_CPU_C : : check_entry_PAE ( const char * s , Bit64u entry , Bit64u reserved , unsigned rw , bool * nx_fault )
2012-01-16 00:25:39 +04:00
{
if ( ! ( entry & 0x1 ) ) {
2012-02-14 00:06:04 +04:00
BX_DEBUG ( ( " PAE %s: entry not present " , s ) ) ;
2012-01-16 00:25:39 +04:00
return ERROR_NOT_PRESENT ;
}
2012-02-19 16:16:58 +04:00
if ( entry & reserved ) {
2022-07-30 15:26:47 +03:00
BX_DEBUG ( ( " PAE %s: reserved bit is set 0x " FMT_ADDRX64 " (reserved: " FMT_ADDRX64 " ) " , s , entry , entry & reserved ) ) ;
2012-01-16 00:25:39 +04:00
return ERROR_RESERVED | ERROR_PROTECTION ;
}
if ( entry & PAGE_DIRECTORY_NX_BIT ) {
if ( rw = = BX_EXECUTE ) {
2019-12-09 19:29:23 +03:00
BX_DEBUG ( ( " PAE %s: non-executable page fault occurred " , s ) ) ;
2021-05-25 09:27:49 +03:00
* nx_fault = true ;
2012-01-16 00:25:39 +04:00
}
}
return - 1 ;
}
2015-03-01 23:55:23 +03:00
# if BX_SUPPORT_MEMTYPE
2015-03-21 23:08:58 +03:00
BX_CPP_INLINE Bit32u calculate_pcd_pwt ( Bit32u entry )
{
Bit32u pcd_pwt = ( entry > > 3 ) & 0x3 ; // PCD, PWT are stored in bits 3 and 4
return pcd_pwt ;
}
2015-03-01 23:55:23 +03:00
// extract PCD, PWT and PAT pat bits from page table entry
BX_CPP_INLINE Bit32u calculate_pat ( Bit32u entry , Bit32u lpf_mask )
{
2015-03-21 23:08:58 +03:00
Bit32u pcd_pwt = calculate_pcd_pwt ( entry ) ;
2015-03-01 23:55:23 +03:00
// PAT is stored in bit 12 for large pages and in bit 7 for small pages
Bit32u pat = ( ( lpf_mask < 0x1000 ) ? ( entry > > 7 ) : ( entry > > 12 ) ) & 0x1 ;
2015-03-21 23:08:58 +03:00
return pcd_pwt | ( pat < < 2 ) ;
2015-03-01 23:55:23 +03:00
}
# endif
2010-05-05 00:16:38 +04:00
# if BX_SUPPORT_X86_64
2009-09-26 10:05:23 +04:00
// Translate a linear address to a physical address in long mode
2016-03-02 23:44:42 +03:00
bx_phy_address BX_CPU_C : : translate_linear_long_mode ( bx_address laddr , Bit32u & lpf_mask , Bit32u & pkey , unsigned user , unsigned rw )
2009-09-26 10:05:23 +04:00
{
2010-04-04 13:04:12 +04:00
bx_phy_address ppf = BX_CPU_THIS_PTR cr3 & BX_CR3_PAGING_MASK ;
2015-03-21 23:08:58 +03:00
bx_phy_address entry_addr [ 4 ] ;
2010-04-01 09:26:20 +04:00
Bit64u entry [ 4 ] ;
2015-03-21 23:08:58 +03:00
BxMemtype entry_memtype [ 4 ] = { 0 } ;
2021-05-25 09:27:49 +03:00
bool nx_fault = false ;
2012-01-17 22:20:55 +04:00
int leaf ;
2012-01-19 10:38:22 +04:00
2012-02-12 23:13:57 +04:00
Bit64u offset_mask = BX_CONST64 ( 0x0000ffffffffffff ) ;
2012-02-14 00:06:04 +04:00
lpf_mask = 0xfff ;
2019-12-15 21:45:04 +03:00
Bit32u combined_access = ( BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ) ;
2015-03-21 23:08:58 +03:00
Bit64u curr_entry = BX_CPU_THIS_PTR cr3 ;
2007-12-17 00:03:46 +03:00
2012-02-19 16:16:58 +04:00
Bit64u reserved = PAGING_PAE_RESERVED_BITS ;
if ( ! BX_CPU_THIS_PTR efer . get_NXE ( ) )
reserved | = PAGE_DIRECTORY_NX_BIT ;
2010-04-01 16:23:52 +04:00
for ( leaf = BX_LEVEL_PML4 ; ; - - leaf ) {
2010-04-04 13:04:12 +04:00
entry_addr [ leaf ] = ppf + ( ( laddr > > ( 9 + 9 * leaf ) ) & 0xff8 ) ;
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) )
2022-07-30 15:26:47 +03:00
entry_addr [ leaf ] = translate_guest_physical ( entry_addr [ leaf ] , laddr , true /* laddr_valid */ , true /* page walk */ , IS_USER_PAGE ( combined_access ) , BX_READ ) ;
2010-04-07 21:12:17 +04:00
}
2012-02-14 03:29:01 +04:00
# endif
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
entry_addr [ leaf ] = nested_walk ( entry_addr [ leaf ] , BX_RW , 1 ) ;
}
2010-04-07 21:12:17 +04:00
# endif
2015-03-21 23:08:58 +03:00
# if BX_SUPPORT_MEMTYPE
2015-03-21 23:28:22 +03:00
entry_memtype [ leaf ] = resolve_memtype ( memtype_by_mtrr ( entry_addr [ leaf ] ) , memtype_by_pat ( calculate_pcd_pwt ( ( Bit32u ) curr_entry ) ) ) ;
2015-03-21 23:08:58 +03:00
# endif
2010-04-01 16:23:52 +04:00
access_read_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-03-21 23:08:58 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , entry_memtype [ leaf ] , BX_READ , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-02-12 23:13:57 +04:00
offset_mask > > = 9 ;
2008-05-19 22:10:32 +04:00
2015-03-21 23:08:58 +03:00
curr_entry = entry [ leaf ] ;
2012-02-19 16:16:58 +04:00
int fault = check_entry_PAE ( bx_paging_level [ leaf ] , curr_entry , reserved , rw , & nx_fault ) ;
2010-04-01 15:53:22 +04:00
if ( fault > = 0 )
2011-05-29 20:28:26 +04:00
page_fault ( fault , laddr , user , rw ) ;
2010-04-01 09:26:20 +04:00
2010-04-03 20:52:33 +04:00
ppf = curr_entry & BX_CONST64 ( 0x000ffffffffff000 ) ;
2002-09-17 00:23:38 +04:00
2010-04-03 20:52:33 +04:00
if ( leaf = = BX_LEVEL_PTE ) break ;
2008-12-12 00:00:01 +03:00
2010-04-01 15:53:22 +04:00
if ( curr_entry & 0x80 ) {
2014-08-31 23:22:41 +04:00
if ( leaf > ( BX_LEVEL_PDE + ! ! is_cpu_extension_supported ( BX_ISA_1G_PAGES ) ) ) {
2020-05-29 15:35:30 +03:00
BX_DEBUG ( ( " long mode %s: PS bit set ! " , bx_paging_level [ leaf ] ) ) ;
2011-05-29 20:28:26 +04:00
page_fault ( ERROR_RESERVED | ERROR_PROTECTION , laddr , user , rw ) ;
2010-04-01 15:53:22 +04:00
}
2008-12-12 00:00:01 +03:00
2012-02-12 23:13:57 +04:00
ppf & = BX_CONST64 ( 0x000fffffffffe000 ) ;
if ( ppf & offset_mask ) {
2020-05-29 15:35:30 +03:00
BX_DEBUG ( ( " long mode %s: reserved bit is set: 0x " FMT_ADDRX64 , bx_paging_level [ leaf ] , curr_entry ) ) ;
2012-02-12 23:13:57 +04:00
page_fault ( ERROR_RESERVED | ERROR_PROTECTION , laddr , user , rw ) ;
2010-04-01 15:53:22 +04:00
}
2008-12-12 00:00:01 +03:00
2012-11-27 19:40:45 +04:00
lpf_mask = ( Bit32u ) offset_mask ;
2012-02-12 23:13:57 +04:00
break ;
2008-05-19 22:10:32 +04:00
}
2019-12-20 10:42:07 +03:00
combined_access & = curr_entry ; // U/S and R/W
2008-05-19 22:10:32 +04:00
}
2021-01-30 11:35:35 +03:00
bool isWrite = ( rw & 1 ) ; // write or r-m-w
2005-03-03 23:24:52 +03:00
2016-03-02 23:44:42 +03:00
# if BX_SUPPORT_PKEYS
2020-05-29 15:35:30 +03:00
if ( rw ! = BX_EXECUTE ) {
if ( BX_CPU_THIS_PTR cr4 . get_PKE ( ) ) {
pkey = ( entry [ leaf ] > > 59 ) & 0xf ;
2016-03-02 23:44:42 +03:00
// check of accessDisable bit set
2020-05-29 15:35:30 +03:00
if ( user ) {
if ( BX_CPU_THIS_PTR pkru & ( 1 < < ( pkey * 2 ) ) ) {
BX_ERROR ( ( " protection key access not allowed PKRU=%x pkey=%d " , BX_CPU_THIS_PTR pkru , pkey ) ) ;
page_fault ( ERROR_PROTECTION | ERROR_PKEY , laddr , user , rw ) ;
}
2019-12-04 20:27:57 +03:00
}
2016-03-02 23:44:42 +03:00
// check of writeDisable bit set
if ( BX_CPU_THIS_PTR pkru & ( 1 < < ( pkey * 2 + 1 ) ) ) {
2019-12-04 20:27:57 +03:00
if ( isWrite & & ( user | | BX_CPU_THIS_PTR cr0 . get_WP ( ) ) ) {
2020-05-29 15:35:30 +03:00
BX_ERROR ( ( " protection key write not allowed PKRU=%x pkey=%d " , BX_CPU_THIS_PTR pkru , pkey ) ) ;
page_fault ( ERROR_PROTECTION | ERROR_PKEY , laddr , user , rw ) ;
}
}
}
if ( BX_CPU_THIS_PTR cr4 . get_PKS ( ) & & ! user ) {
pkey = ( entry [ leaf ] > > 59 ) & 0xf ;
// check of accessDisable bit set
if ( BX_CPU_THIS_PTR pkrs & ( 1 < < ( pkey * 2 ) ) ) {
BX_ERROR ( ( " protection key access not allowed PKRS=%x pkey=%d " , BX_CPU_THIS_PTR pkrs , pkey ) ) ;
page_fault ( ERROR_PROTECTION | ERROR_PKEY , laddr , user , rw ) ;
}
// check of writeDisable bit set
if ( BX_CPU_THIS_PTR pkrs & ( 1 < < ( pkey * 2 + 1 ) ) ) {
if ( isWrite & & BX_CPU_THIS_PTR cr0 . get_WP ( ) ) {
BX_ERROR ( ( " protection key write not allowed PKRS=%x pkey=%d " , BX_CPU_THIS_PTR pkrs , pkey ) ) ;
2016-03-02 23:44:42 +03:00
page_fault ( ERROR_PROTECTION | ERROR_PKEY , laddr , user , rw ) ;
2019-12-04 20:27:57 +03:00
}
2016-03-02 23:44:42 +03:00
}
}
}
# endif
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
2021-01-30 11:35:35 +03:00
bool shadow_stack = ( rw & 4 ) ! = 0 ;
2019-12-20 10:42:07 +03:00
if ( shadow_stack ) {
// shadow stack pages:
// - R/W bit=1 in every paging structure entry except the leaf
// - R/W bit=0 and Dirty=1 for leaf entry
2021-01-30 11:35:35 +03:00
bool shadow_stack_page = ( ( combined_access & BX_COMBINED_ACCESS_WRITE ) ! = 0 ) & & ( ( entry [ leaf ] & 0x40 ) ! = 0 ) & & ( ( entry [ leaf ] & 0x02 ) = = 0 ) ;
2019-12-20 10:42:07 +03:00
if ( ! shadow_stack_page ) {
BX_DEBUG ( ( " shadow stack access to not shadow stack page CA=%x entry=%lx \n " , combined_access , Bit32u ( entry [ leaf ] & 0xfff ) ) ) ;
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
combined_access & = entry [ leaf ] ; // U/S and R/W
// must be to shadow stack page, check that U/S match
if ( ( combined_access & BX_COMBINED_ACCESS_USER ) ^ ( user < < 2 ) ) {
BX_DEBUG ( ( " shadow stack U/S access mismatch " ) ) ;
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
combined_access | = BX_COMBINED_SHADOW_STACK ;
}
else
# endif
{
combined_access & = entry [ leaf ] ; // U/S and R/W
unsigned priv_index = ( BX_CPU_THIS_PTR cr0 . get_WP ( ) < < 4 ) | // bit 4
( user < < 3 ) | // bit 3
2021-07-27 22:18:34 +03:00
( combined_access | ( unsigned ) isWrite ) ; // bit 2,1,0
2019-12-20 10:42:07 +03:00
if ( ! priv_check [ priv_index ] | | nx_fault )
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2011-05-29 20:28:26 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_SMEP ( ) & & rw = = BX_EXECUTE & & ! user ) {
2019-12-15 21:45:04 +03:00
if ( combined_access & BX_COMBINED_ACCESS_USER )
2011-05-29 20:28:26 +04:00
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2002-09-17 00:23:38 +04:00
2012-09-10 19:22:26 +04:00
// SMAP protections are disabled if EFLAGS.AC=1
if ( BX_CPU_THIS_PTR cr4 . get_SMAP ( ) & & ! BX_CPU_THIS_PTR get_AC ( ) & & rw ! = BX_EXECUTE & & ! user ) {
2019-12-15 21:45:04 +03:00
if ( combined_access & BX_COMBINED_ACCESS_USER )
2012-09-10 19:22:26 +04:00
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2009-05-30 19:09:38 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_PGE ( ) )
2019-12-20 10:42:07 +03:00
combined_access | = ( entry [ leaf ] & BX_COMBINED_GLOBAL_PAGE ) ;
2010-04-01 09:26:20 +04:00
2015-03-01 23:55:23 +03:00
# if BX_SUPPORT_MEMTYPE
combined_access | = ( memtype_by_pat ( calculate_pat ( ( Bit32u ) entry [ leaf ] , lpf_mask ) ) < < 9 ) ;
# endif
2015-02-28 17:06:04 +03:00
2012-02-14 00:06:04 +04:00
// Update A/D bits if needed
2015-03-21 23:08:58 +03:00
update_access_dirty_PAE ( entry_addr , entry , entry_memtype , BX_LEVEL_PML4 , leaf , isWrite ) ;
2012-02-14 00:06:04 +04:00
2015-02-28 17:01:11 +03:00
return ( ppf | combined_access ) ;
2012-02-14 00:06:04 +04:00
}
# endif
2015-03-21 23:08:58 +03:00
void BX_CPU_C : : update_access_dirty_PAE ( bx_phy_address * entry_addr , Bit64u * entry , BxMemtype * entry_memtype , unsigned max_level , unsigned leaf , unsigned write )
2012-02-14 00:06:04 +04:00
{
2012-01-17 22:20:55 +04:00
// Update A bit if needed
2012-02-14 00:06:04 +04:00
for ( unsigned level = max_level ; level > leaf ; level - - ) {
2010-04-01 09:26:20 +04:00
if ( ! ( entry [ level ] & 0x20 ) ) {
entry [ level ] | = 0x20 ;
access_write_physical ( entry_addr [ level ] , 8 , & entry [ level ] ) ;
2015-03-21 23:08:58 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ level ] , 8 , entry_memtype [ level ] , BX_WRITE ,
2012-01-18 01:50:15 +04:00
( BX_PTE_ACCESS + level ) , ( Bit8u * ) ( & entry [ level ] ) ) ;
2010-04-01 09:26:20 +04:00
}
2009-09-26 10:05:23 +04:00
}
2012-01-17 22:20:55 +04:00
// Update A/D bits if needed
2012-02-14 00:06:04 +04:00
if ( ! ( entry [ leaf ] & 0x20 ) | | ( write & & ! ( entry [ leaf ] & 0x40 ) ) ) {
entry [ leaf ] | = ( 0x20 | ( write < < 6 ) ) ; // Update A and possibly D bits
2010-04-01 09:26:20 +04:00
access_write_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-03-21 23:15:57 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , entry_memtype [ leaf ] , BX_WRITE ,
2012-01-18 01:50:15 +04:00
( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2009-09-26 10:05:23 +04:00
}
}
2010-04-01 16:23:52 +04:00
// Format of Legacy PAE PDPTR entry (PDPTE)
// -----------------------------------------------------------
// 00 | Present (P)
// 02-01 | Reserved (must be zero)
// 03 | Page-Level Write-Through (PWT) (486+), 0=reserved otherwise
// 04 | Page-Level Cache-Disable (PCD) (486+), 0=reserved otherwise
// 08-05 | Reserved (must be zero)
// 11-09 | (ignored)
// PA-12 | Physical address of 4-KByte aligned page directory
// 63-PA | Reserved (must be zero)
// -----------------------------------------------------------
2018-01-27 22:31:39 +03:00
const Bit64u PAGING_PAE_PDPTE_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS | BX_CONST64 ( 0xFFF00000000001E6 ) ;
2010-04-01 16:23:52 +04:00
2021-01-30 11:35:35 +03:00
bool BX_CPP_AttrRegparmN ( 1 ) BX_CPU_C : : CheckPDPTR ( bx_phy_address cr3_val )
2010-04-01 16:23:52 +04:00
{
2012-02-11 00:39:46 +04:00
// with Nested Paging PDPTRs are not loaded for guest page tables but
// accessed on demand as part of the guest page walk
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED )
return 1 ;
# endif
2010-04-01 16:23:52 +04:00
cr3_val & = 0xffffffe0 ;
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) )
2022-07-30 15:26:47 +03:00
cr3_val = translate_guest_physical ( cr3_val , 0 , false /* laddr_valid */ , true /* page walk */ , 0 , BX_READ ) ;
2010-04-07 21:12:17 +04:00
}
# endif
2010-04-01 16:23:52 +04:00
2010-04-03 20:52:33 +04:00
Bit64u pdptr [ 4 ] ;
2012-01-16 00:25:39 +04:00
unsigned n ;
2010-04-03 20:52:33 +04:00
for ( n = 0 ; n < 4 ; n + + ) {
// read and check PDPTE entries
bx_phy_address pdpe_entry_addr = ( bx_phy_address ) ( cr3_val | ( n < < 3 ) ) ;
access_read_physical ( pdpe_entry_addr , 8 , & ( pdptr [ n ] ) ) ;
2015-02-21 00:50:59 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( pdpe_entry_addr , 8 , BX_MEMTYPE_INVALID , BX_READ , ( BX_PDPTR0_ACCESS + n ) , ( Bit8u * ) & ( pdptr [ n ] ) ) ;
2010-04-13 21:56:50 +04:00
2010-04-03 20:52:33 +04:00
if ( pdptr [ n ] & 0x1 ) {
2010-04-03 22:00:30 +04:00
if ( pdptr [ n ] & PAGING_PAE_PDPTE_RESERVED_BITS ) return 0 ;
2010-04-03 20:52:33 +04:00
}
2010-04-01 16:23:52 +04:00
}
2010-04-03 20:52:33 +04:00
// load new PDPTRs
for ( n = 0 ; n < 4 ; n + + )
BX_CPU_THIS_PTR PDPTR_CACHE . entry [ n ] = pdptr [ n ] ;
2010-04-01 16:23:52 +04:00
return 1 ; /* PDPTRs are fine */
}
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
2021-01-30 11:35:35 +03:00
bool BX_CPP_AttrRegparmN ( 1 ) BX_CPU_C : : CheckPDPTR ( Bit64u * pdptr )
2010-04-07 21:12:17 +04:00
{
2012-01-16 00:25:39 +04:00
for ( unsigned n = 0 ; n < 4 ; n + + ) {
2010-04-07 21:12:17 +04:00
if ( pdptr [ n ] & 0x1 ) {
if ( pdptr [ n ] & PAGING_PAE_PDPTE_RESERVED_BITS ) return 0 ;
}
}
return 1 ; /* PDPTRs are fine */
}
# endif
2012-02-14 03:29:01 +04:00
bx_phy_address BX_CPU_C : : translate_linear_load_PDPTR ( bx_address laddr , unsigned user , unsigned rw )
2009-09-26 10:05:23 +04:00
{
2012-02-14 03:29:01 +04:00
unsigned index = ( laddr > > 30 ) & 0x3 ;
Bit64u pdptr ;
2009-09-26 10:05:23 +04:00
2012-01-15 21:54:13 +04:00
# if BX_SUPPORT_SVM
2022-08-23 21:38:00 +03:00
if ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED )
2012-02-11 00:39:46 +04:00
{
2012-02-14 03:29:01 +04:00
bx_phy_address cr3_val = BX_CPU_THIS_PTR cr3 & 0xffffffe0 ;
cr3_val = nested_walk ( cr3_val , BX_RW , 1 ) ;
bx_phy_address pdpe_entry_addr = ( bx_phy_address ) ( cr3_val | ( index < < 3 ) ) ;
access_read_physical ( pdpe_entry_addr , 8 , & pdptr ) ;
2015-02-21 00:50:59 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( pdpe_entry_addr , 8 , BX_MEMTYPE_INVALID , BX_READ , ( BX_PDPTR0_ACCESS + index ) , ( Bit8u * ) & pdptr ) ;
2012-02-14 03:29:01 +04:00
if ( pdptr & 0x1 ) {
if ( pdptr & PAGING_PAE_PDPTE_RESERVED_BITS ) {
BX_DEBUG ( ( " PAE PDPTE%d entry reserved bits set: 0x " FMT_ADDRX64 , index , pdptr ) ) ;
page_fault ( ERROR_RESERVED | ERROR_PROTECTION , laddr , user , rw ) ;
}
}
2012-02-11 00:39:46 +04:00
}
else
2012-01-15 21:54:13 +04:00
# endif
2012-02-11 00:39:46 +04:00
{
2012-02-14 03:29:01 +04:00
pdptr = BX_CPU_THIS_PTR PDPTR_CACHE . entry [ index ] ;
2009-09-26 10:05:23 +04:00
}
2012-02-11 00:39:46 +04:00
2012-02-14 03:29:01 +04:00
if ( ! ( pdptr & 0x1 ) ) {
2012-01-16 19:26:25 +04:00
BX_DEBUG ( ( " PAE PDPTE entry not present ! " ) ) ;
page_fault ( ERROR_NOT_PRESENT , laddr , user , rw ) ;
}
2009-09-26 10:05:23 +04:00
2012-02-14 03:29:01 +04:00
return pdptr ;
}
// Translate a linear address to a physical address in PAE paging mode
2015-02-28 17:01:11 +03:00
bx_phy_address BX_CPU_C : : translate_linear_PAE ( bx_address laddr , Bit32u & lpf_mask , unsigned user , unsigned rw )
2012-02-14 03:29:01 +04:00
{
bx_phy_address entry_addr [ 2 ] ;
Bit64u entry [ 2 ] ;
2015-03-21 23:08:58 +03:00
BxMemtype entry_memtype [ 2 ] = { 0 } ;
2021-05-25 09:27:49 +03:00
bool nx_fault = false ;
2012-02-14 03:29:01 +04:00
int leaf ;
lpf_mask = 0xfff ;
2019-12-15 21:45:04 +03:00
Bit32u combined_access = ( BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ) ;
2012-02-14 03:29:01 +04:00
2012-02-19 16:16:58 +04:00
Bit64u reserved = PAGING_LEGACY_PAE_RESERVED_BITS ;
if ( ! BX_CPU_THIS_PTR efer . get_NXE ( ) )
reserved | = PAGE_DIRECTORY_NX_BIT ;
2012-02-14 03:29:01 +04:00
Bit64u pdpte = translate_linear_load_PDPTR ( laddr , user , rw ) ;
2012-01-17 22:20:55 +04:00
bx_phy_address ppf = pdpte & BX_CONST64 ( 0x000ffffffffff000 ) ;
2015-03-21 23:08:58 +03:00
Bit64u curr_entry = pdpte ;
2009-09-26 10:05:23 +04:00
2012-01-17 22:20:55 +04:00
for ( leaf = BX_LEVEL_PDE ; ; - - leaf ) {
entry_addr [ leaf ] = ppf + ( ( laddr > > ( 9 + 9 * leaf ) ) & 0xff8 ) ;
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) )
2022-07-30 15:26:47 +03:00
entry_addr [ leaf ] = translate_guest_physical ( entry_addr [ leaf ] , laddr , true /* laddr_valid */ , true /* page walk */ , IS_USER_PAGE ( combined_access ) , BX_READ ) ;
2010-04-07 21:12:17 +04:00
}
2012-02-14 03:29:01 +04:00
# endif
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
entry_addr [ leaf ] = nested_walk ( entry_addr [ leaf ] , BX_RW , 1 ) ;
}
2010-04-07 21:12:17 +04:00
# endif
2015-03-21 23:08:58 +03:00
# if BX_SUPPORT_MEMTYPE
2015-03-21 23:28:22 +03:00
entry_memtype [ leaf ] = resolve_memtype ( memtype_by_mtrr ( entry_addr [ leaf ] ) , memtype_by_pat ( calculate_pcd_pwt ( ( Bit32u ) curr_entry ) ) ) ;
2015-03-21 23:08:58 +03:00
# endif
2012-01-17 22:20:55 +04:00
access_read_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-03-21 23:08:58 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , entry_memtype [ leaf ] , BX_READ , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2009-09-26 10:05:23 +04:00
2015-03-21 23:08:58 +03:00
curr_entry = entry [ leaf ] ;
2012-02-19 16:16:58 +04:00
int fault = check_entry_PAE ( bx_paging_level [ leaf ] , curr_entry , reserved , rw , & nx_fault ) ;
2010-04-02 00:06:09 +04:00
if ( fault > = 0 )
2011-05-29 20:28:26 +04:00
page_fault ( fault , laddr , user , rw ) ;
2009-09-26 10:05:23 +04:00
2012-01-17 22:20:55 +04:00
ppf = curr_entry & BX_CONST64 ( 0x000ffffffffff000 ) ;
if ( leaf = = BX_LEVEL_PTE ) break ;
2009-09-26 10:05:23 +04:00
2012-01-17 22:20:55 +04:00
// Ignore CR4.PSE in PAE mode
if ( curr_entry & 0x80 ) {
if ( curr_entry & PAGING_PAE_PDE2M_RESERVED_BITS ) {
2012-02-12 23:13:57 +04:00
BX_DEBUG ( ( " PAE PDE2M: reserved bit is set PDE=0x " FMT_ADDRX64 , curr_entry ) ) ;
2012-01-17 22:20:55 +04:00
page_fault ( ERROR_RESERVED | ERROR_PROTECTION , laddr , user , rw ) ;
}
2012-02-12 23:13:57 +04:00
// Make up the physical page frame address
2012-02-15 23:49:35 +04:00
ppf = ( bx_phy_address ) ( curr_entry & BX_CONST64 ( 0x000fffffffe00000 ) ) ;
2012-01-17 22:20:55 +04:00
lpf_mask = 0x1fffff ;
break ;
}
2019-12-20 10:42:07 +03:00
combined_access & = curr_entry ; // U/S and R/W
2010-04-02 00:06:09 +04:00
}
2021-01-30 11:35:35 +03:00
bool isWrite = ( rw & 1 ) ; // write or r-m-w
2010-04-02 00:06:09 +04:00
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
2021-01-30 11:35:35 +03:00
bool shadow_stack = ( rw & 4 ) ! = 0 ;
2019-12-20 10:42:07 +03:00
if ( shadow_stack ) {
// shadow stack pages:
// - R/W bit=1 in every paging structure entry except the leaf
// - R/W bit=0 and Dirty=1 for leaf entry
2021-01-30 11:35:35 +03:00
bool shadow_stack_page = ( ( combined_access & BX_COMBINED_ACCESS_WRITE ) ! = 0 ) & & ( ( entry [ leaf ] & 0x40 ) ! = 0 ) & & ( ( entry [ leaf ] & 0x02 ) = = 0 ) ;
2019-12-20 10:42:07 +03:00
if ( ! shadow_stack_page )
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
2009-09-26 10:05:23 +04:00
2019-12-20 10:42:07 +03:00
combined_access & = entry [ leaf ] ; // U/S and R/W
// must be to shadow stack page, check that U/S match
if ( ( combined_access & BX_COMBINED_ACCESS_USER ) ^ ( user < < 2 ) ) {
BX_DEBUG ( ( " shadow stack U/S access mismatch " ) ) ;
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
combined_access | = BX_COMBINED_SHADOW_STACK ;
}
else
# endif
{
combined_access & = entry [ leaf ] ; // U/S and R/W
unsigned priv_index = ( BX_CPU_THIS_PTR cr0 . get_WP ( ) < < 4 ) | // bit 4
( user < < 3 ) | // bit 3
2021-07-27 22:18:34 +03:00
( combined_access | ( unsigned ) isWrite ) ; // bit 2,1,0
2019-12-20 10:42:07 +03:00
if ( ! priv_check [ priv_index ] | | nx_fault )
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2011-05-29 20:28:26 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_SMEP ( ) & & rw = = BX_EXECUTE & & ! user ) {
2019-12-15 21:45:04 +03:00
if ( combined_access & BX_COMBINED_ACCESS_USER )
2011-05-29 20:28:26 +04:00
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2009-09-26 10:05:23 +04:00
2012-09-10 19:22:26 +04:00
// SMAP protections are disabled if EFLAGS.AC=1
if ( BX_CPU_THIS_PTR cr4 . get_SMAP ( ) & & ! BX_CPU_THIS_PTR get_AC ( ) & & rw ! = BX_EXECUTE & & ! user ) {
2019-12-15 21:45:04 +03:00
if ( combined_access & BX_COMBINED_ACCESS_USER )
2012-09-10 19:22:26 +04:00
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2009-09-26 10:05:23 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_PGE ( ) )
2019-12-20 10:42:07 +03:00
combined_access | = ( entry [ leaf ] & BX_COMBINED_GLOBAL_PAGE ) ; // G
2015-02-28 17:06:04 +03:00
2015-03-01 23:55:23 +03:00
# if BX_SUPPORT_MEMTYPE
combined_access | = ( memtype_by_pat ( calculate_pat ( ( Bit32u ) entry [ leaf ] , lpf_mask ) ) < < 9 ) ;
# endif
2002-09-17 00:23:38 +04:00
2012-01-17 22:20:55 +04:00
// Update A/D bits if needed
2015-03-21 23:08:58 +03:00
update_access_dirty_PAE ( entry_addr , entry , entry_memtype , BX_LEVEL_PDE , leaf , isWrite ) ;
2008-05-19 22:10:32 +04:00
2015-02-28 17:01:11 +03:00
return ( ppf | combined_access ) ;
2008-05-19 22:10:32 +04:00
}
2009-06-15 13:30:56 +04:00
# endif
2008-08-01 17:28:44 +04:00
2010-04-01 16:23:52 +04:00
// Format of a PDE that Maps a 4-MByte Page
// -----------------------------------------------------------
// 00 | Present (P)
// 01 | R/W
// 02 | U/S
// 03 | Page-Level Write-Through (PWT)
// 04 | Page-Level Cache-Disable (PCD)
// 05 | Accessed (A)
// 06 | Dirty (D)
// 07 | Page size, must be 1 to indicate 4-Mbyte page
// 08 | Global (G) (if CR4.PGE=1, ignored otherwise)
// 11-09 | (ignored)
// 12 | PAT (if PAT is supported, reserved otherwise)
// PA-13 | Bits PA-32 of physical address of the 4-MByte page
// 21-PA | Reserved (must be zero)
// 31-22 | Bits 31-22 of physical address of the 4-MByte page
// -----------------------------------------------------------
2018-11-22 14:51:33 +03:00
# if BX_PHY_ADDRESS_WIDTH > 40
const Bit32u PAGING_PDE4M_RESERVED_BITS = 0 ; // there are no reserved bits in PDE4M when physical address is wider than 40 bit
# else
2018-01-27 22:31:39 +03:00
const Bit32u PAGING_PDE4M_RESERVED_BITS = ( ( 1 < < ( 41 - BX_PHY_ADDRESS_WIDTH ) ) - 1 ) < < ( 13 + BX_PHY_ADDRESS_WIDTH - 32 ) ;
2018-11-22 14:51:33 +03:00
# endif
2010-04-01 16:23:52 +04:00
2012-01-15 21:54:13 +04:00
// Translate a linear address to a physical address in legacy paging mode
2015-02-28 17:01:11 +03:00
bx_phy_address BX_CPU_C : : translate_linear_legacy ( bx_address laddr , Bit32u & lpf_mask , unsigned user , unsigned rw )
2012-01-15 21:54:13 +04:00
{
2012-01-22 22:39:15 +04:00
bx_phy_address entry_addr [ 2 ] , ppf = ( Bit32u ) BX_CPU_THIS_PTR cr3 & BX_CR3_PAGING_MASK ;
Bit32u entry [ 2 ] ;
2015-03-21 23:08:58 +03:00
BxMemtype entry_memtype [ 2 ] = { 0 } ;
2012-01-17 22:20:55 +04:00
int leaf ;
2012-01-15 21:54:13 +04:00
2012-01-19 10:38:22 +04:00
lpf_mask = 0xfff ;
2019-12-15 21:45:04 +03:00
Bit32u combined_access = ( BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ) ;
2015-03-21 23:08:58 +03:00
Bit32u curr_entry = ( Bit32u ) BX_CPU_THIS_PTR cr3 ;
2012-01-19 10:38:22 +04:00
2012-01-17 22:20:55 +04:00
for ( leaf = BX_LEVEL_PDE ; ; - - leaf ) {
entry_addr [ leaf ] = ppf + ( ( laddr > > ( 10 + 10 * leaf ) ) & 0xffc ) ;
2012-01-15 21:54:13 +04:00
# if BX_SUPPORT_VMX >= 2
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) )
2022-07-30 15:26:47 +03:00
entry_addr [ leaf ] = translate_guest_physical ( entry_addr [ leaf ] , laddr , true /* laddr_valid */ , true /* page walk */ , IS_USER_PAGE ( combined_access ) , BX_READ ) ;
2012-01-15 21:54:13 +04:00
}
2012-02-14 03:29:01 +04:00
# endif
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
entry_addr [ leaf ] = nested_walk ( entry_addr [ leaf ] , BX_RW , 1 ) ;
}
2012-01-15 21:54:13 +04:00
# endif
2015-03-21 23:08:58 +03:00
# if BX_SUPPORT_MEMTYPE
2015-03-21 23:28:22 +03:00
entry_memtype [ leaf ] = resolve_memtype ( memtype_by_mtrr ( entry_addr [ leaf ] ) , memtype_by_pat ( calculate_pcd_pwt ( curr_entry ) ) ) ;
2015-03-21 23:08:58 +03:00
# endif
2012-01-17 22:20:55 +04:00
access_read_physical ( entry_addr [ leaf ] , 4 , & entry [ leaf ] ) ;
2015-03-21 23:08:58 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 4 , entry_memtype [ leaf ] , BX_READ , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-01-15 21:54:13 +04:00
2015-03-21 23:15:57 +03:00
curr_entry = entry [ leaf ] ;
2012-01-17 22:20:55 +04:00
if ( ! ( curr_entry & 0x1 ) ) {
BX_DEBUG ( ( " %s: entry not present " , bx_paging_level [ leaf ] ) ) ;
2012-01-15 21:54:13 +04:00
page_fault ( ERROR_NOT_PRESENT , laddr , user , rw ) ;
}
2012-01-17 22:20:55 +04:00
ppf = curr_entry & 0xfffff000 ;
2012-01-15 21:54:13 +04:00
2012-01-17 22:20:55 +04:00
if ( leaf = = BX_LEVEL_PTE ) break ;
# if BX_CPU_LEVEL >= 5
if ( ( curr_entry & 0x80 ) ! = 0 & & BX_CPU_THIS_PTR cr4 . get_PSE ( ) ) {
// 4M paging, only if CR4.PSE enabled, ignore PDE.PS otherwise
if ( curr_entry & PAGING_PDE4M_RESERVED_BITS ) {
BX_DEBUG ( ( " PSE PDE4M: reserved bit is set: PDE=0x%08x " , entry [ BX_LEVEL_PDE ] ) ) ;
page_fault ( ERROR_RESERVED | ERROR_PROTECTION , laddr , user , rw ) ;
}
// make up the physical frame number
2012-02-15 23:49:35 +04:00
ppf = ( curr_entry & 0xffc00000 ) ;
2012-01-17 22:20:55 +04:00
# if BX_PHY_ADDRESS_WIDTH > 32
ppf | = ( ( bx_phy_address ) ( curr_entry & 0x003fe000 ) ) < < 19 ;
# endif
lpf_mask = 0x3fffff ;
break ;
}
# endif
2019-12-20 10:42:07 +03:00
combined_access & = curr_entry ; // U/S and R/W
2012-01-15 23:38:00 +04:00
}
2021-01-30 11:35:35 +03:00
bool isWrite = ( rw & 1 ) ; // write or r-m-w
2012-01-19 10:38:22 +04:00
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
2021-01-30 11:35:35 +03:00
bool shadow_stack = ( rw & 4 ) ! = 0 ;
2019-12-20 10:42:07 +03:00
if ( shadow_stack ) {
// shadow stack pages:
// - R/W bit=1 in every paging structure entry except the leaf
// - R/W bit=0 and Dirty=1 for leaf entry
2021-01-30 11:35:35 +03:00
bool shadow_stack_page = ( ( combined_access & BX_COMBINED_ACCESS_WRITE ) ! = 0 ) & & ( ( entry [ leaf ] & 0x40 ) ! = 0 ) & & ( ( entry [ leaf ] & 0x02 ) = = 0 ) ;
2019-12-20 10:42:07 +03:00
if ( ! shadow_stack_page )
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
combined_access & = entry [ leaf ] ; // U/S and R/W
// must be to shadow stack page, check that U/S match
if ( ( combined_access & BX_COMBINED_ACCESS_USER ) ^ ( user < < 2 ) ) {
BX_DEBUG ( ( " shadow stack U/S access mismatch " ) ) ;
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
combined_access | = BX_COMBINED_SHADOW_STACK ;
}
else
# endif
{
combined_access & = entry [ leaf ] ; // U/S and R/W
unsigned priv_index =
2012-01-15 21:54:13 +04:00
# if BX_CPU_LEVEL >= 4
2019-12-20 10:42:07 +03:00
( BX_CPU_THIS_PTR cr0 . get_WP ( ) < < 4 ) | // bit 4
2012-01-15 21:54:13 +04:00
# endif
2019-12-20 10:42:07 +03:00
( user < < 3 ) | // bit 3
2021-07-27 22:18:34 +03:00
( combined_access | ( unsigned ) isWrite ) ; // bit 2,1,0
2012-01-15 21:54:13 +04:00
2019-12-20 10:42:07 +03:00
if ( ! priv_check [ priv_index ] )
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2012-01-15 21:54:13 +04:00
# if BX_CPU_LEVEL >= 6
2012-01-15 23:38:00 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_SMEP ( ) & & rw = = BX_EXECUTE & & ! user ) {
2019-12-15 21:45:04 +03:00
if ( combined_access & BX_COMBINED_ACCESS_USER )
2012-01-15 23:38:00 +04:00
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2012-01-15 21:54:13 +04:00
2012-09-10 19:22:26 +04:00
// SMAP protections are disabled if EFLAGS.AC=1
if ( BX_CPU_THIS_PTR cr4 . get_SMAP ( ) & & ! BX_CPU_THIS_PTR get_AC ( ) & & rw ! = BX_EXECUTE & & ! user ) {
2019-12-15 21:45:04 +03:00
if ( combined_access & BX_COMBINED_ACCESS_USER )
2012-09-10 19:22:26 +04:00
page_fault ( ERROR_PROTECTION , laddr , user , rw ) ;
}
2012-01-15 23:38:00 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_PGE ( ) )
2019-12-20 10:42:07 +03:00
combined_access | = ( entry [ leaf ] & BX_COMBINED_GLOBAL_PAGE ) ;
2015-03-01 23:55:23 +03:00
# if BX_SUPPORT_MEMTYPE
combined_access | = ( memtype_by_pat ( calculate_pat ( entry [ leaf ] , lpf_mask ) ) < < 9 ) ;
2012-01-15 21:54:13 +04:00
# endif
2015-03-01 23:55:23 +03:00
# endif
2015-02-28 17:06:04 +03:00
2015-03-21 23:08:58 +03:00
update_access_dirty ( entry_addr , entry , entry_memtype , leaf , isWrite ) ;
2012-02-14 00:06:04 +04:00
2015-02-28 17:01:11 +03:00
return ( ppf | combined_access ) ;
2012-02-14 00:06:04 +04:00
}
2015-03-21 23:08:58 +03:00
void BX_CPU_C : : update_access_dirty ( bx_phy_address * entry_addr , Bit32u * entry , BxMemtype * entry_memtype , unsigned leaf , unsigned write )
2012-02-14 00:06:04 +04:00
{
2012-01-15 23:38:00 +04:00
if ( leaf = = BX_LEVEL_PTE ) {
// Update PDE A bit if needed
if ( ! ( entry [ BX_LEVEL_PDE ] & 0x20 ) ) {
entry [ BX_LEVEL_PDE ] | = 0x20 ;
access_write_physical ( entry_addr [ BX_LEVEL_PDE ] , 4 , & entry [ BX_LEVEL_PDE ] ) ;
2015-03-21 23:08:58 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ BX_LEVEL_PDE ] , 4 , entry_memtype [ BX_LEVEL_PDE ] , BX_WRITE , BX_PDE_ACCESS , ( Bit8u * ) ( & entry [ BX_LEVEL_PDE ] ) ) ;
2012-01-15 21:54:13 +04:00
}
2012-01-15 23:38:00 +04:00
}
2012-01-15 21:54:13 +04:00
2012-01-15 23:38:00 +04:00
// Update A/D bits if needed
2012-02-14 00:06:04 +04:00
if ( ! ( entry [ leaf ] & 0x20 ) | | ( write & & ! ( entry [ leaf ] & 0x40 ) ) ) {
entry [ leaf ] | = ( 0x20 | ( write < < 6 ) ) ; // Update A and possibly D bits
2012-01-15 23:38:00 +04:00
access_write_physical ( entry_addr [ leaf ] , 4 , & entry [ leaf ] ) ;
2015-03-21 23:08:58 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 4 , entry_memtype [ leaf ] , BX_WRITE , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-01-15 21:54:13 +04:00
}
}
2008-05-19 22:10:32 +04:00
// Translate a linear address to a physical address
2013-01-27 23:27:30 +04:00
bx_phy_address BX_CPU_C : : translate_linear ( bx_TLB_entry * tlbEntry , bx_address laddr , unsigned user , unsigned rw )
2008-05-19 22:10:32 +04:00
{
2011-05-29 20:28:26 +04:00
# if BX_SUPPORT_X86_64
2012-01-20 00:01:32 +04:00
if ( ! long_mode ( ) ) laddr & = 0xffffffff ;
2011-05-29 20:28:26 +04:00
# endif
2008-05-19 22:10:32 +04:00
bx_phy_address paddress , ppf , poffset = PAGE_OFFSET ( laddr ) ;
2011-06-04 00:50:55 +04:00
unsigned isWrite = rw & 1 ; // write or r-m-w
unsigned isExecute = ( rw = = BX_EXECUTE ) ;
2019-12-20 10:42:07 +03:00
unsigned isShadowStack = ( rw & 4 ) ; // 4 if shadowstack and 0 otherwise
2015-09-28 22:09:32 +03:00
bx_address lpf = LPFOf ( laddr ) ;
2008-05-19 22:10:32 +04:00
2014-10-14 19:59:10 +04:00
INC_TLB_STAT ( tlbLookups ) ;
2015-09-28 22:09:32 +03:00
if ( isExecute )
INC_TLB_STAT ( tlbExecuteLookups ) ;
if ( isWrite )
INC_TLB_STAT ( tlbWriteLookups ) ;
2008-05-19 22:10:32 +04:00
// already looked up TLB for code access
2013-01-16 21:28:20 +04:00
if ( ! isExecute & & TLB_LPFOf ( tlbEntry - > lpf ) = = lpf )
2008-05-19 22:10:32 +04:00
{
2008-08-03 23:53:09 +04:00
paddress = tlbEntry - > ppf | poffset ;
2008-05-19 22:10:32 +04:00
2016-03-02 23:44:42 +03:00
# if BX_SUPPORT_PKEYS
if ( isWrite ) {
2019-12-20 10:42:07 +03:00
if ( tlbEntry - > accessBits & ( 1 < < ( isShadowStack | ( isWrite < < 1 ) | user ) ) & BX_CPU_THIS_PTR wr_pkey [ tlbEntry - > pkey ] )
2016-03-02 23:44:42 +03:00
return paddress ;
}
else {
2019-12-20 10:42:07 +03:00
if ( tlbEntry - > accessBits & ( 1 < < ( isShadowStack | user ) ) & BX_CPU_THIS_PTR rd_pkey [ tlbEntry - > pkey ] )
2016-03-02 23:44:42 +03:00
return paddress ;
}
# else
2019-12-20 10:42:07 +03:00
if ( tlbEntry - > accessBits & ( 1 < < ( isShadowStack | ( isWrite < < 1 ) | user ) ) )
2008-05-19 22:10:32 +04:00
return paddress ;
2016-03-02 23:44:42 +03:00
# endif
2008-05-19 22:10:32 +04:00
// The current access does not have permission according to the info
// in our TLB cache entry. Re-walk the page tables, in case there is
// updated information in the memory image, and let the long path code
// generate an exception if one is warranted.
2018-08-30 23:18:27 +03:00
// Invalidate the TLB entry before re-walk as re-walk may end with paging fault.
// The entry will be reinitialized later if page walk succeeds.
tlbEntry - > invalidate ( ) ;
2008-05-19 22:10:32 +04:00
}
2014-10-14 19:59:10 +04:00
INC_TLB_STAT ( tlbMisses ) ;
2015-09-28 22:09:32 +03:00
if ( isExecute )
INC_TLB_STAT ( tlbExecuteMisses ) ;
if ( isWrite )
INC_TLB_STAT ( tlbWriteMisses ) ;
2012-01-17 22:20:55 +04:00
2015-03-01 23:55:23 +03:00
Bit32u lpf_mask = 0xfff ; // 4K pages
2019-12-20 10:42:07 +03:00
Bit32u combined_access = BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ;
2016-03-02 23:44:42 +03:00
# if BX_SUPPORT_X86_64
Bit32u pkey = 0 ;
# endif
2015-03-01 23:55:23 +03:00
2010-03-19 20:00:05 +03:00
if ( BX_CPU_THIS_PTR cr0 . get_PG ( ) )
{
2019-12-20 10:42:07 +03:00
BX_DEBUG ( ( " page walk for%s address 0x " FMT_LIN_ADDRX , isShadowStack ? " shadow stack " : " " , laddr ) ) ;
2008-05-19 22:10:32 +04:00
2009-06-15 13:30:56 +04:00
# if BX_CPU_LEVEL >= 6
2012-01-17 22:20:55 +04:00
# if BX_SUPPORT_X86_64
if ( long_mode ( ) )
2016-03-02 23:44:42 +03:00
paddress = translate_linear_long_mode ( laddr , lpf_mask , pkey , user , rw ) ;
2010-03-19 20:00:05 +03:00
else
2012-01-17 22:20:55 +04:00
# endif
if ( BX_CPU_THIS_PTR cr4 . get_PAE ( ) )
2015-02-28 17:01:11 +03:00
paddress = translate_linear_PAE ( laddr , lpf_mask , user , rw ) ;
2012-01-17 22:20:55 +04:00
else
2022-08-23 21:38:00 +03:00
# endif
2015-02-28 17:01:11 +03:00
paddress = translate_linear_legacy ( laddr , lpf_mask , user , rw ) ;
2015-03-01 23:55:23 +03:00
// translate_linear functions return combined U/S, R/W bits, Global Page bit
// and also effective page tables memory type in lower 12 bits of the physical address.
// Bit 1 - R/W bit
// Bit 2 - U/S bit
// Bit 9,10,11 - Effective Memory Table from page tables
2015-02-28 17:01:11 +03:00
combined_access = paddress & lpf_mask ;
paddress = ( paddress & ~ ( ( Bit64u ) lpf_mask ) ) | ( laddr & lpf_mask ) ;
2001-04-10 05:04:59 +04:00
2009-06-15 13:30:56 +04:00
# if BX_CPU_LEVEL >= 5
2019-12-09 21:37:02 +03:00
if ( lpf_mask > 0xfff ) {
if ( isExecute )
BX_CPU_THIS_PTR ITLB . split_large = true ;
else
BX_CPU_THIS_PTR DTLB . split_large = true ;
}
2008-12-19 19:03:25 +03:00
# endif
2010-03-19 20:00:05 +03:00
}
else {
// no paging
2012-02-15 23:49:35 +04:00
paddress = ( bx_phy_address ) laddr ;
2015-03-01 23:55:23 +03:00
combined_access | = ( BX_MEMTYPE_WB < < 9 ) ; // act as memory type by paging is WB
2010-03-19 20:00:05 +03:00
}
2008-12-19 19:03:25 +03:00
2012-01-06 14:30:07 +04:00
// Calculate physical memory address and fill in TLB cache entry
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
2022-08-14 21:18:25 +03:00
bool spp_page = false ;
2010-04-07 21:12:17 +04:00
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) ) {
2022-08-14 21:18:25 +03:00
paddress = translate_guest_physical ( paddress , laddr , true /* laddr_valid */ , false /* page walk */ , IS_USER_PAGE ( combined_access ) , rw , isShadowStack & ! user , & spp_page ) ;
2010-04-07 21:12:17 +04:00
}
}
# endif
2012-02-14 03:29:01 +04:00
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
2021-04-27 11:22:45 +03:00
// hack: ignore isExecute attribute in SMM mode under SVM virtualization
if ( BX_CPU_THIS_PTR in_smm & & rw = = BX_EXECUTE ) rw = BX_READ ;
2012-02-14 03:29:01 +04:00
paddress = nested_walk ( paddress , rw , 0 ) ;
}
# endif
2012-02-15 23:49:35 +04:00
paddress = A20ADDR ( paddress ) ;
ppf = PPFOf ( paddress ) ;
2010-04-07 21:12:17 +04:00
2008-08-15 02:26:15 +04:00
// direct memory access is NOT allowed by default
2012-01-15 21:54:13 +04:00
tlbEntry - > lpf = lpf | TLB_NoHostPtr ;
2010-04-07 18:38:53 +04:00
tlbEntry - > lpf_mask = lpf_mask ;
2016-03-02 23:44:42 +03:00
# if BX_SUPPORT_PKEYS
tlbEntry - > pkey = pkey ;
# endif
2008-04-22 00:17:45 +04:00
tlbEntry - > ppf = ppf ;
2008-08-15 02:26:15 +04:00
tlbEntry - > accessBits = 0 ;
2008-08-08 02:14:38 +04:00
2019-12-09 21:37:02 +03:00
if ( isExecute ) {
2013-01-20 00:14:44 +04:00
tlbEntry - > accessBits | = TLB_SysExecuteOK ;
2019-12-09 21:37:02 +03:00
}
else {
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
if ( isShadowStack ) {
tlbEntry - > accessBits | = TLB_SysReadOK | TLB_SysReadShadowStackOK ;
if ( isWrite )
tlbEntry - > accessBits | = TLB_SysWriteShadowStackOK ;
}
else
# endif
{
tlbEntry - > accessBits | = TLB_SysReadOK ;
if ( isWrite )
tlbEntry - > accessBits | = TLB_SysWriteOK ;
}
2019-12-09 21:37:02 +03:00
}
2013-01-16 21:28:20 +04:00
2013-01-20 00:14:44 +04:00
if ( ! BX_CPU_THIS_PTR cr0 . get_PG ( )
# if BX_SUPPORT_VMX >= 2
2013-01-27 23:27:30 +04:00
& & ! ( BX_CPU_THIS_PTR in_vmx_guest & & SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) )
2013-01-16 21:28:20 +04:00
# endif
2013-01-20 00:14:44 +04:00
# if BX_SUPPORT_SVM
& & ! ( BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED )
# endif
) {
2019-12-09 21:37:02 +03:00
if ( isExecute )
tlbEntry - > accessBits | = TLB_UserExecuteOK ;
else
tlbEntry - > accessBits | = TLB_UserReadOK | TLB_UserWriteOK ;
2013-01-16 21:28:20 +04:00
}
2013-01-20 00:14:44 +04:00
else {
2019-12-15 21:45:04 +03:00
if ( ( combined_access & BX_COMBINED_ACCESS_USER ) ! = 0 ) {
2013-01-20 00:14:44 +04:00
if ( user ) {
2019-12-09 21:37:02 +03:00
if ( isExecute ) {
2013-01-20 00:14:44 +04:00
tlbEntry - > accessBits | = TLB_UserExecuteOK ;
2019-12-09 21:37:02 +03:00
}
else {
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
if ( isShadowStack ) {
tlbEntry - > accessBits | = TLB_UserReadOK | TLB_UserReadShadowStackOK ;
if ( isWrite )
tlbEntry - > accessBits | = TLB_UserWriteShadowStackOK ;
}
else
# endif
{
tlbEntry - > accessBits | = TLB_UserReadOK ;
if ( isWrite )
tlbEntry - > accessBits | = TLB_UserWriteOK ;
}
2019-12-09 21:37:02 +03:00
}
2013-01-20 00:14:44 +04:00
}
2013-01-16 21:28:20 +04:00
# if BX_CPU_LEVEL >= 6
2019-12-09 21:37:02 +03:00
if ( isExecute ) {
if ( BX_CPU_THIS_PTR cr4 . get_SMEP ( ) )
tlbEntry - > accessBits & = ~ TLB_SysExecuteOK ;
}
else {
if ( BX_CPU_THIS_PTR cr4 . get_SMAP ( ) )
tlbEntry - > accessBits & = ~ ( TLB_SysReadOK | TLB_SysWriteOK ) ;
}
2013-01-16 21:28:20 +04:00
# endif
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
// system shadow stack accesses cannot access user pages
tlbEntry - > accessBits & = ~ ( TLB_SysReadShadowStackOK | TLB_SysWriteShadowStackOK ) ;
# endif
2013-01-20 00:14:44 +04:00
}
2008-08-11 00:32:00 +04:00
}
2008-08-08 02:14:38 +04:00
2022-08-14 21:09:18 +03:00
# if BX_SUPPORT_VMX >= 2
2022-08-14 21:18:25 +03:00
if ( spp_page ) {
2022-08-14 21:09:18 +03:00
// the page was write-allowed only due to SPP, such pages cannot be cached as WriteOK
tlbEntry - > accessBits & = ~ ( TLB_SysWriteShadowStackOK | TLB_UserWriteShadowStackOK | TLB_SysWriteOK | TLB_UserWriteOK ) ;
}
# endif
2009-06-15 13:30:56 +04:00
# if BX_CPU_LEVEL >= 6
2019-12-20 10:42:07 +03:00
if ( combined_access & BX_COMBINED_GLOBAL_PAGE ) // Global bit
2008-08-15 02:26:15 +04:00
tlbEntry - > accessBits | = TLB_GlobalPage ;
2010-03-23 22:58:20 +03:00
# endif
2004-12-14 01:26:36 +03:00
// Attempt to get a host pointer to this physical page. Put that
// pointer in the TLB cache. Note if the request is vetoed, NULL
Now, when you compile with --enable-guest2host-tlb, non-paged
mode uses the notion of the guest-to-host TLB. This has the
benefit of allowing more uniform and streamlined acceleration
code in access.cc which does not have to check if CR0.PG
is set, eliminating a few instructions per guest access.
Shaved just a little off execution time, as expected.
Also, access_linear now breaks accesses which span two pages,
into two calls the the physical memory routines, when paging
is off, just like it always has for paging on. Besides
being more uniform, this allows the physical memory access
routines to known the complete data item is contained
within a single physical page, and stop reapplying the
A20ADDR() macro to pointers as it increments them.
Perhaps things can be optimized a little more now there too...
I renamed the routines to {read,write}PhysicalPage() as
a reminder that these routines now operate on data
solely within one page.
I also added a little code so that the paging module is
notified when the A20 line is tweaked, so it can dump
whatever mappings it wants to.
2002-09-05 06:31:24 +04:00
// will be returned, and it's OK to OR zero in anyways.
2010-03-16 17:51:20 +03:00
tlbEntry - > hostPageAddr = BX_CPU_THIS_PTR getHostMemAddr ( ppf , rw ) ;
2008-04-22 00:17:45 +04:00
if ( tlbEntry - > hostPageAddr ) {
2005-06-15 00:55:57 +04:00
// All access allowed also via direct pointer
2008-05-23 21:49:46 +04:00
# if BX_X86_DEBUGGER
2011-04-09 09:12:28 +04:00
if ( ! hwbreakpoint_check ( laddr , BX_HWDebugMemW , BX_HWDebugMemRW ) )
2008-05-23 21:49:46 +04:00
# endif
2008-08-15 18:30:50 +04:00
tlbEntry - > lpf = lpf ; // allow direct access with HostPtr
2005-06-15 00:55:57 +04:00
}
2001-04-10 05:04:59 +04:00
2015-02-19 23:23:08 +03:00
# if BX_SUPPORT_MEMTYPE
2015-03-02 23:51:59 +03:00
tlbEntry - > memtype = resolve_memtype ( memtype_by_mtrr ( tlbEntry - > ppf ) , combined_access > > 9 /* effective page tables memory type */ ) ;
2015-02-19 23:23:08 +03:00
# endif
2007-09-20 21:33:35 +04:00
return paddress ;
2001-04-10 05:04:59 +04:00
}
2015-02-23 00:26:26 +03:00
const char * get_memtype_name ( BxMemtype memtype )
{
static const char * mem_type_string [ 9 ] = { " UC " , " WC " , " RESERVED2 " , " RESERVED3 " , " WT " , " WP " , " WB " , " UC- " , " INVALID " } ;
if ( memtype > BX_MEMTYPE_INVALID ) memtype = BX_MEMTYPE_INVALID ;
return mem_type_string [ memtype ] ;
}
2015-02-23 22:55:55 +03:00
# if BX_SUPPORT_MEMTYPE
2015-02-23 00:26:26 +03:00
BxMemtype BX_CPP_AttrRegparmN ( 1 ) BX_CPU_C : : memtype_by_mtrr ( bx_phy_address pAddr )
2015-02-19 23:23:08 +03:00
{
# if BX_CPU_LEVEL >= 6
if ( is_cpu_extension_supported ( BX_ISA_MTRR ) ) {
2015-02-21 00:50:59 +03:00
const Bit32u BX_MTRR_DEFTYPE_FIXED_MTRR_ENABLE_MASK = ( 1 < < 10 ) ;
const Bit32u BX_MTRR_ENABLE_MASK = ( 1 < < 11 ) ;
if ( BX_CPU_THIS_PTR msr . mtrr_deftype & BX_MTRR_ENABLE_MASK ) {
// fixed range MTRR take priority over variable range MTRR when enabled
if ( pAddr < 0x100000 & & ( BX_CPU_THIS_PTR msr . mtrr_deftype & BX_MTRR_DEFTYPE_FIXED_MTRR_ENABLE_MASK ) ) {
if ( pAddr < 0x80000 ) {
unsigned index = ( pAddr > > 16 ) & 0x7 ;
2015-02-24 00:17:33 +03:00
return ( BxMemtype ) BX_CPU_THIS_PTR msr . mtrrfix64k . ubyte ( index ) ;
2015-02-21 00:50:59 +03:00
}
if ( pAddr < 0xc0000 ) {
unsigned index = ( ( pAddr - 0x80000 ) > > 14 ) & 0xf ;
2015-02-24 00:17:33 +03:00
return ( BxMemtype ) BX_CPU_THIS_PTR msr . mtrrfix16k [ index > > 3 ] . ubyte ( index & 0x7 ) ;
2015-02-21 00:50:59 +03:00
}
else {
unsigned index = ( pAddr - 0xc0000 ) > > 12 ;
2015-02-24 00:17:33 +03:00
return ( BxMemtype ) BX_CPU_THIS_PTR msr . mtrrfix4k [ index > > 3 ] . ubyte ( index & 0x7 ) ;
2015-02-21 00:50:59 +03:00
}
}
int memtype = - 1 ;
for ( unsigned i = 0 ; i < BX_NUM_VARIABLE_RANGE_MTRRS ; i + + ) {
Bit64u base = BX_CPU_THIS_PTR msr . mtrrphys [ i * 2 ] ;
Bit64u mask = BX_CPU_THIS_PTR msr . mtrrphys [ i * 2 + 1 ] ;
if ( ( mask & BX_MTRR_ENABLE_MASK ) = = 0 ) continue ;
mask = PPFOf ( mask ) ;
if ( ( pAddr & mask ) = = ( base & mask ) ) {
//
// Matched variable MTRR, check overlap rules:
// - if two or more variable memory ranges match and the memory types are identical,
// then that memory type is used.
// - if two or more variable memory ranges match and one of the memory types is UC,
// the UC memory type used.
// - if two or more variable memory ranges match and the memory types are WT and WB,
// the WT memory type is used.
// - For overlaps not defined by the above rules, processor behavior is undefined.
//
BxMemtype curr_memtype = BxMemtype ( base & 0xff ) ;
if ( curr_memtype = = BX_MEMTYPE_UC )
return BX_MEMTYPE_UC ;
if ( memtype = = - 1 ) {
memtype = curr_memtype ; // first match
}
2015-02-23 00:26:26 +03:00
else if ( memtype ! = ( int ) curr_memtype ) {
2015-02-21 00:50:59 +03:00
if ( curr_memtype = = BX_MEMTYPE_WT & & memtype = = BX_MEMTYPE_WB )
memtype = BX_MEMTYPE_WT ;
else if ( curr_memtype = = BX_MEMTYPE_WB & & memtype = = BX_MEMTYPE_WT )
memtype = BX_MEMTYPE_WT ;
else
memtype = BX_MEMTYPE_INVALID ;
}
}
}
if ( memtype ! = - 1 )
return BxMemtype ( memtype ) ;
// didn't match any variable range MTRR, return default memory type
return BxMemtype ( BX_CPU_THIS_PTR msr . mtrr_deftype & 0xff ) ;
}
// return UC memory type when MTRRs are not enabled
return BX_MEMTYPE_UC ;
2015-02-19 23:23:08 +03:00
}
# endif
2015-02-21 00:50:59 +03:00
// return INVALID memory type when MTRRs are not supported
return BX_MEMTYPE_INVALID ;
2015-02-19 23:23:08 +03:00
}
2015-02-23 00:26:26 +03:00
BxMemtype BX_CPP_AttrRegparmN ( 1 ) BX_CPU_C : : memtype_by_pat ( unsigned pat )
{
2015-02-24 00:17:33 +03:00
return ( BxMemtype ) BX_CPU_THIS_PTR msr . pat . ubyte ( pat ) ;
2015-02-23 00:26:26 +03:00
}
2015-03-02 23:51:59 +03:00
BxMemtype BX_CPP_AttrRegparmN ( 2 ) BX_CPU_C : : resolve_memtype ( BxMemtype mtrr_memtype , BxMemtype pat_memtype )
2015-02-23 00:26:26 +03:00
{
if ( BX_CPU_THIS_PTR cr0 . get_CD ( ) )
return BX_MEMTYPE_UC ;
if ( mtrr_memtype = = BX_MEMTYPE_INVALID ) // will result in ignore of MTRR memory type
mtrr_memtype = BX_MEMTYPE_WB ;
switch ( pat_memtype ) {
case BX_MEMTYPE_UC :
case BX_MEMTYPE_WC :
return pat_memtype ;
case BX_MEMTYPE_WT :
case BX_MEMTYPE_WP :
if ( mtrr_memtype = = BX_MEMTYPE_WC ) return BX_MEMTYPE_UC ;
return ( mtrr_memtype < pat_memtype ) ? mtrr_memtype : pat_memtype ;
case BX_MEMTYPE_WB :
2015-03-02 23:51:59 +03:00
return mtrr_memtype ;
2015-02-23 00:26:26 +03:00
case BX_MEMTYPE_UC_WEAK :
return ( mtrr_memtype = = BX_MEMTYPE_WC ) ? BX_MEMTYPE_WC : BX_MEMTYPE_UC ;
default :
BX_PANIC ( ( " unexpected PAT memory type: %u " , ( unsigned ) pat_memtype ) ) ;
}
return BX_MEMTYPE_INVALID ; // keep compiler happy
}
2015-02-19 23:23:08 +03:00
# endif
2012-02-14 03:29:01 +04:00
# if BX_SUPPORT_SVM
void BX_CPU_C : : nested_page_fault ( unsigned fault , bx_phy_address guest_paddr , unsigned rw , unsigned is_page_walk )
{
unsigned isWrite = rw & 1 ;
Bit64u error_code = fault | ( 1 < < 2 ) | ( isWrite < < 1 ) ;
if ( rw = = BX_EXECUTE )
error_code | = ERROR_CODE_ACCESS ; // I/D = 1
if ( is_page_walk )
error_code | = BX_CONST64 ( 1 ) < < 33 ;
2021-04-22 14:12:26 +03:00
else
error_code | = BX_CONST64 ( 1 ) < < 32 ;
2012-02-14 03:29:01 +04:00
2012-02-19 16:16:58 +04:00
Svm_Vmexit ( SVM_VMEXIT_NPF , error_code , guest_paddr ) ;
2012-02-14 03:29:01 +04:00
}
2021-01-30 11:35:35 +03:00
bx_phy_address BX_CPU_C : : nested_walk_long_mode ( bx_phy_address guest_paddr , unsigned rw , bool is_page_walk )
2012-02-14 03:29:01 +04:00
{
bx_phy_address entry_addr [ 4 ] ;
Bit64u entry [ 4 ] ;
2015-04-21 11:20:28 +03:00
BxMemtype entry_memtype [ 4 ] = { BX_MEMTYPE_INVALID } ;
2021-05-25 09:27:49 +03:00
bool nx_fault = false ;
2012-02-14 03:29:01 +04:00
int leaf ;
SVM_CONTROLS * ctrls = & BX_CPU_THIS_PTR vmcb . ctrls ;
SVM_HOST_STATE * host_state = & BX_CPU_THIS_PTR vmcb . host_state ;
bx_phy_address ppf = ctrls - > ncr3 & BX_CR3_PAGING_MASK ;
Bit64u offset_mask = BX_CONST64 ( 0x0000ffffffffffff ) ;
2019-12-15 21:45:04 +03:00
unsigned combined_access = BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ;
2012-02-14 03:29:01 +04:00
2012-02-19 16:16:58 +04:00
Bit64u reserved = PAGING_PAE_RESERVED_BITS ;
if ( ! host_state - > efer . get_NXE ( ) )
reserved | = PAGE_DIRECTORY_NX_BIT ;
2012-02-14 03:29:01 +04:00
for ( leaf = BX_LEVEL_PML4 ; ; - - leaf ) {
entry_addr [ leaf ] = ppf + ( ( guest_paddr > > ( 9 + 9 * leaf ) ) & 0xff8 ) ;
access_read_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-02-21 00:50:59 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , BX_MEMTYPE_INVALID , BX_READ , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-02-14 03:29:01 +04:00
offset_mask > > = 9 ;
Bit64u curr_entry = entry [ leaf ] ;
2012-02-19 16:16:58 +04:00
int fault = check_entry_PAE ( bx_paging_level [ leaf ] , curr_entry , reserved , rw , & nx_fault ) ;
2012-02-14 03:29:01 +04:00
if ( fault > = 0 )
nested_page_fault ( fault , guest_paddr , rw , is_page_walk ) ;
combined_access & = curr_entry ; // U/S and R/W
ppf = curr_entry & BX_CONST64 ( 0x000ffffffffff000 ) ;
if ( leaf = = BX_LEVEL_PTE ) break ;
if ( curr_entry & 0x80 ) {
2014-08-31 23:22:41 +04:00
if ( leaf > ( BX_LEVEL_PDE + ! ! is_cpu_extension_supported ( BX_ISA_1G_PAGES ) ) ) {
2012-02-14 03:29:01 +04:00
BX_DEBUG ( ( " Nested PAE Walk %s: PS bit set ! " , bx_paging_level [ leaf ] ) ) ;
nested_page_fault ( ERROR_RESERVED | ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
}
ppf & = BX_CONST64 ( 0x000fffffffffe000 ) ;
if ( ppf & offset_mask ) {
BX_DEBUG ( ( " Nested PAE Walk %s: reserved bit is set: 0x " FMT_ADDRX64 , bx_paging_level [ leaf ] , curr_entry ) ) ;
nested_page_fault ( ERROR_RESERVED | ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
}
break ;
}
}
2021-01-30 11:35:35 +03:00
bool isWrite = ( rw & 1 ) ; // write or r-m-w
2012-02-14 03:29:01 +04:00
unsigned priv_index = ( 1 < < 3 ) /* user */ | ( combined_access | isWrite ) ;
if ( ! priv_check [ priv_index ] | | nx_fault )
nested_page_fault ( ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
// Update A/D bits if needed
2015-04-21 11:20:28 +03:00
update_access_dirty_PAE ( entry_addr , entry , entry_memtype , BX_LEVEL_PML4 , leaf , isWrite ) ;
2012-02-14 03:29:01 +04:00
2012-02-15 23:49:35 +04:00
// Make up the physical page frame address
2022-08-23 21:38:00 +03:00
return ppf | ( bx_phy_address ) ( guest_paddr & offset_mask ) ;
2012-02-14 03:29:01 +04:00
}
2021-01-30 11:35:35 +03:00
bx_phy_address BX_CPU_C : : nested_walk_PAE ( bx_phy_address guest_paddr , unsigned rw , bool is_page_walk )
2012-02-14 03:29:01 +04:00
{
bx_phy_address entry_addr [ 2 ] ;
Bit64u entry [ 2 ] ;
2015-04-21 11:20:28 +03:00
BxMemtype entry_memtype [ 2 ] = { BX_MEMTYPE_INVALID } ;
2021-05-25 09:27:49 +03:00
bool nx_fault = false ;
2012-02-14 03:29:01 +04:00
int leaf ;
2019-12-15 21:45:04 +03:00
unsigned combined_access = BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ;
2012-02-14 03:29:01 +04:00
SVM_CONTROLS * ctrls = & BX_CPU_THIS_PTR vmcb . ctrls ;
SVM_HOST_STATE * host_state = & BX_CPU_THIS_PTR vmcb . host_state ;
bx_phy_address ncr3 = ctrls - > ncr3 & 0xffffffe0 ;
unsigned index = ( guest_paddr > > 30 ) & 0x3 ;
Bit64u pdptr ;
bx_phy_address pdpe_entry_addr = ( bx_phy_address ) ( ncr3 | ( index < < 3 ) ) ;
access_read_physical ( pdpe_entry_addr , 8 , & pdptr ) ;
2015-02-21 00:50:59 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( pdpe_entry_addr , 8 , BX_MEMTYPE_INVALID , BX_READ , ( BX_PDPTR0_ACCESS + index ) , ( Bit8u * ) & pdptr ) ;
2012-02-14 03:29:01 +04:00
if ( ! ( pdptr & 0x1 ) ) {
BX_DEBUG ( ( " Nested PAE Walk PDPTE%d entry not present ! " , index ) ) ;
nested_page_fault ( ERROR_NOT_PRESENT , guest_paddr , rw , is_page_walk ) ;
}
if ( pdptr & PAGING_PAE_PDPTE_RESERVED_BITS ) {
BX_DEBUG ( ( " Nested PAE Walk PDPTE%d entry reserved bits set: 0x " FMT_ADDRX64 , index , pdptr ) ) ;
nested_page_fault ( ERROR_RESERVED | ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
}
2012-02-19 16:16:58 +04:00
Bit64u reserved = PAGING_LEGACY_PAE_RESERVED_BITS ;
if ( ! host_state - > efer . get_NXE ( ) )
reserved | = PAGE_DIRECTORY_NX_BIT ;
2012-02-14 03:29:01 +04:00
bx_phy_address ppf = pdptr & BX_CONST64 ( 0x000ffffffffff000 ) ;
for ( leaf = BX_LEVEL_PDE ; ; - - leaf ) {
entry_addr [ leaf ] = ppf + ( ( guest_paddr > > ( 9 + 9 * leaf ) ) & 0xff8 ) ;
access_read_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-02-21 00:50:59 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , BX_MEMTYPE_INVALID , BX_READ , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-02-14 03:29:01 +04:00
Bit64u curr_entry = entry [ leaf ] ;
2012-02-19 16:16:58 +04:00
int fault = check_entry_PAE ( bx_paging_level [ leaf ] , curr_entry , reserved , rw , & nx_fault ) ;
2012-02-14 03:29:01 +04:00
if ( fault > = 0 )
nested_page_fault ( fault , guest_paddr , rw , is_page_walk ) ;
combined_access & = curr_entry ; // U/S and R/W
ppf = curr_entry & BX_CONST64 ( 0x000ffffffffff000 ) ;
if ( leaf = = BX_LEVEL_PTE ) break ;
// Ignore CR4.PSE in PAE mode
if ( curr_entry & 0x80 ) {
if ( curr_entry & PAGING_PAE_PDE2M_RESERVED_BITS ) {
BX_DEBUG ( ( " PAE PDE2M: reserved bit is set PDE=0x " FMT_ADDRX64 , curr_entry ) ) ;
nested_page_fault ( ERROR_RESERVED | ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
}
// Make up the physical page frame address
ppf = ( bx_phy_address ) ( ( curr_entry & BX_CONST64 ( 0x000fffffffe00000 ) ) | ( guest_paddr & 0x001ff000 ) ) ;
break ;
}
}
2021-01-30 11:35:35 +03:00
bool isWrite = ( rw & 1 ) ; // write or r-m-w
2012-02-14 03:29:01 +04:00
unsigned priv_index = ( 1 < < 3 ) /* user */ | ( combined_access | isWrite ) ;
if ( ! priv_check [ priv_index ] | | nx_fault )
nested_page_fault ( ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
// Update A/D bits if needed
2015-04-21 11:20:28 +03:00
update_access_dirty_PAE ( entry_addr , entry , entry_memtype , BX_LEVEL_PDE , leaf , isWrite ) ;
2012-02-14 03:29:01 +04:00
Bit32u page_offset = PAGE_OFFSET ( guest_paddr ) ;
return ppf | page_offset ;
}
2021-01-30 11:35:35 +03:00
bx_phy_address BX_CPU_C : : nested_walk_legacy ( bx_phy_address guest_paddr , unsigned rw , bool is_page_walk )
2012-02-14 03:29:01 +04:00
{
bx_phy_address entry_addr [ 2 ] ;
Bit32u entry [ 2 ] ;
2015-04-21 11:20:28 +03:00
BxMemtype entry_memtype [ 2 ] = { BX_MEMTYPE_INVALID } ;
2012-02-14 03:29:01 +04:00
int leaf ;
SVM_CONTROLS * ctrls = & BX_CPU_THIS_PTR vmcb . ctrls ;
SVM_HOST_STATE * host_state = & BX_CPU_THIS_PTR vmcb . host_state ;
bx_phy_address ppf = ctrls - > ncr3 & BX_CR3_PAGING_MASK ;
2019-12-15 21:45:04 +03:00
unsigned combined_access = BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER ;
2012-02-14 03:29:01 +04:00
for ( leaf = BX_LEVEL_PDE ; ; - - leaf ) {
entry_addr [ leaf ] = ppf + ( ( guest_paddr > > ( 10 + 10 * leaf ) ) & 0xffc ) ;
access_read_physical ( entry_addr [ leaf ] , 4 , & entry [ leaf ] ) ;
2015-02-21 00:50:59 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 4 , BX_MEMTYPE_INVALID , BX_READ , ( BX_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-02-14 03:29:01 +04:00
Bit32u curr_entry = entry [ leaf ] ;
if ( ! ( curr_entry & 0x1 ) ) {
BX_DEBUG ( ( " Nested %s Walk: entry not present " , bx_paging_level [ leaf ] ) ) ;
nested_page_fault ( ERROR_NOT_PRESENT , guest_paddr , rw , is_page_walk ) ;
}
combined_access & = curr_entry ; // U/S and R/W
ppf = curr_entry & 0xfffff000 ;
if ( leaf = = BX_LEVEL_PTE ) break ;
if ( ( curr_entry & 0x80 ) ! = 0 & & host_state - > cr4 . get_PSE ( ) ) {
// 4M paging, only if CR4.PSE enabled, ignore PDE.PS otherwise
if ( curr_entry & PAGING_PDE4M_RESERVED_BITS ) {
BX_DEBUG ( ( " Nested PSE Walk PDE4M: reserved bit is set: PDE=0x%08x " , entry [ BX_LEVEL_PDE ] ) ) ;
nested_page_fault ( ERROR_RESERVED | ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
}
// make up the physical frame number
ppf = ( curr_entry & 0xffc00000 ) | ( guest_paddr & 0x003ff000 ) ;
# if BX_PHY_ADDRESS_WIDTH > 32
ppf | = ( ( bx_phy_address ) ( curr_entry & 0x003fe000 ) ) < < 19 ;
# endif
break ;
}
}
2021-01-30 11:35:35 +03:00
bool isWrite = ( rw & 1 ) ; // write or r-m-w
2012-02-14 03:29:01 +04:00
unsigned priv_index = ( 1 < < 3 ) /* user */ | ( combined_access | isWrite ) ;
if ( ! priv_check [ priv_index ] )
nested_page_fault ( ERROR_PROTECTION , guest_paddr , rw , is_page_walk ) ;
2015-04-21 11:20:28 +03:00
update_access_dirty ( entry_addr , entry , entry_memtype , leaf , isWrite ) ;
2012-02-14 03:29:01 +04:00
Bit32u page_offset = PAGE_OFFSET ( guest_paddr ) ;
return ppf | page_offset ;
}
2021-01-30 11:35:35 +03:00
bx_phy_address BX_CPU_C : : nested_walk ( bx_phy_address guest_paddr , unsigned rw , bool is_page_walk )
2012-02-14 03:29:01 +04:00
{
SVM_HOST_STATE * host_state = & BX_CPU_THIS_PTR vmcb . host_state ;
2013-02-14 23:30:59 +04:00
BX_DEBUG ( ( " Nested walk for guest paddr 0x " FMT_PHY_ADDRX , guest_paddr ) ) ;
2012-03-29 01:11:19 +04:00
2012-02-14 03:29:01 +04:00
if ( host_state - > efer . get_LMA ( ) )
return nested_walk_long_mode ( guest_paddr , rw , is_page_walk ) ;
else if ( host_state - > cr4 . get_PAE ( ) )
return nested_walk_PAE ( guest_paddr , rw , is_page_walk ) ;
else
return nested_walk_legacy ( guest_paddr , rw , is_page_walk ) ;
}
# endif
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
/* EPT access type */
2018-01-27 22:31:39 +03:00
enum {
BX_EPT_READ = 0x01 ,
BX_EPT_WRITE = 0x02 ,
2022-07-30 15:26:47 +03:00
BX_EPT_EXECUTE = 0x04 ,
BX_EPT_MBE_SUPERVISOR_EXECUTE = BX_EPT_EXECUTE ,
BX_EPT_MBE_USER_EXECUTE = 0x400
2018-01-27 22:31:39 +03:00
} ;
2010-04-07 21:12:17 +04:00
/* EPT access mask */
2019-12-15 21:45:04 +03:00
enum {
BX_EPT_ENTRY_NOT_PRESENT = 0x00 ,
BX_EPT_ENTRY_READ_ONLY = 0x01 ,
BX_EPT_ENTRY_WRITE_ONLY = 0x02 ,
BX_EPT_ENTRY_READ_WRITE = 0x03 ,
BX_EPT_ENTRY_EXECUTE_ONLY = 0x04 ,
BX_EPT_ENTRY_READ_EXECUTE = 0x05 ,
BX_EPT_ENTRY_WRITE_EXECUTE = 0x06 ,
BX_EPT_ENTRY_READ_WRITE_EXECUTE = 0x07
} ;
2010-04-07 21:12:17 +04:00
2019-12-20 10:42:07 +03:00
# define BX_VMX_EPT_ACCESS_DIRTY_ENABLED (BX_CPU_THIS_PTR vmcs.eptptr & 0x40)
# define BX_VMX_EPT_SUPERVISOR_SHADOW_STACK_CTRL_ENABLED (BX_CPU_THIS_PTR vmcs.eptptr & 0x80)
2015-03-23 23:27:36 +03:00
2010-05-05 00:16:38 +04:00
// Format of a EPT Entry
// -----------------------------------------------------------
// 00 | Read access
// 01 | Write access
// 02 | Execute Access
// 05-03 | EPT Memory type (for leaf entries, reserved otherwise)
// 06 | Ignore PAT memory type (for leaf entries, reserved otherwise)
// 07 | Page Size, must be 1 to indicate a Large Page
2015-03-23 23:27:36 +03:00
// 08 | Accessed bit (if supported, ignored otherwise)
// 09 | Dirty bit (for leaf entries, if supported, ignored otherwise)
// 11-10 | (ignored)
2010-05-05 00:16:38 +04:00
// PA-12 | Physical address
// 51-PA | Reserved (must be zero)
2019-12-20 10:42:07 +03:00
// 61-52 | (ignored)
2019-12-29 23:40:18 +03:00
// 60 | Supervisor Shadow Stack Page (CET)
2022-07-30 18:38:22 +03:00
// 61 | Sub Page Protected (SPP)
2018-01-28 00:20:33 +03:00
// 63 | Suppress #VE
2010-05-05 00:16:38 +04:00
// -----------------------------------------------------------
2018-01-28 00:20:33 +03:00
const Bit64u BX_SUPPRESS_EPT_VIOLATION_EXCEPTION = ( BX_CONST64 ( 1 ) < < 63 ) ;
const Bit64u BX_SUB_PAGE_PROTECTED = ( BX_CONST64 ( 1 ) < < 61 ) ;
2019-12-29 23:40:18 +03:00
const Bit64u BX_SUPERVISOR_SHADOW_STACK_PAGE = ( BX_CONST64 ( 1 ) < < 60 ) ;
2018-01-28 00:20:33 +03:00
2018-01-27 22:31:39 +03:00
const Bit64u PAGING_EPT_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS ;
2010-04-07 21:12:17 +04:00
2022-08-14 21:09:18 +03:00
// entries which were allowed to write only because of SPP cannot be cached in DTLB as writeable
2022-08-14 21:18:25 +03:00
bx_phy_address BX_CPU_C : : translate_guest_physical ( bx_phy_address guest_paddr , bx_address guest_laddr , bool guest_laddr_valid , bool is_page_walk , unsigned user_page , unsigned rw , bool supervisor_shadow_stack , bool * spp_page )
2010-04-07 21:12:17 +04:00
{
VMCS_CACHE * vm = & BX_CPU_THIS_PTR vmcs ;
2012-02-12 23:13:57 +04:00
bx_phy_address entry_addr [ 4 ] , ppf = LPFOf ( vm - > eptptr ) ;
2010-04-07 21:12:17 +04:00
Bit64u entry [ 4 ] ;
2012-01-17 22:20:55 +04:00
int leaf ;
2012-02-12 23:13:57 +04:00
2015-03-23 23:27:36 +03:00
# if BX_SUPPORT_MEMTYPE
// The MTRRs have no effect on the memory type used for an access to an EPT paging structures.
BxMemtype eptptr_memtype = BX_CPU_THIS_PTR cr0 . get_CD ( ) ? ( BX_MEMTYPE_UC ) : BxMemtype ( vm - > eptptr & 0x7 ) ;
# endif
2012-02-12 23:13:57 +04:00
Bit64u offset_mask = BX_CONST64 ( 0x0000ffffffffffff ) ;
2022-07-30 15:26:47 +03:00
Bit32u combined_access = 0x7 , access_mask = 0 ;
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_MBE_CTRL ) )
combined_access | = BX_EPT_MBE_USER_EXECUTE ;
2010-04-07 21:12:17 +04:00
2013-02-14 23:30:59 +04:00
BX_DEBUG ( ( " EPT walk for guest paddr 0x " FMT_PHY_ADDRX , guest_paddr ) ) ;
2010-04-07 21:12:17 +04:00
2012-05-02 22:11:39 +04:00
// when EPT A/D enabled treat guest page table accesses as writes
2012-05-27 23:17:13 +04:00
if ( BX_VMX_EPT_ACCESS_DIRTY_ENABLED & & is_page_walk & & guest_laddr_valid )
2012-05-02 22:11:39 +04:00
rw = BX_WRITE ;
2022-07-30 15:26:47 +03:00
if ( rw = = BX_EXECUTE ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_MBE_CTRL ) ) {
access_mask | = user_page ? BX_EPT_MBE_USER_EXECUTE : BX_EPT_MBE_SUPERVISOR_EXECUTE ;
}
else {
access_mask | = BX_EPT_EXECUTE ;
}
}
2010-04-07 21:12:17 +04:00
if ( rw & 1 ) access_mask | = BX_EPT_WRITE ; // write or r-m-w
2019-12-20 10:42:07 +03:00
if ( ( rw & 3 ) = = BX_READ ) access_mask | = BX_EPT_READ ; // handle correctly shadow stack reads
2010-04-07 21:12:17 +04:00
2013-01-28 20:30:25 +04:00
Bit32u vmexit_reason = 0 ;
2010-04-07 21:12:17 +04:00
for ( leaf = BX_LEVEL_PML4 ; ; - - leaf ) {
2012-02-12 23:13:57 +04:00
entry_addr [ leaf ] = ppf + ( ( guest_paddr > > ( 9 + 9 * leaf ) ) & 0xff8 ) ;
2010-04-07 21:12:17 +04:00
access_read_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-03-23 23:27:36 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , MEMTYPE ( eptptr_memtype ) , BX_READ , ( BX_EPT_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2010-04-07 21:12:17 +04:00
2012-02-12 23:13:57 +04:00
offset_mask > > = 9 ;
2010-04-07 21:12:17 +04:00
Bit64u curr_entry = entry [ leaf ] ;
Bit32u curr_access_mask = curr_entry & 0x7 ;
2022-07-30 15:26:47 +03:00
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_MBE_CTRL ) ) {
curr_access_mask | = ( curr_entry & BX_EPT_MBE_USER_EXECUTE ) ;
}
2010-04-07 21:12:17 +04:00
if ( curr_access_mask = = BX_EPT_ENTRY_NOT_PRESENT ) {
BX_DEBUG ( ( " EPT %s: not present " , bx_paging_level [ leaf ] ) ) ;
vmexit_reason = VMX_VMEXIT_EPT_VIOLATION ;
break ;
}
2022-07-30 15:26:47 +03:00
if ( ( curr_access_mask & ( BX_EPT_READ | BX_EPT_WRITE ) ) = = BX_EPT_ENTRY_WRITE_ONLY ) {
BX_DEBUG ( ( " EPT %s: EPT misconfiguration access_mask=%x " , bx_paging_level [ leaf ] , curr_access_mask ) ) ;
2010-04-07 21:12:17 +04:00
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION ;
break ;
}
2021-01-30 11:35:35 +03:00
extern bool isMemTypeValidMTRR ( unsigned memtype ) ;
2010-04-07 21:12:17 +04:00
if ( ! isMemTypeValidMTRR ( ( curr_entry > > 3 ) & 7 ) ) {
2011-08-09 22:00:19 +04:00
BX_DEBUG ( ( " EPT %s: EPT misconfiguration memtype=%d " ,
bx_paging_level [ leaf ] , ( unsigned ) ( ( curr_entry > > 3 ) & 7 ) ) ) ;
2010-04-07 21:12:17 +04:00
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION ;
break ;
}
if ( curr_entry & PAGING_EPT_RESERVED_BITS ) {
2022-07-30 15:26:47 +03:00
BX_DEBUG ( ( " EPT %s: reserved bit is set 0x " FMT_ADDRX64 " (reserved: " FMT_ADDRX64 " ) " , bx_paging_level [ leaf ] , curr_entry , curr_entry & PAGING_EPT_RESERVED_BITS ) ) ;
2010-04-07 21:12:17 +04:00
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION ;
break ;
}
2012-02-12 23:13:57 +04:00
ppf = curr_entry & BX_CONST64 ( 0x000ffffffffff000 ) ;
2010-04-07 21:12:17 +04:00
2012-02-12 23:13:57 +04:00
if ( leaf = = BX_LEVEL_PTE ) break ;
2010-04-07 21:12:17 +04:00
if ( curr_entry & 0x80 ) {
2014-08-31 23:22:41 +04:00
if ( leaf > ( BX_LEVEL_PDE + ! ! is_cpu_extension_supported ( BX_ISA_1G_PAGES ) ) ) {
2011-05-30 00:09:31 +04:00
BX_DEBUG ( ( " EPT %s: PS bit set ! " , bx_paging_level [ leaf ] ) ) ;
2012-02-13 01:30:22 +04:00
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION ;
2010-04-07 21:12:17 +04:00
break ;
}
2012-02-12 23:13:57 +04:00
ppf & = BX_CONST64 ( 0x000fffffffffe000 ) ;
if ( ppf & offset_mask ) {
BX_DEBUG ( ( " EPT %s: reserved bit is set: 0x " FMT_ADDRX64 , bx_paging_level [ leaf ] , curr_entry ) ) ;
2012-02-13 01:30:22 +04:00
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION ;
2012-02-12 23:13:57 +04:00
break ;
2010-04-07 21:12:17 +04:00
}
2012-02-12 23:13:57 +04:00
// Make up the physical page frame address
ppf + = ( bx_phy_address ) ( guest_paddr & offset_mask ) ;
break ;
2010-04-07 21:12:17 +04:00
}
2016-12-10 08:06:59 +03:00
// EPT non leaf entry, check for reserved bits
if ( ( curr_entry > > 3 ) & 0xf ) {
BX_DEBUG ( ( " EPT %s: EPT misconfiguration, reserved bits set for non-leaf entry " , bx_paging_level [ leaf ] ) ) ;
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION ;
break ;
}
2019-12-29 23:40:18 +03:00
combined_access & = curr_access_mask ;
2010-04-07 21:12:17 +04:00
}
2019-12-29 23:40:18 +03:00
// defer final combined_access calculation (with leaf entry) until CET is handled
if ( ! vmexit_reason ) {
# if BX_SUPPORT_CET
if ( BX_VMX_EPT_SUPERVISOR_SHADOW_STACK_CTRL_ENABLED & & supervisor_shadow_stack ) {
// The EPT.R bit is set in all EPT paging-structure entry controlling the translation
// The EPT.W bit is set in all EPT paging-structure entry controlling the translation except the leaf entry (allowed for shadow stack write access)
// The SSS bit (bit 60) is 1 in the EPT paging-structure entry maps the page
2022-08-23 21:38:00 +03:00
bool supervisor_shadow_stack_page = ( ( combined_access & BX_EPT_ENTRY_READ_WRITE ) = = BX_EPT_ENTRY_READ_WRITE ) & &
2019-12-29 23:40:18 +03:00
( ( entry [ leaf ] & BX_EPT_READ ) ! = 0 ) & &
( ( ( entry [ leaf ] & BX_EPT_WRITE ) = = 0 ) | | ! ( access_mask & BX_EPT_WRITE ) ) & &
( ( entry [ leaf ] & BX_SUPERVISOR_SHADOW_STACK_PAGE ) ! = 0 ) ;
if ( ! supervisor_shadow_stack_page ) {
BX_ERROR ( ( " VMEXIT: supervisor shadow stack access to non supervisor shadow stack page " ) ) ;
vmexit_reason = VMX_VMEXIT_EPT_VIOLATION ;
}
}
else
# endif
{
combined_access & = entry [ leaf ] ;
if ( ( access_mask & combined_access ) ! = access_mask ) {
vmexit_reason = VMX_VMEXIT_EPT_VIOLATION ;
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_SUBPAGE_WR_PROTECT_CTRL ) & & ( entry [ leaf ] & BX_SUB_PAGE_PROTECTED ) ! = 0 & & leaf = = BX_LEVEL_PTE ) {
2022-07-31 22:07:16 +03:00
// if cumulative read-access bit is 0, the write access is not eligible for SPP
2022-08-14 21:09:18 +03:00
if ( ( access_mask & BX_EPT_WRITE ) ! = 0 & & ( combined_access & BX_EPT_ENTRY_READ_WRITE ) = = BX_EPT_ENTRY_READ_ONLY & & guest_laddr_valid & & ! is_page_walk ) {
if ( spp_walk ( guest_paddr , guest_laddr , BX_MEMTYPE_WB ) ) { // memory type indicated in IA32_VMX_BASIC MSR
2022-08-14 21:18:25 +03:00
if ( spp_page ) * spp_page = true ;
2019-12-29 23:40:18 +03:00
vmexit_reason = 0 ;
2022-08-14 21:09:18 +03:00
}
}
2019-12-29 23:40:18 +03:00
}
}
2018-01-28 00:20:33 +03:00
}
2010-04-07 21:12:17 +04:00
}
if ( vmexit_reason ) {
2013-02-14 23:30:59 +04:00
BX_ERROR ( ( " VMEXIT: EPT %s for guest paddr 0x " FMT_PHY_ADDRX " laddr 0x " FMT_ADDRX ,
2012-01-06 02:23:05 +04:00
( vmexit_reason = = VMX_VMEXIT_EPT_VIOLATION ) ? " violation " : " misconfig " , guest_paddr , guest_laddr ) ) ;
2013-01-28 20:30:25 +04:00
Bit32u vmexit_qualification = 0 ;
2019-12-30 00:00:35 +03:00
// no VMExit qualification for EPT Misconfiguration VMExit
2013-01-28 20:30:25 +04:00
if ( vmexit_reason = = VMX_VMEXIT_EPT_VIOLATION ) {
2019-12-30 00:00:35 +03:00
combined_access & = entry [ leaf ] ;
2013-01-28 20:30:25 +04:00
vmexit_qualification = access_mask | ( combined_access < < 3 ) ;
2022-07-30 15:26:47 +03:00
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_MBE_CTRL ) & & ( rw = = BX_EXECUTE ) ) {
vmexit_qualification & = ( 0x3f ) ; // reset all bit bits beyond [5:0]
vmexit_qualification | = ( 1 < < 2 ) ; // bit2 indicate the operation was instruction fetch
if ( combined_access & BX_EPT_MBE_USER_EXECUTE )
vmexit_qualification | = ( 1 < < 6 ) ;
}
2012-01-06 02:23:05 +04:00
if ( guest_laddr_valid ) {
2013-03-07 01:11:23 +04:00
vmexit_qualification | = ( 1 < < 7 ) ;
if ( ! is_page_walk ) vmexit_qualification | = ( 1 < < 8 ) ;
2012-01-06 02:23:05 +04:00
}
2013-03-07 01:11:23 +04:00
if ( BX_CPU_THIS_PTR nmi_unblocking_iret )
vmexit_qualification | = ( 1 < < 12 ) ;
2019-12-29 23:40:18 +03:00
# if BX_SUPPORT_CET
if ( rw & 4 ) // shadow stack access
vmexit_qualification | = ( 1 < < 13 ) ;
2013-01-28 20:30:25 +04:00
2019-12-29 23:40:18 +03:00
if ( BX_VMX_EPT_SUPERVISOR_SHADOW_STACK_CTRL_ENABLED & & ( entry [ leaf ] & BX_SUPERVISOR_SHADOW_STACK_PAGE ) ! = 0 )
vmexit_qualification | = ( 1 < < 14 ) ;
# endif
2013-01-28 20:30:25 +04:00
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_VIOLATION_EXCEPTION ) ) {
if ( ( entry [ leaf ] & BX_SUPPRESS_EPT_VIOLATION_EXCEPTION ) = = 0 )
Virtualization_Exception ( vmexit_qualification , guest_paddr , guest_laddr ) ;
}
2010-04-07 21:12:17 +04:00
}
2013-01-28 20:30:25 +04:00
VMwrite64 ( VMCS_64BIT_GUEST_PHYSICAL_ADDR , guest_paddr ) ;
VMwrite_natural ( VMCS_GUEST_LINEAR_ADDR , guest_laddr ) ;
VMexit ( vmexit_reason , vmexit_qualification ) ;
2010-04-07 21:12:17 +04:00
}
2012-05-27 23:17:13 +04:00
if ( BX_VMX_EPT_ACCESS_DIRTY_ENABLED ) {
2015-05-06 22:55:44 +03:00
// write access and Dirty-bit is not set in the leaf entry
unsigned dirty_update = ( rw & 1 ) & & ! ( entry [ leaf ] & 0x200 ) ;
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_PML_ENABLE ) )
vmx_page_modification_logging ( guest_paddr , dirty_update ) ;
2015-03-23 23:27:36 +03:00
update_ept_access_dirty ( entry_addr , entry , MEMTYPE ( eptptr_memtype ) , leaf , rw & 1 ) ;
2012-05-02 22:11:39 +04:00
}
2010-04-07 21:12:17 +04:00
Bit32u page_offset = PAGE_OFFSET ( guest_paddr ) ;
return ppf | page_offset ;
}
2012-05-02 22:11:39 +04:00
// Access bit 8, Dirty bit 9
2015-03-23 23:27:36 +03:00
void BX_CPU_C : : update_ept_access_dirty ( bx_phy_address * entry_addr , Bit64u * entry , BxMemtype eptptr_memtype , unsigned leaf , unsigned write )
2012-05-02 22:11:39 +04:00
{
// Update A bit if needed
for ( unsigned level = BX_LEVEL_PML4 ; level > leaf ; level - - ) {
if ( ! ( entry [ level ] & 0x100 ) ) {
entry [ level ] | = 0x100 ;
access_write_physical ( entry_addr [ level ] , 8 , & entry [ level ] ) ;
2015-03-23 23:27:36 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ level ] , 8 , MEMTYPE ( eptptr_memtype ) , BX_WRITE , ( BX_EPT_PTE_ACCESS + level ) , ( Bit8u * ) ( & entry [ level ] ) ) ;
2012-05-02 22:11:39 +04:00
}
}
// Update A/D bits if needed
if ( ! ( entry [ leaf ] & 0x100 ) | | ( write & & ! ( entry [ leaf ] & 0x200 ) ) ) {
entry [ leaf ] | = ( 0x100 | ( write < < 9 ) ) ; // Update A and possibly D bits
access_write_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2015-03-23 23:27:36 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , MEMTYPE ( eptptr_memtype ) , BX_WRITE , ( BX_EPT_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2012-05-02 22:11:39 +04:00
}
}
2018-01-28 00:20:33 +03:00
const Bit64u PAGING_SPP_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS | BX_CONST64 ( 0xFFF0000000000FFE ) ;
const Bit32u VMX_SPP_NOT_PRESENT_QUALIFICATION = ( 1 < < 11 ) ;
2021-01-30 11:35:35 +03:00
bool BX_CPU_C : : spp_walk ( bx_phy_address guest_paddr , bx_address guest_laddr , BxMemtype memtype )
2018-01-28 00:20:33 +03:00
{
VMCS_CACHE * vm = & BX_CPU_THIS_PTR vmcs ;
bx_phy_address entry_addr [ 4 ] , ppf = LPFOf ( vm - > spptp ) ;
Bit64u entry [ 4 ] ;
int leaf ;
BX_DEBUG ( ( " SPP walk for guest paddr 0x " FMT_PHY_ADDRX , guest_paddr ) ) ;
Bit32u vmexit_reason = 0 ;
Bit32u vmexit_qualification = 0 ;
for ( leaf = BX_LEVEL_PML4 ; ; - - leaf ) {
entry_addr [ leaf ] = ppf + ( ( guest_paddr > > ( 9 + 9 * leaf ) ) & 0xff8 ) ;
access_read_physical ( entry_addr [ leaf ] , 8 , & entry [ leaf ] ) ;
2018-01-28 00:25:46 +03:00
BX_NOTIFY_PHY_MEMORY_ACCESS ( entry_addr [ leaf ] , 8 , MEMTYPE ( memtype ) , BX_READ , ( BX_EPT_SPP_PTE_ACCESS + leaf ) , ( Bit8u * ) ( & entry [ leaf ] ) ) ;
2018-01-28 00:20:33 +03:00
if ( leaf = = BX_LEVEL_PTE ) break ;
Bit64u curr_entry = entry [ leaf ] ;
if ( ! ( curr_entry & 1 ) ) {
BX_DEBUG ( ( " SPP %s: not present " , bx_paging_level [ leaf ] ) ) ;
vmexit_reason = VMX_VMEXIT_SPP ;
vmexit_qualification = VMX_SPP_NOT_PRESENT_QUALIFICATION ;
break ;
}
if ( curr_entry & PAGING_SPP_RESERVED_BITS ) {
BX_DEBUG ( ( " SPP %s: reserved bit is set 0x " FMT_ADDRX64 , bx_paging_level [ leaf ] , curr_entry ) ) ;
vmexit_reason = VMX_VMEXIT_SPP ;
break ;
}
ppf = curr_entry & BX_CONST64 ( 0x000ffffffffff000 ) ;
}
2022-07-31 19:57:38 +03:00
if ( ! vmexit_reason ) {
const Bit64u leaf_reserved_bits = BX_CONST64 ( 0xAAAAAAAAAAAAAAAA ) ;
if ( entry [ BX_LEVEL_PTE ] & leaf_reserved_bits ) {
BX_DEBUG ( ( " SPP PTE: reserved (odd) bits are set " ) ) ;
vmexit_reason = VMX_VMEXIT_SPP ;
}
}
2018-01-28 00:20:33 +03:00
if ( vmexit_reason ) {
BX_ERROR ( ( " VMEXIT: SPP %s for guest paddr 0x " FMT_PHY_ADDRX " laddr 0x " FMT_ADDRX ,
2022-07-31 19:57:38 +03:00
( vmexit_qualification = = VMX_SPP_NOT_PRESENT_QUALIFICATION ) ? " not present " : " misconfig " , guest_paddr , guest_laddr ) ) ;
2018-01-28 00:20:33 +03:00
if ( BX_CPU_THIS_PTR nmi_unblocking_iret )
vmexit_qualification | = ( 1 < < 12 ) ;
VMwrite64 ( VMCS_64BIT_GUEST_PHYSICAL_ADDR , guest_paddr ) ;
VMwrite_natural ( VMCS_GUEST_LINEAR_ADDR , guest_laddr ) ;
VMexit ( vmexit_reason , vmexit_qualification ) ;
}
Bit32u spp_bit = 2 * ( ( guest_paddr & 0xFFF ) > > 7 ) ;
return ( entry [ BX_LEVEL_PTE ] > > spp_bit ) & 1 ;
}
2010-04-07 21:12:17 +04:00
# endif
2010-05-05 00:16:38 +04:00
# if BX_DEBUGGER
2010-05-06 00:10:15 +04:00
void dbg_print_paging_pte ( int level , Bit64u entry )
2010-05-05 00:16:38 +04:00
{
2010-05-06 00:10:15 +04:00
dbg_printf ( " %4s: 0x%08x%08x " , bx_paging_level [ level ] , GET32H ( entry ) , GET32L ( entry ) ) ;
2010-05-05 00:16:38 +04:00
2012-06-28 14:59:30 +04:00
if ( entry & BX_CONST64 ( 0x8000000000000000 ) )
dbg_printf ( " XD " ) ;
else
dbg_printf ( " " ) ;
2010-05-06 00:10:15 +04:00
if ( level = = BX_LEVEL_PTE ) {
dbg_printf ( " %s %s %s " ,
( entry & 0x0100 ) ? " G " : " g " ,
( entry & 0x0080 ) ? " PAT " : " pat " ,
( entry & 0x0040 ) ? " D " : " d " ) ;
}
else {
if ( entry & 0x80 ) {
dbg_printf ( " PS %s %s %s " ,
( entry & 0x0100 ) ? " G " : " g " ,
( entry & 0x1000 ) ? " PAT " : " pat " ,
( entry & 0x0040 ) ? " D " : " d " ) ;
}
else {
dbg_printf ( " ps " ) ;
}
}
2010-05-05 00:16:38 +04:00
2010-05-06 00:10:15 +04:00
dbg_printf ( " %s %s %s %s %s %s \n " ,
( entry & 0x20 ) ? " A " : " a " ,
2010-05-05 00:16:38 +04:00
( entry & 0x10 ) ? " PCD " : " pcd " ,
( entry & 0x08 ) ? " PWT " : " pwt " ,
( entry & 0x04 ) ? " U " : " S " ,
( entry & 0x02 ) ? " W " : " R " ,
( entry & 0x01 ) ? " P " : " p " ) ;
}
# if BX_SUPPORT_VMX >= 2
2022-07-30 15:26:47 +03:00
void dbg_print_ept_paging_pte ( int level , Bit64u entry , bool mbe )
2010-05-05 00:16:38 +04:00
{
2010-05-06 00:10:15 +04:00
dbg_printf ( " EPT %4s: 0x%08x%08x " , bx_paging_level [ level ] , GET32H ( entry ) , GET32L ( entry ) ) ;
2010-05-05 00:16:38 +04:00
if ( level ! = BX_LEVEL_PTE & & ( entry & 0x80 ) )
dbg_printf ( " PS " ) ;
else
dbg_printf ( " " ) ;
2022-07-30 15:26:47 +03:00
if ( mbe )
dbg_printf ( " %s " , ( entry & 0x400 ) ? " XU " : " xu " ) ;
2015-03-02 00:04:34 +03:00
dbg_printf ( " %s %s %s " ,
2022-07-30 15:26:47 +03:00
( entry & 0x04 ) ? " X " : " x " ,
2010-05-05 00:16:38 +04:00
( entry & 0x02 ) ? " W " : " w " ,
( entry & 0x01 ) ? " R " : " r " ) ;
2015-03-02 00:04:34 +03:00
if ( level = = BX_LEVEL_PTE | | ( entry & 0x80 ) ) {
dbg_printf ( " %s %s \n " ,
( entry & 0x40 ) ? " IGNORE_PAT " : " ignore_pat " ,
get_memtype_name ( BxMemtype ( ( entry > > 3 ) & 0x7 ) ) ) ;
}
else {
dbg_printf ( " \n " ) ;
}
2010-05-05 00:16:38 +04:00
}
# endif
# endif // BX_DEBUGGER
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
2022-08-22 07:20:47 +03:00
bool BX_CPU_C : : dbg_translate_guest_physical_ept ( bx_phy_address guest_paddr , bx_phy_address * phy , bool verbose )
2010-04-07 21:12:17 +04:00
{
VMCS_CACHE * vm = & BX_CPU_THIS_PTR vmcs ;
bx_phy_address pt_address = LPFOf ( vm - > eptptr ) ;
2010-04-08 19:50:39 +04:00
Bit64u offset_mask = BX_CONST64 ( 0x0000ffffffffffff ) ;
2010-04-07 21:12:17 +04:00
for ( int level = 3 ; level > = 0 ; - - level ) {
Bit64u pte ;
pt_address + = ( ( guest_paddr > > ( 9 + 9 * level ) ) & 0xff8 ) ;
2010-04-08 19:50:39 +04:00
offset_mask > > = 9 ;
2010-05-05 00:16:38 +04:00
BX_MEM ( 0 ) - > readPhysicalPage ( BX_CPU_THIS , pt_address , 8 , & pte ) ;
# if BX_DEBUGGER
if ( verbose )
2022-07-30 15:26:47 +03:00
dbg_print_ept_paging_pte ( level , pte , SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_MBE_CTRL ) ) ;
2010-05-05 00:16:38 +04:00
# endif
2010-04-07 21:12:17 +04:00
switch ( pte & 7 ) {
case BX_EPT_ENTRY_NOT_PRESENT :
case BX_EPT_ENTRY_WRITE_ONLY :
case BX_EPT_ENTRY_WRITE_EXECUTE :
return 0 ;
}
if ( pte & BX_PAGING_PHY_ADDRESS_RESERVED_BITS )
return 0 ;
pt_address = bx_phy_address ( pte & BX_CONST64 ( 0x000ffffffffff000 ) ) ;
2010-04-13 21:56:50 +04:00
if ( level = = BX_LEVEL_PTE ) break ;
2010-04-07 21:12:17 +04:00
if ( pte & 0x80 ) {
2014-08-31 23:22:41 +04:00
if ( level > ( BX_LEVEL_PDE + ! ! is_cpu_extension_supported ( BX_ISA_1G_PAGES ) ) )
2010-04-13 21:56:50 +04:00
return 0 ;
2010-04-07 21:12:17 +04:00
pt_address & = BX_CONST64 ( 0x000fffffffffe000 ) ;
if ( pt_address & offset_mask ) return 0 ;
break ;
}
}
* phy = pt_address + ( bx_phy_address ) ( guest_paddr & offset_mask ) ;
return 1 ;
}
# endif
2022-08-22 07:20:47 +03:00
# if BX_SUPPORT_SVM
bool BX_CPU_C : : dbg_translate_guest_physical_npt ( bx_phy_address guest_paddr , bx_phy_address * phy , bool verbose )
{
// Nested page table walk works in the same manner as the standard page walk.
return dbg_xlate_linear2phy ( guest_paddr , phy , NULL , verbose , true ) ;
}
# endif
bool BX_CPU_C : : dbg_xlate_linear2phy ( bx_address laddr , bx_phy_address * phy , bx_address * lpf_mask , bool verbose , bool nested_walk )
2001-04-10 05:04:59 +04:00
{
2007-10-09 00:45:30 +04:00
bx_phy_address paddress ;
2018-08-14 11:09:09 +03:00
bx_address offset_mask = 0xfff ;
2005-06-15 00:55:57 +04:00
2012-01-19 10:38:22 +04:00
# if BX_SUPPORT_X86_64
2012-01-20 00:01:32 +04:00
if ( ! long_mode ( ) ) laddr & = 0xffffffff ;
2012-01-19 10:38:22 +04:00
# endif
2011-06-24 17:05:36 +04:00
if ( ! BX_CPU_THIS_PTR cr0 . get_PG ( ) ) {
paddress = ( bx_phy_address ) laddr ;
2004-10-21 22:20:40 +04:00
}
2011-06-24 17:05:36 +04:00
else {
bx_phy_address pt_address = BX_CPU_THIS_PTR cr3 & BX_CR3_PAGING_MASK ;
2022-08-22 07:20:47 +03:00
# if BX_SUPPORT_SVM
if ( nested_walk ) {
pt_address = LPFOf ( BX_CPU_THIS_PTR vmcb . ctrls . ncr3 ) ;
}
# endif
2011-06-24 17:05:36 +04:00
2009-06-15 13:30:56 +04:00
# if BX_CPU_LEVEL >= 6
2011-06-24 17:05:36 +04:00
if ( BX_CPU_THIS_PTR cr4 . get_PAE ( ) ) {
2018-08-14 11:09:09 +03:00
offset_mask = BX_CONST64 ( 0x0000ffffffffffff ) ;
2011-06-24 17:05:36 +04:00
int level = 3 ;
if ( ! long_mode ( ) ) {
2012-01-17 22:20:55 +04:00
pt_address = BX_CPU_THIS_PTR PDPTR_CACHE . entry [ ( laddr > > 30 ) & 3 ] ;
2018-08-14 11:09:09 +03:00
if ( ! ( pt_address & 0x1 ) ) {
offset_mask = 0x3fffffff ;
2012-01-17 22:20:55 +04:00
goto page_fault ;
2018-08-14 11:09:09 +03:00
}
2011-06-24 17:05:36 +04:00
offset_mask > > = 18 ;
2018-08-14 11:09:09 +03:00
pt_address & = BX_CONST64 ( 0x000ffffffffff000 ) ;
2011-06-24 17:05:36 +04:00
level = 1 ;
}
2010-04-14 19:41:57 +04:00
2011-06-24 17:05:36 +04:00
for ( ; level > = 0 ; - - level ) {
Bit64u pte ;
pt_address + = ( ( laddr > > ( 9 + 9 * level ) ) & 0xff8 ) ;
offset_mask > > = 9 ;
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
2011-06-24 17:05:36 +04:00
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) ) {
2022-08-22 07:20:47 +03:00
if ( ! dbg_translate_guest_physical_ept ( pt_address , & pt_address , verbose ) )
2011-06-24 17:05:36 +04:00
goto page_fault ;
}
2010-04-07 21:12:17 +04:00
}
2022-08-22 07:20:47 +03:00
# endif
# if BX_SUPPORT_SVM
if ( ! nested_walk & & BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
if ( ! dbg_translate_guest_physical_npt ( pt_address , & pt_address , verbose ) )
goto page_fault ;
}
2010-04-07 21:12:17 +04:00
# endif
2011-06-24 17:05:36 +04:00
BX_MEM ( 0 ) - > readPhysicalPage ( BX_CPU_THIS , pt_address , 8 , & pte ) ;
2010-05-05 00:16:38 +04:00
# if BX_DEBUGGER
2011-06-24 17:05:36 +04:00
if ( verbose )
dbg_print_paging_pte ( level , pte ) ;
2010-05-05 00:16:38 +04:00
# endif
2011-06-24 17:05:36 +04:00
if ( ! ( pte & 1 ) )
goto page_fault ;
if ( pte & BX_PAGING_PHY_ADDRESS_RESERVED_BITS )
goto page_fault ;
pt_address = bx_phy_address ( pte & BX_CONST64 ( 0x000ffffffffff000 ) ) ;
if ( level = = BX_LEVEL_PTE ) break ;
if ( pte & 0x80 ) {
2012-02-14 00:06:04 +04:00
// large page
2012-01-19 10:38:22 +04:00
pt_address & = BX_CONST64 ( 0x000fffffffffe000 ) ;
2012-02-14 00:06:04 +04:00
if ( pt_address & offset_mask )
goto page_fault ;
2014-08-31 23:22:41 +04:00
if ( is_cpu_extension_supported ( BX_ISA_1G_PAGES ) & & level = = BX_LEVEL_PDPTE ) break ;
2012-02-14 00:06:04 +04:00
if ( level = = BX_LEVEL_PDE ) break ;
2011-06-24 17:05:36 +04:00
goto page_fault ;
2009-09-26 17:50:09 +04:00
}
2005-02-16 21:58:48 +03:00
}
2011-06-24 17:05:36 +04:00
paddress = pt_address + ( bx_phy_address ) ( laddr & offset_mask ) ;
2005-02-16 21:58:48 +03:00
}
2011-06-24 17:05:36 +04:00
else // not PAE
2005-11-17 20:52:00 +03:00
# endif
2011-06-24 17:05:36 +04:00
{
2018-08-14 11:09:09 +03:00
offset_mask = 0xfff ;
2011-06-24 17:05:36 +04:00
for ( int level = 1 ; level > = 0 ; - - level ) {
Bit32u pte ;
pt_address + = ( ( laddr > > ( 10 + 10 * level ) ) & 0xffc ) ;
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
2011-06-24 17:05:36 +04:00
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) ) {
2022-08-22 07:20:47 +03:00
if ( ! dbg_translate_guest_physical_ept ( pt_address , & pt_address , verbose ) )
2011-06-24 17:05:36 +04:00
goto page_fault ;
}
2010-04-07 21:12:17 +04:00
}
2022-08-22 07:20:47 +03:00
# endif
# if BX_SUPPORT_SVM
if ( ! nested_walk & & BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
if ( ! dbg_translate_guest_physical_npt ( pt_address , & pt_address , verbose ) )
goto page_fault ;
}
2010-04-07 21:12:17 +04:00
# endif
2011-06-24 17:05:36 +04:00
BX_MEM ( 0 ) - > readPhysicalPage ( BX_CPU_THIS , pt_address , 4 , & pte ) ;
2010-05-05 00:16:38 +04:00
# if BX_DEBUGGER
2011-06-24 17:05:36 +04:00
if ( verbose )
dbg_print_paging_pte ( level , pte ) ;
2010-05-05 00:16:38 +04:00
# endif
2022-08-23 21:38:00 +03:00
if ( ! ( pte & 1 ) )
2011-06-24 17:05:36 +04:00
goto page_fault ;
pt_address = pte & 0xfffff000 ;
2010-05-25 22:52:01 +04:00
# if BX_CPU_LEVEL >= 6
2011-06-24 17:05:36 +04:00
if ( level = = BX_LEVEL_PDE & & ( pte & 0x80 ) ! = 0 & & BX_CPU_THIS_PTR cr4 . get_PSE ( ) ) {
offset_mask = 0x3fffff ;
pt_address = pte & 0xffc00000 ;
2009-10-25 01:00:43 +04:00
# if BX_PHY_ADDRESS_WIDTH > 32
2011-06-24 17:05:36 +04:00
pt_address + = ( ( bx_phy_address ) ( pte & 0x003fe000 ) ) < < 19 ;
2009-10-25 01:00:43 +04:00
# endif
2011-06-24 17:05:36 +04:00
break ;
}
2010-05-25 22:52:01 +04:00
# endif
2011-06-24 17:05:36 +04:00
}
paddress = pt_address + ( bx_phy_address ) ( laddr & offset_mask ) ;
2005-02-16 21:58:48 +03:00
}
2005-01-20 22:37:43 +03:00
}
2010-04-07 21:12:17 +04:00
# if BX_SUPPORT_VMX >= 2
if ( BX_CPU_THIS_PTR in_vmx_guest ) {
if ( SECONDARY_VMEXEC_CONTROL ( VMX_VM_EXEC_CTRL3_EPT_ENABLE ) ) {
2022-08-22 07:20:47 +03:00
if ( ! dbg_translate_guest_physical_ept ( paddress , & paddress , verbose ) )
2010-04-07 21:12:17 +04:00
goto page_fault ;
}
}
# endif
2022-08-22 07:20:47 +03:00
# if BX_SUPPORT_SVM
if ( ! nested_walk & & BX_CPU_THIS_PTR in_svm_guest & & SVM_NESTED_PAGING_ENABLED ) {
if ( ! dbg_translate_guest_physical_npt ( paddress , & paddress , verbose ) )
goto page_fault ;
}
# endif
2010-04-07 21:12:17 +04:00
2018-08-14 11:09:09 +03:00
if ( lpf_mask )
* lpf_mask = offset_mask ;
2011-07-19 00:22:59 +04:00
* phy = A20ADDR ( paddress ) ;
2006-06-17 16:09:55 +04:00
return 1 ;
2001-04-10 05:04:59 +04:00
page_fault :
2018-08-14 11:09:09 +03:00
if ( lpf_mask )
* lpf_mask = offset_mask ;
2001-04-10 05:04:59 +04:00
* phy = 0 ;
2006-06-17 16:09:55 +04:00
return 0 ;
2001-04-10 05:04:59 +04:00
}
2021-01-02 16:43:10 +03:00
2019-12-20 10:42:07 +03:00
int BX_CPU_C : : access_write_linear ( bx_address laddr , unsigned len , unsigned curr_pl , unsigned xlate_rw , Bit32u ac_mask , void * data )
2001-04-10 05:04:59 +04:00
{
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
2020-02-02 10:25:00 +03:00
BX_ASSERT ( xlate_rw = = BX_WRITE | | xlate_rw = = BX_SHADOW_STACK_WRITE ) ;
# else
BX_ASSERT ( xlate_rw = = BX_WRITE ) ;
2019-12-20 10:42:07 +03:00
# endif
2007-12-27 02:07:44 +03:00
Bit32u pageOffset = PAGE_OFFSET ( laddr ) ;
2001-04-10 05:04:59 +04:00
2021-01-30 11:35:35 +03:00
bool user = ( curr_pl = = 3 ) ;
2015-09-23 22:25:07 +03:00
2019-12-09 21:37:02 +03:00
bx_TLB_entry * tlbEntry = BX_DTLB_ENTRY_OF ( laddr , 0 ) ;
2013-01-27 23:27:30 +04:00
2014-07-03 10:40:42 +04:00
# if BX_SUPPORT_X86_64
if ( ! IsCanonical ( laddr ) ) {
BX_ERROR ( ( " access_write_linear(): canonical failure " ) ) ;
return - 1 ;
}
# endif
# if BX_CPU_LEVEL >= 4 && BX_SUPPORT_ALIGNMENT_CHECK
2015-09-23 22:25:07 +03:00
if ( BX_CPU_THIS_PTR alignment_check ( ) & & user ) {
2014-07-03 10:40:42 +04:00
if ( pageOffset & ac_mask ) {
BX_ERROR ( ( " access_write_linear(): #AC misaligned access " ) ) ;
exception ( BX_AC_EXCEPTION , 0 ) ;
}
}
# endif
2010-03-19 20:00:05 +03:00
/* check for reference across multiple pages */
if ( ( pageOffset + len ) < = 4096 ) {
// Access within single page.
2019-12-20 10:42:07 +03:00
BX_CPU_THIS_PTR address_xlation . paddress1 = translate_linear ( tlbEntry , laddr , user , xlate_rw ) ;
2010-03-19 20:00:05 +03:00
BX_CPU_THIS_PTR address_xlation . pages = 1 ;
2015-02-23 00:26:26 +03:00
# if BX_SUPPORT_MEMTYPE
BX_CPU_THIS_PTR address_xlation . memtype1 = tlbEntry - > get_memtype ( ) ;
# endif
2008-03-29 21:18:08 +03:00
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr , BX_CPU_THIS_PTR address_xlation . paddress1 ,
2019-12-20 10:42:07 +03:00
len , tlbEntry - > get_memtype ( ) , xlate_rw , ( Bit8u * ) data ) ;
2008-04-19 17:21:23 +04:00
2010-03-19 20:00:05 +03:00
access_write_physical ( BX_CPU_THIS_PTR address_xlation . paddress1 , len , data ) ;
2012-06-06 18:01:45 +04:00
# if BX_X86_DEBUGGER
2019-12-20 10:42:07 +03:00
hwbreakpoint_match ( laddr , len , xlate_rw ) ;
2012-06-06 18:01:45 +04:00
# endif
2008-03-29 21:18:08 +03:00
}
else {
2010-03-19 20:00:05 +03:00
// access across 2 pages
BX_CPU_THIS_PTR address_xlation . len1 = 4096 - pageOffset ;
2011-04-24 00:39:27 +04:00
BX_CPU_THIS_PTR address_xlation . len2 = len - BX_CPU_THIS_PTR address_xlation . len1 ;
BX_CPU_THIS_PTR address_xlation . pages = 2 ;
2010-03-19 20:00:05 +03:00
bx_address laddr2 = laddr + BX_CPU_THIS_PTR address_xlation . len1 ;
2011-05-27 12:50:38 +04:00
# if BX_SUPPORT_X86_64
if ( ! long64_mode ( ) ) laddr2 & = 0xffffffff ; /* handle linear address wrap in legacy mode */
2013-12-22 01:56:55 +04:00
else {
if ( ! IsCanonical ( laddr2 ) ) {
BX_ERROR ( ( " access_write_linear(): canonical failure for second half of page split access " ) ) ;
return - 1 ;
}
}
2011-05-27 12:50:38 +04:00
# endif
2013-12-22 01:56:55 +04:00
2019-12-09 21:37:02 +03:00
bx_TLB_entry * tlbEntry2 = BX_DTLB_ENTRY_OF ( laddr2 , 0 ) ;
2015-02-19 23:23:08 +03:00
2019-12-20 10:42:07 +03:00
BX_CPU_THIS_PTR address_xlation . paddress1 = translate_linear ( tlbEntry , laddr , user , xlate_rw ) ;
BX_CPU_THIS_PTR address_xlation . paddress2 = translate_linear ( tlbEntry2 , laddr2 , user , xlate_rw ) ;
2015-02-23 00:26:26 +03:00
# if BX_SUPPORT_MEMTYPE
BX_CPU_THIS_PTR address_xlation . memtype1 = tlbEntry - > get_memtype ( ) ;
BX_CPU_THIS_PTR address_xlation . memtype2 = tlbEntry2 - > get_memtype ( ) ;
# endif
2008-03-29 21:18:08 +03:00
# ifdef BX_LITTLE_ENDIAN
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr , BX_CPU_THIS_PTR address_xlation . paddress1 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len1 , tlbEntry - > get_memtype ( ) ,
2019-12-20 10:42:07 +03:00
xlate_rw , ( Bit8u * ) data ) ;
2010-03-19 20:00:05 +03:00
access_write_physical ( BX_CPU_THIS_PTR address_xlation . paddress1 ,
BX_CPU_THIS_PTR address_xlation . len1 , data ) ;
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr2 , BX_CPU_THIS_PTR address_xlation . paddress2 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len2 , tlbEntry2 - > get_memtype ( ) ,
2019-12-20 10:42:07 +03:00
xlate_rw , ( ( Bit8u * ) data ) + BX_CPU_THIS_PTR address_xlation . len1 ) ;
2010-03-19 20:00:05 +03:00
access_write_physical ( BX_CPU_THIS_PTR address_xlation . paddress2 ,
BX_CPU_THIS_PTR address_xlation . len2 ,
( ( Bit8u * ) data ) + BX_CPU_THIS_PTR address_xlation . len1 ) ;
2008-03-29 21:18:08 +03:00
# else // BX_BIG_ENDIAN
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr , BX_CPU_THIS_PTR address_xlation . paddress1 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len1 , tlbEntry - > get_memtype ( ) ,
2019-12-20 10:42:07 +03:00
xlate_rw , ( ( Bit8u * ) data ) + ( len - BX_CPU_THIS_PTR address_xlation . len1 ) ) ;
2010-03-19 20:00:05 +03:00
access_write_physical ( BX_CPU_THIS_PTR address_xlation . paddress1 ,
BX_CPU_THIS_PTR address_xlation . len1 ,
( ( Bit8u * ) data ) + ( len - BX_CPU_THIS_PTR address_xlation . len1 ) ) ;
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr2 , BX_CPU_THIS_PTR address_xlation . paddress2 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len2 , tlbEntry2 - > get_memtype ( ) ,
2019-12-20 10:42:07 +03:00
xlate_rw , ( Bit8u * ) data ) ;
2010-03-19 20:00:05 +03:00
access_write_physical ( BX_CPU_THIS_PTR address_xlation . paddress2 ,
BX_CPU_THIS_PTR address_xlation . len2 , data ) ;
2008-03-29 21:18:08 +03:00
# endif
2012-06-06 18:01:45 +04:00
# if BX_X86_DEBUGGER
2019-12-20 10:42:07 +03:00
hwbreakpoint_match ( laddr , BX_CPU_THIS_PTR address_xlation . len1 , xlate_rw ) ;
hwbreakpoint_match ( laddr2 , BX_CPU_THIS_PTR address_xlation . len2 , xlate_rw ) ;
2012-06-06 18:01:45 +04:00
# endif
2008-03-29 21:18:08 +03:00
}
2013-12-22 01:56:55 +04:00
return 0 ;
2008-03-29 21:18:08 +03:00
}
2014-07-03 10:40:42 +04:00
int BX_CPU_C : : access_read_linear ( bx_address laddr , unsigned len , unsigned curr_pl , unsigned xlate_rw , Bit32u ac_mask , void * data )
2008-03-29 21:18:08 +03:00
{
2019-12-20 10:42:07 +03:00
# if BX_SUPPORT_CET
2020-02-02 10:25:00 +03:00
BX_ASSERT ( xlate_rw = = BX_READ | | xlate_rw = = BX_RW | | xlate_rw = = BX_SHADOW_STACK_READ | | xlate_rw = = BX_SHADOW_STACK_RW ) ;
# else
BX_ASSERT ( xlate_rw = = BX_READ | | xlate_rw = = BX_RW ) ;
2019-12-20 10:42:07 +03:00
# endif
2008-03-30 00:12:11 +03:00
2008-03-29 21:18:08 +03:00
Bit32u pageOffset = PAGE_OFFSET ( laddr ) ;
2021-01-30 11:35:35 +03:00
bool user = ( curr_pl = = 3 ) ;
2015-09-23 22:25:07 +03:00
2014-07-03 10:40:42 +04:00
# if BX_SUPPORT_X86_64
if ( ! IsCanonical ( laddr ) ) {
BX_ERROR ( ( " access_read_linear(): canonical failure " ) ) ;
return - 1 ;
}
# endif
# if BX_CPU_LEVEL >= 4 && BX_SUPPORT_ALIGNMENT_CHECK
2015-09-23 22:25:07 +03:00
if ( BX_CPU_THIS_PTR alignment_check ( ) & & user ) {
2014-07-03 10:40:42 +04:00
if ( pageOffset & ac_mask ) {
BX_ERROR ( ( " access_read_linear(): #AC misaligned access " ) ) ;
exception ( BX_AC_EXCEPTION , 0 ) ;
}
}
# endif
2019-12-09 21:37:02 +03:00
bx_TLB_entry * tlbEntry = BX_DTLB_ENTRY_OF ( laddr , 0 ) ;
2013-01-27 23:27:30 +04:00
2010-03-19 20:00:05 +03:00
/* check for reference across multiple pages */
if ( ( pageOffset + len ) < = 4096 ) {
// Access within single page.
2015-09-23 22:25:07 +03:00
BX_CPU_THIS_PTR address_xlation . paddress1 = translate_linear ( tlbEntry , laddr , user , xlate_rw ) ;
2010-03-19 20:00:05 +03:00
BX_CPU_THIS_PTR address_xlation . pages = 1 ;
2015-02-23 00:26:26 +03:00
# if BX_SUPPORT_MEMTYPE
BX_CPU_THIS_PTR address_xlation . memtype1 = tlbEntry - > get_memtype ( ) ;
# endif
2010-03-19 20:00:05 +03:00
access_read_physical ( BX_CPU_THIS_PTR address_xlation . paddress1 , len , data ) ;
2015-02-19 23:23:08 +03:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr , BX_CPU_THIS_PTR address_xlation . paddress1 , len , tlbEntry - > get_memtype ( ) , xlate_rw , ( Bit8u * ) data ) ;
2012-06-06 18:01:45 +04:00
# if BX_X86_DEBUGGER
hwbreakpoint_match ( laddr , len , xlate_rw ) ;
# endif
2005-02-16 21:58:48 +03:00
}
2001-04-10 05:04:59 +04:00
else {
2010-03-19 20:00:05 +03:00
// access across 2 pages
BX_CPU_THIS_PTR address_xlation . len1 = 4096 - pageOffset ;
2011-04-24 00:39:27 +04:00
BX_CPU_THIS_PTR address_xlation . len2 = len - BX_CPU_THIS_PTR address_xlation . len1 ;
BX_CPU_THIS_PTR address_xlation . pages = 2 ;
2010-03-19 20:00:05 +03:00
bx_address laddr2 = laddr + BX_CPU_THIS_PTR address_xlation . len1 ;
2011-05-27 12:50:38 +04:00
# if BX_SUPPORT_X86_64
if ( ! long64_mode ( ) ) laddr2 & = 0xffffffff ; /* handle linear address wrap in legacy mode */
2013-12-22 01:56:55 +04:00
else {
if ( ! IsCanonical ( laddr2 ) ) {
BX_ERROR ( ( " access_read_linear(): canonical failure for second half of page split access " ) ) ;
return - 1 ;
}
}
2011-05-27 12:50:38 +04:00
# endif
2015-02-19 23:23:08 +03:00
2019-12-09 21:37:02 +03:00
bx_TLB_entry * tlbEntry2 = BX_DTLB_ENTRY_OF ( laddr2 , 0 ) ;
2015-02-19 23:23:08 +03:00
2015-09-23 22:25:07 +03:00
BX_CPU_THIS_PTR address_xlation . paddress1 = translate_linear ( tlbEntry , laddr , user , xlate_rw ) ;
BX_CPU_THIS_PTR address_xlation . paddress2 = translate_linear ( tlbEntry2 , laddr2 , user , xlate_rw ) ;
2015-02-23 00:26:26 +03:00
# if BX_SUPPORT_MEMTYPE
BX_CPU_THIS_PTR address_xlation . memtype1 = tlbEntry - > get_memtype ( ) ;
BX_CPU_THIS_PTR address_xlation . memtype2 = tlbEntry2 - > get_memtype ( ) ;
# endif
Now, when you compile with --enable-guest2host-tlb, non-paged
mode uses the notion of the guest-to-host TLB. This has the
benefit of allowing more uniform and streamlined acceleration
code in access.cc which does not have to check if CR0.PG
is set, eliminating a few instructions per guest access.
Shaved just a little off execution time, as expected.
Also, access_linear now breaks accesses which span two pages,
into two calls the the physical memory routines, when paging
is off, just like it always has for paging on. Besides
being more uniform, this allows the physical memory access
routines to known the complete data item is contained
within a single physical page, and stop reapplying the
A20ADDR() macro to pointers as it increments them.
Perhaps things can be optimized a little more now there too...
I renamed the routines to {read,write}PhysicalPage() as
a reminder that these routines now operate on data
solely within one page.
I also added a little code so that the paging module is
notified when the A20 line is tweaked, so it can dump
whatever mappings it wants to.
2002-09-05 06:31:24 +04:00
# ifdef BX_LITTLE_ENDIAN
2010-03-19 20:00:05 +03:00
access_read_physical ( BX_CPU_THIS_PTR address_xlation . paddress1 ,
BX_CPU_THIS_PTR address_xlation . len1 , data ) ;
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr , BX_CPU_THIS_PTR address_xlation . paddress1 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len1 , tlbEntry - > get_memtype ( ) ,
2013-07-24 22:54:18 +04:00
xlate_rw , ( Bit8u * ) data ) ;
2010-03-19 20:00:05 +03:00
access_read_physical ( BX_CPU_THIS_PTR address_xlation . paddress2 ,
BX_CPU_THIS_PTR address_xlation . len2 ,
( ( Bit8u * ) data ) + BX_CPU_THIS_PTR address_xlation . len1 ) ;
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr2 , BX_CPU_THIS_PTR address_xlation . paddress2 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len2 , tlbEntry2 - > get_memtype ( ) ,
2013-07-24 22:54:18 +04:00
xlate_rw , ( ( Bit8u * ) data ) + BX_CPU_THIS_PTR address_xlation . len1 ) ;
Now, when you compile with --enable-guest2host-tlb, non-paged
mode uses the notion of the guest-to-host TLB. This has the
benefit of allowing more uniform and streamlined acceleration
code in access.cc which does not have to check if CR0.PG
is set, eliminating a few instructions per guest access.
Shaved just a little off execution time, as expected.
Also, access_linear now breaks accesses which span two pages,
into two calls the the physical memory routines, when paging
is off, just like it always has for paging on. Besides
being more uniform, this allows the physical memory access
routines to known the complete data item is contained
within a single physical page, and stop reapplying the
A20ADDR() macro to pointers as it increments them.
Perhaps things can be optimized a little more now there too...
I renamed the routines to {read,write}PhysicalPage() as
a reminder that these routines now operate on data
solely within one page.
I also added a little code so that the paging module is
notified when the A20 line is tweaked, so it can dump
whatever mappings it wants to.
2002-09-05 06:31:24 +04:00
# else // BX_BIG_ENDIAN
2010-03-19 20:00:05 +03:00
access_read_physical ( BX_CPU_THIS_PTR address_xlation . paddress1 ,
BX_CPU_THIS_PTR address_xlation . len1 ,
( ( Bit8u * ) data ) + ( len - BX_CPU_THIS_PTR address_xlation . len1 ) ) ;
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr , BX_CPU_THIS_PTR address_xlation . paddress1 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len1 , tlbEntry - > get_memtype ( ) ,
2013-07-24 22:54:18 +04:00
xlate_rw , ( ( Bit8u * ) data ) + ( len - BX_CPU_THIS_PTR address_xlation . len1 ) ) ;
2010-03-19 20:00:05 +03:00
access_read_physical ( BX_CPU_THIS_PTR address_xlation . paddress2 ,
BX_CPU_THIS_PTR address_xlation . len2 , data ) ;
2012-03-20 22:26:04 +04:00
BX_NOTIFY_LIN_MEMORY_ACCESS ( laddr2 , BX_CPU_THIS_PTR address_xlation . paddress2 ,
2015-02-19 23:23:08 +03:00
BX_CPU_THIS_PTR address_xlation . len2 , tlbEntry2 - > get_memtype ( ) ,
2013-07-24 22:54:18 +04:00
xlate_rw , ( Bit8u * ) data ) ;
2005-02-16 21:58:48 +03:00
# endif
2012-06-06 18:01:45 +04:00
# if BX_X86_DEBUGGER
hwbreakpoint_match ( laddr , BX_CPU_THIS_PTR address_xlation . len1 , xlate_rw ) ;
hwbreakpoint_match ( laddr2 , BX_CPU_THIS_PTR address_xlation . len2 , xlate_rw ) ;
# endif
2005-02-16 21:58:48 +03:00
}
2013-12-22 01:56:55 +04:00
return 0 ;
2001-04-10 05:04:59 +04:00
}
2009-02-17 22:20:47 +03:00
void BX_CPU_C : : access_write_physical ( bx_phy_address paddr , unsigned len , void * data )
{
2011-09-26 23:48:58 +04:00
# if BX_SUPPORT_VMX && BX_SUPPORT_X86_64
2010-03-16 17:51:20 +03:00
if ( is_virtual_apic_page ( paddr ) ) {
VMX_Virtual_Apic_Write ( paddr , len , data ) ;
return ;
}
# endif
2009-02-23 20:09:39 +03:00
# if BX_SUPPORT_APIC
2009-03-09 00:23:40 +03:00
if ( BX_CPU_THIS_PTR lapic . is_selected ( paddr ) ) {
BX_CPU_THIS_PTR lapic . write ( paddr , data , len ) ;
2009-02-17 22:20:47 +03:00
return ;
}
2009-02-23 20:09:39 +03:00
# endif
2009-02-17 22:20:47 +03:00
BX_MEM ( 0 ) - > writePhysicalPage ( BX_CPU_THIS , paddr , len , data ) ;
}
void BX_CPU_C : : access_read_physical ( bx_phy_address paddr , unsigned len , void * data )
{
2011-09-26 23:48:58 +04:00
# if BX_SUPPORT_VMX && BX_SUPPORT_X86_64
2010-03-16 17:51:20 +03:00
if ( is_virtual_apic_page ( paddr ) ) {
2012-10-26 22:43:53 +04:00
paddr = VMX_Virtual_Apic_Read ( paddr , len , data ) ;
2010-03-16 17:51:20 +03:00
}
# endif
2009-02-23 20:09:39 +03:00
# if BX_SUPPORT_APIC
2009-03-09 00:23:40 +03:00
if ( BX_CPU_THIS_PTR lapic . is_selected ( paddr ) ) {
BX_CPU_THIS_PTR lapic . read ( paddr , data , len ) ;
2009-02-17 22:20:47 +03:00
return ;
}
2009-02-23 20:09:39 +03:00
# endif
2009-02-17 22:20:47 +03:00
BX_MEM ( 0 ) - > readPhysicalPage ( BX_CPU_THIS , paddr , len , data ) ;
}
2010-03-16 17:51:20 +03:00
2012-01-17 22:20:55 +04:00
bx_hostpageaddr_t BX_CPU_C : : getHostMemAddr ( bx_phy_address paddr , unsigned rw )
2010-03-16 17:51:20 +03:00
{
2011-09-26 23:48:58 +04:00
# if BX_SUPPORT_VMX && BX_SUPPORT_X86_64
2012-01-17 22:20:55 +04:00
if ( is_virtual_apic_page ( paddr ) )
2010-03-16 17:51:20 +03:00
return 0 ; // Do not allow direct access to virtual apic page
# endif
# if BX_SUPPORT_APIC
2012-01-17 22:20:55 +04:00
if ( BX_CPU_THIS_PTR lapic . is_selected ( paddr ) )
2010-03-16 17:51:20 +03:00
return 0 ; // Vetoed! APIC address space
# endif
2012-01-17 22:20:55 +04:00
return ( bx_hostpageaddr_t ) BX_MEM ( 0 ) - > getHostMemAddr ( BX_CPU_THIS , paddr , rw ) ;
2010-03-16 17:51:20 +03:00
}
2011-07-22 21:46:06 +04:00
# if BX_LARGE_RAMFILE
2021-01-30 11:35:35 +03:00
bool BX_CPU_C : : check_addr_in_tlb_buffers ( const Bit8u * addr , const Bit8u * end )
2011-07-22 21:46:06 +04:00
{
2019-12-13 21:31:43 +03:00
# if BX_SUPPORT_VMX
if ( BX_CPU_THIS_PTR vmcshostptr ) {
2019-12-20 10:42:07 +03:00
if ( ( BX_CPU_THIS_PTR vmcshostptr > = ( const bx_hostpageaddr_t ) addr ) & &
( BX_CPU_THIS_PTR vmcshostptr < ( const bx_hostpageaddr_t ) end ) ) return true ;
2019-12-13 21:31:43 +03:00
}
# endif
2019-12-13 21:47:51 +03:00
# if BX_SUPPORT_SVM
if ( BX_CPU_THIS_PTR vmcbhostptr ) {
if ( ( BX_CPU_THIS_PTR vmcbhostptr > = ( const bx_hostpageaddr_t ) addr ) & &
( BX_CPU_THIS_PTR vmcbhostptr < ( const bx_hostpageaddr_t ) end ) ) return true ;
}
# endif
2019-12-09 21:37:02 +03:00
for ( unsigned tlb_entry_num = 0 ; tlb_entry_num < BX_DTLB_SIZE ; tlb_entry_num + + ) {
bx_TLB_entry * tlbEntry = & BX_CPU_THIS_PTR DTLB . entry [ tlb_entry_num ] ;
2016-05-06 09:57:00 +03:00
if ( tlbEntry - > valid ( ) ) {
2019-12-13 21:31:43 +03:00
if ( ( tlbEntry - > hostPageAddr > = ( const bx_hostpageaddr_t ) addr ) & &
( tlbEntry - > hostPageAddr < ( const bx_hostpageaddr_t ) end ) )
2016-05-06 09:57:00 +03:00
return true ;
}
2011-07-22 21:46:06 +04:00
}
2019-12-09 21:37:02 +03:00
for ( unsigned tlb_entry_num = 0 ; tlb_entry_num < BX_ITLB_SIZE ; tlb_entry_num + + ) {
bx_TLB_entry * tlbEntry = & BX_CPU_THIS_PTR ITLB . entry [ tlb_entry_num ] ;
if ( tlbEntry - > valid ( ) ) {
2019-12-13 21:31:43 +03:00
if ( ( tlbEntry - > hostPageAddr > = ( const bx_hostpageaddr_t ) addr ) & &
( tlbEntry - > hostPageAddr < ( const bx_hostpageaddr_t ) end ) )
2019-12-09 21:37:02 +03:00
return true ;
}
}
2011-07-22 21:46:06 +04:00
return false ;
}
# endif